Full Code of peter-iakovlev/MtProtoKit for AI

master 3c271bb1446b cached
298 files
31.5 MB
831.3k tokens
474 symbols
1 requests
Download .txt
Showing preview only (3,323K chars total). Download the full file or copy to clipboard to get everything.
Repository: peter-iakovlev/MtProtoKit
Branch: master
Commit: 3c271bb1446b
Files: 298
Total size: 31.5 MB

Directory structure:
gitextract_n8bpxe0q/

├── .gitignore
├── MTAes.h
├── MTAes.m
├── MTAtomic.h
├── MTAtomic.m
├── MTBackupAddressSignals.h
├── MTBackupAddressSignals.m
├── MTBag.h
├── MTBag.m
├── MTBindingTempAuthKeyContext.h
├── MTBindingTempAuthKeyContext.m
├── MTConnectionProbing.h
├── MTConnectionProbing.m
├── MTDNS.h
├── MTDNS.m
├── MTDatacenterVerificationData.h
├── MTDatacenterVerificationData.m
├── MTDiscoverConnectionSignals.h
├── MTDiscoverConnectionSignals.m
├── MTDisposable.h
├── MTDisposable.m
├── MTGzip.h
├── MTGzip.m
├── MTNetworkUsageCalculationInfo.h
├── MTNetworkUsageCalculationInfo.m
├── MTNetworkUsageManager.h
├── MTNetworkUsageManager.m
├── MTPKCS.h
├── MTPKCS.m
├── MTProtoKit/
│   ├── Info.plist
│   ├── MTApiEnvironment.h
│   ├── MTApiEnvironment.m
│   ├── MTBadMsgNotificationMessage.h
│   ├── MTBadMsgNotificationMessage.m
│   ├── MTBuffer.h
│   ├── MTBuffer.m
│   ├── MTBufferReader.h
│   ├── MTBufferReader.m
│   ├── MTContext.h
│   ├── MTContext.m
│   ├── MTDatacenterAddress.h
│   ├── MTDatacenterAddress.m
│   ├── MTDatacenterAddressListData.h
│   ├── MTDatacenterAddressListData.m
│   ├── MTDatacenterAddressSet.h
│   ├── MTDatacenterAddressSet.m
│   ├── MTDatacenterAuthAction.h
│   ├── MTDatacenterAuthAction.m
│   ├── MTDatacenterAuthInfo.h
│   ├── MTDatacenterAuthInfo.m
│   ├── MTDatacenterAuthMessageService.h
│   ├── MTDatacenterAuthMessageService.m
│   ├── MTDatacenterSaltInfo.h
│   ├── MTDatacenterSaltInfo.m
│   ├── MTDatacenterTransferAuthAction.h
│   ├── MTDatacenterTransferAuthAction.m
│   ├── MTDestroySessionResponseMessage.h
│   ├── MTDestroySessionResponseMessage.m
│   ├── MTDiscoverDatacenterAddressAction.h
│   ├── MTDiscoverDatacenterAddressAction.m
│   ├── MTDropResponseContext.h
│   ├── MTDropResponseContext.m
│   ├── MTDropRpcResultMessage.h
│   ├── MTDropRpcResultMessage.m
│   ├── MTEncryption.h
│   ├── MTEncryption.m
│   ├── MTExportedAuthorizationData.h
│   ├── MTExportedAuthorizationData.m
│   ├── MTFileBasedKeychain.h
│   ├── MTFileBasedKeychain.m
│   ├── MTFutureSaltsMessage.h
│   ├── MTFutureSaltsMessage.m
│   ├── MTHttpRequestOperation.h
│   ├── MTHttpRequestOperation.m
│   ├── MTIncomingMessage.h
│   ├── MTIncomingMessage.m
│   ├── MTInputStream.h
│   ├── MTInputStream.m
│   ├── MTInternalId.h
│   ├── MTInternalMessageParser.h
│   ├── MTInternalMessageParser.m
│   ├── MTKeychain.h
│   ├── MTKeychain.m
│   ├── MTLogging.h
│   ├── MTLogging.m
│   ├── MTMessage.h
│   ├── MTMessage.m
│   ├── MTMessageEncryptionKey.h
│   ├── MTMessageEncryptionKey.m
│   ├── MTMessageService.h
│   ├── MTMessageTransaction.h
│   ├── MTMessageTransaction.m
│   ├── MTMsgAllInfoMessage.h
│   ├── MTMsgAllInfoMessage.m
│   ├── MTMsgContainerMessage.h
│   ├── MTMsgContainerMessage.m
│   ├── MTMsgDetailedInfoMessage.h
│   ├── MTMsgDetailedInfoMessage.m
│   ├── MTMsgResendReqMessage.h
│   ├── MTMsgResendReqMessage.m
│   ├── MTMsgsAckMessage.h
│   ├── MTMsgsAckMessage.m
│   ├── MTMsgsStateInfoMessage.h
│   ├── MTMsgsStateInfoMessage.m
│   ├── MTMsgsStateReqMessage.h
│   ├── MTMsgsStateReqMessage.m
│   ├── MTNetworkAvailability.h
│   ├── MTNetworkAvailability.m
│   ├── MTNewSessionCreatedMessage.h
│   ├── MTNewSessionCreatedMessage.m
│   ├── MTOutgoingMessage.h
│   ├── MTOutgoingMessage.m
│   ├── MTOutputStream.h
│   ├── MTOutputStream.m
│   ├── MTPingMessage.h
│   ├── MTPingMessage.m
│   ├── MTPongMessage.h
│   ├── MTPongMessage.m
│   ├── MTPreparedMessage.h
│   ├── MTPreparedMessage.m
│   ├── MTProto.h
│   ├── MTProto.m
│   ├── MTProtoKit.h
│   ├── MTQueue.h
│   ├── MTQueue.m
│   ├── MTRequest.h
│   ├── MTRequest.m
│   ├── MTRequestContext.h
│   ├── MTRequestContext.m
│   ├── MTRequestErrorContext.h
│   ├── MTRequestErrorContext.m
│   ├── MTRequestMessageService.h
│   ├── MTRequestMessageService.m
│   ├── MTResPqMessage.h
│   ├── MTResPqMessage.m
│   ├── MTResendMessageService.h
│   ├── MTResendMessageService.m
│   ├── MTRpcError.h
│   ├── MTRpcError.m
│   ├── MTRpcResultMessage.h
│   ├── MTRpcResultMessage.m
│   ├── MTSerialization.h
│   ├── MTServerDhInnerDataMessage.h
│   ├── MTServerDhInnerDataMessage.m
│   ├── MTServerDhParamsMessage.h
│   ├── MTServerDhParamsMessage.m
│   ├── MTSessionInfo.h
│   ├── MTSessionInfo.m
│   ├── MTSetClientDhParamsResponseMessage.h
│   ├── MTSetClientDhParamsResponseMessage.m
│   ├── MTTcpConnection.h
│   ├── MTTcpConnection.m
│   ├── MTTcpConnectionBehaviour.h
│   ├── MTTcpConnectionBehaviour.m
│   ├── MTTcpTransport.h
│   ├── MTTcpTransport.m
│   ├── MTTime.h
│   ├── MTTime.m
│   ├── MTTimeFixContext.h
│   ├── MTTimeFixContext.m
│   ├── MTTimeSyncMessageService.h
│   ├── MTTimeSyncMessageService.m
│   ├── MTTimer.h
│   ├── MTTimer.m
│   ├── MTTransport.h
│   ├── MTTransport.m
│   ├── MTTransportScheme.h
│   ├── MTTransportScheme.m
│   ├── MTTransportTransaction.h
│   └── MTTransportTransaction.m
├── MTProxyConnectivity.h
├── MTProxyConnectivity.m
├── MTRsa.h
├── MTRsa.m
├── MTSignal.h
├── MTSignal.m
├── MTSubscriber.h
├── MTSubscriber.m
├── MTTransportSchemeStats.h
├── MTTransportSchemeStats.m
├── MtProtoKit.xcodeproj/
│   ├── project.pbxproj
│   └── project.xcworkspace/
│       └── contents.xcworkspacedata
├── MtProtoKitDynamic/
│   ├── Info.plist
│   └── MtProtoKitDynamic.h
├── MtProtoKitDynamicTests/
│   ├── Info.plist
│   └── MtProtoKitDynamicTests.m
├── MtProtoKitMac/
│   ├── Info.plist
│   └── MtProtoKitMac.h
├── MtProtoKitMacTests/
│   ├── Info.plist
│   └── MtProtoKitMacTests.m
├── MtProtoKitOSX/
│   ├── MtProtoKitOSX-Info.plist
│   ├── MtProtoKitOSX-Prefix.pch
│   └── en.lproj/
│       └── InfoPlist.strings
├── MtProtoKitStabilityTests/
│   ├── Info.plist
│   └── MtProtoKitStabilityTests.m
├── MtProtoKitTests/
│   ├── Info.plist
│   ├── MtProtoKitTests-Info.plist
│   ├── MtProtoKitTests.m
│   └── en.lproj/
│       └── InfoPlist.strings
├── MtProtoKitiOS/
│   ├── MtProtoKit-Info.plist
│   ├── MtProtoKit-Prefix.pch
│   └── en.lproj/
│       └── InfoPlist.strings
├── PingFoundation.h
├── PingFoundation.m
├── README.md
├── openssl/
│   ├── OSX/
│   │   └── libcrypto.a
│   ├── iOS/
│   │   └── libcrypto.a
│   └── openssl/
│       ├── aes.h
│       ├── asn1.h
│       ├── asn1_mac.h
│       ├── asn1t.h
│       ├── async.h
│       ├── bio.h
│       ├── blowfish.h
│       ├── bn.h
│       ├── buffer.h
│       ├── camellia.h
│       ├── cast.h
│       ├── cmac.h
│       ├── cms.h
│       ├── comp.h
│       ├── conf.h
│       ├── conf_api.h
│       ├── crypto.h
│       ├── ct.h
│       ├── des.h
│       ├── des_old.h
│       ├── dh.h
│       ├── dsa.h
│       ├── dso.h
│       ├── dtls1.h
│       ├── e_os2.h
│       ├── ebcdic.h
│       ├── ec.h
│       ├── ecdh.h
│       ├── ecdsa.h
│       ├── engine.h
│       ├── err.h
│       ├── evp.h
│       ├── hmac.h
│       ├── idea.h
│       ├── kdf.h
│       ├── krb5_asn.h
│       ├── kssl.h
│       ├── lhash.h
│       ├── md2.h
│       ├── md4.h
│       ├── md5.h
│       ├── mdc2.h
│       ├── modes.h
│       ├── obj_mac.h
│       ├── objects.h
│       ├── ocsp.h
│       ├── opensslconf.h
│       ├── opensslv.h
│       ├── ossl_typ.h
│       ├── pem.h
│       ├── pem2.h
│       ├── pkcs12.h
│       ├── pkcs7.h
│       ├── pqueue.h
│       ├── rand.h
│       ├── rc2.h
│       ├── rc4.h
│       ├── rc5.h
│       ├── ripemd.h
│       ├── rsa.h
│       ├── safestack.h
│       ├── seed.h
│       ├── sha.h
│       ├── srp.h
│       ├── srtp.h
│       ├── ssl.h
│       ├── ssl2.h
│       ├── ssl23.h
│       ├── ssl3.h
│       ├── stack.h
│       ├── symhacks.h
│       ├── tls1.h
│       ├── ts.h
│       ├── txt_db.h
│       ├── ui.h
│       ├── ui_compat.h
│       ├── whrlpool.h
│       ├── x509.h
│       ├── x509_vfy.h
│       └── x509v3.h
└── thirdparty/
    ├── AFNetworking/
    │   ├── AFHTTPClient.h
    │   ├── AFHTTPClient.m
    │   ├── AFHTTPRequestOperation.h
    │   ├── AFHTTPRequestOperation.m
    │   ├── AFJSONUtilities.h
    │   ├── AFJSONUtilities.m
    │   ├── AFNetworking.h
    │   ├── AFURLConnectionOperation.h
    │   └── AFURLConnectionOperation.m
    └── AsyncSocket/
        ├── GCDAsyncSocket.h
        └── GCDAsyncSocket.m

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
fastlane/README.md
fastlane/report.xml
fastlane/test_output/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.xcscmblueprint
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
.DS_Store
*.dSYM
*.dSYM.zip
*.ipa
*/xcuserdata/*


================================================
FILE: MTAes.h
================================================
#import <Foundation/Foundation.h>

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTEncryption.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTEncryption.h>
#else
#   import <MTProtoKit/MTEncryption.h>
#endif

void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);


================================================
FILE: MTAes.m
================================================
#import "MTAes.h"

#import <CommonCrypto/CommonCrypto.h>

# define AES_MAXNR 14
# define AES_BLOCK_SIZE 16

#define N_WORDS (AES_BLOCK_SIZE / sizeof(unsigned long))
typedef struct {
    unsigned long data[N_WORDS];
} aes_block_t;

/* XXX: probably some better way to do this */
#if defined(__i386__) || defined(__x86_64__)
# define UNALIGNED_MEMOPS_ARE_FAST 1
#else
# define UNALIGNED_MEMOPS_ARE_FAST 0
#endif

#if UNALIGNED_MEMOPS_ARE_FAST
# define load_block(d, s)        (d) = *(const aes_block_t *)(s)
# define store_block(d, s)       *(aes_block_t *)(d) = (s)
#else
# define load_block(d, s)        memcpy((d).data, (s), AES_BLOCK_SIZE)
# define store_block(d, s)       memcpy((d), (s).data, AES_BLOCK_SIZE)
#endif

void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
    int len;
    size_t n;
    void const *inB;
    void *outB;
    
    unsigned char aesIv[AES_BLOCK_SIZE];
    memcpy(aesIv, iv, AES_BLOCK_SIZE);
    unsigned char ccIv[AES_BLOCK_SIZE];
    memcpy(ccIv, iv + AES_BLOCK_SIZE, AES_BLOCK_SIZE);
    
    assert(((size_t)inBytes | (size_t)outBytes | (size_t)aesIv | (size_t)ccIv) % sizeof(long) ==
           0);
    
    void *tmpInBytes = malloc(length);
    len = length / AES_BLOCK_SIZE;
    inB = inBytes;
    outB = tmpInBytes;
    
    aes_block_t *inp = (aes_block_t *)inB;
    aes_block_t *outp = (aes_block_t *)outB;
    for (n = 0; n < N_WORDS; ++n) {
        outp->data[n] = inp->data[n];
    }
    
    --len;
    inB += AES_BLOCK_SIZE;
    outB += AES_BLOCK_SIZE;
    void const *inBCC = inBytes;
    
    aes_block_t const *iv3p = (aes_block_t *)ccIv;
    
    if (len > 0) {
        while (len) {
            aes_block_t *inp = (aes_block_t *)inB;
            aes_block_t *outp = (aes_block_t *)outB;
            
            for (n = 0; n < N_WORDS; ++n) {
                outp->data[n] = inp->data[n] ^ iv3p->data[n];
            }
            
            iv3p = inBCC;
            --len;
            inBCC += AES_BLOCK_SIZE;
            inB += AES_BLOCK_SIZE;
            outB += AES_BLOCK_SIZE;
        }
    }
    
    size_t realOutLength = 0;
    CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0, key, keyLength, aesIv, tmpInBytes, length, outBytes, length, &realOutLength);
    free(tmpInBytes);
    
    assert(result == kCCSuccess);
    
    len = length / AES_BLOCK_SIZE;
    
    aes_block_t const *ivp = inB;
    aes_block_t *iv2p = (aes_block_t *)ccIv;
    
    inB = inBytes;
    outB = outBytes;
    
    while (len) {
        aes_block_t *inp = (aes_block_t *)inB;
        aes_block_t *outp = (aes_block_t *)outB;
        
        for (n = 0; n < N_WORDS; ++n) {
            outp->data[n] ^= iv2p->data[n];
        }
        ivp = outp;
        iv2p = inp;
        --len;
        inB += AES_BLOCK_SIZE;
        outB += AES_BLOCK_SIZE;
    }
    
    memcpy(iv, ivp->data, AES_BLOCK_SIZE);
    memcpy(iv + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE);
}

void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
    unsigned char aesIv[AES_BLOCK_SIZE];
    memcpy(aesIv, iv, AES_BLOCK_SIZE);
    unsigned char ccIv[AES_BLOCK_SIZE];
    memcpy(ccIv, iv + AES_BLOCK_SIZE, AES_BLOCK_SIZE);
    
    assert(((size_t)inBytes | (size_t)outBytes | (size_t)aesIv | (size_t)ccIv) % sizeof(long) ==
           0);
    
    CCCryptorRef decryptor = NULL;
    CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &decryptor);
    if (decryptor != NULL) {
        int len;
        size_t n;
        
        len = length / AES_BLOCK_SIZE;
        
        aes_block_t *ivp = (aes_block_t *)(aesIv);
        aes_block_t *iv2p = (aes_block_t *)(ccIv);
        
        while (len) {
            aes_block_t tmp;
            aes_block_t *inp = (aes_block_t *)inBytes;
            aes_block_t *outp = (aes_block_t *)outBytes;
            
            for (n = 0; n < N_WORDS; ++n)
                tmp.data[n] = inp->data[n] ^ iv2p->data[n];
            
            size_t dataOutMoved = 0;
            CCCryptorStatus result = CCCryptorUpdate(decryptor, &tmp, AES_BLOCK_SIZE, outBytes, AES_BLOCK_SIZE, &dataOutMoved);
            assert(result == kCCSuccess);
            assert(dataOutMoved == AES_BLOCK_SIZE);
            
            for (n = 0; n < N_WORDS; ++n)
                outp->data[n] ^= ivp->data[n];
            
            ivp = inp;
            iv2p = outp;
            
            inBytes += AES_BLOCK_SIZE;
            outBytes += AES_BLOCK_SIZE;
            
            --len;
        }
        
        memcpy(iv, ivp->data, AES_BLOCK_SIZE);
        memcpy(iv + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE);
        
        CCCryptorRelease(decryptor);
    }
}

static void ctr128_inc(unsigned char *counter)
{
    uint32_t n = 16, c = 1;
    
    do {
        --n;
        c += counter[n];
        counter[n] = (uint8_t)c;
        c >>= 8;
    } while (n);
}

static void ctr128_inc_aligned(unsigned char *counter)
{
    size_t *data, c, d, n;
    const union {
        long one;
        char little;
    } is_endian = {
        1
    };
    
    if (is_endian.little || ((size_t)counter % sizeof(size_t)) != 0) {
        ctr128_inc(counter);
        return;
    }
    
    data = (size_t *)counter;
    c = 1;
    n = 16 / sizeof(size_t);
    do {
        --n;
        d = data[n] += c;
        /* did addition carry? */
        c = ((d - c) ^ d) >> (sizeof(size_t) * 8 - 1);
    } while (n);
}


@interface MTAesCtr () {
    CCCryptorRef _cryptor;
    
    unsigned char _ivec[16] __attribute__((aligned(16)));
    unsigned int _num;
    unsigned char _ecount[16] __attribute__((aligned(16)));
}

@end

@implementation MTAesCtr

- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv decrypt:(bool)decrypt {
    self = [super init];
    if (self != nil) {
        _num = 0;
        memset(_ecount, 0, 16);
        memcpy(_ivec, iv, 16);
        
        CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &_cryptor);
    }
    return self;
}

- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv ecount:(void *)ecount num:(uint32_t)num {
    self = [super init];
    if (self != nil) {
        _num = num;
        memcpy(_ecount, ecount, 16);
        memcpy(_ivec, iv, 16);
        
        CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &_cryptor);
    }
    return self;
}

- (void)dealloc {
    if (_cryptor) {
        CCCryptorRelease(_cryptor);
    }
}

- (uint32_t)num {
    return _num;
}

- (void *)ecount {
    return _ecount;
}

- (void)getIv:(void *)iv {
    memcpy(iv, _ivec, 16);
}

- (void)encryptIn:(const unsigned char *)in out:(unsigned char *)out len:(size_t)len {
    unsigned int n;
    size_t l = 0;
    
    assert(in && out);
    assert(_num < 16);
    
    n = _num;
    
    if (16 % sizeof(size_t) == 0) { /* always true actually */
        do {
            while (n && len) {
                *(out++) = *(in++) ^ _ecount[n];
                --len;
                n = (n + 1) % 16;
            }
            
            if (((size_t)in|(size_t)out|(size_t)_ivec)%sizeof(size_t) != 0)
                break;
            
            while (len >= 16) {
                size_t dataOutMoved;
                CCCryptorUpdate(_cryptor, _ivec, 16, _ecount, 16, &dataOutMoved);
                ctr128_inc_aligned(_ivec);
                for (n = 0; n < 16; n += sizeof(size_t)) {
                    *(size_t *)(out + n) =
                    *(size_t *)(in + n) ^ *(size_t *)(_ecount + n);
                }
                len -= 16;
                out += 16;
                in += 16;
                n = 0;
            }
            if (len) {
                size_t dataOutMoved;
                CCCryptorUpdate(_cryptor, _ivec, 16, _ecount, 16, &dataOutMoved);
                ctr128_inc_aligned(_ivec);
                while (len--) {
                    out[n] = in[n] ^ _ecount[n];
                    ++n;
                }
            }
            _num = n;
            return;
        } while (0);
    }
    /* the rest would be commonly eliminated by x86* compiler */
    
    while (l < len) {
        if (n == 0) {
            size_t dataOutMoved;
            CCCryptorUpdate(_cryptor, _ivec, 16, _ecount, 16, &dataOutMoved);
            ctr128_inc(_ivec);
        }
        out[l] = in[l] ^ _ecount[n];
        ++l;
        n = (n + 1) % 16;
    }
    
    _num = n;
}

@end


================================================
FILE: MTAtomic.h
================================================
#import <Foundation/Foundation.h>

@interface MTAtomic : NSObject

- (instancetype)initWithValue:(id)value;
- (id)swap:(id)newValue;
- (id)value;
- (id)modify:(id (^)(id))f;
- (id)with:(id (^)(id))f;

@end


================================================
FILE: MTAtomic.m
================================================
#import "MTAtomic.h"

#import <libkern/OSAtomic.h>

@interface MTAtomic ()
{
    volatile OSSpinLock _lock;
    id _value;
}

@end

@implementation MTAtomic

- (instancetype)initWithValue:(id)value
{
    self = [super init];
    if (self != nil)
    {
        _value = value;
    }
    return self;
}

- (id)swap:(id)newValue
{
    id previousValue = nil;
    OSSpinLockLock(&_lock);
    previousValue = _value;
    _value = newValue;
    OSSpinLockUnlock(&_lock);
    return previousValue;
}

- (id)value
{
    id previousValue = nil;
    OSSpinLockLock(&_lock);
    previousValue = _value;
    OSSpinLockUnlock(&_lock);
    
    return previousValue;
}

- (id)modify:(id (^)(id))f
{
    id newValue = nil;
    OSSpinLockLock(&_lock);
    newValue = f(_value);
    _value = newValue;
    OSSpinLockUnlock(&_lock);
    return newValue;
}

- (id)with:(id (^)(id))f
{
    id result = nil;
    OSSpinLockLock(&_lock);
    result = f(_value);
    OSSpinLockUnlock(&_lock);
    return result;
}

@end


================================================
FILE: MTBackupAddressSignals.h
================================================
#import <Foundation/Foundation.h>

@class MTSignal;
@class MTContext;

@interface MTBackupAddressSignals : NSObject

+ (MTSignal * _Nonnull)fetchBackupIps:(bool)isTestingEnvironment currentContext:(MTContext * _Nonnull)currentContext additionalSource:(MTSignal * _Nullable)additionalSource phoneNumber:(NSString * _Nullable)phoneNumber;

@end


================================================
FILE: MTBackupAddressSignals.m
================================================
#import "MTBackupAddressSignals.h"

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTSignal.h>
#   import <MTProtoKitDynamic/MTQueue.h>
#   import <MTProtoKitDynamic/MTHttpRequestOperation.h>
#   import <MTProtoKitDynamic/MTEncryption.h>
#   import <MTProtoKitDynamic/MTRequestMessageService.h>
#   import <MTProtoKitDynamic/MTRequest.h>
#   import <MTProtoKitDynamic/MTContext.h>
#   import <MTProtoKitDynamic/MTApiEnvironment.h>
#   import <MTProtoKitDynamic/MTDatacenterAddress.h>
#   import <MTProtoKitDynamic/MTDatacenterAddressSet.h>
#   import <MTProtoKitDynamic/MTProto.h>
#   import <MTProtoKitDynamic/MTSerialization.h>
#   import <MTProtoKitDynamic/MTLogging.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTSignal.h>
#   import <MTProtoKitMac/MTQueue.h>
#   import <MTProtoKitMac/MTHttpRequestOperation.h>
#   import <MTProtoKitMac/MTEncryption.h>
#   import <MTProtoKitMac/MTRequestMessageService.h>
#   import <MTProtoKitMac/MTRequest.h>
#   import <MTProtoKitMac/MTContext.h>
#   import <MTProtoKitMac/MTApiEnvironment.h>
#   import <MTProtoKitMac/MTDatacenterAddress.h>
#   import <MTProtoKitMac/MTDatacenterAddressSet.h>
#   import <MTProtoKitMac/MTProto.h>
#   import <MTProtoKitMac/MTSerialization.h>
#   import <MTProtoKitMac/MTLogging.h>
#else
#   import <MTProtoKit/MTSignal.h>
#   import <MTProtoKit/MTQueue.h>
#   import <MTProtoKit/MTHttpRequestOperation.h>
#   import <MTProtoKit/MTEncryption.h>
#   import <MTProtoKit/MTRequestMessageService.h>
#   import <MTProtoKit/MTRequest.h>
#   import <MTProtoKit/MTContext.h>
#   import <MTProtoKit/MTApiEnvironment.h>
#   import <MTProtoKit/MTDatacenterAddress.h>
#   import <MTProtoKit/MTDatacenterAddressSet.h>
#   import <MTProtoKit/MTProto.h>
#   import <MTProtoKit/MTSerialization.h>
#   import <MTProtoKit/MTLogging.h>
#endif

static NSData *base64_decode(NSString *str) {
    if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) {
        NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
        return data;
    } else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        return [[NSData alloc] initWithBase64Encoding:[str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [str length])]];
#pragma clang diagnostic pop
    }
}

@implementation MTBackupAddressSignals

+ (bool)checkIpData:(MTBackupDatacenterData *)data timestamp:(int32_t)timestamp source:(NSString *)source {
    if (data.timestamp >= timestamp + 60 * 20 || data.expirationDate <= timestamp - 60 * 20) {
        if (MTLogEnabled()) {
            MTLog(@"[Backup address fetch: backup config from %@ validity interval %d ... %d does not include current %d]", source, data.timestamp, data.expirationDate, timestamp);
        }
        return false;
    } else {
        return true;
    }
}

+ (MTSignal *)fetchBackupIpsResolveGoogle:(bool)isTesting phoneNumber:(NSString *)phoneNumber currentContext:(MTContext *)currentContext {
    NSArray *hosts = @[
        @"google.com",
        @"www.google.com",
        @"google.ru"
    ];
    NSDictionary *headers = @{@"Host": @"dns.google.com"};
    
    NSMutableArray *signals = [[NSMutableArray alloc] init];
    for (NSString *host in hosts) {
        MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16", host, isTesting ? @"tapv2.stel.com" : @"apv2.stel.com"]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            if ([dict respondsToSelector:@selector(objectForKey:)]) {
                NSArray *answer = dict[@"Answer"];
                NSMutableArray *strings = [[NSMutableArray alloc] init];
                if ([answer respondsToSelector:@selector(objectAtIndex:)]) {
                    for (NSDictionary *value in answer) {
                        if ([value respondsToSelector:@selector(objectForKey:)]) {
                            NSString *part = value[@"data"];
                            if ([part respondsToSelector:@selector(characterAtIndex:)]) {
                                [strings addObject:part];
                            }
                        }
                    }
                    [strings sortUsingComparator:^NSComparisonResult(NSString *lhs, NSString *rhs) {
                        if (lhs.length > rhs.length) {
                            return NSOrderedAscending;
                        } else {
                            return NSOrderedDescending;
                        }
                    }];
                    
                    NSString *finalString = @"";
                    for (NSString *string in strings) {
                        finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]];
                    }
                    
                    NSData *result = base64_decode(finalString);
                    NSMutableData *finalData = [[NSMutableData alloc] initWithData:result];
                    [finalData setLength:256];
                    MTBackupDatacenterData *datacenterData = MTIPDataDecode(finalData, phoneNumber);
                    if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveGoogle"]) {
                        return [MTSignal single:datacenterData];
                    }
                }
            }
            return [MTSignal complete];
        }] catch:^MTSignal *(__unused id error) {
            return [MTSignal complete];
        }];
        if (signals.count != 0) {
            signal = [signal delay:signals.count onQueue:[[MTQueue alloc] init]];
        }
        [signals addObject:signal];
    }
    
    return [[MTSignal mergeSignals:signals] take:1];
}

+ (MTSignal *)fetchConfigFromAddress:(MTBackupDatacenterAddress *)address currentContext:(MTContext *)currentContext {
    MTApiEnvironment *apiEnvironment = [currentContext.apiEnvironment copy];
    
    NSMutableDictionary *datacenterAddressOverrides = [[NSMutableDictionary alloc] init];
    
    datacenterAddressOverrides[@(address.datacenterId)] = [[MTDatacenterAddress alloc] initWithIp:address.ip port:(uint16_t)address.port preferForMedia:false restrictToTcp:false cdn:false preferForProxy:false secret:address.secret];
    apiEnvironment.datacenterAddressOverrides = datacenterAddressOverrides;
    
    apiEnvironment.apiId = currentContext.apiEnvironment.apiId;
    apiEnvironment.layer = currentContext.apiEnvironment.layer;
    apiEnvironment = [apiEnvironment withUpdatedLangPackCode:currentContext.apiEnvironment.langPackCode];
    apiEnvironment.disableUpdates = true;
    apiEnvironment.langPack = currentContext.apiEnvironment.langPack;
    
    MTContext *context = [[MTContext alloc] initWithSerialization:currentContext.serialization apiEnvironment:apiEnvironment isTestingEnvironment:currentContext.isTestingEnvironment useTempAuthKeys:address.datacenterId != 0 ? currentContext.useTempAuthKeys : false];
    
    if (address.datacenterId != 0) {
        //context.keychain = currentContext.keychain;
    }
    
    MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:address.datacenterId usageCalculationInfo:nil];
    if (address.datacenterId != 0) {
        mtProto.useTempAuthKeys = currentContext.useTempAuthKeys;
    }
    MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
    [mtProto addMessageService:requestService];
    
    [mtProto resume];
    
    MTRequest *request = [[MTRequest alloc] init];
    
    NSData *getConfigData = nil;
    MTRequestDatacenterAddressListParser responseParser = [currentContext.serialization requestDatacenterAddressWithData:&getConfigData];
    
    [request setPayload:getConfigData metadata:@"getConfig" shortMetadata:@"getConfig" responseParser:responseParser];
    
    __weak MTContext *weakCurrentContext = currentContext;
    return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
        [request setCompleted:^(MTDatacenterAddressListData *result, __unused NSTimeInterval completionTimestamp, id error)
         {
             if (error == nil) {
                 __strong MTContext *strongCurrentContext = weakCurrentContext;
                 if (strongCurrentContext != nil) {
                     [result.addressList enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, NSArray *list, __unused BOOL *stop) {
                         MTDatacenterAddressSet *addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:list];
                         
                         MTDatacenterAddressSet *currentAddressSet = [context addressSetForDatacenterWithId:[nDatacenterId integerValue]];
                         
                         if (currentAddressSet == nil || ![addressSet isEqual:currentAddressSet])
                         {
                             if (MTLogEnabled()) {
                                 MTLog(@"[Backup address fetch: updating datacenter %d address set to %@]", [nDatacenterId intValue], addressSet);
                             }
                             
                             [strongCurrentContext updateAddressSetForDatacenterWithId:[nDatacenterId integerValue] addressSet:addressSet forceUpdateSchemes:true];
                             [subscriber putNext:@true];
                             [subscriber putCompletion];
                         }
                     }];
                 }
             } else {
                 [subscriber putCompletion];
             }
         }];
        
        [requestService addRequest:request];
        
        id requestId = request.internalId;
        return [[MTBlockDisposable alloc] initWithBlock:^{
            [requestService removeRequestByInternalId:requestId];
            [mtProto pause];
        }];
    }];
}

+ (MTSignal * _Nonnull)fetchBackupIps:(bool)isTestingEnvironment currentContext:(MTContext * _Nonnull)currentContext additionalSource:(MTSignal * _Nullable)additionalSource phoneNumber:(NSString * _Nullable)phoneNumber {
    NSMutableArray *signals = [[NSMutableArray alloc] init];
    [signals addObject:[self fetchBackupIpsResolveGoogle:isTestingEnvironment phoneNumber:phoneNumber currentContext:currentContext]];
    if (additionalSource != nil) {
        [signals addObject:additionalSource];
    }
    
    return [[[MTSignal mergeSignals:signals] take:1] mapToSignal:^MTSignal *(MTBackupDatacenterData *data) {
        if (data != nil && data.addressList.count != 0) {
            NSMutableArray *signals = [[NSMutableArray alloc] init];
            NSTimeInterval delay = 0.0;
            for (MTBackupDatacenterAddress *address in data.addressList) {
                MTSignal *signal = [self fetchConfigFromAddress:address currentContext:currentContext];
                if (delay > DBL_EPSILON) {
                    signal = [signal delay:delay onQueue:[[MTQueue alloc] init]];
                }
                [signals addObject:signal];
                delay += 5.0;
            }
            return [[MTSignal mergeSignals:signals] take:1];
        }
        return [MTSignal complete];
    }];
}

@end


================================================
FILE: MTBag.h
================================================
#import <Foundation/Foundation.h>

@interface MTBag : NSObject

- (NSInteger)addItem:(id)item;
- (void)enumerateItems:(void (^)(id))block;
- (void)removeItem:(NSInteger)key;
- (bool)isEmpty;
- (NSArray *)copyItems;

@end


================================================
FILE: MTBag.m
================================================
#import "MTBag.h"

@interface MTBag ()
{
    NSInteger _nextKey;
    NSMutableArray *_items;
    NSMutableArray *_itemKeys;
}

@end

@implementation MTBag

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        _items = [[NSMutableArray alloc] init];
        _itemKeys = [[NSMutableArray alloc] init];
    }
    return self;
}

- (NSInteger)addItem:(id)item
{
    if (item == nil)
        return -1;
    
    NSInteger key = _nextKey;
    [_items addObject:item];
    [_itemKeys addObject:@(key)];
    _nextKey++;
    
    return key;
}

- (void)enumerateItems:(void (^)(id))block
{
    if (block)
    {
        for (id item in _items)
        {
            block(item);
        }
    }
}

- (void)removeItem:(NSInteger)key
{
    NSUInteger index = 0;
    for (NSNumber *itemKey in _itemKeys)
    {
        if ([itemKey integerValue] == key)
        {
            [_items removeObjectAtIndex:index];
            [_itemKeys removeObjectAtIndex:index];
            break;
        }
        index++;
    }
}

- (bool)isEmpty
{
    return _items.count == 0;
}

- (NSArray *)copyItems
{
    return [[NSArray alloc] initWithArray:_items];
}

@end


================================================
FILE: MTBindingTempAuthKeyContext.h
================================================
#import <Foundation/Foundation.h>

@interface MTBindingTempAuthKeyContext : NSObject

@property (nonatomic, readonly) int64_t messageId;
@property (nonatomic, readonly) int32_t messageSeqNo;
@property (nonatomic, strong, readonly) id transactionId;

- (instancetype)initWithMessageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo transactionId:(id)transactionId;

@end


================================================
FILE: MTBindingTempAuthKeyContext.m
================================================
#import "MTBindingTempAuthKeyContext.h"

@implementation MTBindingTempAuthKeyContext

- (instancetype)initWithMessageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo transactionId:(id)transactionId
{
    self = [super init];
    if (self != nil)
    {
        _messageId = messageId;
        _messageSeqNo = messageSeqNo;
        _transactionId = transactionId;
    }
    return self;
}

@end


================================================
FILE: MTConnectionProbing.h
================================================
#import <Foundation/Foundation.h>

@class MTSignal;
@class MTContext;
@class MTSocksProxySettings;

@interface MTConnectionProbing : NSObject

+ (MTSignal *)probeProxyWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId settings:(MTSocksProxySettings *)settings;

@end


================================================
FILE: MTConnectionProbing.m
================================================
#import "MTConnectionProbing.h"

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTSignal.h>
#   import <MTProtoKitDynamic/MTQueue.h>
#   import <MTProtoKitDynamic/MTAtomic.h>
#   import <MTProtoKitDynamic/MTHttpRequestOperation.h>
#   import <MTProtoKitDynamic/MTEncryption.h>
#   import <MTProtoKitDynamic/MTRequestMessageService.h>
#   import <MTProtoKitDynamic/MTRequest.h>
#   import <MTProtoKitDynamic/MTContext.h>
#   import <MTProtoKitDynamic/MTApiEnvironment.h>
#   import <MTProtoKitDynamic/MTDatacenterAddress.h>
#   import <MTProtoKitDynamic/MTDatacenterAddressSet.h>
#   import <MTProtoKitDynamic/MTProto.h>
#   import <MTProtoKitDynamic/MTSerialization.h>
#   import <MTProtoKitDynamic/MTLogging.h>
#   import <MTProtoKitDynamic/MTProxyConnectivity.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTSignal.h>
#   import <MTProtoKitMac/MTQueue.h>
#   import <MTProtoKitMac/MTAtomic.h>
#   import <MTProtoKitMac/MTHttpRequestOperation.h>
#   import <MTProtoKitMac/MTEncryption.h>
#   import <MTProtoKitMac/MTRequestMessageService.h>
#   import <MTProtoKitMac/MTRequest.h>
#   import <MTProtoKitMac/MTContext.h>
#   import <MTProtoKitMac/MTApiEnvironment.h>
#   import <MTProtoKitMac/MTDatacenterAddress.h>
#   import <MTProtoKitMac/MTDatacenterAddressSet.h>
#   import <MTProtoKitMac/MTProto.h>
#   import <MTProtoKitMac/MTSerialization.h>
#   import <MTProtoKitMac/MTLogging.h>
#   import <MTProtoKitMac/MTProxyConnectivity.h>
#else
#   import <MTProtoKit/MTSignal.h>
#   import <MTProtoKit/MTQueue.h>
#   import <MTProtoKit/MTAtomic.h>
#   import <MTProtoKit/MTHttpRequestOperation.h>
#   import <MTProtoKit/MTEncryption.h>
#   import <MTProtoKit/MTRequestMessageService.h>
#   import <MTProtoKit/MTRequest.h>
#   import <MTProtoKit/MTContext.h>
#   import <MTProtoKit/MTApiEnvironment.h>
#   import <MTProtoKit/MTDatacenterAddress.h>
#   import <MTProtoKit/MTDatacenterAddressSet.h>
#   import <MTProtoKit/MTProto.h>
#   import <MTProtoKit/MTSerialization.h>
#   import <MTProtoKit/MTLogging.h>
#   import <MTProtoKit/MTProxyConnectivity.h>
#endif

#import "PingFoundation.h"

@interface MTPingHelper : NSObject <PingFoundationDelegate> {
    void (^_success)();
    PingFoundation *_ping;
}

@end

@implementation MTPingHelper
    
+ (void)runLoopThreadFunc {
    while (true) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
    }
}
    
+ (NSThread *)runLoopThread {
    static NSThread *thread = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        thread = [[NSThread alloc] initWithTarget:self selector:@selector(runLoopThreadFunc) object:nil];
        thread.name = @"MTPingHelper";
        [thread start];
    });
    return thread;
}
    
+ (void)dispatchOnRunLoopThreadImpl:(void (^)())f {
    if (f) {
        f();
    }
}
    
+ (void)dispatchOnRunLoopThread:(void (^)())block {
    [self performSelector:@selector(dispatchOnRunLoopThreadImpl:) onThread:[self runLoopThread] withObject:[block copy] waitUntilDone:false];
}

- (instancetype)initWithSuccess:(void (^)())success {
    self = [super init];
    if (self != nil) {
        _success = [success copy];
        
        NSArray *hosts = @[
            @"google.com",
            @"8.8.8.8"
        ];
        
        NSString *host = hosts[(int)(arc4random_uniform((uint32_t)hosts.count))];
        
        _ping = [[PingFoundation alloc] initWithHostName:host];
        _ping.delegate = self;
        [_ping start];
    }
    return self;
}

- (void)dealloc {
#if DEBUG
    assert(_ping.delegate == nil);
#endif
    if (_ping.delegate != nil) {
        _ping.delegate = nil;
        [_ping stop];
    }
}

- (void)stop {
    _ping.delegate = nil;
    [_ping stop];
}

- (void)pingFoundation:(PingFoundation *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber {
    if (_success) {
        _success();
    }
}

- (void)pingFoundation:(PingFoundation *)pinger didStartWithAddress:(NSData *)__unused address {
    [pinger sendPingWithData:nil];
}

@end

@implementation MTConnectionProbing

+ (MTSignal *)pingAddress {
    return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
        MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
        
        [MTPingHelper dispatchOnRunLoopThread:^{
            MTPingHelper *helper = [[MTPingHelper alloc] initWithSuccess:^{
                [subscriber putNext:@true];
                [subscriber putCompletion];
            }];
            
            [disposable setDisposable:[[MTBlockDisposable alloc] initWithBlock:^{
                [MTPingHelper dispatchOnRunLoopThread:^{
                    [helper stop];
                }];
            }]];
        }];
        
        return disposable;
    }];
}

+ (MTSignal *)probeProxyWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId settings:(MTSocksProxySettings *)settings {
    MTSignal *proxyAvailable = [[[MTProxyConnectivity pingProxyWithContext:context datacenterId:datacenterId settings:settings] map:^id(MTProxyConnectivityStatus *status) {
        return @(status.reachable);
    }] timeout:10.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal single:@false]];
    MTSignal *referenceAvailable = [[self pingAddress] timeout:10.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal single:@false]];
    MTSignal *combined = [[MTSignal combineSignals:@[proxyAvailable, referenceAvailable]] map:^id(NSArray *values) {
        NSNumber *proxy = values[0];
        NSNumber *ping = values[1];
        if (![proxy boolValue] && [ping boolValue]) {
            return @true;
        } else {
            return @false;
        }
    }];
    return combined;
}

@end


================================================
FILE: MTDNS.h
================================================
#import <Foundation/Foundation.h>

@class MTSignal;

@interface MTDNS : NSObject

+ (MTSignal *)resolveHostname:(NSString *)hostname;
+ (MTSignal *)resolveHostnameNative:(NSString *)hostname port:(int32_t)port;
+ (MTSignal *)resolveHostnameUniversal:(NSString *)hostname port:(int32_t)port;

@end


================================================
FILE: MTDNS.m
================================================
#import "MTDNS.h"

#import <arpa/inet.h>
#include <netinet/tcp.h>
#import <fcntl.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <netinet/in.h>
#import <net/if.h>

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTQueue.h>
#   import <MTProtoKitDynamic/MTSignal.h>
#   import <MTProtoKitDynamic/MTBag.h>
#   import <MTProtoKitDynamic/MTAtomic.h>
#   import <MTProtoKitDynamic/MTHttpRequestOperation.h>
#   import <MTProtoKitDynamic/MTEncryption.h>
#   import <MTProtoKitDynamic/MTRequestMessageService.h>
#   import <MTProtoKitDynamic/MTRequest.h>
#   import <MTProtoKitDynamic/MTContext.h>
#   import <MTProtoKitDynamic/MTApiEnvironment.h>
#   import <MTProtoKitDynamic/MTDatacenterAddress.h>
#   import <MTProtoKitDynamic/MTDatacenterAddressSet.h>
#   import <MTProtoKitDynamic/MTProto.h>
#   import <MTProtoKitDynamic/MTSerialization.h>
#   import <MTProtoKitDynamic/MTLogging.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTQueue.h>
#   import <MTProtoKitMac/MTSignal.h>
#   import <MTProtoKitMac/MTBag.h>
#   import <MTProtoKitMac/MTAtomic.h>
#   import <MTProtoKitMac/MTHttpRequestOperation.h>
#   import <MTProtoKitMac/MTEncryption.h>
#   import <MTProtoKitMac/MTRequestMessageService.h>
#   import <MTProtoKitMac/MTRequest.h>
#   import <MTProtoKitMac/MTContext.h>
#   import <MTProtoKitMac/MTApiEnvironment.h>
#   import <MTProtoKitMac/MTDatacenterAddress.h>
#   import <MTProtoKitMac/MTDatacenterAddressSet.h>
#   import <MTProtoKitMac/MTProto.h>
#   import <MTProtoKitMac/MTSerialization.h>
#   import <MTProtoKitMac/MTLogging.h>
#else
#   import <MTProtoKit/MTQueue.h>
#   import <MTProtoKit/MTSignal.h>
#   import <MTProtoKit/MTBag.h>
#   import <MTProtoKit/MTAtomic.h>
#   import <MTProtoKit/MTHttpRequestOperation.h>
#   import <MTProtoKit/MTEncryption.h>
#   import <MTProtoKit/MTRequestMessageService.h>
#   import <MTProtoKit/MTRequest.h>
#   import <MTProtoKit/MTContext.h>
#   import <MTProtoKit/MTApiEnvironment.h>
#   import <MTProtoKit/MTDatacenterAddress.h>
#   import <MTProtoKit/MTDatacenterAddressSet.h>
#   import <MTProtoKit/MTProto.h>
#   import <MTProtoKit/MTSerialization.h>
#   import <MTProtoKit/MTLogging.h>
#endif

#import <netinet/in.h>
#import <arpa/inet.h>

@interface MTDNSHostContext : NSObject {
    MTBag *_subscribers;
    id<MTDisposable> _disposable;
}

@end

@implementation MTDNSHostContext

- (instancetype)initWithHost:(NSString *)host disposable:(id<MTDisposable>)disposable {
    self = [super init];
    if (self != nil) {
        _subscribers = [[MTBag alloc] init];
        _disposable = disposable;
    }
    return self;
}

- (void)dealloc {
    [_disposable dispose];
}

- (NSInteger)addSubscriber:(void (^)(NSString *))completion {
    return [_subscribers addItem:[completion copy]];
}

- (void)removeSubscriber:(NSInteger)index {
    [_subscribers removeItem:index];
}

- (bool)isEmpty {
    return [_subscribers isEmpty];
}

- (void)complete:(NSString *)result {
    for (void (^completion)(NSString *) in [_subscribers copyItems]) {
        completion(result);
    }
}

@end

@interface MTDNSContext : NSObject {
    NSMutableDictionary<NSString *, MTDNSHostContext *> *_contexts;
}

@end

@implementation MTDNSContext

+ (MTQueue *)sharedQueue {
    static MTQueue *queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = [[MTQueue alloc] init];
    });
    return queue;
}

+ (MTSignal *)shared {
    return [[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
        static MTDNSContext *instance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[MTDNSContext alloc] init];
        });
        [subscriber putNext:instance];
        [subscriber putCompletion];
        return nil;
    }] startOn:[self sharedQueue]];
}

- (instancetype)init {
    self = [super init];
    if (self != nil) {
        _contexts = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (id<MTDisposable>)subscribe:(NSString *)host port:(int32_t)port completion:(void (^)(NSString *))completion {
    NSString *key = [NSString stringWithFormat:@"%@:%d", host, port];
    
    MTMetaDisposable *disposable = nil;
    if (_contexts[key] == nil) {
        disposable = [[MTMetaDisposable alloc] init];
        _contexts[key] = [[MTDNSHostContext alloc] initWithHost:host disposable:disposable];
    }
    MTDNSHostContext *context = _contexts[key];
    
    NSInteger index = [context addSubscriber:^(NSString *result) {
        if (completion) {
            completion(result);
        }
    }];
    
    if (disposable != nil) {
        __weak MTDNSContext *weakSelf = self;
        [disposable setDisposable:[[[self performLookup:host port:port] deliverOn:[MTDNSContext sharedQueue]] startWithNext:^(NSString *result) {
            __strong MTDNSContext *strongSelf = weakSelf;
            if (strongSelf == nil) {
                return;
            }
            if (strongSelf->_contexts[key] != nil) {
                [strongSelf->_contexts[key] complete:result];
                [strongSelf->_contexts removeObjectForKey:key];
            }
        }]];
    }
    
    __weak MTDNSContext *weakSelf = self;
    __weak MTDNSHostContext *weakContext = context;
    return [[MTBlockDisposable alloc] initWithBlock:^{
        [[MTDNSContext sharedQueue] dispatchOnQueue:^{
            __strong MTDNSContext *strongSelf = weakSelf;
            __strong MTDNSHostContext *strongContext = weakContext;
            if (strongSelf == nil || strongContext == nil) {
                return;
            }
            if (strongSelf->_contexts[key] != nil && strongSelf->_contexts[key] == strongContext) {
                [strongSelf->_contexts[key] removeSubscriber:index];
                if ([strongSelf->_contexts[key] isEmpty]) {
                    [strongSelf->_contexts removeObjectForKey:key];
                }
            }
        }];
    }];
}

- (MTSignal *)performLookup:(NSString *)host port:(int32_t)port {
    MTSignal *lookupOnce = [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
        MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
        [[MTQueue concurrentDefaultQueue] dispatchOnQueue:^{
            struct addrinfo hints, *res, *res0;
            
            memset(&hints, 0, sizeof(hints));
            hints.ai_family   = PF_UNSPEC;
            hints.ai_socktype = SOCK_STREAM;
            hints.ai_protocol = IPPROTO_TCP;
            
            NSString *portStr = [NSString stringWithFormat:@"%d", port];
            if (MTLogEnabled()) {
                MTLog(@"[MTDNS lookup %@:%@]", host, portStr);
            }
            int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
            
            NSString *address4 = nil;
            NSString *address6 = nil;
            
            if (gai_error == 0) {
                for(res = res0; res; res = res->ai_next) {
                    if ((address4 == nil) && (res->ai_family == AF_INET)) {
                        struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;
                        char *s = malloc(INET_ADDRSTRLEN);
                        inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
                        address4 = [NSString stringWithUTF8String:s];
                        free(s);
                    } else if ((address6 == nil) && (res->ai_family == AF_INET6)) {
                        struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res->ai_addr;
                        char *s = malloc(INET6_ADDRSTRLEN);
                        inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
                        address6 = [NSString stringWithUTF8String:s];
                        free(s);
                    }
                }
                freeaddrinfo(res0);
            }
            
            if (address4 != nil) {
                if (MTLogEnabled()) {
                    MTLog(@"[MTDNS lookup %@:%@ success ipv4]", host, portStr);
                }
                [subscriber putNext:address4];
                [subscriber putCompletion];
            } else if (address6 != nil) {
                if (MTLogEnabled()) {
                    MTLog(@"[MTDNS lookup %@:%@ success ipv6]", host, portStr);
                }
                [subscriber putNext:address6];
                [subscriber putCompletion];
            } else {
                if (MTLogEnabled()) {
                    MTLog(@"[MTDNS lookup %@:%@ error %d]", host, portStr, gai_error);
                }
                [subscriber putError:nil];
            }
        }];
        return disposable;
    }];
    return [[[lookupOnce catch:^MTSignal *(__unused id error) {
        return [[MTSignal complete] delay:2.0 onQueue:[MTDNSContext sharedQueue]];
    }] restart] take:1];
}

@end

@interface MTDNSCachedHostname : NSObject

@property (nonatomic, strong) NSString *ip;
@property (nonatomic) NSTimeInterval timestamp;

@end

@implementation MTDNSCachedHostname

- (instancetype)initWithIp:(NSString *)ip timestamp:(NSTimeInterval)timestamp {
    self = [super init];
    if (self != nil) {
        _ip = ip;
        _timestamp = timestamp;
    }
    return self;
}

@end

@implementation MTDNS

+ (MTAtomic *)hostnameCache {
    static MTAtomic *result = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        result = [[MTAtomic alloc] initWithValue:[[NSMutableDictionary alloc] init]];
    });
    return result;
}

+ (NSString *)cachedIp:(NSString *)hostname {
    return [[self hostnameCache] with:^id (NSMutableDictionary *dict) {
        MTDNSCachedHostname *result = dict[hostname];
        if (result != nil && result.timestamp > CFAbsoluteTimeGetCurrent() - 10.0 * 60.0) {
            return result.ip;
        }
        return nil;
    }];
}

+ (void)cacheIp:(NSString *)hostname ip:(NSString *)ip {
    [[self hostnameCache] with:^id (NSMutableDictionary *dict) {
        dict[hostname] = [[MTDNSCachedHostname alloc] initWithIp:ip timestamp:CFAbsoluteTimeGetCurrent()];
        return nil;
    }];
}

+ (MTSignal *)resolveHostname:(NSString *)hostname {
    return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
        NSString *cached = [self cachedIp:hostname];
        if (cached != nil) {
            [subscriber putNext:cached];
            [subscriber putCompletion];
            return nil;
        }
        NSDictionary *headers = @{@"Host": @"dns.google.com"};
        
        return [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://google.com/resolve?name=%@", hostname]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            if ([dict respondsToSelector:@selector(objectForKey:)]) {
                NSArray *answer = dict[@"Answer"];
                if ([answer respondsToSelector:@selector(objectAtIndex:)]) {
                    for (NSDictionary *item in answer) {
                        if ([item respondsToSelector:@selector(objectForKey:)]) {
                            NSString *itemData = item[@"data"];
                            if ([itemData respondsToSelector:@selector(characterAtIndex:)]) {
                                bool isIp = true;
                                struct in_addr ip4;
                                struct in6_addr ip6;
                                if (inet_aton(itemData.UTF8String, &ip4) == 0) {
                                    if (inet_pton(AF_INET6, itemData.UTF8String, &ip6) == 0) {
                                        isIp = false;
                                    }
                                }
                                if (isIp) {
                                    [self cacheIp:hostname ip:itemData];
                                    return [MTSignal single:itemData];
                                }
                            }
                        }
                    }
                }
            }
            [subscriber putNext:hostname];
            [subscriber putCompletion];
            return nil;
        }] startWithNext:^(id next) {
            [subscriber putNext:next];
            [subscriber putCompletion];
        } error:^(id error) {
            [subscriber putNext:hostname];
            [subscriber putCompletion];
        } completed:nil];
    }];
}

+ (MTSignal *)resolveHostnameNative:(NSString *)hostname port:(int32_t)port {
    return [[MTDNSContext shared] mapToSignal:^MTSignal *(MTDNSContext *context) {
        return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
            return [context subscribe:hostname port:port completion:^(NSString *result) {
                [subscriber putNext:result];
                [subscriber putCompletion];
            }];
        }];
    }];
}

+ (MTSignal *)resolveHostnameUniversal:(NSString *)hostname port:(int32_t)port {
    return [[self resolveHostname:hostname] timeout:10.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[self resolveHostnameNative:hostname port:port]];
}

@end


================================================
FILE: MTDatacenterVerificationData.h
================================================
#import <Foundation/Foundation.h>

@interface MTDatacenterVerificationData : NSObject

@property (nonatomic, readonly) NSInteger datacenterId;
@property (nonatomic, readonly) bool isTestingEnvironment;

- (instancetype _Nonnull)initWithDatacenterId:(NSInteger)datacenterId isTestingEnvironment:(bool)isTestingEnvironment;

@end


================================================
FILE: MTDatacenterVerificationData.m
================================================
#import "MTDatacenterVerificationData.h"

@implementation MTDatacenterVerificationData

- (instancetype _Nonnull)initWithDatacenterId:(NSInteger)datacenterId isTestingEnvironment:(bool)isTestingEnvironment {
    self = [super init];
    if (self != nil) {
        _datacenterId = datacenterId;
        _isTestingEnvironment = isTestingEnvironment;
    }
    return self;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"datacenterId: %d, isTestingEnvironment: %d", (int)_datacenterId, _isTestingEnvironment ? 1 : 0];
}

@end


================================================
FILE: MTDiscoverConnectionSignals.h
================================================
#import <Foundation/Foundation.h>

@class MTContext;
@class MTDatacenterAddress;
@class MTSignal;

typedef struct {
    uint8_t nonce[16];
} MTPayloadData;

@interface MTDiscoverConnectionSignals : NSObject

+ (NSData *)payloadData:(MTPayloadData *)outPayloadData context:(MTContext *)context address:(MTDatacenterAddress *)address;

+ (MTSignal *)discoverSchemeWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId addressList:(NSArray *)addressList media:(bool)media isProxy:(bool)isProxy;

@end


================================================
FILE: MTDiscoverConnectionSignals.m
================================================
#import "MTDiscoverConnectionSignals.h"

#import "MTTcpConnection.h"

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTTransportScheme.h>
#   import <MTProtoKitDynamic/MTTcpTransport.h>
#   import <MTProtoKitDynamic/MTQueue.h>
#   import <MTProtoKitDynamic/MTProtoKitDynamic.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTTransportScheme.h>
#   import <MTProtoKitMac/MTTcpTransport.h>
#   import <MTProtoKitMac/MTQueue.h>
#   import <MTProtoKitMac/MTProtoKitMac.h>
#else
#   import <MTProtoKit/MTTransportScheme.h>
#   import <MTProtoKit/MTTcpTransport.h>
#   import <MTProtoKit/MTQueue.h>
#   import <MTProtoKit/MTProtoKit.h>
#endif

#import "MTDatacenterAddress.h"

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTDisposable.h>
#   import <MTProtoKitDynamic/MTSignal.h>
#   import <MTProtoKitDynamic/MTAtomic.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTDisposable.h>
#   import <MTProtoKitMac/MTSignal.h>
#   import <MTProtoKitMac/MTAtomic.h>
#else
#   import <MTProtoKit/MTDisposable.h>
#   import <MTProtoKit/MTSignal.h>
#   import <MTProtoKit/MTAtomic.h>
#endif

#import <netinet/in.h>
#import <arpa/inet.h>

@implementation MTDiscoverConnectionSignals

+ (NSData *)payloadData:(MTPayloadData *)outPayloadData context:(MTContext *)context address:(MTDatacenterAddress *)address {
    uint8_t reqPqBytes[] = {
        0, 0, 0, 0, 0, 0, 0, 0, // zero * 8
        0, 0, 0, 0, 0, 0, 0, 0, // message id
        20, 0, 0, 0, // message length
        0xf1, 0x8e, 0x7e, 0xbe, // req_pq_multi
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // nonce
    };
    
    MTPayloadData payloadData;
    arc4random_buf(&payloadData.nonce, 16);
    if (outPayloadData)
        *outPayloadData = payloadData;
    
    int64_t messageId = (int64_t)([[NSDate date] timeIntervalSince1970] * 4294967296);
    memcpy(reqPqBytes + 8, &messageId, 8);
    
    memcpy(reqPqBytes + 8 + 8 + 4 + 4, payloadData.nonce, 16);
    
    NSMutableData *data = [[NSMutableData alloc] initWithBytes:reqPqBytes length:sizeof(reqPqBytes)];
    
    NSData *secret = address.secret;
    if (context.apiEnvironment.socksProxySettings != nil) {
        if (context.apiEnvironment.socksProxySettings.secret != nil) {
            secret = context.apiEnvironment.socksProxySettings.secret;
        }
    }
    
    bool extendedPadding = false;
    if (secret != nil) {
        if ([MTSocksProxySettings secretSupportsExtendedPadding:secret]) {
            extendedPadding = true;
        }
    }
    
    if (extendedPadding) {
        uint32_t paddingSize = arc4random_uniform(128);
        if (paddingSize != 0) {
            uint8_t padding[128];
            arc4random_buf(padding, paddingSize);
            [data appendBytes:padding length:paddingSize];
        }
    }
    return data;
}

+ (bool)isResponseValid:(NSData *)data payloadData:(MTPayloadData)payloadData {
    if (data.length >= 84) {
        uint8_t zero[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
        uint8_t resPq[] = { 0x63, 0x24, 0x16, 0x05 };
        if (memcmp((uint8_t * const)data.bytes, zero, 8) == 0 && memcmp(((uint8_t * const)data.bytes) + 20, resPq, 4) == 0 && memcmp(((uint8_t * const)data.bytes) + 24, payloadData.nonce, 16) == 0) {
            return true;
        }
    }
    
    return false;
}

+ (bool)isIpv6:(NSString *)ip
{
    const char *utf8 = [ip UTF8String];
    int success;
    
    struct in6_addr dst6;
    success = inet_pton(AF_INET6, utf8, &dst6);
    
    return success == 1;
}

+ (MTSignal *)tcpConnectionWithContext:(MTContext *)context datacenterId:(NSUInteger)datacenterId address:(MTDatacenterAddress *)address;
{
    return [[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber)
    {
        MTPayloadData payloadData;
        NSData *data = [self payloadData:&payloadData context:context address:address];
        
        MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:false] interface:nil usageCalculationInfo:nil];
        __weak MTTcpConnection *weakConnection = connection;
        connection.connectionOpened = ^
        {
            __strong MTTcpConnection *strongConnection = weakConnection;
            if (strongConnection != nil)
                [strongConnection sendDatas:@[data] completion:nil requestQuickAck:false expectDataInResponse:true];
        };
        MTAtomic *processedData = [[MTAtomic alloc] initWithValue:@false];
        connection.connectionReceivedData = ^(NSData *data)
        {
            [processedData swap:@true];
            if ([self isResponseValid:data payloadData:payloadData])
            {
                if (MTLogEnabled()) {
                    MTLog(@"success tcp://%@:%d", address.ip, (int)address.port);
                }
                [subscriber putCompletion];
            }
            else
            {
                if (MTLogEnabled()) {
                    MTLog(@"failed tcp://%@:%d (invalid response)", address.ip, (int)address.port);
                }
                [subscriber putError:nil];
            }
        };
        connection.connectionClosed = ^
        {
            __block bool received = false;
            [processedData with:^id (NSNumber *value) {
                received = [value boolValue];
                return nil;
            }];
            if (!received) {
                if (MTLogEnabled()) {
                    MTLog(@"failed tcp://%@:%d (disconnected)", address.ip, (int)address.port);
                }
                [subscriber putError:nil];
            }
        };
        if (MTLogEnabled()) {
            MTLog(@"trying tcp://%@:%d", address.ip, (int)address.port);
        }
        [connection start];
        
        return [[MTBlockDisposable alloc] initWithBlock:^
        {
            [connection stop];
        }];
    }] startOn:[MTTcpConnection tcpQueue]];
}

+ (MTSignal *)discoverSchemeWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId addressList:(NSArray *)addressList media:(bool)media isProxy:(bool)isProxy
{
    NSMutableArray *bestAddressList = [[NSMutableArray alloc] init];
    
    for (MTDatacenterAddress *address in addressList)
    {
        if (media == address.preferForMedia && isProxy == address.preferForProxy) {
            [bestAddressList addObject:address];
        }
    }
    
    if (bestAddressList.count == 0 && media)
        [bestAddressList addObjectsFromArray:addressList];
    
    NSMutableArray *bestTcp4Signals = [[NSMutableArray alloc] init];
    NSMutableArray *bestTcp6Signals = [[NSMutableArray alloc] init];
    NSMutableArray *bestHttpSignals = [[NSMutableArray alloc] init];
    
    NSMutableDictionary *tcpIpsByPort = [[NSMutableDictionary alloc] init];
    
    for (MTDatacenterAddress *address in bestAddressList) {
        NSMutableSet *ips = tcpIpsByPort[@(address.port)];
        if (ips == nil) {
            ips = [[NSMutableSet alloc] init];
            tcpIpsByPort[@(address.port)] = ips;
        }
        [ips addObject:address.ip];
    }
    
    for (MTDatacenterAddress *address in bestAddressList) {
        MTTransportScheme *tcpTransportScheme = [[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:media];
        
        if ([self isIpv6:address.ip])
        {
            MTSignal *signal = [[[[self tcpConnectionWithContext:context datacenterId:datacenterId address:address] then:[MTSignal single:tcpTransportScheme]] timeout:5.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal fail:nil]] catch:^MTSignal *(__unused id error)
            {
                return [MTSignal complete];
            }];
            [bestTcp6Signals addObject:signal];
        }
        else
        {
            MTSignal *tcpConnectionWithTimeout = [[[self tcpConnectionWithContext:context datacenterId:datacenterId address:address] then:[MTSignal single:tcpTransportScheme]] timeout:5.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal fail:nil]];
            MTSignal *signal = [tcpConnectionWithTimeout catch:^MTSignal *(__unused id error)
            {
                return [MTSignal complete];
            }];
            [bestTcp4Signals addObject:signal];
            
            NSArray *alternatePorts = @[@80, @5222];
            for (NSNumber *nPort in alternatePorts) {
                NSSet *ipsWithPort = tcpIpsByPort[nPort];
                if (![ipsWithPort containsObject:address.ip]) {
                    MTDatacenterAddress *portAddress = [[MTDatacenterAddress alloc] initWithIp:address.ip port:[nPort intValue] preferForMedia:address.preferForMedia restrictToTcp:address.restrictToTcp cdn:address.cdn preferForProxy:address.preferForProxy secret:address.secret];
                    MTTransportScheme *tcpPortTransportScheme = [[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:portAddress media:media];
                    MTSignal *tcpConnectionWithTimeout = [[[self tcpConnectionWithContext:context datacenterId:datacenterId address:portAddress] then:[MTSignal single:tcpPortTransportScheme]] timeout:5.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal fail:nil]];
                    tcpConnectionWithTimeout = [tcpConnectionWithTimeout mapToSignal:^(id next) {
                        return [[MTSignal single:next] delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]];
                    }];
                    MTSignal *signal = [tcpConnectionWithTimeout catch:^MTSignal *(__unused id error) {
                        return [MTSignal complete];
                    }];
                    [bestTcp4Signals addObject:signal];
                }
            }
        }
    }
    
    MTSignal *repeatDelaySignal = [[MTSignal complete] delay:1.0 onQueue:[MTQueue concurrentDefaultQueue]];
    MTSignal *optimalDelaySignal = [[MTSignal complete] delay:30.0 onQueue:[MTQueue concurrentDefaultQueue]];
    
    MTSignal *firstTcp4Match = [[[[MTSignal mergeSignals:bestTcp4Signals] then:repeatDelaySignal] restart] take:1];
    MTSignal *firstTcp6Match = [[[[MTSignal mergeSignals:bestTcp6Signals] then:repeatDelaySignal] restart] take:1];
    MTSignal *firstHttpMatch = [[[[MTSignal mergeSignals:bestHttpSignals] then:repeatDelaySignal] restart] take:1];
    
    MTSignal *optimalTcp4Match = [[[[MTSignal mergeSignals:bestTcp4Signals] then:optimalDelaySignal] restart] take:1];
    MTSignal *optimalTcp6Match = [[[[MTSignal mergeSignals:bestTcp6Signals] then:optimalDelaySignal] restart] take:1];
    
    MTSignal *anySignal = [[MTSignal mergeSignals:@[firstTcp4Match, firstTcp6Match, firstHttpMatch]] take:1];
    MTSignal *optimalSignal = [[MTSignal mergeSignals:@[optimalTcp4Match, optimalTcp6Match]] take:1];
    
    MTSignal *signal = [anySignal mapToSignal:^MTSignal *(MTTransportScheme *scheme)
    {
        if (![scheme isOptimal])
        {
            return [[MTSignal single:scheme] then:[optimalSignal delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]]];
        }
        else
            return [MTSignal single:scheme];
    }];
    
    return [signal catch:^MTSignal *(id error) {
        return [MTSignal complete];
    }];
}

@end


================================================
FILE: MTDisposable.h
================================================
#import <Foundation/Foundation.h>

@protocol MTDisposable <NSObject>

- (void)dispose;

@end

@interface MTBlockDisposable : NSObject <MTDisposable>

- (instancetype)initWithBlock:(void (^)())block;

@end

@interface MTMetaDisposable : NSObject <MTDisposable>

- (void)setDisposable:(id<MTDisposable>)disposable;

@end

@interface MTDisposableSet : NSObject <MTDisposable>

- (void)add:(id<MTDisposable>)disposable;
- (void)remove:(id<MTDisposable>)disposable;

@end


================================================
FILE: MTDisposable.m
================================================
#import "MTDisposable.h"

#import <libkern/OSAtomic.h>
#import <objc/runtime.h>

@interface MTBlockDisposable ()
{
    void *_block;
}

@end

@implementation MTBlockDisposable

- (instancetype)initWithBlock:(void (^)())block
{
    self = [super init];
    if (self != nil)
    {
        _block = (__bridge_retained void *)[block copy];
    }
    return self;
}

- (void)dealloc
{
    void *block = _block;
    if (block != NULL)
    {
        if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
        {
            if (block != nil)
            {
                __strong id strongBlock = (__bridge_transfer id)block;
                strongBlock = nil;
            }
        }
    }
}

- (void)dispose
{
    void *block = _block;
    if (block != NULL)
    {
        if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
        {
            if (block != nil)
            {
                __strong id strongBlock = (__bridge_transfer id)block;
                ((dispatch_block_t)strongBlock)();
                strongBlock = nil;
            }
        }
    }
}

@end

@interface MTMetaDisposable ()
{
    OSSpinLock _lock;
    bool _disposed;
    id<MTDisposable> _disposable;
}

@end

@implementation MTMetaDisposable

- (void)setDisposable:(id<MTDisposable>)disposable
{
    id<MTDisposable> previousDisposable = nil;
    bool dispose = false;
    
    OSSpinLockLock(&_lock);
    dispose = _disposed;
    if (!dispose)
    {
        previousDisposable = _disposable;
        _disposable = disposable;
    }
    OSSpinLockUnlock(&_lock);
    
    if (previousDisposable != nil)
        [previousDisposable dispose];
    
    if (dispose)
        [disposable dispose];
}

- (void)dispose
{
    id<MTDisposable> disposable = nil;
    
    OSSpinLockLock(&_lock);
    if (!_disposed)
    {
        disposable = _disposable;
        _disposed = true;
    }
    OSSpinLockUnlock(&_lock);
    
    if (disposable != nil)
        [disposable dispose];
}

@end

@interface MTDisposableSet ()
{
    OSSpinLock _lock;
    bool _disposed;
    id<MTDisposable> _singleDisposable;
    NSArray *_multipleDisposables;
}

@end

@implementation MTDisposableSet

- (void)add:(id<MTDisposable>)disposable
{
    if (disposable == nil)
        return;
    
    bool dispose = false;
    
    OSSpinLockLock(&_lock);
    dispose = _disposed;
    if (!dispose)
    {
        if (_multipleDisposables != nil)
        {
            NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
            [multipleDisposables addObject:disposable];
            _multipleDisposables = multipleDisposables;
        }
        else if (_singleDisposable != nil)
        {
            NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithObjects:_singleDisposable, disposable, nil];
            _multipleDisposables = multipleDisposables;
            _singleDisposable = nil;
        }
        else
        {
            _singleDisposable = disposable;
        }
    }
    OSSpinLockUnlock(&_lock);
    
    if (dispose)
        [disposable dispose];
}

- (void)remove:(id<MTDisposable>)disposable {
    OSSpinLockLock(&_lock);
    if (_multipleDisposables != nil)
    {
        NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
        [multipleDisposables removeObject:disposable];
        _multipleDisposables = multipleDisposables;
    }
    else if (_singleDisposable == disposable)
    {
        _singleDisposable = nil;
    }
    OSSpinLockUnlock(&_lock);
}

- (void)dispose
{
    id<MTDisposable> singleDisposable = nil;
    NSArray *multipleDisposables = nil;
    
    OSSpinLockLock(&_lock);
    if (!_disposed)
    {
        _disposed = true;
        singleDisposable = _singleDisposable;
        multipleDisposables = _multipleDisposables;
        _singleDisposable = nil;
        _multipleDisposables = nil;
    }
    OSSpinLockUnlock(&_lock);
    
    if (singleDisposable != nil)
        [singleDisposable dispose];
    if (multipleDisposables != nil)
    {
        for (id<MTDisposable> disposable in multipleDisposables)
        {
            [disposable dispose];
        }
    }
}

@end


================================================
FILE: MTGzip.h
================================================
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MTGzip : NSObject
    
+ (NSData * _Nullable)decompress:(NSData *)data;

@end

NS_ASSUME_NONNULL_END


================================================
FILE: MTGzip.m
================================================
#import "MTGzip.h"

#import <zlib.h>

@implementation MTGzip
    
+ (NSData * _Nullable)decompress:(NSData *)data {
    const int kMemoryChunkSize = 1024;
    
    NSUInteger length = [data length];
    int windowBits = 15 + 32; //Default + gzip header instead of zlib header
    int retCode;
    unsigned char output[kMemoryChunkSize];
    uInt gotBack;
    NSMutableData *result;
    z_stream stream;
    
    if ((length == 0) || (length > UINT_MAX)) //FIXME: Support 64 bit inputs
        return nil;
    
    bzero(&stream, sizeof(z_stream));
    stream.avail_in = (uInt)length;
    stream.next_in = (unsigned char*)[data bytes];
    
    retCode = inflateInit2(&stream, windowBits);
    if(retCode != Z_OK)
    {
        NSLog(@"%s: inflateInit2() failed with error %i", __PRETTY_FUNCTION__, retCode);
        return nil;
    }
    
    result = [NSMutableData dataWithCapacity:(length * 4)];
    do
    {
        stream.avail_out = kMemoryChunkSize;
        stream.next_out = output;
        retCode = inflate(&stream, Z_NO_FLUSH);
        if ((retCode != Z_OK) && (retCode != Z_STREAM_END))
        {
            NSLog(@"%s: inflate() failed with error %i", __PRETTY_FUNCTION__, retCode);
            inflateEnd(&stream);
            return nil;
        }
        gotBack = kMemoryChunkSize - stream.avail_out;
        if (gotBack > 0)
        [result appendBytes:output length:gotBack];
    } while( retCode == Z_OK);
    inflateEnd(&stream);
    
    return (retCode == Z_STREAM_END ? result : nil);
}

@end


================================================
FILE: MTNetworkUsageCalculationInfo.h
================================================
#import <Foundation/Foundation.h>

@class MTQueue;

@interface MTNetworkUsageCalculationInfo : NSObject

@property (nonatomic, strong, readonly) NSString *filePath;
@property (nonatomic, readonly) int32_t incomingWWANKey;
@property (nonatomic, readonly) int32_t outgoingWWANKey;
@property (nonatomic, readonly) int32_t incomingOtherKey;
@property (nonatomic, readonly) int32_t outgoingOtherKey;

- (instancetype)initWithFilePath:(NSString *)filePath incomingWWANKey:(int32_t)incomingWWANKey outgoingWWANKey:(int32_t)outgoingWWANKey incomingOtherKey:(int32_t)incomingOtherKey outgoingOtherKey:(int32_t)outgoingOtherKey;

@end


================================================
FILE: MTNetworkUsageCalculationInfo.m
================================================
#import "MTNetworkUsageCalculationInfo.h"

@implementation MTNetworkUsageCalculationInfo

- (instancetype)initWithFilePath:(NSString *)filePath incomingWWANKey:(int32_t)incomingWWANKey outgoingWWANKey:(int32_t)outgoingWWANKey incomingOtherKey:(int32_t)incomingOtherKey outgoingOtherKey:(int32_t)outgoingOtherKey {
    self = [super init];
    if (self != nil) {
        _filePath = filePath;
        _incomingWWANKey = incomingWWANKey;
        _outgoingWWANKey = outgoingWWANKey;
        _incomingOtherKey = incomingOtherKey;
        _outgoingOtherKey = outgoingOtherKey;
    }
    return self;
}

@end


================================================
FILE: MTNetworkUsageManager.h
================================================
#import <Foundation/Foundation.h>

@class MTSignal;
@class MTNetworkUsageCalculationInfo;

typedef enum {
    MTNetworkUsageManagerInterfaceWWAN,
    MTNetworkUsageManagerInterfaceOther
} MTNetworkUsageManagerInterface;

typedef struct {
    NSUInteger incomingBytes;
    NSUInteger outgoingBytes;
} MTNetworkUsageManagerInterfaceStats;

@interface MTNetworkUsageManager : NSObject

- (instancetype)initWithInfo:(MTNetworkUsageCalculationInfo *)info;

- (void)addIncomingBytes:(NSUInteger)incomingBytes interface:(MTNetworkUsageManagerInterface)interface;
- (void)addOutgoingBytes:(NSUInteger)outgoingBytes interface:(MTNetworkUsageManagerInterface)interface;

- (void)resetKeys:(NSArray<NSNumber *> *)keys setKeys:(NSDictionary<NSNumber *, NSNumber *> *)setKeys completion:(void (^)())completion;
- (MTSignal *)currentStatsForKeys:(NSArray<NSNumber *> *)keys;

@end


================================================
FILE: MTNetworkUsageManager.m
================================================
#import "MTNetworkUsageManager.h"

#include <sys/mman.h>
#import <libkern/OSAtomic.h>

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTNetworkUsageCalculationInfo.h>
#   import <MTProtoKitDynamic/MTSignal.h>
#   import <MTProtoKitDynamic/MTTimer.h>
#   import <MTProtoKitDynamic/MTQueue.h>
#   import <MTProtoKitDynamic/MTAtomic.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTNetworkUsageCalculationInfo.h>
#   import <MTProtoKitMac/MTSignal.h>
#   import <MTProtoKitMac/MTTimer.h>
#   import <MTProtoKitMac/MTQueue.h>
#   import <MTProtoKitMac/MTAtomic.h>
#else
#   import <MTProtoKit/MTNetworkUsageCalculationInfo.h>
#   import <MTProtoKit/MTSignal.h>
#   import <MTProtoKit/MTTimer.h>
#   import <MTProtoKit/MTQueue.h>
#   import <MTProtoKit/MTAtomic.h>
#endif

static int offsetForInterface(MTNetworkUsageCalculationInfo *info, MTNetworkUsageManagerInterface interface, bool incoming) {
    switch (interface) {
        case MTNetworkUsageManagerInterfaceWWAN:
            if (incoming) {
                return info.incomingWWANKey * 8;
            } else {
                return info.outgoingWWANKey * 8;
            }
        case MTNetworkUsageManagerInterfaceOther:
            if (incoming) {
                return info.incomingOtherKey * 8;
            } else {
                return info.outgoingOtherKey * 8;
            }
    }
}

@interface MTNetworkUsageManagerImpl : NSObject {
    MTQueue *_queue;
    MTNetworkUsageCalculationInfo *_info;
    
    NSMutableDictionary<NSNumber *, NSNumber *> *_pendingIncomingBytes;
    NSMutableDictionary<NSNumber *, NSNumber *> *_pendingOutgoingBytes;
    MTTimer *_timer;
}

@end

@implementation MTNetworkUsageManagerImpl

- (instancetype)initWithQueue:(MTQueue *)queue info:(MTNetworkUsageCalculationInfo *)info {
    self = [super init];
    if (self != nil) {
        _queue = queue;
        _info = info;
        
        _pendingIncomingBytes = [[NSMutableDictionary alloc] init];
        _pendingOutgoingBytes = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void)dealloc {
    [self sync];
}

- (void)scheduleSync {
    if (_timer == nil) {
        __weak MTNetworkUsageManagerImpl *weakSelf = self;
        _timer = [[MTTimer alloc] initWithTimeout:1.0 repeat:false completion:^{
            __strong MTNetworkUsageManagerImpl *strongSelf = weakSelf;
            [strongSelf sync];
        } queue:_queue.nativeQueue];
        [_timer start];
    }
}

- (void)sync {
    [_timer invalidate];
    _timer = nil;
    
    int32_t fd = open([_info.filePath UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd >= 0) {
        [_pendingIncomingBytes enumerateKeysAndObjectsUsingBlock:^(NSNumber * nInterface, NSNumber *nValue, __unused BOOL *stop) {
            off_t offset = offsetForInterface(_info, (MTNetworkUsageManagerInterface)[nInterface intValue], true);
            lseek(fd, offset, SEEK_SET);
            int64_t currentValue = 0;
            read(fd, &currentValue, 8);
            currentValue += (int64_t)[nValue intValue];
            lseek(fd, offset, SEEK_SET);
            write(fd, &currentValue, 8);
        }];
        [_pendingOutgoingBytes enumerateKeysAndObjectsUsingBlock:^(NSNumber * nInterface, NSNumber *nValue, __unused BOOL *stop) {
            off_t offset = offsetForInterface(_info, (MTNetworkUsageManagerInterface)[nInterface intValue], false);
            lseek(fd, offset, SEEK_SET);
            int64_t currentValue = 0;
            read(fd, &currentValue, 8);
            currentValue += (int64_t)[nValue intValue];
            lseek(fd, offset, SEEK_SET);
            write(fd, &currentValue, 8);
        }];
        close(fd);
    }
    
    [_pendingIncomingBytes removeAllObjects];
    [_pendingOutgoingBytes removeAllObjects];
}

- (void)addIncomingBytes:(NSUInteger)incomingBytes interface:(MTNetworkUsageManagerInterface)interface {
    _pendingIncomingBytes[@(interface)] = @([_pendingIncomingBytes[@(interface)] unsignedIntegerValue] + incomingBytes);
    [self scheduleSync];
}

- (void)addOutgoingBytes:(NSUInteger)outgoingBytes interface:(MTNetworkUsageManagerInterface)interface {
    _pendingOutgoingBytes[@(interface)] = @([_pendingOutgoingBytes[@(interface)] unsignedIntegerValue] + outgoingBytes);
    [self scheduleSync];
}

- (void)resetKeys:(NSArray<NSNumber *> *)keys setKeys:(NSDictionary<NSNumber *, NSNumber *> *)setKeys completion:(void (^)())completion {
    [self sync];
    int32_t fd = open([_info.filePath UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd >= 0) {
        for (NSNumber *nKey in keys) {
            lseek(fd, [nKey intValue] * 8, SEEK_SET);
            int64_t currentValue = 0;
            write(fd, &currentValue, 8);
        }
        [setKeys enumerateKeysAndObjectsUsingBlock:^(NSNumber *nKey, NSNumber *nValue, __unused BOOL *stop) {
            lseek(fd, [nKey intValue] * 8, SEEK_SET);
            int64_t currentValue = [nValue longLongValue];
            write(fd, &currentValue, 8);
        }];
        close(fd);
    }
    if (completion) {
        completion();
    }
}

- (NSDictionary *)currentStatsForKeys:(NSArray<NSNumber *> *)keys {
    NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
    [self sync];
    int32_t fd = open([_info.filePath UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd >= 0) {
        for (NSNumber *nKey in keys) {
            lseek(fd, [nKey intValue] * 8, SEEK_SET);
            int64_t currentValue = 0;
            read(fd, &currentValue, 8);
            result[nKey] = @(currentValue);
        }
        int64_t currentValue = 0;
        read(fd, &currentValue, 8);
        close(fd);
    }
    return result;
}

@end

@interface MTNetworkUsageManagerImplHolder: NSObject

@property (nonatomic) void *impl;
@property (nonatomic) bool deallocated;

@end

@implementation MTNetworkUsageManagerImplHolder

@end

@interface MTNetworkUsageManager () {
    MTQueue *_queue;
    MTAtomic *_holder;
}

@end

@implementation MTNetworkUsageManager

- (instancetype)initWithInfo:(MTNetworkUsageCalculationInfo *)info {
    self = [super init];
    if (self != nil) {
        _queue = [[MTQueue alloc] init];
        _holder = [[MTAtomic alloc] initWithValue:[[MTNetworkUsageManagerImplHolder alloc] init]];
        [_queue dispatchOnQueue:^{
            [_holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
                if (!holder.deallocated) {
                    holder.impl = (void *)CFBridgingRetain([[MTNetworkUsageManagerImpl alloc] initWithQueue:_queue info:info]);
                }
                return nil;
            }];
        }];
    }
    return self;
}

- (void)dealloc {
    MTAtomic *holder = _holder;
    [holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
        holder.deallocated = true;
        return nil;
    }];
    [_queue dispatchOnQueue:^{
        __block void *impl = nil;
        [holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
            impl = holder.impl;
            holder.impl = nil;
            return nil;
        }];
        CFBridgingRelease(impl);
    }];
}

- (void)with:(void (^)(MTNetworkUsageManagerImpl *))f {
    [_queue dispatchOnQueue:^{
        __block __strong MTNetworkUsageManagerImpl *impl = nil;
        [_holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
            impl = (__bridge MTNetworkUsageManagerImpl *)holder.impl;
            return nil;
        }];
        f(impl);
    }];
}

- (void)addIncomingBytes:(NSUInteger)incomingBytes interface:(MTNetworkUsageManagerInterface)interface {
    [self with:^(MTNetworkUsageManagerImpl *impl) {
        [impl addIncomingBytes:incomingBytes interface:interface];
    }];
}

- (void)addOutgoingBytes:(NSUInteger)outgoingBytes interface:(MTNetworkUsageManagerInterface)interface {
    [self with:^(MTNetworkUsageManagerImpl *impl) {
        [impl addOutgoingBytes:outgoingBytes interface:interface];
    }];
}

- (void)resetKeys:(NSArray<NSNumber *> *)keys setKeys:(NSDictionary<NSNumber *, NSNumber *> *)setKeys completion:(void (^)())completion {
    [self with:^(MTNetworkUsageManagerImpl *impl) {
        [impl resetKeys:keys setKeys:setKeys completion:completion];
    }];
}

- (MTSignal *)currentStatsForKeys:(NSArray<NSNumber *> *)keys {
    return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
        [self with:^(MTNetworkUsageManagerImpl *impl) {
            NSDictionary *result = [impl currentStatsForKeys:keys];
            [subscriber putNext:result];
            [subscriber putCompletion];
        }];
        return nil;
    }];
}

@end


================================================
FILE: MTPKCS.h
================================================
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MTPKCS : NSObject

@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, strong, readonly) NSData *data;

+ (MTPKCS * _Nullable)parse:(const unsigned char *)buffer size:(int)size;

@end

NS_ASSUME_NONNULL_END


================================================
FILE: MTPKCS.m
================================================
#import "MTPKCS.h"

#include <openssl/x509.h>
#include <openssl/pkcs7.h>

@implementation MTPKCS

- (instancetype)initWithName:(NSString *)name data:(NSData *)data {
    self = [super init];
    if (self != nil) {
        _name = name;
        _data = data;
    }
    return self;
}

+ (MTPKCS * _Nullable)parse:(const unsigned char *)buffer size:(int)size {
    MTPKCS * _Nullable result = nil;
    PKCS7 *pkcs7 = NULL;
    STACK_OF(X509) *signers = NULL;
    
    pkcs7 = d2i_PKCS7(NULL, &buffer, size);
    if (pkcs7 == NULL) {
        return nil;
    }
    
    if (!PKCS7_type_is_signed(pkcs7)) {
        if (pkcs7) {
            PKCS7_free(pkcs7);
        }
        return nil;
    }
    
    signers = PKCS7_get0_signers(pkcs7, NULL, PKCS7_BINARY);
    if (signers == NULL) {
        if (pkcs7) {
            PKCS7_free(pkcs7);
        }
        return nil;
    }
    
    const X509* cert = sk_X509_pop(signers);
    if (cert == NULL) {
        if (signers) {
            sk_X509_free(signers);
        }
        if (pkcs7) {
            PKCS7_free(pkcs7);
        }
        
        return nil;
    }
    
    result = [[MTPKCS alloc] initWithName:[NSString stringWithUTF8String:cert->name] data:[NSData dataWithBytes:cert->cert_info->key->public_key->data length:cert->cert_info->key->public_key->length]];
    
    return result;
}

@end


================================================
FILE: MTProtoKit/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>FMWK</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>NSPrincipalClass</key>
	<string></string>
</dict>
</plist>


================================================
FILE: MTProtoKit/MTApiEnvironment.h
================================================


#import <Foundation/Foundation.h>

@interface MTSocksProxySettings : NSObject

@property (nonatomic, strong, readonly) NSString *ip;
@property (nonatomic, readonly) uint16_t port;
@property (nonatomic, strong, readonly) NSString *username;
@property (nonatomic, strong, readonly) NSString *password;
@property (nonatomic, strong, readonly) NSData *secret;

- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret;

+ (bool)secretSupportsExtendedPadding:(NSData *)data;

@end

@interface MTNetworkSettings : NSObject

@property (nonatomic, readonly) bool reducedBackupDiscoveryTimeout;

- (instancetype)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout;

@end

@interface MTApiEnvironment : NSObject

@property (nonatomic) int32_t apiId;
@property (nonatomic, strong, readonly) NSString *deviceModel;
@property (nonatomic, strong, readonly) NSString *systemVersion;
@property (nonatomic, strong) NSString *appVersion;
@property (nonatomic, strong, readonly) NSString *systemLangCode;
@property (nonatomic, strong) NSNumber *layer;
@property (nonatomic, strong, readonly) NSData *systemCode;
    
@property (nonatomic, strong) NSString *langPack;
@property (nonatomic, strong, readonly) NSString *langPackCode;

@property (nonatomic, strong, readonly) NSString *apiInitializationHash;

@property (nonatomic) bool disableUpdates;
@property (nonatomic) NSData *tcpPayloadPrefix;
@property (nonatomic) NSDictionary *datacenterAddressOverrides;

@property (nonatomic, strong, readonly) MTSocksProxySettings *socksProxySettings;
@property (nonatomic, strong, readonly) MTNetworkSettings *networkSettings;

@property (nonatomic, copy) void (^passwordInputHandler)(void);

- (MTApiEnvironment *)withUpdatedLangPackCode:(NSString *)langPackCode;
- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings;
- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings;
- (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode;

@end


================================================
FILE: MTProtoKit/MTApiEnvironment.m
================================================


#import "MTApiEnvironment.h"

#if TARGET_OS_IPHONE
#   import <UIKit/UIKit.h>
#else

#endif

#include <sys/sysctl.h>

#import <CommonCrypto/CommonDigest.h>

#define IFPGA_NAMESTRING                @"iFPGA"

#define IPHONE_1G_NAMESTRING            @"iPhone 1G"
#define IPHONE_3G_NAMESTRING            @"iPhone 3G"
#define IPHONE_3GS_NAMESTRING           @"iPhone 3GS"
#define IPHONE_4_NAMESTRING             @"iPhone 4"
#define IPHONE_4S_NAMESTRING            @"iPhone 4S"
#define IPHONE_5_NAMESTRING             @"iPhone 5"
#define IPHONE_5S_NAMESTRING             @"iPhone 5S"
#define IPHONE_6_NAMESTRING             @"iPhone 6"
#define IPHONE_6Plus_NAMESTRING             @"iPhone 6 Plus"
#define IPHONE_6S_NAMESTRING             @"iPhone 6S"
#define IPHONE_6SPlus_NAMESTRING             @"iPhone 6S Plus"
#define IPHONE_7_NAMESTRING             @"iPhone 7"
#define IPHONE_7Plus_NAMESTRING             @"iPhone 7 Plus"
#define IPHONE_8_NAMESTRING             @"iPhone 8"
#define IPHONE_8Plus_NAMESTRING             @"iPhone 8 Plus"
#define IPHONE_X_NAMESTRING             @"iPhone X"
#define IPHONE_SE_NAMESTRING             @"iPhone SE"
#define IPHONE_UNKNOWN_NAMESTRING       @"Unknown iPhone"
#define IPHONE_XS_NAMESTRING @"iPhone XS"
#define IPHONE_XSMAX_NAMESTRING @"iPhone XS Max"
#define IPHONE_XR_NAMESTRING @"iPhone XR"

#define IPOD_1G_NAMESTRING              @"iPod touch 1G"
#define IPOD_2G_NAMESTRING              @"iPod touch 2G"
#define IPOD_3G_NAMESTRING              @"iPod touch 3G"
#define IPOD_4G_NAMESTRING              @"iPod touch 4G"
#define IPOD_5G_NAMESTRING              @"iPod touch 5G"
#define IPOD_6G_NAMESTRING              @"iPod touch 6G"
#define IPOD_UNKNOWN_NAMESTRING         @"Unknown iPod"

#define IPAD_1G_NAMESTRING              @"iPad 1G"
#define IPAD_2G_NAMESTRING              @"iPad 2G"
#define IPAD_3G_NAMESTRING              @"iPad 3G"
#define IPAD_4G_NAMESTRING              @"iPad 4G"
#define IPAD_5G_NAMESTRING              @"iPad Air 2"
#define IPAD_6G_NAMESTRING  @"iPad Pro"
#define IPAD_PRO_3G_NAMESTRING  @"iPad Pro 12.9 (3rd gen)"
#define IPAD_PRO_11_NAMESTRING  @"iPad Pro 11"
#define IPAD_PRO_6G_NAMESTRING  @"iPad (6th gen)"
#define IPAD_PRO_10_5_NAMESTRING  @"iPad Pro 10.5"
#define IPAD_PRO_12_9_NAMESTRING  @"iPad Pro 12.9"
#define IPAD_UNKNOWN_NAMESTRING         @"Unknown iPad"

#define APPLETV_2G_NAMESTRING           @"Apple TV 2G"
#define APPLETV_3G_NAMESTRING           @"Apple TV 3G"
#define APPLETV_4G_NAMESTRING           @"Apple TV 4G"
#define APPLETV_UNKNOWN_NAMESTRING      @"Unknown Apple TV"

#define IOS_FAMILY_UNKNOWN_DEVICE       @"Unknown iOS device"

#define SIMULATOR_NAMESTRING            @"iPhone Simulator"
#define SIMULATOR_IPHONE_NAMESTRING     @"iPhone Simulator"
#define SIMULATOR_IPAD_NAMESTRING       @"iPad Simulator"
#define SIMULATOR_APPLETV_NAMESTRING    @"Apple TV Simulator" // :)

/*
 iPad8,5, iPad8,6, iPad8,7, iPad8,8 - iPad Pro 12.9" (3rd gen)
 iPad8,1, iPad8,2, iPad8,3, iPad8,4 - iPad Pro 11"
 iPad7,5, iPad7,6 - iPad 6th gen
 iPad7,3, iPad7,4 - iPad Pro 10.5"
 iPad7,1, iPad7,2 - iPad Pro 12.9" (2ng gen)
 */

typedef enum {
    UIDeviceUnknown,
    
    UIDeviceSimulator,
    UIDeviceSimulatoriPhone,
    UIDeviceSimulatoriPad,
    UIDeviceSimulatorAppleTV,
    
    UIDevice1GiPhone,
    UIDevice3GiPhone,
    UIDevice3GSiPhone,
    UIDevice4iPhone,
    UIDevice4SiPhone,
    UIDevice5iPhone,
    UIDevice5SiPhone,
    UIDevice6iPhone,
    UIDevice6PlusiPhone,
    UIDevice6siPhone,
    UIDevice6SPlusiPhone,
    UIDevice7iPhone,
    UIDevice7PlusiPhone,
    UIDevice8iPhone,
    UIDevice8PlusiPhone,
    UIDeviceXiPhone,
    UIDeviceSEPhone,
    UIDeviceXSiPhone,
    UIDeviceXSMaxiPhone,
    UIDeviceXRiPhone,
    
    UIDevice1GiPod,
    UIDevice2GiPod,
    UIDevice3GiPod,
    UIDevice4GiPod,
    UIDevice5GiPod,
    UIDevice6GiPod,
    
    UIDevice1GiPad,
    UIDevice2GiPad,
    UIDevice3GiPad,
    UIDevice4GiPad,
    UIDevice5GiPad,
    UIDevice6GiPad,
    
    UIDeviceiPadPro12_93g,
    UIDeviceiPadPro11,
    UIDeviceiPadPro6g,
    UIDeviceiPadPro10_5,
    UIDeviceiPadPro12_9,
    
    UIDeviceAppleTV2,
    UIDeviceAppleTV3,
    UIDeviceAppleTV4,
    
    UIDeviceUnknowniPhone,
    UIDeviceUnknowniPod,
    UIDeviceUnknowniPad,
    UIDeviceUnknownAppleTV,
    UIDeviceIFPGA,
    
    UIDeviceOSX
    
} UIDevicePlatform;

typedef enum {
    UIDeviceFamilyiPhone,
    UIDeviceFamilyiPod,
    UIDeviceFamilyiPad,
    UIDeviceFamilyAppleTV,
    UIDeviceFamilyUnknown,
    
} UIDeviceFamily;

@implementation MTSocksProxySettings

- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret {
    self = [super init];
    if (self != nil) {
        _ip = ip;
        _port = port;
        _username = username;
        _password = password;
        _secret = secret;
    }
    return self;
}

- (BOOL)isEqual:(id)object {
    if (![object isKindOfClass:[MTSocksProxySettings class]]) {
        return false;
    }
    MTSocksProxySettings *other = object;
    if ((other->_ip != nil) != (_ip != nil) || (_ip != nil && ![_ip isEqual:other->_ip])) {
        return false;
    }
    if (other->_port != _port) {
        return false;
    }
    if ((other->_username != nil) != (_username != nil) || (_username != nil && ![_username isEqual:other->_username])) {
        return false;
    }
    if ((other->_password != nil) != (_password != nil) || (_password != nil && ![_password isEqual:other->_password])) {
        return false;
    }
    if ((other->_secret != nil) != (_secret != nil) || (_secret != nil && ![_secret isEqual:other->_secret])) {
        return false;
    }
    return true;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%@:%d+%@+%@+%@", _ip, (int)_port, _username, _password, _secret];
}

+ (bool)secretSupportsExtendedPadding:(NSData *)data {
    if (data.length == 17) {
        uint8_t first = 0;
        [data getBytes:&first length:1];
        return (first == 0xdd);
    }
    return false;
}

@end

@implementation MTNetworkSettings

- (instancetype)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout {
    self = [super init];
    if (self != nil) {
        _reducedBackupDiscoveryTimeout = reducedBackupDiscoveryTimeout;
    }
    return self;
}

- (BOOL)isEqual:(id)object {
    if (![object isKindOfClass:[MTNetworkSettings class]]) {
        return false;
    }
    MTNetworkSettings *other = object;
    if (_reducedBackupDiscoveryTimeout != other->_reducedBackupDiscoveryTimeout) {
        return false;
    }
    return true;
}

@end

@implementation MTApiEnvironment

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        _deviceModel = [self platformString];
#if TARGET_OS_IPHONE
        _systemVersion = [[UIDevice currentDevice] systemVersion];
#else
        NSProcessInfo *pInfo = [NSProcessInfo processInfo];
        _systemVersion = [[[pInfo operatingSystemVersionString] componentsSeparatedByString:@" "] objectAtIndex:1];
#endif
        
NSString *suffix = @"";
#if TARGET_OS_OSX
#ifdef BETA
        suffix = @" BETA";
#endif
        
#ifdef APPSTORE
        suffix = @" APPSTORE";
#endif
        
#ifdef STABLE
        suffix = @" STABLE";
#endif
#endif
        NSString *versionString = [[NSString alloc] initWithFormat:@"%@ (%@)%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"], suffix];
        _appVersion = versionString;
        
        _systemLangCode = [[NSLocale preferredLanguages] objectAtIndex:0];
    #if TARGET_OS_OSX
        _langPack = @"macos";
    #else
         _langPack = @"ios";
    #endif
        _langPackCode = @"";
        
        [self _updateApiInitializationHash];
    }
    return self;
}

- (void)_updateApiInitializationHash {
    _apiInitializationHash = [[NSString alloc] initWithFormat:@"apiId=%" PRId32 "&deviceModel=%@&systemVersion=%@&appVersion=%@&langCode=%@&layer=%@&langPack=%@&langPackCode=%@&proxy=%@&systemCode=%@", _apiId, _deviceModel, _systemVersion, _appVersion, _systemLangCode, _layer, _langPack, _langPackCode, _socksProxySettings, _systemCode];
}

- (void)setLayer:(NSNumber *)layer {
    _layer = layer;
    
    [self _updateApiInitializationHash];
}

- (void)setAppVersion:(NSString *)appVersion {
    _appVersion = appVersion;
    
    [self _updateApiInitializationHash];
}

- (void)setLangPack:(NSString *)langPack {
    _langPack = langPack;
    
    [self _updateApiInitializationHash];
}

- (void)setLangPackCode:(NSString *)langPackCode {
    _langPackCode = langPackCode;
    
    [self _updateApiInitializationHash];
}

- (NSString *)platformString
{
    switch ([self platformType])
    {
        case UIDevice1GiPhone: return IPHONE_1G_NAMESTRING;
        case UIDevice3GiPhone: return IPHONE_3G_NAMESTRING;
        case UIDevice3GSiPhone: return IPHONE_3GS_NAMESTRING;
        case UIDevice4iPhone: return IPHONE_4_NAMESTRING;
        case UIDevice4SiPhone: return IPHONE_4S_NAMESTRING;
        case UIDevice5iPhone: return IPHONE_5_NAMESTRING;
        case UIDevice5SiPhone: return IPHONE_5S_NAMESTRING;
        case UIDevice6iPhone: return IPHONE_6_NAMESTRING;
        case UIDevice6PlusiPhone: return IPHONE_6Plus_NAMESTRING;
        case UIDevice6siPhone: return IPHONE_6S_NAMESTRING;
        case UIDevice6SPlusiPhone: return IPHONE_6SPlus_NAMESTRING;
        case UIDevice7iPhone: return IPHONE_7_NAMESTRING;
        case UIDevice7PlusiPhone: return IPHONE_7Plus_NAMESTRING;
        case UIDevice8iPhone: return IPHONE_8_NAMESTRING;
        case UIDevice8PlusiPhone: return IPHONE_8Plus_NAMESTRING;
        case UIDeviceXiPhone: return IPHONE_X_NAMESTRING;
        case UIDeviceSEPhone: return IPHONE_SE_NAMESTRING;
        case UIDeviceXSiPhone: return IPHONE_XS_NAMESTRING;
        case UIDeviceXSMaxiPhone: return IPHONE_XSMAX_NAMESTRING;
        case UIDeviceXRiPhone: return IPHONE_XR_NAMESTRING;
            
        case UIDeviceUnknowniPhone: return IPHONE_UNKNOWN_NAMESTRING;
            
        case UIDevice1GiPod: return IPOD_1G_NAMESTRING;
        case UIDevice2GiPod: return IPOD_2G_NAMESTRING;
        case UIDevice3GiPod: return IPOD_3G_NAMESTRING;
        case UIDevice4GiPod: return IPOD_4G_NAMESTRING;
        case UIDevice5GiPod: return IPOD_5G_NAMESTRING;
        case UIDevice6GiPod: return IPOD_6G_NAMESTRING;
        case UIDeviceUnknowniPod: return IPOD_UNKNOWN_NAMESTRING;
            
        case UIDevice1GiPad : return IPAD_1G_NAMESTRING;
        case UIDevice2GiPad : return IPAD_2G_NAMESTRING;
        case UIDevice3GiPad : return IPAD_3G_NAMESTRING;
        case UIDevice4GiPad : return IPAD_4G_NAMESTRING;
        case UIDevice5GiPad : return IPAD_5G_NAMESTRING;
        case UIDevice6GiPad : return IPAD_6G_NAMESTRING;
        case UIDeviceiPadPro12_93g : return IPAD_PRO_12_9_NAMESTRING;
        case UIDeviceiPadPro11 : return IPAD_PRO_11_NAMESTRING;
        case UIDeviceiPadPro6g : return IPAD_PRO_6G_NAMESTRING;
        case UIDeviceiPadPro10_5 : return IPAD_PRO_10_5_NAMESTRING;
        case UIDeviceiPadPro12_9 : return IPAD_PRO_12_9_NAMESTRING;
        case UIDeviceUnknowniPad : return IPAD_UNKNOWN_NAMESTRING;
            
        case UIDeviceAppleTV2 : return APPLETV_2G_NAMESTRING;
        case UIDeviceAppleTV3 : return APPLETV_3G_NAMESTRING;
        case UIDeviceAppleTV4 : return APPLETV_4G_NAMESTRING;
        case UIDeviceUnknownAppleTV: return APPLETV_UNKNOWN_NAMESTRING;
            
        case UIDeviceSimulator: return SIMULATOR_NAMESTRING;
        case UIDeviceSimulatoriPhone: return SIMULATOR_IPHONE_NAMESTRING;
        case UIDeviceSimulatoriPad: return SIMULATOR_IPAD_NAMESTRING;
        case UIDeviceSimulatorAppleTV: return SIMULATOR_APPLETV_NAMESTRING;
            
        case UIDeviceIFPGA: return IFPGA_NAMESTRING;
            
        case UIDeviceOSX: return [self macHWName];
        
        default: return IOS_FAMILY_UNKNOWN_DEVICE;
    }
}
    
-(NSString *)macHWName {
    size_t len = 0;
    sysctlbyname("hw.model", NULL, &len, NULL, 0);
    if (len) {
        char *model = malloc(len*sizeof(char));
        sysctlbyname("hw.model", model, &len, NULL, 0);
        NSString *name = [[NSString alloc] initWithUTF8String:model];
        free(model);
        return name;
    };
    return @"macOS";
}

- (NSUInteger)platformType
{
#if TARGET_OS_IPHONE
    NSString *platform = [self platform];
    
    // The ever mysterious iFPGA
    if ([platform isEqualToString:@"iFPGA"])        return UIDeviceIFPGA;
    
    // iPhone
    if ([platform isEqualToString:@"iPhone1,1"])    return UIDevice1GiPhone;
    if ([platform isEqualToString:@"iPhone1,2"])    return UIDevice3GiPhone;
    if ([platform hasPrefix:@"iPhone2"])            return UIDevice3GSiPhone;
    if ([platform hasPrefix:@"iPhone3"])            return UIDevice4iPhone;
    if ([platform hasPrefix:@"iPhone4"])            return UIDevice4SiPhone;
    if ([platform hasPrefix:@"iPhone5"])            return UIDevice5iPhone;
    if ([platform hasPrefix:@"iPhone6"])            return UIDevice5SiPhone;
    
    if ([platform isEqualToString:@"iPhone7,1"])    return UIDevice6PlusiPhone;
    if ([platform isEqualToString:@"iPhone7,2"])    return UIDevice6iPhone;
    if ([platform isEqualToString:@"iPhone8,1"])    return UIDevice6siPhone;
    if ([platform isEqualToString:@"iPhone8,2"])    return UIDevice6SPlusiPhone;
    if ([platform isEqualToString:@"iPhone9,1"])    return UIDevice7iPhone;
    if ([platform isEqualToString:@"iPhone9,3"])    return UIDevice7iPhone;
    if ([platform isEqualToString:@"iPhone9,2"])    return UIDevice7PlusiPhone;
    if ([platform isEqualToString:@"iPhone9,4"])    return UIDevice7PlusiPhone;
    
    if ([platform isEqualToString:@"iPhone10,1"])    return UIDevice8iPhone;
    if ([platform isEqualToString:@"iPhone10,4"])    return UIDevice8iPhone;
    if ([platform isEqualToString:@"iPhone10,2"])    return UIDevice8PlusiPhone;
    if ([platform isEqualToString:@"iPhone10,5"])    return UIDevice8PlusiPhone;
    if ([platform isEqualToString:@"iPhone10,3"])    return UIDeviceXiPhone;
    if ([platform isEqualToString:@"iPhone10,6"])    return UIDeviceXiPhone;
    if ([platform isEqualToString:@"iPhone11,2"])    return UIDeviceXSiPhone;
    if ([platform isEqualToString:@"iPhone11,6"])    return UIDeviceXSMaxiPhone;
    if ([platform isEqualToString:@"iPhone11,4"])    return UIDeviceXSMaxiPhone;
    if ([platform isEqualToString:@"iPhone11,8"])    return UIDeviceXRiPhone;
    
    if ([platform isEqualToString:@"iPhone8,4"])    return UIDeviceSEPhone;
    
    // iPod
    if ([platform hasPrefix:@"iPod1"])              return UIDevice1GiPod;
    if ([platform hasPrefix:@"iPod2"])              return UIDevice2GiPod;
    if ([platform hasPrefix:@"iPod3"])              return UIDevice3GiPod;
    if ([platform hasPrefix:@"iPod4"])              return UIDevice4GiPod;
    if ([platform hasPrefix:@"iPod5"])              return UIDevice5GiPod;
    if ([platform hasPrefix:@"iPod7"])              return UIDevice6GiPod;
    
    // iPad
    if ([platform hasPrefix:@"iPad1"])              return UIDevice1GiPad;
    if ([platform hasPrefix:@"iPad2"])              return UIDevice2GiPad;
    if ([platform hasPrefix:@"iPad3"])              return UIDevice3GiPad;
    if ([platform hasPrefix:@"iPad4"])              return UIDevice4GiPad;
    if ([platform hasPrefix:@"iPad5"])              return UIDevice5GiPad;
    if ([platform hasPrefix:@"iPad6"])              return UIDevice6GiPad;
    
    if ([platform isEqualToString:@"iPad8,5"] ||
        [platform isEqualToString:@"iPad8,6"] ||
        [platform isEqualToString:@"iPad8,7"] ||
        [platform isEqualToString:@"iPad8,8"]) {
        return UIDeviceiPadPro12_93g;
    }
    
    if ([platform isEqualToString:@"iPad8,1"] ||
        [platform isEqualToString:@"iPad8,2"] ||
        [platform isEqualToString:@"iPad8,3"] ||
        [platform isEqualToString:@"iPad8,4"]) {
        return UIDeviceiPadPro11;
    }
    
    if ([platform isEqualToString:@"iPad7,5"] ||
        [platform isEqualToString:@"iPad7,6"]) {
        return UIDeviceiPadPro6g;
    }
    
    if ([platform isEqualToString:@"iPad7,3"] ||
        [platform isEqualToString:@"iPad7,4"]) {
        return UIDeviceiPadPro10_5;
    }
    
    if ([platform isEqualToString:@"iPad7,1"] ||
        [platform isEqualToString:@"iPad7,2"]) {
        return UIDeviceiPadPro12_9;
    }
    
    // Apple TV
    if ([platform hasPrefix:@"AppleTV2"])           return UIDeviceAppleTV2;
    if ([platform hasPrefix:@"AppleTV3"])           return UIDeviceAppleTV3;
    
    if ([platform hasPrefix:@"iPhone"])             return UIDeviceUnknowniPhone;
    if ([platform hasPrefix:@"iPod"])               return UIDeviceUnknowniPod;
    if ([platform hasPrefix:@"iPad"])               return UIDeviceUnknowniPad;
    if ([platform hasPrefix:@"AppleTV"])            return UIDeviceUnknownAppleTV;
    
#define IPAD_PRO_3G_NAMESTRING  @"iPad Pro 12.9 (3rd gen)"
#define IPAD_PRO_11_NAMESTRING  @"iPad Pro 11"
#define IPAD_PRO_6G_NAMESTRING  @"iPad (6th gen)"
#define IPAD_PRO_10_5_NAMESTRING  @"iPad Pro 10.5"
#define IPAD_PRO_12_9_NAMESTRING  @"iPad Pro 12.9"
    
    // Simulator thanks Jordan Breeding
    if ([platform hasSuffix:@"86"] || [platform isEqual:@"x86_64"])
    {
        return UIDeviceSimulatoriPhone;
    }
#else
    return UIDeviceOSX;
#endif
    
    return UIDeviceUnknown;
}

- (NSString *)getSysInfoByName:(char *)typeSpecifier
{
    size_t size;
    sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
    
    char *answer = malloc(size);
    sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
    
    NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
    
    free(answer);
    return results;
}

- (NSString *)platform
{
    return [self getSysInfoByName:"hw.machine"];
}

- (MTApiEnvironment *)withUpdatedLangPackCode:(NSString *)langPackCode {
    MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
    
    result.apiId = self.apiId;
    result.appVersion = self.appVersion;
    result.layer = self.layer;
    
    result.langPack = self.langPack;
    
    result->_langPackCode = langPackCode;
    
    result.disableUpdates = self.disableUpdates;
    result.tcpPayloadPrefix = self.tcpPayloadPrefix;
    result.datacenterAddressOverrides = self.datacenterAddressOverrides;
    result->_socksProxySettings = self.socksProxySettings;
    result->_networkSettings = self.networkSettings;
    result->_systemCode = self.systemCode;
    
    [result _updateApiInitializationHash];
    
    return result;
}

- (instancetype)copyWithZone:(NSZone *)__unused zone {
    MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
    
    result.apiId = self.apiId;
    result.appVersion = self.appVersion;
    result.layer = self.layer;
    
    result.langPack = self.langPack;
    
    result->_langPackCode = self.langPackCode;
    result->_socksProxySettings = self.socksProxySettings;
    result->_networkSettings = self.networkSettings;
    result->_systemCode = self.systemCode;
    
    result.disableUpdates = self.disableUpdates;
    result.tcpPayloadPrefix = self.tcpPayloadPrefix;
    result.datacenterAddressOverrides = self.datacenterAddressOverrides;
    
    [result _updateApiInitializationHash];
    
    return result;
}

- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings {
    MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
    
    result.apiId = self.apiId;
    result.appVersion = self.appVersion;
    result.layer = self.layer;
    
    result.langPack = self.langPack;
    
    result->_langPackCode = self.langPackCode;
    result->_socksProxySettings = socksProxySettings;
    result->_networkSettings = self.networkSettings;
    result->_systemCode = self.systemCode;
    
    result.disableUpdates = self.disableUpdates;
    result.tcpPayloadPrefix = self.tcpPayloadPrefix;
    result.datacenterAddressOverrides = self.datacenterAddressOverrides;
    
    [result _updateApiInitializationHash];
    
    return result;
}

- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings {
    MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
    
    result.apiId = self.apiId;
    result.appVersion = self.appVersion;
    result.layer = self.layer;
    
    result.langPack = self.langPack;
    
    result->_langPackCode = self.langPackCode;
    result->_socksProxySettings = self.socksProxySettings;
    result->_networkSettings = networkSettings;
    result->_systemCode = self.systemCode;
    
    result.disableUpdates = self.disableUpdates;
    result.tcpPayloadPrefix = self.tcpPayloadPrefix;
    result.datacenterAddressOverrides = self.datacenterAddressOverrides;
    
    [result _updateApiInitializationHash];
    
    return result;
}

- (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode {
    MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
    
    result.apiId = self.apiId;
    result.appVersion = self.appVersion;
    result.layer = self.layer;
    
    result.langPack = self.langPack;
    
    result->_langPackCode = self.langPackCode;
    result->_socksProxySettings = self.socksProxySettings;
    result->_networkSettings = self.networkSettings;
    result->_systemCode = systemCode;
    
    result.disableUpdates = self.disableUpdates;
    result.tcpPayloadPrefix = self.tcpPayloadPrefix;
    result.datacenterAddressOverrides = self.datacenterAddressOverrides;
    
    [result _updateApiInitializationHash];
    
    return result;
}

@end



================================================
FILE: MTProtoKit/MTBadMsgNotificationMessage.h
================================================
#import <Foundation/Foundation.h>

@interface MTBadMsgNotificationMessage : NSObject

@property (nonatomic, readonly) int64_t badMessageId;
@property (nonatomic, readonly) int32_t badMessageSeqNo;
@property (nonatomic, readonly) int32_t errorCode;

- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode;

@end

@interface MTBadServerSaltNotificationMessage : MTBadMsgNotificationMessage

@property (nonatomic, readonly) int64_t nextServerSalt;

- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode nextServerSalt:(int64_t)nextServerSalt;

@end


================================================
FILE: MTProtoKit/MTBadMsgNotificationMessage.m
================================================
#import "MTBadMsgNotificationMessage.h"

@implementation MTBadMsgNotificationMessage

- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode
{
    self = [super init];
    if (self != nil)
    {
        _badMessageId = badMessageId;
        _badMessageSeqNo = badMessageSeqNo;
        _errorCode = errorCode;
    }
    return self;
}

@end

@implementation MTBadServerSaltNotificationMessage

- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode nextServerSalt:(int64_t)nextServerSalt
{
    self = [super initWithBadMessageId:badMessageId badMessageSeqNo:badMessageSeqNo errorCode:errorCode];
    if (self != nil)
    {
        _nextServerSalt = nextServerSalt;
    }
    return self;
}

@end

================================================
FILE: MTProtoKit/MTBuffer.h
================================================
#import <Foundation/Foundation.h>

@interface MTBuffer : NSObject

- (void)appendInt32:(int32_t)value;
- (void)appendInt64:(int64_t)value;
- (void)appendBytes:(void const *)bytes length:(NSUInteger)length;

- (NSData *)data;

@end

@interface MTBuffer (TL)

- (void)appendTLBytes:(NSData *)bytes;
- (void)appendTLString:(NSString *)string;

@end

================================================
FILE: MTProtoKit/MTBuffer.m
================================================
#import "MTBuffer.h"

@interface MTBuffer ()
{
    NSMutableData *_data;
}

@end

@implementation MTBuffer

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        _data = [[NSMutableData alloc] init];
    }
    return self;
}

- (void)appendInt32:(int32_t)value
{
    [_data appendBytes:&value length:4];
}

- (void)appendInt64:(int64_t)value
{
    [_data appendBytes:&value length:8];
}

- (void)appendBytes:(void const *)bytes length:(NSUInteger)length
{
    [_data appendBytes:bytes length:length];
}

- (NSData *)data
{
    return [[NSData alloc] initWithData:_data];
}

@end

static inline int roundUp(int numToRound, int multiple)
{
    return multiple == 0 ? numToRound : ((numToRound % multiple) == 0 ? numToRound : (numToRound + multiple - (numToRound % multiple)));
}

@implementation MTBuffer (TL)

- (void)appendTLBytes:(NSData *)bytes
{
    int32_t length = (int32_t)bytes.length;
    
    if (bytes == nil || length == 0)
    {
        [self appendInt32:0];
        return;
    }
    
    int paddingBytes = 0;
    
    if (length >= 254)
    {
        uint8_t tmp = 254;
        [self appendBytes:&tmp length:1];
        
        [self appendBytes:(const uint8_t *)&length length:3];
        
        paddingBytes = roundUp(length, 4) - length;
    }
    else
    {
        [self appendBytes:(const uint8_t *)&length length:1];
        paddingBytes = roundUp(length + 1, 4) - (length + 1);
    }
    
    [self appendBytes:bytes.bytes length:length];
    
    uint8_t tmp = 0;
    for (int i = 0; i < paddingBytes; i++)
        [self appendBytes:&tmp length:1];
}

- (void)appendTLString:(NSString *)string
{
    [self appendTLBytes:[string dataUsingEncoding:NSUTF8StringEncoding]];
}

@end


================================================
FILE: MTProtoKit/MTBufferReader.h
================================================
#import "MTBuffer.h"

@interface MTBufferReader : NSObject

- (instancetype)initWithData:(NSData *)data;

- (bool)readBytes:(void *)bytes length:(NSUInteger)length;
- (bool)readInt32:(int32_t *)value;
- (bool)readInt64:(int64_t *)value;
- (NSData *)readRest;

@end

@interface MTBufferReader (TL)

- (bool)readTLString:(__autoreleasing NSString **)value;
- (bool)readTLBytes:(__autoreleasing NSData **)value;

@end


================================================
FILE: MTProtoKit/MTBufferReader.m
================================================
#import "MTBufferReader.h"

@interface MTBufferReader ()
{
    NSData *_data;
    NSUInteger _offset;
}

@end

@implementation MTBufferReader

- (instancetype)initWithData:(NSData *)data
{
    self = [super init];
    if (self != nil)
    {
        _data = data;
    }
    return self;
}

- (bool)readBytes:(void *)bytes length:(NSUInteger)length
{
    if (_offset + length > _data.length)
        return false;
    if (bytes != NULL)
        memcpy(bytes, _data.bytes + _offset, length);
    _offset += length;
    return true;
}

- (bool)readInt32:(int32_t *)value
{
    return [self readBytes:value length:4];
}

- (bool)readInt64:(int64_t *)value
{
    return [self readBytes:value length:8];
}

- (NSData *)readRest
{
    return [_data subdataWithRange:NSMakeRange(_offset, _data.length - _offset)];
}

@end

static inline int roundUpInput(int32_t numToRound, int32_t multiple)
{
    if (multiple == 0)
    {
        return numToRound;
    }
    
    int remainder = numToRound % multiple;
    if (remainder == 0)
    {
        return numToRound;
    }
    
    return numToRound + multiple - remainder;
}

@implementation MTBufferReader (TL)

- (bool)readTLString:(__autoreleasing NSString **)value
{
    NSData *bytes = nil;
    if ([self readTLBytes:&bytes])
    {
        if (value)
            *value = [[NSString alloc] initWithData:bytes encoding:NSUTF8StringEncoding];
        return true;
    }
    
    return false;
}

- (bool)readTLBytes:(__autoreleasing NSData **)value
{
    uint8_t tmp = 0;
    if ([self readBytes:&tmp length:1])
    {
        NSUInteger paddingBytes = 0;
        
        int32_t length = tmp;
        if (length == 254)
        {
            length = 0;
            
            if (![self readBytes:((uint8_t *)&length) + 1 length:3])
                return false;
            
            length >>= 8;
            
            paddingBytes = roundUpInput(length, 4) - length;
        }
        else
        {
            paddingBytes = roundUpInput(length + 1, 4) - (length + 1);
        }
        
        uint8_t *bytes = (uint8_t *)malloc(length);
        if (![self readBytes:bytes length:length])
            return false;
        
        NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true];
        
        for (int i = 0; i < paddingBytes; i++)
        {
            if (![self readBytes:&tmp length:1])
                return false;
        }
        
        if (value)
            *value = result;
        
        return true;
    }
    
    return false;
}

@end


================================================
FILE: MTProtoKit/MTContext.h
================================================
#import <Foundation/Foundation.h>

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTDatacenterAuthInfo.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTDatacenterAuthInfo.h>
#else
#   import <MTProtoKit/MTDatacenterAuthInfo.h>
#endif

@class MTDatacenterAddress;
@class MTDatacenterAddressSet;
@protocol MTSerialization;
@class MTContext;
@class MTTransportScheme;
@protocol MTKeychain;
@class MTSessionInfo;
@class MTApiEnvironment;
@class MTSignal;

@protocol MTContextChangeListener <NSObject>

@optional

- (void)contextDatacenterAddressSetUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet;
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo;
- (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken;
- (void)contextDatacenterTransportSchemesUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)contextIsPasswordRequiredUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)contextDatacenterPublicKeysUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
- (MTSignal *)fetchContextDatacenterPublicKeys:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)contextApiEnvironmentUpdated:(MTContext *)context apiEnvironment:(MTApiEnvironment *)apiEnvironment;
- (MTSignal *)isContextNetworkAccessAllowed:(MTContext *)context;

@end

@interface MTContextBlockChangeListener : NSObject <MTContextChangeListener>

@property (nonatomic, copy) void (^contextIsPasswordRequiredUpdated)(MTContext *, NSInteger);
@property (nonatomic, copy) MTSignal *(^fetchContextDatacenterPublicKeys)(MTContext *, NSInteger);
@property (nonatomic, copy) MTSignal *(^isContextNetworkAccessAllowed)(MTContext *);

@end

@interface MTContext : NSObject

@property (nonatomic, strong) id<MTKeychain> keychain;

@property (nonatomic, strong, readonly) id<MTSerialization> serialization;
@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment;
@property (nonatomic, readonly) bool isTestingEnvironment;
@property (nonatomic, readonly) bool useTempAuthKeys;

- (instancetype)initWithSerialization:(id<MTSerialization>)serialization apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys;

- (void)performBatchUpdates:(void (^)())block;

- (void)addChangeListener:(id<MTContextChangeListener>)changeListener;
- (void)removeChangeListener:(id<MTContextChangeListener>)changeListener;

- (void)setDiscoverBackupAddressListSignal:(MTSignal *)signal;

- (NSTimeInterval)globalTime;
- (NSTimeInterval)globalTimeDifference;
- (NSTimeInterval)globalTimeOffsetFromUTC;
- (void)setGlobalTimeDifference:(NSTimeInterval)globalTimeDifference;

- (void)setSeedAddressSetForDatacenterWithId:(NSInteger)datacenterId seedAddressSet:(MTDatacenterAddressSet *)seedAddressSet;
- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes;
- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address;
- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media isProxy:(bool)isProxy;
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo;

- (bool)isPasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId;
- (bool)updatePasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId required:(bool)required;

- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo;
- (void)collectSessionIdsForCleanupWithAuthKeyId:(int64_t)authKeyId completion:(void (^)(NSArray *sessionIds))completion;
- (void)sessionIdsDeletedForAuthKeyId:(int64_t)authKeyId sessionIds:(NSArray *)sessionIds;

- (NSArray *)knownDatacenterIds;
- (void)enumerateAddressSetsForDatacenters:(void (^)(NSInteger datacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop))block;

- (MTDatacenterAddressSet *)addressSetForDatacenterWithId:(NSInteger)datacenterId;
- (void)reportTransportSchemeFailureForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme;
- (void)reportTransportSchemeSuccessForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme;
- (void)invalidateTransportSchemesForDatacenterIds:(NSArray<NSNumber *> * _Nonnull)datacenterIds;
- (void)invalidateTransportSchemesForKnownDatacenterIds;
- (MTTransportScheme * _Nullable)chooseTransportSchemeForConnectionToDatacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes;
- (NSArray<MTTransportScheme *> * _Nonnull)transportSchemesForDatacenterWithId:(NSInteger)datacenterId media:(bool)media enforceMedia:(bool)enforceMedia isProxy:(bool)isProxy;
- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId media:(bool)media;
- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media;
- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media;
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId;
    
- (NSArray<NSDictionary *> *)publicKeysForDatacenterWithId:(NSInteger)datacenterId;
- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
- (void)publicKeysForDatacenterWithIdRequired:(NSInteger)datacenterId;

- (void)removeAllAuthTokens;
- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId;
- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken;

- (void)addressSetForDatacenterWithIdRequired:(NSInteger)datacenterId;
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn;
- (void)tempAuthKeyForDatacenterWithIdRequired:(NSInteger)datacenterId keyType:(MTDatacenterAuthTempKeyType)keyType;
- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id)authToken masterDatacenterId:(NSInteger)masterDatacenterId;

- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address;
    
- (void)updateApiEnvironment:(MTApiEnvironment *(^)(MTApiEnvironment *))f;

- (void)beginExplicitBackupAddressDiscovery;

@end


================================================
FILE: MTProtoKit/MTContext.m
================================================
#import "MTContext.h"

#import <inttypes.h>

#import "MTLogging.h"
#import "MTTimer.h"
#import "MTQueue.h"
#import "MTKeychain.h"

#import "MTDatacenterAddressSet.h"
#import "MTDatacenterAddress.h"
#import "MTDatacenterAuthInfo.h"
#import "MTDatacenterSaltInfo.h"
#import "MTSessionInfo.h"
#import "MTApiEnvironment.h"

#import "MTDiscoverDatacenterAddressAction.h"
#import "MTDatacenterAuthAction.h"
#import "MTDatacenterTransferAuthAction.h"

#import "MTTransportScheme.h"
#import "MTTcpTransport.h"

#import "MTApiEnvironment.h"

#import <libkern/OSAtomic.h>

#import "MTDiscoverConnectionSignals.h"

#import "MTTransportSchemeStats.h"

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTDisposable.h>
#   import <MTProtoKitDynamic/MTSignal.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTDisposable.h>
#   import <MTProtoKitMac/MTSignal.h>
#else
#   import <MTProtoKit/MTDisposable.h>
#   import <MTProtoKit/MTSignal.h>
#endif

@implementation MTContextBlockChangeListener

- (void)contextIsPasswordRequiredUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId
{
    if (_contextIsPasswordRequiredUpdated)
        _contextIsPasswordRequiredUpdated(context, datacenterId);
}

- (MTSignal *)fetchContextDatacenterPublicKeys:(MTContext *)context datacenterId:(NSInteger)datacenterId {
    if (_fetchContextDatacenterPublicKeys) {
        return _fetchContextDatacenterPublicKeys(context, datacenterId);
    } else {
        return nil;
    }
}

- (MTSignal *)isContextNetworkAccessAllowed:(MTContext *)context {
    if (_isContextNetworkAccessAllowed) {
        return _isContextNetworkAccessAllowed(context);
    } else {
        return nil;
    }
}

@end

@interface MTTransportSchemeKey : NSObject<NSCoding, NSCopying>

@property (nonatomic, readonly) NSInteger datacenterId;
@property (nonatomic, readonly) bool isProxy;
@property (nonatomic, readonly) bool isMedia;

@end

@implementation MTTransportSchemeKey

- (instancetype)initWithDatacenterId:(NSInteger)datacenterId isProxy:(bool)isProxy isMedia:(bool)isMedia {
    self = [super init];
    if (self != nil) {
        _datacenterId = datacenterId;
        _isProxy = isProxy;
        _isMedia = isMedia;
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    return [self initWithDatacenterId:[aDecoder decodeIntegerForKey:@"datacenterId"] isProxy:[aDecoder decodeBoolForKey:@"isProxy"] isMedia:[aDecoder decodeBoolForKey:@"isMedia"]];
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeInteger:_datacenterId forKey:@"datacenterId"];
    [aCoder encodeBool:_isProxy forKey:@"isProxy"];
    [aCoder encodeBool:_isMedia forKey:@"isMedia"];
}

- (instancetype)copyWithZone:(NSZone *)__unused zone {
    return self;
}

- (NSUInteger)hash {
    return _datacenterId * 31 * 31 + (_isProxy ? 1 : 0) * 31 + (_isMedia ? 1 : 0);
}

- (BOOL)isEqual:(id)object {
    if (![object isKindOfClass:[MTTransportSchemeKey class]]) {
        return false;
    }
    MTTransportSchemeKey *other = (MTTransportSchemeKey *)object;
    if (_datacenterId != other->_datacenterId) {
        return false;
    }
    if (_isProxy != other->_isProxy) {
        return false;
    }
    if (_isMedia != other->_isMedia) {
        return false;
    }
    return true;
}

@end

@interface MTContext () <MTDiscoverDatacenterAddressActionDelegate, MTDatacenterAuthActionDelegate, MTDatacenterTransferAuthActionDelegate>
{
    int64_t _uniqueId;
    
    NSTimeInterval _globalTimeDifference;
    
    NSMutableDictionary *_datacenterSeedAddressSetById;
    NSMutableDictionary *_datacenterAddressSetById;
    NSMutableDictionary<MTTransportSchemeKey *, MTTransportScheme *> *_datacenterManuallySelectedSchemeById;
    
    NSMutableDictionary<NSNumber *, NSMutableDictionary<MTDatacenterAddress *, MTTransportSchemeStats *> *> *_transportSchemeStats;
    MTTimer *_schemeStatsSyncTimer;
    
    NSMutableDictionary *_datacenterAuthInfoById;
    
    NSMutableDictionary *_datacenterPublicKeysById;
    
    NSMutableDictionary *_authTokenById;
    
    NSMutableArray *_changeListeners;
    
    MTSignal *_discoverBackupAddressListSignal;
    
    NSMutableDictionary *_discoverDatacenterAddressActions;
    NSMutableDictionary *_datacenterAuthActions;
    NSMutableDictionary *_datacenterTempAuthActions;
    NSMutableDictionary *_datacenterTransferAuthActions;
    
    NSMutableDictionary *_cleanupSessionIdsByAuthKeyId;
    NSMutableArray *_currentSessionInfos;
    
    NSMutableDictionary *_periodicTasksTimerByDatacenterId;
    
    volatile OSSpinLock _passwordEntryRequiredLock;
    NSMutableDictionary *_passwordRequiredByDatacenterId;
    
    NSMutableDictionary *_transportSchemeDisposableByDatacenterId;
    id<MTDisposable> _backupAddressListDisposable;
    
    NSMutableDictionary<NSNumber *, id<MTDisposable> > *_fetchPublicKeysActions;
    
    MTDisposableSet *_cleanupSessionInfoDisposables;
}

@end

@implementation MTContext

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        NSAssert(false, @"use initWithSerialization:apiEnvironment:");
    }
    return self;
}

- (instancetype)initWithSerialization:(id<MTSerialization>)serialization apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys
{
#ifdef DEBUG
    NSAssert(serialization != nil, @"serialization should not be nil");
    NSAssert(apiEnvironment != nil, @"apiEnvironment should not be nil");
#endif
    
    self = [super init];
    if (self != nil)
    {
        arc4random_buf(&_uniqueId, sizeof(_uniqueId));
        
        _serialization = serialization;
        _apiEnvironment = apiEnvironment;
        _isTestingEnvironment = isTestingEnvironment;
        _useTempAuthKeys = useTempAuthKeys;
        
        _datacenterSeedAddressSetById = [[NSMutableDictionary alloc] init];
        
        _datacenterAddressSetById = [[NSMutableDictionary alloc] init];
        _datacenterManuallySelectedSchemeById = [[NSMutableDictionary alloc] init];
        
        _transportSchemeStats = [[NSMutableDictionary alloc] init];
        
        _datacenterAuthInfoById = [[NSMutableDictionary alloc] init];
        _datacenterPublicKeysById = [[NSMutableDictionary alloc] init];
        
        _authTokenById = [[NSMutableDictionary alloc] init];
        
        _changeListeners = [[NSMutableArray alloc] init];
        
        _discoverDatacenterAddressActions = [[NSMutableDictionary alloc] init];
        _datacenterAuthActions = [[NSMutableDictionary alloc] init];
        _datacenterTempAuthActions = [[NSMutableDictionary alloc] init];
        _datacenterTransferAuthActions = [[NSMutableDictionary alloc] init];
        
        _cleanupSessionIdsByAuthKeyId = [[NSMutableDictionary alloc] init];
        _currentSessionInfos = [[NSMutableArray alloc] init];
        
        _passwordRequiredByDatacenterId = [[NSMutableDictionary alloc] init];
        
        _fetchPublicKeysActions = [[NSMutableDictionary alloc] init];
        
        _cleanupSessionInfoDisposables = [[MTDisposableSet alloc] init];
        
        [self updatePeriodicTasks];
    }
    return self;
}

- (void)dealloc
{
    [self cleanup];
}

+ (MTQueue *)contextQueue
{
    static MTQueue *queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        queue = [[MTQueue alloc] initWithName:"com.mtproto.MTContextQueue"];
    });
    return queue;
}

- (void)cleanup
{
    NSDictionary *datacenterAuthActions = _datacenterAuthActions;
    _datacenterAuthActions = nil;
    
    NSDictionary *datacenterTempAuthActions = _datacenterTempAuthActions;
    _datacenterTempAuthActions = nil;
    
    NSDictionary *discoverDatacenterAddressActions = _discoverDatacenterAddressActions;
    _discoverDatacenterAddressActions = nil;
    
    NSDictionary *datacenterTransferAuthActions = _datacenterTransferAuthActions;
    _datacenterTransferAuthActions = nil;
    
    NSDictionary *fetchPublicKeysActions = _fetchPublicKeysActions;
    _fetchPublicKeysActions = nil;
    
    id<MTDisposable> cleanupSessionInfoDisposables = _cleanupSessionInfoDisposables;
    
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        for (NSNumber *nDatacenterId in discoverDatacenterAddressActions)
        {
            MTDiscoverDatacenterAddressAction *action = discoverDatacenterAddressActions[nDatacenterId];
            action.delegate = nil;
            [action cancel];
        }

        for (NSNumber *nDatacenterId in datacenterAuthActions)
        {
            MTDatacenterAuthAction *action = datacenterAuthActions[nDatacenterId];
            action.delegate = nil;
            [action cancel];
        }
        
        for (NSNumber *nDatacenterId in datacenterTempAuthActions)
        {
            MTDatacenterAuthAction *action = datacenterTempAuthActions[nDatacenterId];
            action.delegate = nil;
            [action cancel];
        }
        
        for (NSNumber *nDatacenterId in datacenterTransferAuthActions)
        {
            MTDatacenterTransferAuthAction *action = datacenterTransferAuthActions[nDatacenterId];
            action.delegate = nil;
            [action cancel];
        }
        
        for (NSNumber *nDatacenterId in fetchPublicKeysActions)
        {
            id<MTDisposable> disposable = fetchPublicKeysActions[nDatacenterId];
            [disposable dispose];
        }
        
        [cleanupSessionInfoDisposables dispose];
    }];
}

- (void)performBatchUpdates:(void (^)())block
{
    if (block != nil)
        [[MTContext contextQueue] dispatchOnQueue:block];
}

- (void)setKeychain:(id<MTKeychain>)keychain
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        _keychain = keychain;
        
        if (_keychain != nil)
        {
            NSNumber *nGlobalTimeDifference = [keychain objectForKey:@"globalTimeDifference" group:@"temp"];
            if (nGlobalTimeDifference != nil)
                _globalTimeDifference = [nGlobalTimeDifference doubleValue];
            
            NSDictionary *datacenterAddressSetById = [keychain objectForKey:@"datacenterAddressSetById" group:@"persistent"];
            if (datacenterAddressSetById != nil) {
                _datacenterAddressSetById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAddressSetById];
                if (MTLogEnabled()) {
                    MTLog(@"[MTContext loaded datacenterAddressSetById: %@]", _datacenterAddressSetById);
                }
            }
            
            NSDictionary *datacenterManuallySelectedSchemeById = [keychain objectForKey:@"datacenterManuallySelectedSchemeById_v1" group:@"persistent"];
            if (datacenterManuallySelectedSchemeById != nil) {
                _datacenterManuallySelectedSchemeById = [[NSMutableDictionary alloc] initWithDictionary:datacenterManuallySelectedSchemeById];
                if (MTLogEnabled()) {
                    MTLog(@"[MTContext loaded datacenterManuallySelectedSchemeById: %@]", _datacenterManuallySelectedSchemeById);
                }
            }
            
            
            [_apiEnvironment.datacenterAddressOverrides enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAddress *address, __unused BOOL *stop) {
                _datacenterAddressSetById[nDatacenterId] = [[MTDatacenterAddressSet alloc] initWithAddressList:@[address]];
            }];
            
            NSDictionary *datacenterAuthInfoById = [keychain objectForKey:@"datacenterAuthInfoById" group:@"persistent"];
            if (datacenterAuthInfoById != nil)
                _datacenterAuthInfoById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAuthInfoById];
            
            NSDictionary *datacenterPublicKeysById = [keychain objectForKey:@"datacenterPublicKeysById" group:@"ephemeral"];
            if (datacenterPublicKeysById != nil) {
                _datacenterPublicKeysById = [[NSMutableDictionary alloc] initWithDictionary:datacenterPublicKeysById];
            }
            
            NSDictionary *transportSchemeStats = [keychain objectForKey:@"transportSchemeStats_v1" group:@"temp"];
            if (transportSchemeStats != nil) {
                [_transportSchemeStats removeAllObjects];
                [transportSchemeStats enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, NSDictionary<MTDatacenterAddress *, MTTransportSchemeStats *> *values, __unused BOOL *stop) {
                    _transportSchemeStats[nDatacenterId] = [[NSMutableDictionary alloc] initWithDictionary:values];
                }];
                if (MTLogEnabled()) {
                    MTLog(@"[MTContext] loaded transportSchemeStats:\n%@", [MTTransportSchemeStats formatStats:_transportSchemeStats]);
                }
            }
            
            NSDictionary *authTokenById = [keychain objectForKey:@"authTokenById" group:@"persistent"];
            if (authTokenById != nil)
                _authTokenById = [[NSMutableDictionary alloc] initWithDictionary:authTokenById];
            
            NSDictionary *cleanupSessionIdsByAuthKeyId = [keychain objectForKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
            if (cleanupSessionIdsByAuthKeyId != nil)
                _cleanupSessionIdsByAuthKeyId = [[NSMutableDictionary alloc] initWithDictionary:cleanupSessionIdsByAuthKeyId];
            
            if (MTLogEnabled()) {
                MTLog(@"[MTContext#%x: received keychain globalTimeDifference:%f datacenterAuthInfoById:%@]", (int)self, _globalTimeDifference, _datacenterAuthInfoById);
            }
        } else {
            if (MTLogEnabled()) {
                MTLog(@"[MTContext#%x: received keychain nil]", (int)self);
            }
        }
    }];
}

- (void)addChangeListener:(id<MTContextChangeListener>)changeListener
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (![_changeListeners containsObject:changeListener])
        {
            [_changeListeners addObject:changeListener];
        }
    }];
}

- (void)removeChangeListener:(id<MTContextChangeListener>)changeListener
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        [_changeListeners removeObject:changeListener];
    } synchronous:true];
}

- (void)setDiscoverBackupAddressListSignal:(MTSignal *)signal {
    [[MTContext contextQueue] dispatchOnQueue:^ {
        _discoverBackupAddressListSignal = signal;
    } synchronous:true];
}

- (NSTimeInterval)globalTime
{
    return [[NSDate date] timeIntervalSince1970] + [self globalTimeDifference];
}

- (NSTimeInterval)globalTimeDifference
{
    __block NSTimeInterval result = 0.0;
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        result = _globalTimeDifference;
    } synchronous:true];
    
    return result;
}

- (NSTimeInterval)globalTimeOffsetFromUTC
{
    return [self globalTimeDifference] + [[NSTimeZone localTimeZone] secondsFromGMT];
}

- (void)setGlobalTimeDifference:(NSTimeInterval)globalTimeDifference
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        _globalTimeDifference = globalTimeDifference;
        
        if (MTLogEnabled()) {
            MTLog(@"[MTContext#%x: global time difference changed: %.1fs]", (int)self, globalTimeDifference);
        }
        
        [_keychain setObject:@(_globalTimeDifference) forKey:@"globalTimeDifference" group:@"temp"];
    }];
}

- (void)setSeedAddressSetForDatacenterWithId:(NSInteger)datacenterId seedAddressSet:(MTDatacenterAddressSet *)seedAddressSet
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        _datacenterSeedAddressSetById[@(datacenterId)] = seedAddressSet;
    }];
}

- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (addressSet != nil && datacenterId != 0)
        {
            if (MTLogEnabled()) {
                MTLog(@"[MTContext#%x: address set updated for %d]", (int)self, datacenterId);
            }
            
            bool updateSchemes = forceUpdateSchemes;
            
            bool previousAddressSetWasEmpty = ((MTDatacenterAddressSet *)_datacenterAddressSetById[@(datacenterId)]).addressList.count == 0;
            
            _datacenterAddressSetById[@(datacenterId)] = addressSet;
            [_keychain setObject:_datacenterAddressSetById forKey:@"datacenterAddressSetById" group:@"persistent"];
            
            NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
            
            for (id<MTContextChangeListener> listener in currentListeners)
            {
                if ([listener respondsToSelector:@selector(contextDatacenterAddressSetUpdated:datacenterId:addressSet:)])
                    [listener contextDatacenterAddressSetUpdated:self datacenterId:datacenterId addressSet:addressSet];
            }
            
            if (previousAddressSetWasEmpty || updateSchemes || true) {
                for (id<MTContextChangeListener> listener in currentListeners) {
                    if ([listener respondsToSelector:@selector(contextDatacenterTransportSchemesUpdated:datacenterId:)]) {
                        [listener contextDatacenterTransportSchemesUpdated:self datacenterId:datacenterId];
                    }
                }
            } else {
                /*for (NSNumber *nMedia in @[@false, @true]) {
                    for (NSNumber *nIsProxy in @[@false, @true]) {
                        MTDatacenterAddress *address = [self transportSchemeForDatacenterWithId:datacenterId media:[nMedia boolValue] isProxy:[nIsProxy boolValue]].address;
                        bool matches = false;
                        if (address != nil) {
                            for (MTDatacenterAddress *listAddress in addressSet.addressList) {
                                if ([listAddress.ip isEqualToString:address.ip]) {
                                    if (listAddress.secret != nil && address.secret != nil && [listAddress.secret isEqualToData:address.secret]) {
                                        matches = true;
                                    } else if (listAddress.secret == nil && address.secret == nil) {
                                        matches = true;
                                    }
                                }
                            }
                        }
                        if (!matches) {
                            if (MTLogEnabled()) {
                                MTLog(@"[MTContext#%x: updated address set for %d doesn't contain current %@, updating]", (int)self, datacenterId, address);
                            }
                            
                            [self updateTransportSchemeForDatacenterWithId:datacenterId transportScheme:[self defaultTransportSchemeForDatacenterWithId:datacenterId media:[nMedia boolValue] isProxy:[nIsProxy boolValue]] media:[nMedia boolValue] isProxy:[nIsProxy boolValue]];
                        }
                    }
                }*/
            }
            
            if (updateSchemes) {
                id<MTDisposable> disposable = _transportSchemeDisposableByDatacenterId[@(datacenterId)];
                if (disposable != nil) {
                    [disposable dispose];
                    [_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
                    
                    [self transportSchemeForDatacenterWithIdRequired:datacenterId moreOptimalThan:nil beginWithHttp:false media:false isProxy:_apiEnvironment.socksProxySettings != nil];
                }
            }
        }
    }];
}

- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address
{
    if (address == nil || datacenterId == 0)
        return;
    
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        bool updated = false;
        
        MTDatacenterAddressSet *addressSet = [self addressSetForDatacenterWithId:datacenterId];
        if (addressSet == nil)
        {
            addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:@[address]];
            updated = true;
        }
        else if (![addressSet.addressList containsObject:address])
        {
            NSMutableArray *updatedAddressList = [[NSMutableArray alloc] init];
            [updatedAddressList addObject:address];
            [updatedAddressList addObjectsFromArray:addressSet.addressList];
            
            addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:updatedAddressList];
            updated = true;
        }
        
        if (updated)
        {
            if (MTLogEnabled()) {
                MTLog(@"[MTContext#%x: added address %@ for datacenter %d]", (int)self, address, datacenterId);
            }
            
            _datacenterAddressSetById[@(datacenterId)] = addressSet;
            [_keychain setObject:_datacenterAddressSetById forKey:@"datacenterAddressSetById" group:@"persistent"];
            
            NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
            
            for (id<MTContextChangeListener> listener in currentListeners)
            {
                if ([listener respondsToSelector:@selector(contextDatacenterAddressSetUpdated:datacenterId:addressSet:)])
                    [listener contextDatacenterAddressSetUpdated:self datacenterId:datacenterId addressSet:addressSet];
            }
        }
    }];
}

- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (authInfo != nil && datacenterId != 0)
        {
            if (MTLogEnabled()) {
                MTLog(@"[MTContext#%x: auth info updated for %d]", (int)self, datacenterId);
            }
            
            _datacenterAuthInfoById[@(datacenterId)] = authInfo;
            [_keychain setObject:_datacenterAuthInfoById forKey:@"datacenterAuthInfoById" group:@"persistent"];
            
            NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
            
            for (id<MTContextChangeListener> listener in currentListeners)
            {
                if ([listener respondsToSelector:@selector(contextDatacenterAuthInfoUpdated:datacenterId:authInfo:)])
                    [listener contextDatacenterAuthInfoUpdated:self datacenterId:datacenterId authInfo:authInfo];
            }
        }
    }];
}

- (bool)isPasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId
{
    OSSpinLockLock(&_passwordEntryRequiredLock);
    bool currentValue = [_passwordRequiredByDatacenterId[@(datacenterId)] boolValue];
    OSSpinLockUnlock(&_passwordEntryRequiredLock);
    
    return currentValue;
}

- (bool)updatePasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId required:(bool)required
{
    OSSpinLockLock(&_passwordEntryRequiredLock);
    bool currentValue = [_passwordRequiredByDatacenterId[@(datacenterId)] boolValue];
    bool updated = currentValue != required;
    _passwordRequiredByDatacenterId[@(datacenterId)] = @(required);
    OSSpinLockUnlock(&_passwordEntryRequiredLock);
    
    if (updated)
    {
        [[MTContext contextQueue] dispatchOnQueue:^
        {
            NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
            
            for (id<MTContextChangeListener> listener in currentListeners)
            {
                if ([listener respondsToSelector:@selector(contextIsPasswordRequiredUpdated:datacenterId:)])
                    [listener contextIsPasswordRequiredUpdated:self datacenterId:datacenterId];
            }
        }];
    }
    
    return currentValue;
}

- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media isProxy:(bool)isProxy {
    [[MTContext contextQueue] dispatchOnQueue:^ {
        if (transportScheme != nil && datacenterId != 0) {
            _datacenterManuallySelectedSchemeById[[[MTTransportSchemeKey alloc] initWithDatacenterId:datacenterId isProxy:isProxy isMedia:media]] = transportScheme;
            [_keychain setObject:_datacenterManuallySelectedSchemeById forKey:@"datacenterManuallySelectedSchemeById_v1" group:@"persistent"];
            
            [self reportTransportSchemeSuccessForDatacenterId:datacenterId transportScheme:transportScheme];
            [self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:transportScheme process:^MTTransportSchemeStats *(MTTransportSchemeStats *current) {
                current = [current withUpdatedLastFailureTimestamp:0];
                current = [current withUpdatedLastResponseTimestamp:(int32_t)CFAbsoluteTimeGetCurrent()];
                return current;
            }];
            
            NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
            
            if (MTLogEnabled()) {
                MTLog(@"[MTContext#%x: %@ transport scheme updated for %d: %@]", (int)self, media ? @"media" : @"generic", datacenterId, transportScheme);
            }
            
            for (id<MTContextChangeListener> listener in currentListeners) {
                if ([listener respondsToSelector:@selector(contextDatacenterTransportSchemesUpdated:datacenterId:)])
                    [listener contextDatacenterTransportSchemesUpdated:self datacenterId:datacenterId];
            }
        }
    }];
}

- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        
#warning implement and reenable
        return;
        
        if (authKeyId == 0 || sessionInfo == nil)
            return;
        
        NSMutableArray *sessionIds = _cleanupSessionIdsByAuthKeyId[@(authKeyId)];
        if (sessionIds == nil)
        {
            sessionIds = [[NSMutableArray alloc] init];
            _cleanupSessionIdsByAuthKeyId[@(authKeyId)] = sessionIds;
        }
        else if (![sessionIds respondsToSelector:@selector(setObject:forKey:)])
        {
            sessionIds = [[NSMutableArray alloc] initWithArray:sessionIds];
            _cleanupSessionIdsByAuthKeyId[@(authKeyId)] = sessionIds;
        }
        
        [sessionIds addObject:@(sessionInfo.sessionId)];
        [_currentSessionInfos addObject:sessionInfo];
        
        [_keychain setObject:_cleanupSessionIdsByAuthKeyId forKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
    }];
}

- (void)collectSessionIdsForCleanupWithAuthKeyId:(int64_t)authKeyId completion:(void (^)(NSArray *sessionIds))completion
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        NSMutableSet *liveSessionIds = [[NSMutableSet alloc] init];
        for (NSInteger i = (NSInteger)_currentSessionInfos.count - 1; i >= 0; i--)
        {
            MTSessionInfo *sessionInfo = _currentSessionInfos[i];
            if (!sessionInfo.canBeDeleted)
                [liveSessionIds addObject:@(sessionInfo.sessionId)];
            else
                [_currentSessionInfos removeObjectAtIndex:(NSUInteger)i];
        }
        
        NSMutableArray *currentSessionIds = [[NSMutableArray alloc] init];
        for (NSNumber *nSessionId in _cleanupSessionIdsByAuthKeyId[@(authKeyId)])
        {
            if (![liveSessionIds containsObject:nSessionId])
                [currentSessionIds addObject:nSessionId];
        }
        
        if (completion)
            completion(currentSessionIds);
    }];
}

- (void)sessionIdsDeletedForAuthKeyId:(int64_t)authKeyId sessionIds:(NSArray *)sessionIds
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        for (NSInteger i = (NSInteger)_currentSessionInfos.count - 1; i >= 0; i--)
        {
            MTSessionInfo *sessionInfo = _currentSessionInfos[i];
            if ([sessionIds containsObject:@(sessionInfo.sessionId)])
                [_currentSessionInfos removeObjectAtIndex:(NSUInteger)i];
        }
        
        NSMutableArray *cleanupSessionIds = _cleanupSessionIdsByAuthKeyId[@(authKeyId)];
        if (![cleanupSessionIds respondsToSelector:@selector(removeObjectAtIndex:)])
        {
            cleanupSessionIds = [[NSMutableArray alloc] initWithArray:cleanupSessionIds];
            _cleanupSessionIdsByAuthKeyId[@(authKeyId)] = cleanupSessionIds;
        }
        for (NSInteger i = ((NSUInteger)cleanupSessionIds.count) - 1; i >= 0; i--)
        {
            if ([sessionIds containsObject:cleanupSessionIds[(NSUInteger)i]])
                [cleanupSessionIds removeObjectAtIndex:(NSUInteger)i];
        }
        
        [_keychain setObject:_cleanupSessionIdsByAuthKeyId forKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
    }];
}

- (NSArray *)knownDatacenterIds
{
    NSMutableSet *datacenterIds = [[NSMutableSet alloc] init];
    
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        for (NSNumber *nDatacenterId in _datacenterSeedAddressSetById.allKeys)
        {
            [datacenterIds addObject:nDatacenterId];
        }
        
        for (NSNumber *nDatacenterId in _datacenterAddressSetById.allKeys)
        {
            [datacenterIds addObject:nDatacenterId];
        }
    } synchronous:true];
    
    return [[datacenterIds allObjects] sortedArrayUsingComparator:^NSComparisonResult(NSNumber *n1, NSNumber *n2)
    {
        return [n1 compare:n2];
    }];
}

- (void)enumerateAddressSetsForDatacenters:(void (^)(NSInteger datacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop))block
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (block == nil)
            return;
        
        NSMutableSet *processedDatacenterIds = [[NSMutableSet alloc] init];
        
        [_datacenterAddressSetById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop)
        {
            [processedDatacenterIds addObject:nDatacenterId];
            block([nDatacenterId integerValue], addressSet, stop);
        }];
        
        [_datacenterSeedAddressSetById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop)
        {
            if (![processedDatacenterIds containsObject:nDatacenterId])
                block([nDatacenterId integerValue], addressSet, stop);
        }];
    } synchronous:true];
}

- (MTDatacenterAddressSet *)addressSetForDatacenterWithId:(NSInteger)datacenterId
{
    __block MTDatacenterAddressSet *result = nil;
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        MTDatacenterAddressSet *addressSet = _datacenterAddressSetById[@(datacenterId)];
        if (addressSet != nil && addressSet.addressList.count != 0) {
            result = _datacenterAddressSetById[@(datacenterId)];
        } else {
            result = _datacenterSeedAddressSetById[@(datacenterId)];
        }
    } synchronous:true];
    
    return result;
}

- (MTTransportScheme * _Nullable)chooseTransportSchemeForConnectionToDatacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes {
    __block MTTransportScheme *result = nil;
    
    [[MTContext contextQueue] dispatchOnQueue:^{
        __block MTTransportScheme *schemeWithEarliestFailure = nil;
        __block int32_t earliestFailure = INT32_MAX;
        
        int32_t timestamp = (int32_t)CFAbsoluteTimeGetCurrent();
        __block bool scanIpv6 = false;
        for (MTTransportScheme *scheme in schemes) {
            if (scheme.address.isIpv6) {
                [self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:scheme process:^MTTransportSchemeStats *(MTTransportSchemeStats *current) {
                    if (scheme.address.isIpv6 && current.lastResponseTimestamp > timestamp - 60 * 60) {
                        scanIpv6 = true;
                    }
                    return current;
                }];
            }
        }
        
        for (MTTransportScheme *scheme in schemes.reverseObjectEnumerator) {
            if (scheme.address.isIpv6 && !scanIpv6) {
                continue;
            }
            [self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:scheme process:^MTTransportSchemeStats *(MTTransportSchemeStats *current) {
                if (schemeWithEarliestFailure == nil) {
                    schemeWithEarliestFailure = scheme;
                    earliestFailure = current.lastFailureTimestamp;
                } else if (earliestFailure > current.lastFailureTimestamp) {
                    earliestFailure = current.lastFailureTimestamp;
                    schemeWithEarliestFailure = scheme;
                }
                return current;
            }];
        }
        result = schemeWithEarliestFailure;
    } synchronous:true];
    
    return result;
}

- (NSArray<MTTransportScheme *> * _Nonnull)transportSchemesForDatacenterWithId:(NSInteger)datacenterId media:(bool)media enforceMedia:(bool)enforceMedia isProxy:(bool)isProxy
{
    __block NSMutableArray <MTTransportScheme *> *results = [[NSMutableArray alloc] init];
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        MTDatacenterAddress *overrideAddress = _apiEnvironment.datacenterAddressOverrides[@(datacenterId)];
        if (overrideAddress != nil) {
            [results addObject:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:overrideAddress media:false]];
        } else {
            [results addObjectsFromArray:[self _allTransportSchemesForDatacenterWithId:datacenterId]];
        }
        MTTransportScheme *manualScheme = _datacenterManuallySelectedSchemeById[[[MTTransportSchemeKey alloc] initWithDatacenterId:datacenterId isProxy:isProxy isMedia:media]];
        if (manualScheme != nil && ![results containsObject:manualScheme]) {
            [results addObject:manualScheme];
        }
    } synchronous:true];
    
    for (int i = (int)(results.count - 1); i >= 0; i--) {
        if (enforceMedia && !results[i].address.preferForMedia) {
            [results removeObjectAtIndex:i];
        } else if (!media && results[i].address.preferForMedia) {
            [results removeObjectAtIndex:i];
        }
    }
    
    return results;
}

- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId {
    __block MTDatacenterAuthInfo *result = nil;
    [[MTContext contextQueue] dispatchOnQueue:^{
        result = _datacenterAuthInfoById[@(datacenterId)];
    } synchronous:true];
    
    return result;
}
    
- (NSArray<NSDictionary *> *)publicKeysForDatacenterWithId:(NSInteger)datacenterId {
    __block NSArray<NSDictionary *> *result = nil;
    [[MTContext contextQueue] dispatchOnQueue:^{
        result = _datacenterPublicKeysById[@(datacenterId)];
    } synchronous:true];
    
    return result;
}
    
- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys {
    [[MTContext contextQueue] dispatchOnQueue:^{
        if (publicKeys != nil) {
            _datacenterPublicKeysById[@(datacenterId)] = publicKeys;
            [_keychain setObject:_datacenterPublicKeysById forKey:@"datacenterPublicKeysById" group:@"ephemeral"];
            
            for (id<MTContextChangeListener> listener in _changeListeners) {
                if ([listener respondsToSelector:@selector(contextDatacenterPublicKeysUpdated:datacenterId:publicKeys:)]) {
                    [listener contextDatacenterPublicKeysUpdated:self datacenterId:datacenterId publicKeys:publicKeys];
                }
            }
        }
    } synchronous:false];
}
    
- (void)publicKeysForDatacenterWithIdRequired:(NSInteger)datacenterId {
    [[MTContext contextQueue] dispatchOnQueue:^{
        if (_fetchPublicKeysActions[@(datacenterId)] == nil) {
            for (id<MTContextChangeListener> listener in _changeListeners) {
                if ([listener respondsToSelector:@selector(fetchContextDatacenterPublicKeys:datacenterId:)]) {
                    MTSignal *signal = [listener fetchContextDatacenterPublicKeys:self datacenterId:datacenterId];
                    if (signal != nil) {
                        __weak MTContext *weakSelf = self;
                        MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
                        _fetchPublicKeysActions[@(datacenterId)] = disposable;
                        [disposable setDisposable:[signal startWithNext:^(NSArray<NSDictionary *> *next) {
                            [[MTContext contextQueue] dispatchOnQueue:^{
                                __strong MTContext *strongSelf = weakSelf;
                                if (strongSelf != nil) {
                                    [strongSelf->_fetchPublicKeysActions removeObjectForKey:@(datacenterId)];
                                    [strongSelf updatePublicKeysForDatacenterWithId:datacenterId publicKeys:next];
                                }
                            } synchronous:false];
                        }]];
                        break;
                    }
                }
            }
        }
    } synchronous:false];
}

- (void)removeAllAuthTokens
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        [_authTokenById removeAllObjects];
        [_keychain setObject:_authTokenById forKey:@"authTokenById" group:@"persistent"];
        
        for (NSNumber *nDatacenterId in _datacenterTransferAuthActions)
        {
            MTDatacenterTransferAuthAction *action = _datacenterTransferAuthActions[nDatacenterId];
            action.delegate = nil;
            [action cancel];
        }
        [_datacenterTransferAuthActions removeAllObjects];
    }];
}

- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId
{
    __block id result = nil;
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        result = _authTokenById[@(datacenterId)];
    } synchronous:true];
    
    return result;
}

- (NSArray<MTTransportScheme *> * _Nonnull)_allTransportSchemesForDatacenterWithId:(NSInteger)datacenterId {
    NSMutableArray<MTTransportScheme *> *result = [[NSMutableArray alloc] init];
    MTDatacenterAddressSet *addressSet = [self addressSetForDatacenterWithId:datacenterId];
    if (addressSet == nil) {
        [self addressSetForDatacenterWithIdRequired:datacenterId];
    } else {
        for (MTDatacenterAddress *address in addressSet.addressList) {
            [result addObject:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:address.preferForMedia]];
        }
    }
    return result;
}

- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId media:(bool)media
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        [self transportSchemeForDatacenterWithIdRequired:datacenterId moreOptimalThan:nil beginWithHttp:false media:media isProxy:_apiEnvironment.socksProxySettings != nil];
    }];
}

- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId moreOptimalThan:(MTTransportScheme *)suboptimalScheme beginWithHttp:(bool)beginWithHttp media:(bool)media isProxy:(bool)isProxy
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (_transportSchemeDisposableByDatacenterId == nil)
            _transportSchemeDisposableByDatacenterId = [[NSMutableDictionary alloc] init];
        id<MTDisposable> disposable = _transportSchemeDisposableByDatacenterId[@(datacenterId)];
        if (disposable == nil)
        {
            __weak MTContext *weakSelf = self;
            MTDatacenterAddressSet *initialAddressSet = [self addressSetForDatacenterWithId:datacenterId];
            NSMutableArray *addressList = [[NSMutableArray alloc] initWithArray:initialAddressSet.addressList];
            MTDatacenterAddressSet *seedAddress = _datacenterSeedAddressSetById[@(datacenterId)];
            if (seedAddress != nil) {
                for (MTDatacenterAddress *address in seedAddress.addressList) {
                    if (![addressList containsObject:address]) {
                        [addressList addObject:address];
                    }
                }
            }
            MTDatacenterAddressSet *addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:addressList];
            MTSignal *discoverSignal = [MTDiscoverConnectionSignals discoverSchemeWithContext:self datacenterId:datacenterId addressList:addressSet.addressList media:media isProxy:isProxy];
            MTSignal *conditionSignal = [MTSignal single:@(true)];
            for (id<MTContextChangeListener> listener in _changeListeners) {
                if ([listener respondsToSelector:@selector(isContextNetworkAccessAllowed:)]) {
                    MTSignal *signal = [listener isContextNetworkAccessAllowed:self];
                    if (signal != nil) {
                        conditionSignal = signal;
                    }
                }
            }
            MTSignal *filteredSignal = [[conditionSignal mapToSignal:^(NSNumber *value) {
                if ([value boolValue]) {
                    return discoverSignal;
                } else {
                    return [MTSignal never];
                }
            }] take:1];

            _transportSchemeDisposableByDatacenterId[@(datacenterId)] = [[filteredSignal onDispose:^
            {
                __strong MTContext *strongSelf = weakSelf;
                if (strongSelf != nil)
                {
                    [[MTContext contextQueue] dispatchOnQueue:^
                    {
                        [strongSelf->_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
                    }];
                }
            }] startWithNext:^(MTTransportScheme *next)
            {
                if (MTLogEnabled()) {
                    MTLog(@"scheme: %@", next);
                }
                __strong MTContext *strongSelf = weakSelf;
                if (strongSelf != nil)
                {
                    [strongSelf updateTransportSchemeForDatacenterWithId:datacenterId transportScheme:next media:media isProxy:isProxy];
                }
            } error:^(id error)
            {
                
            } completed:^
            {
                
            }];
        }
    }];
}

- (void)_withTransportSchemeStatsForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme process:(MTTransportSchemeStats * (^)(MTTransportSchemeStats *))process {
    NSAssert([[MTContext contextQueue] isCurrentQueue], @"[[MTContext contextQueue] isCurrentQueue]");
    if (_transportSchemeStats[@(datacenterId)] == nil) {
        _transportSchemeStats[@(datacenterId)] = [[NSMutableDictionary alloc] init];
    }
    MTTransportSchemeStats *current = _transportSchemeStats[@(datacenterId)][transportScheme.address];
    if (current == nil) {
        current = [[MTTransportSchemeStats alloc] initWithLastFailureTimestamp:0 lastResponseTimestamp:0];
    }
    MTTransportSchemeStats *updated = process(current);
    if (updated == nil || ![updated isEqual:current]) {
        if (updated == nil) {
            [_transportSchemeStats[@(datacenterId)] removeObjectForKey:transportScheme.address];
        } else {
            if (MTLogEnabled()) {
                //MTLog(@"Updated stats for %@: %@", transportScheme.address, updated);
            }
            _transportSchemeStats[@(datacenterId)][transportScheme.address] = updated;
        }
        [self _scheduleSyncTransportSchemeStats];
    }
}

- (void)_scheduleSyncTransportSchemeStats {
    NSAssert([[MTContext contextQueue] isCurrentQueue], @"[[MTContext contextQueue] isCurrentQueue]");
    if (_schemeStatsSyncTimer == nil) {
        __weak MTContext *weakSelf = self;
        _schemeStatsSyncTimer = [[MTTimer alloc] initWithTimeout:5.0 repeat:false completion:^{
            __strong MTContext *strongSelf = weakSelf;
            if (strongSelf == nil) {
                return;
            }
            strongSelf->_schemeStatsSyncTimer = nil;
            [strongSelf _syncTransportSchemeStats];
        } queue:[MTContext contextQueue].nativeQueue];
        [_schemeStatsSyncTimer start];
    }
}

- (void)_syncTransportSchemeStats {
    NSAssert([[MTContext contextQueue] isCurrentQueue], @"[[MTContext contextQueue] isCurrentQueue]");
    [_keychain setObject:_transportSchemeStats forKey:@"transportSchemeStats_v1" group:@"temp"];
}

- (void)reportTransportSchemeFailureForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme {
    [[MTContext contextQueue] dispatchOnQueue:^{
        [self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:transportScheme process:^(MTTransportSchemeStats *current) {
            return [current withUpdatedLastFailureTimestamp:(int32_t)CFAbsoluteTimeGetCurrent()];
        }];
    }];
}

- (void)reportTransportSchemeSuccessForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme {
    [[MTContext contextQueue] dispatchOnQueue:^{
        [self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:transportScheme process:^(MTTransportSchemeStats *current) {
            return [current withUpdatedLastResponseTimestamp:(int32_t)CFAbsoluteTimeGetCurrent()];
        }];
    }];
}

- (void)invalidateTransportSchemesForDatacenterIds:(NSArray<NSNumber *> * _Nonnull)datacenterIds {
    [[MTContext contextQueue] dispatchOnQueue:^{
        for (NSNumber *datacenterId in datacenterIds) {
            [self transportSchemeForDatacenterWithIdRequired:[datacenterId integerValue] moreOptimalThan:nil beginWithHttp:false media:false isProxy:_apiEnvironment.socksProxySettings != nil];
        }
    }];
}

- (void)invalidateTransportSchemesForKnownDatacenterIds {
    [[MTContext contextQueue] dispatchOnQueue:^{
        NSMutableSet *datacenterIds = [[NSMutableSet alloc] init];

        for (NSNumber *nId in _datacenterAddressSetById.allKeys) {
            [datacenterIds addObject:nId];
        }
        
        for (NSNumber *nId in _datacenterSeedAddressSetById.allKeys) {
            [datacenterIds addObject:nId];
        }
        
        for (NSNumber *datacenterId in datacenterIds) {
            [self transportSchemeForDatacenterWithIdRequired:[datacenterId integerValue] moreOptimalThan:nil beginWithHttp:false media:false isProxy:_apiEnvironment.socksProxySettings != nil];
        }
    }];
}

- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media
{
    if (transportScheme == nil)
        return;
    
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        [self transportSchemeForDatacenterWithIdRequired:datacenterId moreOptimalThan:transportScheme beginWithHttp:isProbablyHttp media:media isProxy:_apiEnvironment.socksProxySettings != nil];
        
        double delay = 20.0f;
        if (_apiEnvironment.networkSettings == nil || _apiEnvironment.networkSettings.reducedBackupDiscoveryTimeout) {
            delay = 5.0;
        }
        [self _beginBackupAddressDiscoveryWithDelay:delay];
    }];
}

- (void)_beginBackupAddressDiscoveryWithDelay:(double)delay {
    if (_backupAddressListDisposable == nil && _discoverBackupAddressListSignal != nil) {
        __weak MTContext *weakSelf = self;
        _backupAddressListDisposable = [[[_discoverBackupAddressListSignal delay:delay onQueue:[MTQueue mainQueue]] onDispose:^{
            __strong MTContext *strongSelf = weakSelf;
            if (strongSelf != nil) {
                [strongSelf->_backupAddressListDisposable dispose];
                strongSelf->_backupAddressListDisposable = nil;
            }
        }] startWithNext:nil];
    }
}

- (void)beginExplicitBackupAddressDiscovery {
    [[MTContext contextQueue] dispatchOnQueue:^{
        [_backupAddressListDisposable dispose];
        _backupAddressListDisposable = nil;
        [self _beginBackupAddressDiscoveryWithDelay:0.0];
    }];
}

- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if ([transportScheme isOptimal])
        {
            if (_transportSchemeDisposableByDatacenterId == nil)
                _transportSchemeDisposableByDatacenterId = [[NSMutableDictionary alloc] init];
            id<MTDisposable> disposable = _transportSchemeDisposableByDatacenterId[@(datacenterId)];
            [disposable dispose];
            [_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
        }
        if (_backupAddressListDisposable != nil) {
            [_backupAddressListDisposable dispose];
            _backupAddressListDisposable = nil;
        }
    }];
}

- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (authToken != nil)
            _authTokenById[@(datacenterId)] = authToken;
        else
            [_authTokenById removeObjectForKey:@(datacenterId)];
        [_keychain setObject:_authTokenById forKey:@"authTokenById" group:@"persistent"];
        
        NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
        
        for (id<MTContextChangeListener> listener in currentListeners)
        {
            if ([listener respondsToSelector:@selector(contextDatacenterAuthTokenUpdated:datacenterId:authToken:)])
                [listener contextDatacenterAuthTokenUpdated:self datacenterId:datacenterId authToken:authToken];
        }
    }];
}

- (void)addressSetForDatacenterWithIdRequired:(NSInteger)datacenterId
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (_discoverDatacenterAddressActions[@(datacenterId)] == nil)
        {
            MTDiscoverDatacenterAddressAction *discoverAction = [[MTDiscoverDatacenterAddressAction alloc] init];
            discoverAction.delegate = self;
            _discoverDatacenterAddressActions[@(datacenterId)] = discoverAction;
            [discoverAction execute:self datacenterId:datacenterId];
        }
    }];
}

- (void)discoverDatacenterAddressActionCompleted:(MTDiscoverDatacenterAddressAction *)action
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        for (NSNumber *nDatacenterId in _discoverDatacenterAddressActions)
        {
            if (_discoverDatacenterAddressActions[nDatacenterId] == action)
            {
                [_discoverDatacenterAddressActions removeObjectForKey:nDatacenterId];
                
                break;
            }
        }
    }];
}

- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (_datacenterAuthActions[@(datacenterId)] == nil)
        {
            MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithTempAuth:false tempAuthKeyType:MTDatacenterAuthTempKeyTypeMain];
            authAction.delegate = self;
            _datacenterAuthActions[@(datacenterId)] = authAction;
            [authAction execute:self datacenterId:datacenterId isCdn:isCdn];
        }
    }];
}

- (void)tempAuthKeyForDatacenterWithIdRequired:(NSInteger)datacenterId keyType:(MTDatacenterAuthTempKeyType)keyType {
    [[MTContext contextQueue] dispatchOnQueue:^{
        if (_datacenterTempAuthActions[@(datacenterId)] == nil) {
            MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithTempAuth:true tempAuthKeyType:keyType];
            authAction.delegate = self;
            _datacenterTempAuthActions[@(datacenterId)] = authAction;
            [authAction execute:self datacenterId:datacenterId isCdn:false];
        }
    }];
}

- (void)datacenterAuthActionCompleted:(MTDatacenterAuthAction *)action
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (action.tempAuth) {
            for (NSNumber *nDatacenterId in _datacenterTempAuthActions) {
                if (_datacenterTempAuthActions[nDatacenterId] == action) {
                    [_datacenterTempAuthActions removeObjectForKey:nDatacenterId];
                    
                    break;
                }
            }
        } else {
            for (NSNumber *nDatacenterId in _datacenterAuthActions) {
                if (_datacenterAuthActions[nDatacenterId] == action) {
                    [_datacenterAuthActions removeObjectForKey:nDatacenterId];
                    
                    break;
                }
            }
        }
    }];
}

- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id)authToken masterDatacenterId:(NSInteger)masterDatacenterId
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        if (authToken == nil)
            return;
        
        if (_datacenterTransferAuthActions[@(datacenterId)] == nil && masterDatacenterId != datacenterId)
        {
            MTDatacenterTransferAuthAction *transferAction = [[MTDatacenterTransferAuthAction alloc] init];
            transferAction.delegate = self;
            _datacenterTransferAuthActions[@(datacenterId)] = transferAction;
            [transferAction execute:self masterDatacenterId:masterDatacenterId destinationDatacenterId:datacenterId authToken:authToken];
        }
    }];
}

- (void)datacenterTransferAuthActionCompleted:(MTDatacenterTransferAuthAction *)action
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        for (NSNumber *nDatacenterId in _datacenterTransferAuthActions)
        {
            if (_datacenterTransferAuthActions[nDatacenterId] == action)
            {
                [_datacenterTransferAuthActions removeObjectForKey:nDatacenterId];
                
                break;
            }
        }
    }];
}

- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address
{
    
}
    
- (void)updateApiEnvironment:(MTApiEnvironment *(^)(MTApiEnvironment *))f {
    [[MTContext contextQueue] dispatchOnQueue:^{
        MTApiEnvironment *apiEnvironment = f(_apiEnvironment);
        if (apiEnvironment != nil) {
            _apiEnvironment = apiEnvironment;
            
            NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
            for (id<MTContextChangeListener> listener in currentListeners)
            {
                if ([listener respondsToSelector:@selector(contextApiEnvironmentUpdated:apiEnvironment:)]) {
                    [listener contextApiEnvironmentUpdated:self apiEnvironment:apiEnvironment];
                }
            }
        }
    }];
}

- (void)updatePeriodicTasks
{
    [[MTContext contextQueue] dispatchOnQueue:^
    {
        int64_t saltsRequiredAtLeastUntilMessageId = (int64_t)(([self globalTime] + 24 * 60.0 * 60.0) * 4294967296);
        
        [_datacenterAuthInfoById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAuthInfo *authInfo, __unused BOOL *stop)
        {
            if ([authInfo authSaltForMessageId:saltsRequiredAtLeastUntilMessageId == 0])
            {
#warning TODO
            }
        }];
    }];
}

@end


================================================
FILE: MTProtoKit/MTDatacenterAddress.h
================================================


#import <Foundation/Foundation.h>

@interface MTDatacenterAddress : NSObject <NSCoding, NSCopying>

@property (nonatomic, strong, readonly) NSString *host;
@property (nonatomic, strong, readonly) NSString *ip;
@property (nonatomic, readonly) uint16_t port;
@property (nonatomic, readonly) bool preferForMedia;
@property (nonatomic, readonly) bool restrictToTcp;
@property (nonatomic, readonly) bool cdn;
@property (nonatomic, readonly) bool preferForProxy;
@property (nonatomic, readonly) NSData *secret;

- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port preferForMedia:(bool)preferForMedia restrictToTcp:(bool)restrictToTcp cdn:(bool)cdn preferForProxy:(bool)preferForProxy secret:(NSData *)secret;

- (BOOL)isEqualToAddress:(MTDatacenterAddress *)other;
- (BOOL)isIpv6;

@end


================================================
FILE: MTProtoKit/MTDatacenterAddress.m
================================================


#import "MTDatacenterAddress.h"

#import <netinet/in.h>
#import <arpa/inet.h>

@implementation MTDatacenterAddress

- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port preferForMedia:(bool)preferForMedia restrictToTcp:(bool)restrictToTcp cdn:(bool)cdn preferForProxy:(bool)preferForProxy secret:(NSData *)secret
{
    self = [super init];
    if (self != nil)
    {
        _ip = ip;
        _port = port;
        _preferForMedia = preferForMedia;
        _restrictToTcp = restrictToTcp;
        _cdn = cdn;
        _preferForProxy = preferForProxy;
        _secret = secret;
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self != nil)
    {
        _ip = [aDecoder decodeObjectForKey:@"ip"];
        _host = [aDecoder decodeObjectForKey:@"host"];
        _port = (uint16_t)[aDecoder decodeIntForKey:@"port"];
        _preferForMedia = [aDecoder decodeBoolForKey:@"preferForMedia"];
        _restrictToTcp = [aDecoder decodeBoolForKey:@"restrictToTcp"];
        _cdn = [aDecoder decodeBoolForKey:@"cdn"];
        _preferForProxy = [aDecoder decodeBoolForKey:@"preferForProxy"];
        _secret = [aDecoder decodeObjectForKey:@"secret"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_ip forKey:@"ip"];
    [aCoder encodeObject:_host forKey:@"host"];
    [aCoder encodeInt:_port forKey:@"port"];
    [aCoder encodeBool:_preferForMedia forKey:@"preferForMedia"];
    [aCoder encodeBool:_restrictToTcp forKey:@"restrictToTcp"];
    [aCoder encodeBool:_cdn forKey:@"cdn"];
    [aCoder encodeBool:_preferForProxy forKey:@"preferForProxy"];
    [aCoder encodeObject:_secret forKey:@"secret"];
}

- (instancetype)copyWithZone:(NSZone *)__unused zone {
    return self;
}

- (BOOL)isEqual:(id)object
{
    if (![object isKindOfClass:[MTDatacenterAddress class]])
        return false;
    
    return [self isEqualToAddress:object];
}

- (NSUInteger)hash {
    return [_ip hash] * 31 + ((NSUInteger)_port);
}

- (BOOL)isEqualToAddress:(MTDatacenterAddress *)other
{
    if (![other isKindOfClass:[MTDatacenterAddress class]])
        return false;
    
    if (![_ip isEqualToString:other.ip])
        return false;
    
    if (_port != other.port)
        return false;
    
    if (_preferForMedia != other.preferForMedia)
        return false;
    
    if (_restrictToTcp != other.restrictToTcp) {
        return false;
    }
    
    if (_cdn != other.cdn) {
        return false;
    }
    
    if (_preferForProxy != other.preferForProxy) {
        return false;
    }
    
    if ((_secret != nil) && (other->_secret != nil)) {
        if (![_secret isEqualToData:other->_secret]) {
            return false;
        }
    } else if ((_secret != nil) != (other->_secret != nil)) {
        return false;
    }
    
    return true;
}

- (BOOL)isIpv6
{
    const char *utf8 = [_ip UTF8String];
    int success;
    
    struct in6_addr dst6;
    success = inet_pton(AF_INET6, utf8, &dst6);
    
    return success == 1;
}

- (NSString *)description
{
    return [[NSString alloc] initWithFormat:@"%@:%d (media: %@, cdn: %@, static: %@, secret: %@)", _ip == nil ? _host : _ip, (int)_port, _preferForMedia ? @"yes" : @"no", _cdn ? @"yes" : @"no", _preferForProxy ? @"yes" : @"no", _secret];
}

@end


================================================
FILE: MTProtoKit/MTDatacenterAddressListData.h
================================================
#import <Foundation/Foundation.h>

@interface MTDatacenterAddressListData : NSObject

@property (nonatomic, strong, readonly) NSDictionary<NSNumber *, NSArray *> *addressList;

- (instancetype)initWithAddressList:(NSDictionary<NSNumber *, NSArray *> *)addressList;

@end


================================================
FILE: MTProtoKit/MTDatacenterAddressListData.m
================================================
#import "MTDatacenterAddressListData.h"

@implementation MTDatacenterAddressListData

- (instancetype)initWithAddressList:(NSDictionary<NSNumber *, NSArray *> *)addressList
{
    self = [super init];
    if (self != nil)
    {
        _addressList = addressList;
    }
    return self;
}

- (NSString *)description {
    return _addressList.description;
}

@end


================================================
FILE: MTProtoKit/MTDatacenterAddressSet.h
================================================


#import <Foundation/Foundation.h>

@class MTDatacenterAddress;

@interface MTDatacenterAddressSet : NSObject <NSCoding>

@property (nonatomic, strong, readonly) NSArray *addressList;

- (instancetype)initWithAddressList:(NSArray *)addressList;

- (MTDatacenterAddress *)firstAddress;

@end


================================================
FILE: MTProtoKit/MTDatacenterAddressSet.m
================================================


#import "MTDatacenterAddressSet.h"

#import "MTDatacenterAddress.h"

@implementation MTDatacenterAddressSet

- (instancetype)initWithAddressList:(NSArray *)addressList
{
    self = [super init];
    if (self != nil)
    {
        _addressList = addressList;
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self != nil)
    {
        _addressList = [aDecoder decodeObjectForKey:@"addressList"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_addressList forKey:@"addressList"];
}

- (BOOL)isEqual:(MTDatacenterAddressSet *)another
{
    if (![another isKindOfClass:[MTDatacenterAddressSet class]])
        return false;
    
    if (_addressList.count != another.addressList.count)
        return false;
    
    for (NSUInteger i = 0; i < _addressList.count; i++)
    {
        if (![_addressList[i] isEqual:another.addressList[i]])
            return false;
    }
    
    return true;
}

- (NSString *)description
{
    NSMutableString *string = [[NSMutableString alloc] init];
    [string appendString:@"["];
    for (MTDatacenterAddress *address in _addressList)
    {
        if (string.length != 1)
            [string appendString:@", "];
        [string appendString:[address description]];
    }
    [string appendString:@"]"];
    
    return string;
}

- (MTDatacenterAddress *)firstAddress
{
    return _addressList.count == 0 ? nil : _addressList[0];
}

@end


================================================
FILE: MTProtoKit/MTDatacenterAuthAction.h
================================================
#import <Foundation/Foundation.h>

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTDatacenterAuthInfo.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTDatacenterAuthInfo.h>
#else
#   import <MTProtoKit/MTDatacenterAuthInfo.h>
#endif

@class MTContext;
@class MTDatacenterAuthAction;

@protocol MTDatacenterAuthActionDelegate <NSObject>

- (void)datacenterAuthActionCompleted:(MTDatacenterAuthAction *)action;

@end

@interface MTDatacenterAuthAction : NSObject

@property (nonatomic, readonly) bool tempAuth;
@property (nonatomic, weak) id<MTDatacenterAuthActionDelegate> delegate;

- (instancetype)initWithTempAuth:(bool)tempAuth tempAuthKeyType:(MTDatacenterAuthTempKeyType)tempAuthKeyType;

- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId isCdn:(bool)isCdn;
- (void)cancel;

@end


================================================
FILE: MTProtoKit/MTDatacenterAuthAction.m
================================================
#import "MTDatacenterAuthAction.h"

#import "MTLogging.h"
#import "MTContext.h"
#import "MTProto.h"
#import "MTRequest.h"
#import "MTDatacenterSaltInfo.h"
#import "MTDatacenterAuthInfo.h"
#import "MTApiEnvironment.h"
#import "MTSerialization.h"
#import "MTDatacenterAddressSet.h"

#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTSignal.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTSignal.h>
#else
#   import <MTProtoKit/MTSignal.h>
#endif

#import "MTDatacenterAuthMessageService.h"
#import "MTRequestMessageService.h"

#import "MTBuffer.h"

@interface MTDatacenterAuthAction () <MTDatacenterAuthMessageServiceDelegate>
{
    bool _isCdn;
    MTDatacenterAuthTempKeyType _tempAuthKeyType;
    
    NSInteger _datacenterId;
    __weak MTContext *_context;
    
    bool _awaitingAddresSetUpdate;
    MTProto *_authMtProto;
    
    MTMetaDisposable *_verifyDisposable;
}

@end

@implementation MTDatacenterAuthAction

- (instancetype)initWithTempAuth:(bool)tempAuth tempAuthKeyType:(MTDatacenterAuthTempKeyType)tempAuthKeyType {
    self = [super init];
    if (self != nil) {
        _tempAuth = tempAuth;
        _tempAuthKeyType = tempAuthKeyType;
        _verifyDisposable = [[MTMetaDisposable alloc] init];
    }
    return self;
}

- (void)dealloc
{
    [self cleanup];
}

- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId isCdn:(bool)isCdn
{
    _datacenterId = datacenterId;
    _context = context;
    _isCdn = isCdn;
    
    if (_datacenterId != 0 && context != nil)
    {
        bool alreadyCompleted = false;
        MTDatacenterAuthInfo *currentAuthInfo = [context authInfoForDatacenterWithId:_datacenterId];
        if (currentAuthInfo != nil) {
            if (_tempAuth) {
                if ([currentAuthInfo tempAuthKeyWithType:_tempAuthKeyType] != nil) {
                    alreadyCompleted = true;
                }
            } else {
                alreadyCompleted = true;
            }
        }
        
        if (alreadyCompleted) {
            [self complete];
        } else {
            _authMtProto = [[MTProto alloc] initWithContext:context datacenterId:_datacenterId usageCalculationInfo:nil];
            _authMtProto.cdn = isCdn;
            _authMtProto.useUnauthorizedMode = true;
            if (_tempAuth) {
                switch (_tempAuthKeyType) {
                    case MTDatacenterAuthTempKeyTypeMain:
                        _authMtProto.media = false;
                        break;
                    case MTDatacenterAuthTempKeyTypeMedia:
                        _authMtProto.media = true;
                        _authMtProto.enforceMedia = true;
                        break;
                    default:
                        break;
                }
            }
            
            MTDatacenterAuthMessageService *authService = [[MTDatacenterAuthMessageService alloc] initWithContext:context tempAuth:_tempAuth];
            authService.delegate = self;
            [_authMtProto addMessageService:authService];
        }
    }
    else
        [self fail];
}

- (void)authMessageServiceCompletedWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp {
    [self completeWithAuthKey:authKey timestamp:timestamp];
}

- (void)completeWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp {
    if (_tempAuth) {
        MTContext *mainContext = _context;
        if (mainContext != nil) {
            MTContext *context = _context;
            [context performBatchUpdates:^{
                MTDatacenterAuthInfo *authInfo = [context authInfoForDatacenterWithId:_datacenterId];
                if (authInfo != nil) {
                    authInfo = [authInfo withUpdatedTempAuthKeyWithType:_tempAuthKeyType key:authKey];
                    [context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo];
                }
            }];
            [self complete];
        }
    } else {
        MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil mainTempAuthKey:nil mediaTempAuthKey:nil];
        
        MTContext *context = _context;
        [context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo];
        [self complete];
    }
}

- (void)cleanup
{
    MTProto *authMtProto = _authMtProto;
    _authMtProto = nil;
    
    [authMtProto stop];
    
    [_verifyDisposable dispose];
}

- (void)cancel
{
    [self cleanup];
    [self fail];
}

- (void)complete
{
    id<MTDatacenterAuthActionDelegate> delegate = _delegate;
    if ([delegate respondsToSelector:@selector(datacenterAuthActionCompleted:)])
        [delegate datacenterAuthActionCompleted:self];
}

- (void)fail
{
    id<MTDatacenterAuthActionDelegate> delegate = _delegate;
    if ([delegate respondsToSelector:@selector(datacenterAuthActionCompleted:)])
        [delegate datacenterAuthActionCompleted:self];
}

@end


================================================
FILE: MTProtoKit/MTDatacenterAuthInfo.h
================================================
#import <Foundation/Foundation.h>

typedef NS_ENUM(NSUInteger, MTDatacenterAuthTempKeyType) {
    MTDatacenterAuthTempKeyTypeMain,
    MTDatacenterAuthTempKeyTypeMedia
};

@interface MTDatacenterAuthKey: NSObject <NSCoding>

@property (nonatomic, strong, readonly) NSData *authKey;
@property (nonatomic, readonly) int64_t authKeyId;
@property (nonatomic, readonly) bool notBound;

- (instancetype)initWithAuthKey:(NSData *)tempAuthKey authKeyId:(int64_t)authKeyId notBound:(bool)notBound;

@end

@interface MTDatacenterAuthInfo : NSObject <NSCoding>

@property (nonatomic, strong, readonly) NSData *authKey;
@property (nonatomic, readonly) int64_t authKeyId;
@property (nonatomic, strong, readonly) NSArray *saltSet;
@property (nonatomic, strong, readonly) NSDictionary *authKeyAttributes;
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *mainTempAuthKey;
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *mediaTempAuthKey;

@property (nonatomic, strong, readonly) MTDatacenterAuthKey *persistentAuthKey;

- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes mainTempAuthKey:(MTDatacenterAuthKey *)mainTempAuthKey mediaTempAuthKey:(MTDatacenterAuthKey *)mediaTempAuthKey;

- (int64_t)authSaltForMessageId:(int64_t)messageId;
- (MTDatacenterAuthInfo *)mergeSaltSet:(NSArray *)updatedSaltSet forTimestamp:(NSTimeInterval)timestamp;

- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes;
- (MTDatacenterAuthKey *)tempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type;
- (MTDatacenterAuthInfo *)withUpdatedTempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type key:(MTDatacenterAuthKey *)key;

@end


================================================
FILE: MTProtoKit/MTDatacenterAuthInfo.m
================================================
#import "MTDatacenterAuthInfo.h"
#import "MTDatacenterSaltInfo.h"

@implementation MTDatacenterAuthKey

- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId notBound:(bool)notBound {
    self = [super init];
    if (self != nil) {
        _authKey = authKey;
        _authKeyId = authKeyId;
        _notBound = notBound;
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    return [self initWithAuthKey:[aDecoder decodeObjectForKey:@"key"] authKeyId:[aDecoder decodeInt64ForKey:@"keyId"] notBound:[aDecoder decodeBoolForKey:@"notBound"]];
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_authKey forKey:@"key"];
    [aCoder encodeInt64:_authKeyId forKey:@"keyId"];
    [aCoder encodeBool:_notBound forKey:@"notBound"];
}

@end

@implementation MTDatacenterAuthInfo

- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes mainTempAuthKey:(MTDatacenterAuthKey *)mainTempAuthKey mediaTempAuthKey:(MTDatacenterAuthKey *)mediaTempAuthKey
{
    self = [super init];
    if (self != nil)
    {
        _authKey = authKey;
        _authKeyId = authKeyId;
        _saltSet = saltSet;
        _authKeyAttributes = authKeyAttributes;
        _mainTempAuthKey = mainTempAuthKey;
        _mediaTempAuthKey = mediaTempAuthKey;
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self != nil)
    {
        _authKey = [aDecoder decodeObjectForKey:@"authKey"];
        _authKeyId = [aDecoder decodeInt64ForKey:@"authKeyId"];
        _saltSet = [aDecoder decodeObjectForKey:@"saltSet"];
        _authKeyAttributes = [aDecoder decodeObjectForKey:@"authKeyAttributes"];
        _mainTempAuthKey = [aDecoder decodeObjectForKey:@"tempAuthKey"];
        _mediaTempAuthKey = [aDecoder decodeObjectForKey:@"mediaTempAuthKey"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_authKey forKey:@"authKey"];
    [aCoder encodeInt64:_authKeyId forKey:@"authKeyId"];
    [aCoder encodeObject:_saltSet forKey:@"saltSet"];
    [aCoder encodeObject:_authKeyAttributes forKey:@"authKeyAttributes"];
    [aCoder encodeObject:_mainTempAuthKey forKey:@"tempAuthKey"];
    [aCoder encodeObject:_mediaTempAuthKey forKey:@"mediaTempAuthKey"];
}

- (int64_t)authSaltForMessageId:(int64_t)messageId
{
    int64_t bestSalt = 0;
    int64_t bestValidMessageCount = 0;
    
    for (MTDatacenterSaltInfo *saltInfo in _saltSet)
    {
        int64_t currentValidMessageCount = [saltInfo validMessageCountAfterId:messageId];
        if (currentValidMessageCount != 0 && currentValidMessageCount > bestValidMessageCount)
            bestSalt = saltInfo.salt;
    }
    
    return bestSalt;
}

- (MTDatacenterAuthInfo *)mergeSaltSet:(NSArray *)updatedSaltSet forTimestamp:(NSTimeInterval)timestamp
{
    int64_t referenceMessageId = (int64_t)(timestamp * 4294967296);
    
    NSMutableArray *mergedSaltSet = [[NSMutableArray alloc] init];
    
    for (MTDatacenterSaltInfo *saltInfo in _saltSet)
    {
        if ([saltInfo isValidFutureSaltForMessageId:referenceMessageId])
            [mergedSaltSet addObject:saltInfo];
    }
    
    for (MTDatacenterSaltInfo *saltInfo in updatedSaltSet)
    {
        bool alreadExists = false;
        for (MTDatacenterSaltInfo *existingSaltInfo in mergedSaltSet)
        {
            if (existingSaltInfo.firstValidMessageId == saltInfo.firstValidMessageId)
            {
                alreadExists = true;
                break;
            }
        }
        
        if (!alreadExists)
        {
            if ([saltInfo isValidFutureSaltForMessageId:referenceMessageId])
                [mergedSaltSet addObject:saltInfo];
        }
    }
    
    return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:mergedSaltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:_mediaTempAuthKey];
}

- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes {
    return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:_mediaTempAuthKey];
}

- (MTDatacenterAuthKey *)tempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type {
    switch (type) {
        case MTDatacenterAuthTempKeyTypeMain:
            return _mainTempAuthKey;
        case MTDatacenterAuthTempKeyTypeMedia:
            return _mediaTempAuthKey;
        default:
            NSAssert(false, @"unknown MTDatacenterAuthTempKeyType");
            return nil;
    }
}

- (MTDatacenterAuthInfo *)withUpdatedTempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type key:(MTDatacenterAuthKey *)key {
    switch (type) {
        case MTDatacenterAuthTempKeyTypeMain:
            return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:key mediaTempAuthKey:_mediaTempAuthKey];
        case MTDatacenterAuthTempKeyTypeMedia:
            return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:key];
        default:
            NSAssert(false, @"unknown MTDatacenterAuthTempKeyType");
            return self;
    }
}

- (MTDatacenterAuthKey *)persistentAuthKey {
    return [[MTDatacenterAuthKey alloc] initWithAuthKey:_authKey authKeyId:_authKeyId notBound:false];
}

- (NSString *)description {
    return [NSString stringWithFormat:@"MTDatacenterAuthInfo authKeyId:%" PRId64 " authKey:%lu", _authKeyId, (unsigned long)_authKey.length];
}

@end


================================================
FILE: MTProtoKit/MTDatacenterAuthMessageService.h
================================================


#if defined(MtProtoKitDynamicFramework)
#   import <MTProtoKitDynamic/MTMessageService.h>
#elif defined(MtProtoKitMacFramework)
#   import <MTProtoKitMac/MTMessageService.h>
#else
#   import <MTProtoKit/MTMessageService.h>
#endif

@class MTContext;
@class MTDatacenterAuthMessageService;
@class MTDatacenterAuthKey;

@protocol MTDatacenterAuthMessageServiceDelegate <NSObject>

- (void)authMessageServiceCompletedWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp;

@end

@interface MTDatacenterAuthMessageService : NSObject <MTMessageService>

@property (nonatomic, weak) id<MTDatacenterAuthMessageServiceDelegate> delegate;

- (instancetype)initWithContext:(MTContext *)context tempAuth:(bool)tempAuth;

@end


================================================
FILE: MTProtoKit/MTDatacenterAuthMessageService.m
================================================
#import "MTDatacenterAuthMessageService.h"

#import "MTLogging.h"
#import "MTContext.h"
#import "MTProto.h"
#import "MTSerialization.h"
#import "MTSessionInfo.h"
#import "MTIncomingMessage.h"
#import "MTOutgoingMessage.h"
#import "MTMessageTransaction.h"
#import "MTPreparedMessage.h"
#import "MTDatacenterAuthInfo.h"
#import "MTDatacenterSaltInfo.h"
#import "MTBuffer.h"
#import "MTEncryption.h"

#import "MTInternalMessageParser.h"
#import "MTServerDhInnerDataMessage.h"
#import "MTResPqMessage.h"
#import "MTServerDhParamsMessage.h"
#import "MTSetClientDhParamsResponseMessage.h"

static NSArray *defaultPublicKeys() {
    static NSArray *serverPublicKeys = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
                  {
        serverPublicKeys = [[NSArray alloc] initWithObjects:
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\n"
"ksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\n"
"vCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\n"
"xI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\n"
"XvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\n"
"NTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0x9a996a1db11c729bUL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\n"
"DyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n"
"1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\n"
"g1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\n"
"hRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\n"
"x5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0xb05b2a6f70cdea78UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\n"
"lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\n"
"an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\n"
"Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n"
"8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\n"
"Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0xc3b42b026ce86b21UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\n"
"xDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\n"
"qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n"
"/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\n"
"WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYe
Download .txt
gitextract_n8bpxe0q/

├── .gitignore
├── MTAes.h
├── MTAes.m
├── MTAtomic.h
├── MTAtomic.m
├── MTBackupAddressSignals.h
├── MTBackupAddressSignals.m
├── MTBag.h
├── MTBag.m
├── MTBindingTempAuthKeyContext.h
├── MTBindingTempAuthKeyContext.m
├── MTConnectionProbing.h
├── MTConnectionProbing.m
├── MTDNS.h
├── MTDNS.m
├── MTDatacenterVerificationData.h
├── MTDatacenterVerificationData.m
├── MTDiscoverConnectionSignals.h
├── MTDiscoverConnectionSignals.m
├── MTDisposable.h
├── MTDisposable.m
├── MTGzip.h
├── MTGzip.m
├── MTNetworkUsageCalculationInfo.h
├── MTNetworkUsageCalculationInfo.m
├── MTNetworkUsageManager.h
├── MTNetworkUsageManager.m
├── MTPKCS.h
├── MTPKCS.m
├── MTProtoKit/
│   ├── Info.plist
│   ├── MTApiEnvironment.h
│   ├── MTApiEnvironment.m
│   ├── MTBadMsgNotificationMessage.h
│   ├── MTBadMsgNotificationMessage.m
│   ├── MTBuffer.h
│   ├── MTBuffer.m
│   ├── MTBufferReader.h
│   ├── MTBufferReader.m
│   ├── MTContext.h
│   ├── MTContext.m
│   ├── MTDatacenterAddress.h
│   ├── MTDatacenterAddress.m
│   ├── MTDatacenterAddressListData.h
│   ├── MTDatacenterAddressListData.m
│   ├── MTDatacenterAddressSet.h
│   ├── MTDatacenterAddressSet.m
│   ├── MTDatacenterAuthAction.h
│   ├── MTDatacenterAuthAction.m
│   ├── MTDatacenterAuthInfo.h
│   ├── MTDatacenterAuthInfo.m
│   ├── MTDatacenterAuthMessageService.h
│   ├── MTDatacenterAuthMessageService.m
│   ├── MTDatacenterSaltInfo.h
│   ├── MTDatacenterSaltInfo.m
│   ├── MTDatacenterTransferAuthAction.h
│   ├── MTDatacenterTransferAuthAction.m
│   ├── MTDestroySessionResponseMessage.h
│   ├── MTDestroySessionResponseMessage.m
│   ├── MTDiscoverDatacenterAddressAction.h
│   ├── MTDiscoverDatacenterAddressAction.m
│   ├── MTDropResponseContext.h
│   ├── MTDropResponseContext.m
│   ├── MTDropRpcResultMessage.h
│   ├── MTDropRpcResultMessage.m
│   ├── MTEncryption.h
│   ├── MTEncryption.m
│   ├── MTExportedAuthorizationData.h
│   ├── MTExportedAuthorizationData.m
│   ├── MTFileBasedKeychain.h
│   ├── MTFileBasedKeychain.m
│   ├── MTFutureSaltsMessage.h
│   ├── MTFutureSaltsMessage.m
│   ├── MTHttpRequestOperation.h
│   ├── MTHttpRequestOperation.m
│   ├── MTIncomingMessage.h
│   ├── MTIncomingMessage.m
│   ├── MTInputStream.h
│   ├── MTInputStream.m
│   ├── MTInternalId.h
│   ├── MTInternalMessageParser.h
│   ├── MTInternalMessageParser.m
│   ├── MTKeychain.h
│   ├── MTKeychain.m
│   ├── MTLogging.h
│   ├── MTLogging.m
│   ├── MTMessage.h
│   ├── MTMessage.m
│   ├── MTMessageEncryptionKey.h
│   ├── MTMessageEncryptionKey.m
│   ├── MTMessageService.h
│   ├── MTMessageTransaction.h
│   ├── MTMessageTransaction.m
│   ├── MTMsgAllInfoMessage.h
│   ├── MTMsgAllInfoMessage.m
│   ├── MTMsgContainerMessage.h
│   ├── MTMsgContainerMessage.m
│   ├── MTMsgDetailedInfoMessage.h
│   ├── MTMsgDetailedInfoMessage.m
│   ├── MTMsgResendReqMessage.h
│   ├── MTMsgResendReqMessage.m
│   ├── MTMsgsAckMessage.h
│   ├── MTMsgsAckMessage.m
│   ├── MTMsgsStateInfoMessage.h
│   ├── MTMsgsStateInfoMessage.m
│   ├── MTMsgsStateReqMessage.h
│   ├── MTMsgsStateReqMessage.m
│   ├── MTNetworkAvailability.h
│   ├── MTNetworkAvailability.m
│   ├── MTNewSessionCreatedMessage.h
│   ├── MTNewSessionCreatedMessage.m
│   ├── MTOutgoingMessage.h
│   ├── MTOutgoingMessage.m
│   ├── MTOutputStream.h
│   ├── MTOutputStream.m
│   ├── MTPingMessage.h
│   ├── MTPingMessage.m
│   ├── MTPongMessage.h
│   ├── MTPongMessage.m
│   ├── MTPreparedMessage.h
│   ├── MTPreparedMessage.m
│   ├── MTProto.h
│   ├── MTProto.m
│   ├── MTProtoKit.h
│   ├── MTQueue.h
│   ├── MTQueue.m
│   ├── MTRequest.h
│   ├── MTRequest.m
│   ├── MTRequestContext.h
│   ├── MTRequestContext.m
│   ├── MTRequestErrorContext.h
│   ├── MTRequestErrorContext.m
│   ├── MTRequestMessageService.h
│   ├── MTRequestMessageService.m
│   ├── MTResPqMessage.h
│   ├── MTResPqMessage.m
│   ├── MTResendMessageService.h
│   ├── MTResendMessageService.m
│   ├── MTRpcError.h
│   ├── MTRpcError.m
│   ├── MTRpcResultMessage.h
│   ├── MTRpcResultMessage.m
│   ├── MTSerialization.h
│   ├── MTServerDhInnerDataMessage.h
│   ├── MTServerDhInnerDataMessage.m
│   ├── MTServerDhParamsMessage.h
│   ├── MTServerDhParamsMessage.m
│   ├── MTSessionInfo.h
│   ├── MTSessionInfo.m
│   ├── MTSetClientDhParamsResponseMessage.h
│   ├── MTSetClientDhParamsResponseMessage.m
│   ├── MTTcpConnection.h
│   ├── MTTcpConnection.m
│   ├── MTTcpConnectionBehaviour.h
│   ├── MTTcpConnectionBehaviour.m
│   ├── MTTcpTransport.h
│   ├── MTTcpTransport.m
│   ├── MTTime.h
│   ├── MTTime.m
│   ├── MTTimeFixContext.h
│   ├── MTTimeFixContext.m
│   ├── MTTimeSyncMessageService.h
│   ├── MTTimeSyncMessageService.m
│   ├── MTTimer.h
│   ├── MTTimer.m
│   ├── MTTransport.h
│   ├── MTTransport.m
│   ├── MTTransportScheme.h
│   ├── MTTransportScheme.m
│   ├── MTTransportTransaction.h
│   └── MTTransportTransaction.m
├── MTProxyConnectivity.h
├── MTProxyConnectivity.m
├── MTRsa.h
├── MTRsa.m
├── MTSignal.h
├── MTSignal.m
├── MTSubscriber.h
├── MTSubscriber.m
├── MTTransportSchemeStats.h
├── MTTransportSchemeStats.m
├── MtProtoKit.xcodeproj/
│   ├── project.pbxproj
│   └── project.xcworkspace/
│       └── contents.xcworkspacedata
├── MtProtoKitDynamic/
│   ├── Info.plist
│   └── MtProtoKitDynamic.h
├── MtProtoKitDynamicTests/
│   ├── Info.plist
│   └── MtProtoKitDynamicTests.m
├── MtProtoKitMac/
│   ├── Info.plist
│   └── MtProtoKitMac.h
├── MtProtoKitMacTests/
│   ├── Info.plist
│   └── MtProtoKitMacTests.m
├── MtProtoKitOSX/
│   ├── MtProtoKitOSX-Info.plist
│   ├── MtProtoKitOSX-Prefix.pch
│   └── en.lproj/
│       └── InfoPlist.strings
├── MtProtoKitStabilityTests/
│   ├── Info.plist
│   └── MtProtoKitStabilityTests.m
├── MtProtoKitTests/
│   ├── Info.plist
│   ├── MtProtoKitTests-Info.plist
│   ├── MtProtoKitTests.m
│   └── en.lproj/
│       └── InfoPlist.strings
├── MtProtoKitiOS/
│   ├── MtProtoKit-Info.plist
│   ├── MtProtoKit-Prefix.pch
│   └── en.lproj/
│       └── InfoPlist.strings
├── PingFoundation.h
├── PingFoundation.m
├── README.md
├── openssl/
│   ├── OSX/
│   │   └── libcrypto.a
│   ├── iOS/
│   │   └── libcrypto.a
│   └── openssl/
│       ├── aes.h
│       ├── asn1.h
│       ├── asn1_mac.h
│       ├── asn1t.h
│       ├── async.h
│       ├── bio.h
│       ├── blowfish.h
│       ├── bn.h
│       ├── buffer.h
│       ├── camellia.h
│       ├── cast.h
│       ├── cmac.h
│       ├── cms.h
│       ├── comp.h
│       ├── conf.h
│       ├── conf_api.h
│       ├── crypto.h
│       ├── ct.h
│       ├── des.h
│       ├── des_old.h
│       ├── dh.h
│       ├── dsa.h
│       ├── dso.h
│       ├── dtls1.h
│       ├── e_os2.h
│       ├── ebcdic.h
│       ├── ec.h
│       ├── ecdh.h
│       ├── ecdsa.h
│       ├── engine.h
│       ├── err.h
│       ├── evp.h
│       ├── hmac.h
│       ├── idea.h
│       ├── kdf.h
│       ├── krb5_asn.h
│       ├── kssl.h
│       ├── lhash.h
│       ├── md2.h
│       ├── md4.h
│       ├── md5.h
│       ├── mdc2.h
│       ├── modes.h
│       ├── obj_mac.h
│       ├── objects.h
│       ├── ocsp.h
│       ├── opensslconf.h
│       ├── opensslv.h
│       ├── ossl_typ.h
│       ├── pem.h
│       ├── pem2.h
│       ├── pkcs12.h
│       ├── pkcs7.h
│       ├── pqueue.h
│       ├── rand.h
│       ├── rc2.h
│       ├── rc4.h
│       ├── rc5.h
│       ├── ripemd.h
│       ├── rsa.h
│       ├── safestack.h
│       ├── seed.h
│       ├── sha.h
│       ├── srp.h
│       ├── srtp.h
│       ├── ssl.h
│       ├── ssl2.h
│       ├── ssl23.h
│       ├── ssl3.h
│       ├── stack.h
│       ├── symhacks.h
│       ├── tls1.h
│       ├── ts.h
│       ├── txt_db.h
│       ├── ui.h
│       ├── ui_compat.h
│       ├── whrlpool.h
│       ├── x509.h
│       ├── x509_vfy.h
│       └── x509v3.h
└── thirdparty/
    ├── AFNetworking/
    │   ├── AFHTTPClient.h
    │   ├── AFHTTPClient.m
    │   ├── AFHTTPRequestOperation.h
    │   ├── AFHTTPRequestOperation.m
    │   ├── AFJSONUtilities.h
    │   ├── AFJSONUtilities.m
    │   ├── AFNetworking.h
    │   ├── AFURLConnectionOperation.h
    │   └── AFURLConnectionOperation.m
    └── AsyncSocket/
        ├── GCDAsyncSocket.h
        └── GCDAsyncSocket.m
Download .txt
SYMBOL INDEX (474 symbols across 74 files)

FILE: MTDiscoverConnectionSignals.h
  type MTPayloadData (line 7) | typedef struct {

FILE: MTNetworkUsageManager.h
  type MTNetworkUsageManagerInterface (line 6) | typedef enum {
  type MTNetworkUsageManagerInterfaceStats (line 11) | typedef struct {

FILE: MTProtoKit/MTDatacenterAuthInfo.h
  type MTDatacenterAuthTempKeyTypeMain (line 3) | typedef NS_ENUM(NSUInteger, MTDatacenterAuthTempKeyType) {

FILE: MTProtoKit/MTSerialization.h
  type MTExportedAuthorizationData (line 19) | typedef MTExportedAuthorizationData *(^MTExportAuthorizationResponsePars...
  type MTDatacenterAddressListData (line 20) | typedef MTDatacenterAddressListData *(^MTRequestDatacenterAddressListPar...
  type MTDatacenterVerificationData (line 21) | typedef MTDatacenterVerificationData *(^MTDatacenterVerificationDataPars...

FILE: MTSignal.h
  function interface (line 7) | interface MTSignal : NSObject

FILE: MTSubscriber.h
  function interface (line 5) | interface MTSubscriber : NSObject <MTDisposable>

FILE: PingFoundation.h
  type PingFoundationAddressStyleAny (line 15) | typedef NS_ENUM(NSInteger, PingFoundationAddressStyle) {
  type ICMPHeader (line 217) | struct ICMPHeader
  type ICMPHeader (line 226) | typedef struct ICMPHeader ICMPHeader;

FILE: openssl/openssl/aes.h
  type aes_key_st (line 78) | struct aes_key_st {
  type AES_KEY (line 86) | typedef struct aes_key_st AES_KEY;

FILE: openssl/openssl/asn1.h
  type X509_algor_st (line 161) | struct X509_algor_st
  type ASN1_CTX (line 172) | typedef struct asn1_ctx_st {
  type ASN1_const_CTX (line 187) | typedef struct asn1_const_ctx_st {
  type asn1_object_st (line 210) | struct asn1_object_st {
  type asn1_string_st (line 239) | struct asn1_string_st {
  type ASN1_ENCODING (line 257) | typedef struct ASN1_ENCODING_st {
  type ASN1_STRING_TABLE (line 272) | typedef struct asn1_string_table_st {
  type ASN1_TEMPLATE (line 296) | typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE;
  type ASN1_TLC (line 297) | typedef struct ASN1_TLC_st ASN1_TLC;
  type ASN1_VALUE (line 299) | typedef struct ASN1_VALUE_st ASN1_VALUE;
  type ASN1_ITEM (line 404) | typedef const ASN1_ITEM ASN1_ITEM_EXP;
  type ASN1_ITEM (line 425) | typedef const ASN1_ITEM *ASN1_ITEM_EXP (void);
  function DECLARE_ASN1_SET_OF (line 519) | DECLARE_STACK_OF(ASN1_INTEGER)
  function DECLARE_ASN1_SET_OF (line 555) | DECLARE_STACK_OF(ASN1_TYPE)

FILE: openssl/openssl/asn1t.h
  type ASN1_TEMPLATE_st (line 443) | struct ASN1_TEMPLATE_st {
  type ASN1_ADB_TABLE (line 458) | typedef struct ASN1_ADB_TABLE_st ASN1_ADB_TABLE;
  type ASN1_ADB (line 459) | typedef struct ASN1_ADB_st ASN1_ADB;
  type ASN1_ADB_st (line 461) | struct ASN1_ADB_st {
  type ASN1_ADB_TABLE_st (line 471) | struct ASN1_ADB_TABLE_st {
  type ASN1_ITEM_st (line 563) | struct ASN1_ITEM_st {
  type ASN1_TLC_st (line 641) | struct ASN1_TLC_st {
  type ASN1_VALUE (line 652) | typedef ASN1_VALUE *ASN1_new_func(void);
  type ASN1_VALUE (line 654) | typedef ASN1_VALUE *ASN1_d2i_func(ASN1_VALUE **a, const unsigned char **in,
  type ASN1_COMPAT_FUNCS (line 680) | typedef struct ASN1_COMPAT_FUNCS_st {
  type ASN1_EXTERN_FUNCS (line 687) | typedef struct ASN1_EXTERN_FUNCS_st {
  type ASN1_PRIMITIVE_FUNCS (line 697) | typedef struct ASN1_PRIMITIVE_FUNCS_st {
  type ASN1_AUX (line 724) | typedef struct ASN1_AUX_st {
  type ASN1_PRINT_ARG (line 734) | typedef struct ASN1_PRINT_ARG_st {
  type ASN1_STREAM_ARG (line 741) | typedef struct ASN1_STREAM_ARG_st {

FILE: openssl/openssl/async.h
  type ASYNC_JOB (line 31) | typedef struct async_job_st ASYNC_JOB;
  type ASYNC_WAIT_CTX (line 32) | typedef struct async_wait_ctx_st ASYNC_WAIT_CTX;

FILE: openssl/openssl/bio.h
  type BIO (line 238) | typedef struct bio_st BIO;
  type bio_st (line 298) | struct bio_st
  type bio_st (line 301) | struct bio_st
  type bio_st (line 309) | struct bio_st
  type BIO_METHOD (line 312) | typedef struct bio_method_st {
  type bio_st (line 325) | struct bio_st {
  type bio_f_buffer_ctx_struct (line 346) | struct bio_f_buffer_ctx_struct {
  type bio_dgram_sctp_sndinfo (line 375) | struct bio_dgram_sctp_sndinfo {
  type bio_dgram_sctp_rcvinfo (line 382) | struct bio_dgram_sctp_rcvinfo {
  type bio_dgram_sctp_prinfo (line 392) | struct bio_dgram_sctp_prinfo {
  type bio_st (line 670) | struct bio_st
  type hostent (line 736) | struct hostent

FILE: openssl/openssl/blowfish.h
  type BF_KEY (line 101) | typedef struct bf_key_st {

FILE: openssl/openssl/bn.h
  type BIGNUM (line 304) | typedef struct bignum_st BIGNUM;
  type BN_CTX (line 306) | typedef struct bignum_ctx BN_CTX;
  type BN_BLINDING (line 307) | typedef struct bn_blinding_st BN_BLINDING;
  type BN_MONT_CTX (line 308) | typedef struct bn_mont_ctx_st BN_MONT_CTX;
  type BN_RECP_CTX (line 309) | typedef struct bn_recp_ctx_st BN_RECP_CTX;
  type BN_GENCB (line 310) | typedef struct bn_gencb_st BN_GENCB;
  type bignum_st (line 313) | struct bignum_st {
  type bn_mont_ctx_st (line 324) | struct bn_mont_ctx_st {
  type bn_recp_ctx_st (line 340) | struct bn_recp_ctx_st {
  type bn_gencb_st (line 349) | struct bn_gencb_st {

FILE: openssl/openssl/buffer.h
  type buf_mem_st (line 77) | struct buf_mem_st {

FILE: openssl/openssl/camellia.h
  type camellia_key_st (line 84) | struct camellia_key_st {
  type CAMELLIA_KEY (line 91) | typedef struct camellia_key_st CAMELLIA_KEY;

FILE: openssl/openssl/cast.h
  type CAST_KEY (line 80) | typedef struct cast_key_st {

FILE: openssl/openssl/cmac.h
  type CMAC_CTX (line 65) | typedef struct CMAC_CTX_st CMAC_CTX;

FILE: openssl/openssl/cms.h
  type CMS_ContentInfo (line 68) | typedef struct CMS_ContentInfo_st CMS_ContentInfo;
  type CMS_SignerInfo (line 69) | typedef struct CMS_SignerInfo_st CMS_SignerInfo;
  type CMS_CertificateChoices (line 70) | typedef struct CMS_CertificateChoices CMS_CertificateChoices;
  type CMS_RevocationInfoChoice (line 71) | typedef struct CMS_RevocationInfoChoice_st CMS_RevocationInfoChoice;
  type CMS_RecipientInfo (line 72) | typedef struct CMS_RecipientInfo_st CMS_RecipientInfo;
  type CMS_ReceiptRequest (line 73) | typedef struct CMS_ReceiptRequest_st CMS_ReceiptRequest;
  type CMS_Receipt (line 74) | typedef struct CMS_Receipt_st CMS_Receipt;
  type CMS_RecipientEncryptedKey (line 75) | typedef struct CMS_RecipientEncryptedKey_st CMS_RecipientEncryptedKey;
  type CMS_OtherKeyAttribute (line 76) | typedef struct CMS_OtherKeyAttribute_st CMS_OtherKeyAttribute;

FILE: openssl/openssl/comp.h
  type COMP_CTX (line 15) | typedef struct comp_ctx_st COMP_CTX;
  type COMP_METHOD (line 17) | typedef struct comp_method_st {
  type comp_ctx_st (line 35) | struct comp_ctx_st {

FILE: openssl/openssl/conf.h
  type CONF_VALUE (line 74) | typedef struct {
  type conf_st (line 83) | struct conf_st
  type conf_method_st (line 84) | struct conf_method_st
  type CONF_METHOD (line 85) | typedef struct conf_method_st CONF_METHOD;
  type conf_method_st (line 87) | struct conf_method_st {
  type CONF_IMODULE (line 102) | typedef struct conf_imodule_st CONF_IMODULE;
  type CONF_MODULE (line 103) | typedef struct conf_module_st CONF_MODULE;
  type conf_st (line 147) | struct conf_st {

FILE: openssl/openssl/crypto.h
  type CRYPTO_EX_DATA (line 162) | typedef struct crypto_ex_data_st CRYPTO_EX_DATA;
  type OPENSSL_ITEM (line 175) | typedef struct openssl_item_st {
  type CRYPTO_dynlock (line 262) | typedef struct {
  type BIO_dummy (line 290) | typedef struct bio_st BIO_dummy;
  type crypto_ex_data_st (line 292) | struct crypto_ex_data_st {
  type crypto_ex_data_func_st (line 304) | struct crypto_ex_data_func_st {
  type CRYPTO_EX_DATA_IMPL (line 402) | typedef struct st_CRYPTO_EX_DATA_IMPL CRYPTO_EX_DATA_IMPL;
  type CRYPTO_THREADID (line 448) | typedef struct crypto_threadid_st {
  type CRYPTO_dynlock_value (line 473) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 474) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 480) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 483) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 485) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 488) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 491) | struct CRYPTO_dynlock_value
  type bio_st (line 583) | struct bio_st

FILE: openssl/openssl/ct.h
  type ct_log_entry_type_t (line 30) | typedef enum {
  type sct_version_t (line 36) | typedef enum {
  type sct_source_t (line 41) | typedef enum {
  type sct_validation_status_t (line 48) | typedef enum {

FILE: openssl/openssl/des.h
  type DES_key_schedule (line 85) | typedef struct DES_ks {

FILE: openssl/openssl/des_old.h
  type _ossl_old_des_ks_struct (line 126) | struct _ossl_old_des_ks_struct {

FILE: openssl/openssl/dh.h
  type dh_method (line 117) | struct dh_method {
  type dh_st (line 135) | struct dh_st {

FILE: openssl/openssl/dsa.h
  type DSA_SIG (line 124) | typedef struct DSA_SIG_st {
  type dsa_method (line 129) | struct dsa_method {
  type dsa_st (line 155) | struct dsa_st {

FILE: openssl/openssl/dso.h
  type DSO (line 112) | typedef struct dso_st DSO;
  type DSO_METHOD (line 140) | typedef struct dso_meth_st {
  type dso_st (line 194) | struct dso_st {

FILE: openssl/openssl/dtls1.h
  type DTLS1_BITMAP (line 128) | typedef struct dtls1_bitmap_st {
  type dtls1_retransmit_state (line 135) | struct dtls1_retransmit_state {
  type hm_header_st (line 147) | struct hm_header_st {
  type ccs_header_st (line 157) | struct ccs_header_st {
  type dtls1_timeout_st (line 162) | struct dtls1_timeout_st {
  type record_pqueue (line 171) | typedef struct record_pqueue_st {
  type hm_fragment (line 176) | typedef struct hm_fragment_st {
  type DTLS1_STATE (line 182) | typedef struct dtls1_state_st {
  type DTLS1_RECORD_DATA (line 251) | typedef struct dtls1_record_data_st {

FILE: openssl/openssl/ec.h
  type point_conversion_form_t (line 105) | typedef enum {
  type EC_METHOD (line 116) | typedef struct ec_method_st EC_METHOD;
  type EC_GROUP (line 118) | typedef struct ec_group_st
  type EC_POINT (line 129) | typedef struct ec_point_st EC_POINT;
  type EC_builtin_curve (line 400) | typedef struct {
  type ECPKPARAMETERS (line 718) | typedef struct ecpk_parameters_st ECPKPARAMETERS;
  type EC_KEY (line 741) | typedef struct ec_key_st EC_KEY;

FILE: openssl/openssl/ecdsa.h
  type ECDSA_SIG (line 78) | typedef struct ECDSA_SIG_st {

FILE: openssl/openssl/engine.h
  type ENGINE_CMD_DEFN (line 323) | typedef struct ENGINE_CMD_DEFN_st {
  type EVP_PKEY (line 338) | typedef EVP_PKEY *(*ENGINE_LOAD_KEY_PTR)(ENGINE *, const char *,
  type dynamic_MEM_fns (line 754) | typedef struct st_dynamic_MEM_fns {
  type CRYPTO_dynlock_value (line 765) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 767) | struct CRYPTO_dynlock_value
  type CRYPTO_dynlock_value (line 769) | struct CRYPTO_dynlock_value
  type dynamic_LOCK_fns (line 771) | typedef struct st_dynamic_LOCK_fns {
  type dynamic_fns (line 779) | typedef struct st_dynamic_fns {

FILE: openssl/openssl/err.h
  type ERR_STATE (line 148) | typedef struct err_state_st {
  type ERR_STRING_DATA (line 313) | typedef struct ERR_string_data_st {

FILE: openssl/openssl/evp.h
  type evp_pkey_st (line 129) | struct evp_pkey_st {
  type env_md_st (line 160) | struct env_md_st {
  type env_md_ctx_st (line 268) | struct env_md_ctx_st {
  type evp_cipher_st (line 308) | struct evp_cipher_st {
  type EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM (line 429) | typedef struct {
  type EVP_CIPHER_INFO (line 444) | typedef struct evp_cipher_info_st {
  type evp_cipher_ctx_st (line 449) | struct evp_cipher_ctx_st {
  type EVP_ENCODE_CTX (line 468) | typedef struct evp_Encode_Ctx_st {
  type rsa_st (line 960) | struct rsa_st
  type rsa_st (line 961) | struct rsa_st
  type rsa_st (line 962) | struct rsa_st
  type dsa_st (line 965) | struct dsa_st
  type dsa_st (line 966) | struct dsa_st
  type dsa_st (line 967) | struct dsa_st
  type dh_st (line 970) | struct dh_st
  type dh_st (line 971) | struct dh_st
  type dh_st (line 972) | struct dh_st
  type ec_key_st (line 975) | struct ec_key_st
  type ec_key_st (line 976) | struct ec_key_st
  type ec_key_st (line 977) | struct ec_key_st

FILE: openssl/openssl/hmac.h
  type HMAC_CTX (line 75) | typedef struct hmac_ctx_st {

FILE: openssl/openssl/idea.h
  type IDEA_KEY_SCHEDULE (line 78) | typedef struct idea_key_st {

FILE: openssl/openssl/krb5_asn.h
  type KRB5_ENCDATA (line 83) | typedef struct krb5_encdata_st {
  type krb5_princname_st (line 96) | struct krb5_princname_st {
  type krb5_tktbody_st (line 110) | struct krb5_tktbody_st {
  type KRB5_TICKET (line 117) | typedef STACK_OF(KRB5_TKTBODY) KRB5_TICKET;
  type krb5_ap_req_st (line 131) | struct krb5_ap_req_st {
  type KRB5_APREQ (line 139) | typedef STACK_OF(KRB5_APREQBODY) KRB5_APREQ;
  type krb5_checksum_st (line 149) | struct krb5_checksum_st {
  type krb5_encryptionkey_st (line 161) | struct krb5_encryptionkey_st {
  type krb5_authorization_st (line 173) | struct krb5_authorization_st {
  type krb5_authenticator_st (line 193) | struct krb5_authenticator_st {
  type KRB5_AUTHENT (line 205) | typedef STACK_OF(KRB5_AUTHENTBODY) KRB5_AUTHENT;

FILE: openssl/openssl/kssl.h
  type krb5_octet (line 94) | typedef unsigned char krb5_octet;
  type KSSL_ERR (line 132) | typedef struct kssl_err_st {
  type KSSL_CTX (line 141) | typedef struct kssl_ctx_st {

FILE: openssl/openssl/lhash.h
  type LHASH_NODE (line 79) | typedef struct lhash_node_st {
  type _LHASH (line 139) | typedef struct lhash_st {

FILE: openssl/openssl/md2.h
  type MD2_INT (line 21) | typedef unsigned char MD2_INT;
  type MD2_CTX (line 26) | typedef struct MD2state_st {

FILE: openssl/openssl/md4.h
  type MD4_CTX (line 100) | typedef struct MD4state_st {

FILE: openssl/openssl/md5.h
  type MD5_CTX (line 100) | typedef struct MD5state_st {

FILE: openssl/openssl/mdc2.h
  type MDC2_CTX (line 75) | typedef struct mdc2_ctx_st {

FILE: openssl/openssl/modes.h
  type GCM128_CONTEXT (line 98) | typedef struct gcm128_context GCM128_CONTEXT;
  type CCM128_CONTEXT (line 123) | typedef struct ccm128_context CCM128_CONTEXT;
  type XTS128_CONTEXT (line 144) | typedef struct xts128_context XTS128_CONTEXT;

FILE: openssl/openssl/objects.h
  type OBJ_NAME (line 984) | typedef struct obj_name_st {

FILE: openssl/openssl/ocsp.h
  type OCSP_CERTID (line 102) | typedef struct ocsp_cert_id_st {
  type ocsp_one_request_st (line 115) | struct ocsp_one_request_st {
  function DECLARE_ASN1_SET_OF (line 120) | DECLARE_STACK_OF(OCSP_ONEREQ)
  type OCSP_SIGNATURE (line 141) | typedef struct ocsp_signature_st {
  type OCSP_REQUEST (line 151) | typedef struct ocsp_request_st {
  type OCSP_RESPBYTES (line 177) | typedef struct ocsp_resp_bytes_st {
  type ocsp_response_st (line 186) | struct ocsp_response_st {
  type ocsp_responder_id_st (line 197) | struct ocsp_responder_id_st {
  function DECLARE_ASN1_FUNCTIONS (line 205) | DECLARE_STACK_OF(OCSP_RESPID)
  type OCSP_CERTSTATUS (line 229) | typedef struct ocsp_cert_status_st {
  type OCSP_SINGLERESP (line 245) | typedef struct ocsp_single_response_st {
  function DECLARE_ASN1_SET_OF (line 253) | DECLARE_STACK_OF(OCSP_SINGLERESP)
  type OCSP_BASICRESP (line 297) | typedef struct ocsp_basic_response_st {
  type OCSP_CRLID (line 331) | typedef struct ocsp_crl_id_st {
  type OCSP_SERVICELOC (line 342) | typedef struct ocsp_service_locator_st {

FILE: openssl/openssl/ossl_typ.h
  type ASN1_INTEGER (line 83) | typedef struct asn1_string_st ASN1_INTEGER;
  type ASN1_ENUMERATED (line 84) | typedef struct asn1_string_st ASN1_ENUMERATED;
  type ASN1_BIT_STRING (line 85) | typedef struct asn1_string_st ASN1_BIT_STRING;
  type ASN1_OCTET_STRING (line 86) | typedef struct asn1_string_st ASN1_OCTET_STRING;
  type ASN1_PRINTABLESTRING (line 87) | typedef struct asn1_string_st ASN1_PRINTABLESTRING;
  type ASN1_T61STRING (line 88) | typedef struct asn1_string_st ASN1_T61STRING;
  type ASN1_IA5STRING (line 89) | typedef struct asn1_string_st ASN1_IA5STRING;
  type ASN1_GENERALSTRING (line 90) | typedef struct asn1_string_st ASN1_GENERALSTRING;
  type ASN1_UNIVERSALSTRING (line 91) | typedef struct asn1_string_st ASN1_UNIVERSALSTRING;
  type ASN1_BMPSTRING (line 92) | typedef struct asn1_string_st ASN1_BMPSTRING;
  type ASN1_UTCTIME (line 93) | typedef struct asn1_string_st ASN1_UTCTIME;
  type ASN1_TIME (line 94) | typedef struct asn1_string_st ASN1_TIME;
  type ASN1_GENERALIZEDTIME (line 95) | typedef struct asn1_string_st ASN1_GENERALIZEDTIME;
  type ASN1_VISIBLESTRING (line 96) | typedef struct asn1_string_st ASN1_VISIBLESTRING;
  type ASN1_UTF8STRING (line 97) | typedef struct asn1_string_st ASN1_UTF8STRING;
  type ASN1_STRING (line 98) | typedef struct asn1_string_st ASN1_STRING;
  type ASN1_BOOLEAN (line 99) | typedef int ASN1_BOOLEAN;
  type ASN1_NULL (line 100) | typedef int ASN1_NULL;
  type ASN1_OBJECT (line 103) | typedef struct asn1_object_st ASN1_OBJECT;
  type ASN1_ITEM (line 105) | typedef struct ASN1_ITEM_st ASN1_ITEM;
  type ASN1_PCTX (line 106) | typedef struct asn1_pctx_st ASN1_PCTX;
  type BIGNUM (line 120) | typedef struct bignum_st BIGNUM;
  type BN_CTX (line 121) | typedef struct bignum_ctx BN_CTX;
  type BN_BLINDING (line 122) | typedef struct bn_blinding_st BN_BLINDING;
  type BN_MONT_CTX (line 123) | typedef struct bn_mont_ctx_st BN_MONT_CTX;
  type BN_RECP_CTX (line 124) | typedef struct bn_recp_ctx_st BN_RECP_CTX;
  type BN_GENCB (line 125) | typedef struct bn_gencb_st BN_GENCB;
  type BUF_MEM (line 127) | typedef struct buf_mem_st BUF_MEM;
  type EVP_CIPHER (line 129) | typedef struct evp_cipher_st EVP_CIPHER;
  type EVP_CIPHER_CTX (line 130) | typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
  type EVP_MD (line 131) | typedef struct env_md_st EVP_MD;
  type EVP_MD_CTX (line 132) | typedef struct env_md_ctx_st EVP_MD_CTX;
  type EVP_PKEY (line 133) | typedef struct evp_pkey_st EVP_PKEY;
  type EVP_PKEY_ASN1_METHOD (line 135) | typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;
  type EVP_PKEY_METHOD (line 137) | typedef struct evp_pkey_method_st EVP_PKEY_METHOD;
  type EVP_PKEY_CTX (line 138) | typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
  type DH (line 140) | typedef struct dh_st DH;
  type DH_METHOD (line 141) | typedef struct dh_method DH_METHOD;
  type DSA (line 143) | typedef struct dsa_st DSA;
  type DSA_METHOD (line 144) | typedef struct dsa_method DSA_METHOD;
  type RSA (line 146) | typedef struct rsa_st RSA;
  type RSA_METHOD (line 147) | typedef struct rsa_meth_st RSA_METHOD;
  type RAND_METHOD (line 149) | typedef struct rand_meth_st RAND_METHOD;
  type ECDH_METHOD (line 151) | typedef struct ecdh_method ECDH_METHOD;
  type ECDSA_METHOD (line 152) | typedef struct ecdsa_method ECDSA_METHOD;
  type X509 (line 154) | typedef struct x509_st X509;
  type X509_ALGOR (line 155) | typedef struct X509_algor_st X509_ALGOR;
  type X509_CRL (line 156) | typedef struct X509_crl_st X509_CRL;
  type X509_CRL_METHOD (line 157) | typedef struct x509_crl_method_st X509_CRL_METHOD;
  type X509_REVOKED (line 158) | typedef struct x509_revoked_st X509_REVOKED;
  type X509_NAME (line 159) | typedef struct X509_name_st X509_NAME;
  type X509_PUBKEY (line 160) | typedef struct X509_pubkey_st X509_PUBKEY;
  type X509_STORE (line 161) | typedef struct x509_store_st X509_STORE;
  type X509_STORE_CTX (line 162) | typedef struct x509_store_ctx_st X509_STORE_CTX;
  type PKCS8_PRIV_KEY_INFO (line 164) | typedef struct pkcs8_priv_key_info_st PKCS8_PRIV_KEY_INFO;
  type X509V3_CTX (line 166) | typedef struct v3_ext_ctx X509V3_CTX;
  type CONF (line 167) | typedef struct conf_st CONF;
  type STORE (line 169) | typedef struct store_st STORE;
  type STORE_METHOD (line 170) | typedef struct store_method_st STORE_METHOD;
  type UI (line 172) | typedef struct ui_st UI;
  type UI_METHOD (line 173) | typedef struct ui_method_st UI_METHOD;
  type ERR_FNS (line 175) | typedef struct st_ERR_FNS ERR_FNS;
  type ENGINE (line 177) | typedef struct engine_st ENGINE;
  type SSL (line 178) | typedef struct ssl_st SSL;
  type SSL_CTX (line 179) | typedef struct ssl_ctx_st SSL_CTX;
  type X509_POLICY_NODE (line 181) | typedef struct X509_POLICY_NODE_st X509_POLICY_NODE;
  type X509_POLICY_LEVEL (line 182) | typedef struct X509_POLICY_LEVEL_st X509_POLICY_LEVEL;
  type X509_POLICY_TREE (line 183) | typedef struct X509_POLICY_TREE_st X509_POLICY_TREE;
  type X509_POLICY_CACHE (line 184) | typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE;
  type AUTHORITY_KEYID (line 186) | typedef struct AUTHORITY_KEYID_st AUTHORITY_KEYID;
  type DIST_POINT (line 187) | typedef struct DIST_POINT_st DIST_POINT;
  type ISSUING_DIST_POINT (line 188) | typedef struct ISSUING_DIST_POINT_st ISSUING_DIST_POINT;
  type NAME_CONSTRAINTS (line 189) | typedef struct NAME_CONSTRAINTS_st NAME_CONSTRAINTS;
  type CRYPTO_EX_DATA (line 195) | typedef struct crypto_ex_data_st CRYPTO_EX_DATA;
  type OCSP_REQ_CTX (line 204) | typedef struct ocsp_req_ctx_st OCSP_REQ_CTX;
  type OCSP_RESPONSE (line 205) | typedef struct ocsp_response_st OCSP_RESPONSE;
  type OCSP_RESPID (line 206) | typedef struct ocsp_responder_id_st OCSP_RESPID;

FILE: openssl/openssl/pem.h
  type PEM_ENCODE_SEAL_CTX (line 145) | typedef struct PEM_Encode_Seal_st {
  type PEM_USER (line 157) | typedef struct pem_recip_st {
  type PEM_CTX (line 165) | typedef struct pem_ctx_st {

FILE: openssl/openssl/pkcs12.h
  type PKCS12_MAC_DATA (line 102) | typedef struct {
  type PKCS12 (line 108) | typedef struct {
  type PKCS12_SAFEBAG (line 114) | typedef struct {
  function DECLARE_ASN1_SET_OF (line 126) | DECLARE_STACK_OF(PKCS12_SAFEBAG)

FILE: openssl/openssl/pkcs7.h
  type PKCS7_ISSUER_AND_SERIAL (line 86) | typedef struct pkcs7_issuer_and_serial_st {
  type PKCS7_SIGNER_INFO (line 91) | typedef struct pkcs7_signer_info_st {
  function DECLARE_ASN1_SET_OF (line 103) | DECLARE_STACK_OF(PKCS7_SIGNER_INFO)
  function DECLARE_ASN1_SET_OF (line 114) | DECLARE_STACK_OF(PKCS7_RECIP_INFO)
  type PKCS7_ENC_CONTENT (line 130) | typedef struct pkcs7_enc_content_st {
  type PKCS7_ENVELOPE (line 137) | typedef struct pkcs7_enveloped_st {
  type PKCS7_SIGN_ENVELOPE (line 143) | typedef struct pkcs7_signedandenveloped_st {
  type PKCS7_DIGEST (line 153) | typedef struct pkcs7_digest_st {
  type PKCS7_ENCRYPT (line 160) | typedef struct pkcs7_encrypted_st {
  type PKCS7 (line 165) | typedef struct pkcs7_st {

FILE: openssl/openssl/pqueue.h
  type _pqueue (line 70) | struct _pqueue
  type pitem (line 72) | typedef struct _pitem {
  type _pitem (line 78) | struct _pitem

FILE: openssl/openssl/rand.h
  type rand_meth_st (line 81) | struct rand_meth_st {

FILE: openssl/openssl/rc2.h
  type RC2_KEY (line 77) | typedef struct rc2_key_st {

FILE: openssl/openssl/rc4.h
  type RC4_KEY (line 73) | typedef struct rc4_key_st {

FILE: openssl/openssl/rc5.h
  type RC5_32_KEY (line 36) | typedef struct rc5_key_st {

FILE: openssl/openssl/ripemd.h
  type RIPEMD160_CTX (line 86) | typedef struct RIPEMD160state_st {

FILE: openssl/openssl/rsa.h
  type rsa_meth_st (line 85) | struct rsa_meth_st {
  type rsa_st (line 132) | struct rsa_st {
  type RSA_PSS_PARAMS (line 365) | typedef struct rsa_pss_params_st {
  type rsa_oaep_params_st (line 374) | struct rsa_oaep_params_st {

FILE: openssl/openssl/seed.h
  type SEED_KEY_SCHEDULE (line 110) | typedef struct seed_key_st {

FILE: openssl/openssl/sha.h
  type SHA_CTX (line 100) | typedef struct SHAstate_st {
  type SHA256_CTX (line 134) | typedef struct SHA256state_st {
  type SHA512_CTX (line 183) | typedef struct SHA512state_st {

FILE: openssl/openssl/srp.h
  type SRP_gN_cache (line 76) | typedef struct SRP_gN_cache_st {
  type SRP_user_pwd_st (line 84) | struct SRP_user_pwd_st {
  type SRP_VBASE (line 100) | typedef struct SRP_VBASE_st {
  type SRP_gN (line 112) | typedef struct SRP_gN_st {

FILE: openssl/openssl/ssl.h
  type ssl_st (line 372) | struct ssl_st
  type TLS_SESSION_TICKET_EXT (line 373) | typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
  type SSL_METHOD (line 374) | typedef struct ssl_method_st SSL_METHOD;
  type SSL_CIPHER (line 375) | typedef struct ssl_cipher_st SSL_CIPHER;
  type SSL_SESSION (line 376) | typedef struct ssl_session_st SSL_SESSION;
  type TLS_SIGALGS (line 377) | typedef struct tls_sigalgs_st TLS_SIGALGS;
  type SSL_CONF_CTX (line 378) | typedef struct ssl_conf_ctx_st SSL_CONF_CTX;
  type srtp_protection_profile_st (line 383) | struct srtp_protection_profile_st {
  type ssl_cipher_st (line 418) | struct ssl_cipher_st {
  type ssl_method_st (line 438) | struct ssl_method_st {
  type ssl_session_st (line 498) | struct ssl_session_st {
  type SRP_CTX (line 849) | typedef struct srp_ctx_st {
  type SSL_COMP (line 908) | typedef struct ssl_comp_st SSL_COMP;
  type ssl_comp_st (line 912) | struct ssl_comp_st {
  type ssl_ctx_st (line 925) | struct ssl_ctx_st {
  type ssl_st (line 1226) | struct ssl_st
  type ssl_st (line 1228) | struct ssl_st
  type ssl_ctx_st (line 1231) | struct ssl_ctx_st
  type ssl_ctx_st (line 1235) | struct ssl_ctx_st
  type ssl_st (line 1238) | struct ssl_st
  type ssl_st (line 1243) | struct ssl_st
  type ssl_st (line 1422) | struct ssl_st {
  type evp_pkey_st (line 2429) | struct evp_pkey_st
  type openssl_ssl_test_functions (line 2590) | struct openssl_ssl_test_functions

FILE: openssl/openssl/ssl2.h
  type SSL2_STATE (line 163) | typedef struct ssl2_state_st {

FILE: openssl/openssl/ssl3.h
  type SSL3_RECORD (line 403) | typedef struct ssl3_record_st {
  type SSL3_BUFFER (line 438) | typedef struct ssl3_buffer_st {
  type SSL3_STATE (line 481) | typedef struct ssl3_state_st {

FILE: openssl/openssl/stack.h
  type _STACK (line 66) | typedef struct stack_st {

FILE: openssl/openssl/tls1.h
  type tls_session_ticket_ext_st (line 802) | struct tls_session_ticket_ext_st {

FILE: openssl/openssl/ts.h
  type TS_MSG_IMPRINT (line 108) | typedef struct TS_msg_imprint_st {
  type TS_REQ (line 125) | typedef struct TS_req_st {
  type TS_ACCURACY (line 141) | typedef struct TS_accuracy_st {
  type TS_TST_INFO (line 167) | typedef struct TS_tst_info_st {
  type TS_STATUS_INFO (line 215) | typedef struct TS_status_info_st {
  function DECLARE_ASN1_SET_OF (line 221) | DECLARE_STACK_OF(ASN1_UTF8STRING)
  type ESS_ISSUER_SERIAL (line 245) | typedef struct ESS_issuer_serial {
  type ESS_CERT_ID (line 257) | typedef struct ESS_cert_id {
  function DECLARE_ASN1_SET_OF (line 262) | DECLARE_STACK_OF(ESS_CERT_ID)
  type TS_resp_ctx (line 477) | struct TS_resp_ctx
  type ASN1_INTEGER (line 480) | typedef ASN1_INTEGER *(*TS_serial_cb) (struct TS_resp_ctx *, void *);
  type TS_resp_ctx (line 487) | struct TS_resp_ctx
  type TS_resp_ctx (line 495) | struct TS_resp_ctx
  type TS_RESP_CTX (line 498) | typedef struct TS_resp_ctx {
  type TS_verify_ctx (line 657) | struct TS_verify_ctx {

FILE: openssl/openssl/txt_db.h
  type OPENSSL_STRING (line 80) | typedef OPENSSL_STRING *OPENSSL_PSTRING;
  type TXT_DB (line 83) | typedef struct txt_db_st {

FILE: openssl/openssl/ui.h
  type UI_STRING (line 304) | typedef struct ui_string_st UI_STRING;
  type UI_string_types (line 351) | enum UI_string_types

FILE: openssl/openssl/whrlpool.h
  type WHIRLPOOL_CTX (line 15) | typedef struct {

FILE: openssl/openssl/x509.h
  type X509_OBJECTS (line 137) | typedef struct X509_objects_st {
  type X509_algor_st (line 143) | struct X509_algor_st {
  type X509_VAL (line 152) | typedef struct X509_val_st {
  type X509_pubkey_st (line 157) | struct X509_pubkey_st {
  type X509_SIG (line 163) | typedef struct X509_sig_st {
  type X509_NAME_ENTRY (line 168) | typedef struct X509_name_entry_st {
  function DECLARE_ASN1_SET_OF (line 175) | DECLARE_STACK_OF(X509_NAME_ENTRY)
  type X509_EXTENSION (line 196) | typedef struct X509_extension_st {
  type X509_EXTENSIONS (line 202) | typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
  function DECLARE_ASN1_SET_OF (line 204) | DECLARE_STACK_OF(X509_EXTENSION)
  function DECLARE_ASN1_SET_OF (line 223) | DECLARE_STACK_OF(X509_ATTRIBUTE)
  type X509_REQ (line 235) | typedef struct X509_req_st {
  type X509_CINF (line 242) | typedef struct x509_cinf_st {
  type X509_CERT_AUX (line 262) | typedef struct x509_cert_aux_st {
  type x509_st (line 270) | struct x509_st {
  function DECLARE_ASN1_SET_OF (line 301) | DECLARE_STACK_OF(X509)
  type x509_cert_pair_st (line 317) | struct x509_cert_pair_st {
  type x509_revoked_st (line 427) | struct x509_revoked_st {
  function DECLARE_ASN1_SET_OF (line 438) | DECLARE_STACK_OF(X509_REVOKED)
  type X509_crl_st (line 452) | struct X509_crl_st {
  function DECLARE_ASN1_SET_OF (line 476) | DECLARE_STACK_OF(X509_CRL)
  type X509_INFO (line 496) | typedef struct X509_info_st {
  type NETSCAPE_SPKAC (line 514) | typedef struct Netscape_spkac_st {
  type NETSCAPE_SPKI (line 519) | typedef struct Netscape_spki_st {
  type NETSCAPE_CERT_SEQUENCE (line 526) | typedef struct Netscape_certificate_sequence {
  type PBEPARAM (line 540) | typedef struct PBEPARAM_st {
  type PBE2PARAM (line 547) | typedef struct PBE2PARAM_st {
  type PBKDF2PARAM (line 552) | typedef struct PBKDF2PARAM_st {
  type pkcs8_priv_key_info_st (line 562) | struct pkcs8_priv_key_info_st {

FILE: openssl/openssl/x509_vfy.h
  type X509_HASH_DIR_CTX (line 84) | typedef struct x509_hash_dir_st {
  type X509_CERT_FILE_CTX (line 92) | typedef struct x509_file_st {
  type X509_OBJECT (line 123) | typedef struct x509_object_st {
  type X509_LOOKUP (line 134) | typedef struct x509_lookup_st X509_LOOKUP;
  function DECLARE_STACK_OF (line 136) | DECLARE_STACK_OF(X509_LOOKUP)
  type X509_VERIFY_PARAM_ID (line 159) | typedef struct X509_VERIFY_PARAM_ID_st X509_VERIFY_PARAM_ID;
  type X509_VERIFY_PARAM (line 167) | typedef struct X509_VERIFY_PARAM_st {
  function x509_store_st (line 179) | DECLARE_STACK_OF(X509_VERIFY_PARAM)
  type x509_lookup_st (line 223) | struct x509_lookup_st {
  type x509_store_ctx_st (line 236) | struct x509_store_ctx_st {      /* X509_STORE_CTX */

FILE: openssl/openssl/x509v3.h
  type v3_ext_method (line 78) | struct v3_ext_method
  type v3_ext_ctx (line 79) | struct v3_ext_ctx
  type v3_ext_method (line 88) | struct v3_ext_method
  type v3_ext_method (line 90) | struct v3_ext_method
  type v3_ext_ctx (line 91) | struct v3_ext_ctx
  type v3_ext_method (line 93) | struct v3_ext_method
  type v3_ext_method (line 95) | struct v3_ext_method
  type v3_ext_ctx (line 96) | struct v3_ext_ctx
  type v3_ext_method (line 97) | struct v3_ext_method
  type v3_ext_method (line 99) | struct v3_ext_method
  type v3_ext_ctx (line 100) | struct v3_ext_ctx
  type v3_ext_method (line 104) | struct v3_ext_method {
  type X509V3_CONF_METHOD (line 126) | typedef struct X509V3_CONF_METHOD_st {
  type v3_ext_ctx (line 134) | struct v3_ext_ctx {
  type X509V3_EXT_METHOD (line 146) | typedef struct v3_ext_method X509V3_EXT_METHOD;
  type BIT_STRING_BITNAME (line 155) | typedef BIT_STRING_BITNAME ENUMERATED_NAMES;
  type BASIC_CONSTRAINTS (line 157) | typedef struct BASIC_CONSTRAINTS_st {
  type PKEY_USAGE_PERIOD (line 162) | typedef struct PKEY_USAGE_PERIOD_st {
  type OTHERNAME (line 167) | typedef struct otherName_st {
  type EDIPARTYNAME (line 172) | typedef struct EDIPartyName_st {
  type GENERAL_NAME (line 177) | typedef struct GENERAL_NAME_st {
  type GENERAL_NAMES (line 209) | typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES;
  type ACCESS_DESCRIPTION (line 211) | typedef struct ACCESS_DESCRIPTION_st {
  type AUTHORITY_INFO_ACCESS (line 216) | typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS;
  type EXTENDED_KEY_USAGE (line 218) | typedef STACK_OF(ASN1_OBJECT) EXTENDED_KEY_USAGE;
  function DECLARE_ASN1_SET_OF (line 220) | DECLARE_STACK_OF(GENERAL_NAME)
  type DIST_POINT_st (line 250) | struct DIST_POINT_st {
  type CRL_DIST_POINTS (line 257) | typedef STACK_OF(DIST_POINT) CRL_DIST_POINTS;
  function DECLARE_ASN1_SET_OF (line 259) | DECLARE_STACK_OF(DIST_POINT)
  type SXNETID (line 270) | typedef struct SXNET_ID_st {
  function DECLARE_ASN1_SET_OF (line 275) | DECLARE_STACK_OF(SXNETID)
  type NOTICEREF (line 283) | typedef struct NOTICEREF_st {
  type USERNOTICE (line 288) | typedef struct USERNOTICE_st {
  type POLICYQUALINFO (line 293) | typedef struct POLICYQUALINFO_st {
  function DECLARE_ASN1_SET_OF (line 302) | DECLARE_STACK_OF(POLICYQUALINFO)
  type CERTIFICATEPOLICIES (line 310) | typedef STACK_OF(POLICYINFO) CERTIFICATEPOLICIES;
  function DECLARE_ASN1_SET_OF (line 312) | DECLARE_STACK_OF(POLICYINFO)
  type GENERAL_SUBTREE (line 324) | typedef struct GENERAL_SUBTREE_st {
  function NAME_CONSTRAINTS_st (line 330) | DECLARE_STACK_OF(GENERAL_SUBTREE)
  type POLICY_CONSTRAINTS (line 337) | typedef struct POLICY_CONSTRAINTS_st {
  type PROXY_POLICY (line 343) | typedef struct PROXY_POLICY_st {
  type PROXY_CERT_INFO_EXTENSION (line 348) | typedef struct PROXY_CERT_INFO_EXTENSION_st {
  function DECLARE_ASN1_FUNCTIONS (line 353) | DECLARE_ASN1_FUNCTIONS(PROXY_POLICY)
  type X509_PURPOSE (line 458) | typedef struct x509_purpose_st {
  function DECLARE_ASN1_FUNCTIONS (line 549) | DECLARE_ASN1_FUNCTIONS(OTHERNAME)
  type ASIdOrRange (line 767) | typedef struct ASIdOrRange_st {
  type ASIdOrRanges (line 775) | typedef STACK_OF(ASIdOrRange) ASIdOrRanges;
  type ASIdentifierChoice (line 781) | typedef struct ASIdentifierChoice_st {
  type ASIdentifiers (line 789) | typedef struct ASIdentifiers_st {
  function DECLARE_ASN1_FUNCTIONS (line 793) | DECLARE_ASN1_FUNCTIONS(ASRange)
  type IPAddressOrRange (line 805) | typedef struct IPAddressOrRange_st {
  type IPAddressOrRanges (line 813) | typedef STACK_OF(IPAddressOrRange) IPAddressOrRanges;
  type IPAddressChoice (line 819) | typedef struct IPAddressChoice_st {
  type IPAddressFamily (line 827) | typedef struct IPAddressFamily_st {
  type IPAddrBlocks (line 832) | typedef STACK_OF(IPAddressFamily) IPAddrBlocks;

FILE: thirdparty/AFNetworking/AFHTTPClient.h
  type AFHTTPClientParameterEncoding (line 47) | typedef enum {

FILE: thirdparty/AsyncSocket/GCDAsyncSocket.h
  type GCDAsyncSocketError (line 78) | enum GCDAsyncSocketError
  type GCDAsyncSocketError (line 90) | typedef enum GCDAsyncSocketError GCDAsyncSocketError;
  function interface (line 98) | interface GCDAsyncSocket : NSObject
Condensed preview — 298 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,414K chars).
[
  {
    "path": ".gitignore",
    "chars": 325,
    "preview": "fastlane/README.md\nfastlane/report.xml\nfastlane/test_output/*\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mo"
  },
  {
    "path": "MTAes.h",
    "chars": 480,
    "preview": "#import <Foundation/Foundation.h>\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTEncryption.h>"
  },
  {
    "path": "MTAes.m",
    "chars": 8598,
    "preview": "#import \"MTAes.h\"\n\n#import <CommonCrypto/CommonCrypto.h>\n\n# define AES_MAXNR 14\n# define AES_BLOCK_SIZE 16\n\n#define N_WO"
  },
  {
    "path": "MTAtomic.h",
    "chars": 206,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTAtomic : NSObject\n\n- (instancetype)initWithValue:(id)value;\n- (id)swap:("
  },
  {
    "path": "MTAtomic.m",
    "chars": 996,
    "preview": "#import \"MTAtomic.h\"\n\n#import <libkern/OSAtomic.h>\n\n@interface MTAtomic ()\n{\n    volatile OSSpinLock _lock;\n    id _valu"
  },
  {
    "path": "MTBackupAddressSignals.h",
    "chars": 343,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTSignal;\n@class MTContext;\n\n@interface MTBackupAddressSignals : NSObject\n\n+ ("
  },
  {
    "path": "MTBackupAddressSignals.m",
    "chars": 11564,
    "preview": "#import \"MTBackupAddressSignals.h\"\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTSignal.h>\n# "
  },
  {
    "path": "MTBag.h",
    "chars": 221,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTBag : NSObject\n\n- (NSInteger)addItem:(id)item;\n- (void)enumerateItems:(v"
  },
  {
    "path": "MTBag.m",
    "chars": 1169,
    "preview": "#import \"MTBag.h\"\n\n@interface MTBag ()\n{\n    NSInteger _nextKey;\n    NSMutableArray *_items;\n    NSMutableArray *_itemKe"
  },
  {
    "path": "MTBindingTempAuthKeyContext.h",
    "chars": 377,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTBindingTempAuthKeyContext : NSObject\n\n@property (nonatomic, readonly) in"
  },
  {
    "path": "MTBindingTempAuthKeyContext.m",
    "chars": 401,
    "preview": "#import \"MTBindingTempAuthKeyContext.h\"\n\n@implementation MTBindingTempAuthKeyContext\n\n- (instancetype)initWithMessageId:"
  },
  {
    "path": "MTConnectionProbing.h",
    "chars": 286,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTSignal;\n@class MTContext;\n@class MTSocksProxySettings;\n\n@interface MTConnect"
  },
  {
    "path": "MTConnectionProbing.m",
    "chars": 5782,
    "preview": "#import \"MTConnectionProbing.h\"\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTSignal.h>\n#   i"
  },
  {
    "path": "MTDNS.h",
    "chars": 297,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTSignal;\n\n@interface MTDNS : NSObject\n\n+ (MTSignal *)resolveHostname:(NSStrin"
  },
  {
    "path": "MTDNS.m",
    "chars": 13314,
    "preview": "#import \"MTDNS.h\"\n\n#import <arpa/inet.h>\n#include <netinet/tcp.h>\n#import <fcntl.h>\n#import <ifaddrs.h>\n#import <netdb.h"
  },
  {
    "path": "MTDatacenterVerificationData.h",
    "chars": 328,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTDatacenterVerificationData : NSObject\n\n@property (nonatomic, readonly) N"
  },
  {
    "path": "MTDatacenterVerificationData.m",
    "chars": 547,
    "preview": "#import \"MTDatacenterVerificationData.h\"\n\n@implementation MTDatacenterVerificationData\n\n- (instancetype _Nonnull)initWit"
  },
  {
    "path": "MTDiscoverConnectionSignals.h",
    "chars": 514,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTContext;\n@class MTDatacenterAddress;\n@class MTSignal;\n\ntypedef struct {\n    "
  },
  {
    "path": "MTDiscoverConnectionSignals.m",
    "chars": 11344,
    "preview": "#import \"MTDiscoverConnectionSignals.h\"\n\n#import \"MTTcpConnection.h\"\n\n#if defined(MtProtoKitDynamicFramework)\n#   import"
  },
  {
    "path": "MTDisposable.h",
    "chars": 467,
    "preview": "#import <Foundation/Foundation.h>\n\n@protocol MTDisposable <NSObject>\n\n- (void)dispose;\n\n@end\n\n@interface MTBlockDisposab"
  },
  {
    "path": "MTDisposable.m",
    "chars": 4181,
    "preview": "#import \"MTDisposable.h\"\n\n#import <libkern/OSAtomic.h>\n#import <objc/runtime.h>\n\n@interface MTBlockDisposable ()\n{\n    v"
  },
  {
    "path": "MTGzip.h",
    "chars": 172,
    "preview": "#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface MTGzip : NSObject\n    \n+ (NSData * _Nullable)deco"
  },
  {
    "path": "MTGzip.m",
    "chars": 1518,
    "preview": "#import \"MTGzip.h\"\n\n#import <zlib.h>\n\n@implementation MTGzip\n    \n+ (NSData * _Nullable)decompress:(NSData *)data {\n    "
  },
  {
    "path": "MTNetworkUsageCalculationInfo.h",
    "chars": 625,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTQueue;\n\n@interface MTNetworkUsageCalculationInfo : NSObject\n\n@property (nona"
  },
  {
    "path": "MTNetworkUsageCalculationInfo.m",
    "chars": 603,
    "preview": "#import \"MTNetworkUsageCalculationInfo.h\"\n\n@implementation MTNetworkUsageCalculationInfo\n\n- (instancetype)initWithFilePa"
  },
  {
    "path": "MTNetworkUsageManager.h",
    "chars": 867,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTSignal;\n@class MTNetworkUsageCalculationInfo;\n\ntypedef enum {\n    MTNetworkU"
  },
  {
    "path": "MTNetworkUsageManager.m",
    "chars": 8646,
    "preview": "#import \"MTNetworkUsageManager.h\"\n\n#include <sys/mman.h>\n#import <libkern/OSAtomic.h>\n\n#if defined(MtProtoKitDynamicFram"
  },
  {
    "path": "MTPKCS.h",
    "chars": 304,
    "preview": "#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface MTPKCS : NSObject\n\n@property (nonatomic, strong, "
  },
  {
    "path": "MTPKCS.m",
    "chars": 1349,
    "preview": "#import \"MTPKCS.h\"\n\n#include <openssl/x509.h>\n#include <openssl/pkcs7.h>\n\n@implementation MTPKCS\n\n- (instancetype)initWi"
  },
  {
    "path": "MTProtoKit/Info.plist",
    "chars": 806,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MTProtoKit/MTApiEnvironment.h",
    "chars": 2107,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTSocksProxySettings : NSObject\n\n@property (nonatomic, strong, readonly)"
  },
  {
    "path": "MTProtoKit/MTApiEnvironment.m",
    "chars": 21713,
    "preview": "\n\n#import \"MTApiEnvironment.h\"\n\n#if TARGET_OS_IPHONE\n#   import <UIKit/UIKit.h>\n#else\n\n#endif\n\n#include <sys/sysctl.h>\n\n"
  },
  {
    "path": "MTProtoKit/MTBadMsgNotificationMessage.h",
    "chars": 695,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTBadMsgNotificationMessage : NSObject\n\n@property (nonatomic, readonly) in"
  },
  {
    "path": "MTProtoKit/MTBadMsgNotificationMessage.m",
    "chars": 842,
    "preview": "#import \"MTBadMsgNotificationMessage.h\"\n\n@implementation MTBadMsgNotificationMessage\n\n- (instancetype)initWithBadMessage"
  },
  {
    "path": "MTProtoKit/MTBuffer.h",
    "chars": 345,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTBuffer : NSObject\n\n- (void)appendInt32:(int32_t)value;\n- (void)appendInt"
  },
  {
    "path": "MTProtoKit/MTBuffer.m",
    "chars": 1734,
    "preview": "#import \"MTBuffer.h\"\n\n@interface MTBuffer ()\n{\n    NSMutableData *_data;\n}\n\n@end\n\n@implementation MTBuffer\n\n- (instancet"
  },
  {
    "path": "MTProtoKit/MTBufferReader.h",
    "chars": 415,
    "preview": "#import \"MTBuffer.h\"\n\n@interface MTBufferReader : NSObject\n\n- (instancetype)initWithData:(NSData *)data;\n\n- (bool)readBy"
  },
  {
    "path": "MTProtoKit/MTBufferReader.m",
    "chars": 2552,
    "preview": "#import \"MTBufferReader.h\"\n\n@interface MTBufferReader ()\n{\n    NSData *_data;\n    NSUInteger _offset;\n}\n\n@end\n\n@implemen"
  },
  {
    "path": "MTProtoKit/MTContext.h",
    "chars": 6777,
    "preview": "#import <Foundation/Foundation.h>\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTDatacenterAut"
  },
  {
    "path": "MTProtoKit/MTContext.m",
    "chars": 55512,
    "preview": "#import \"MTContext.h\"\n\n#import <inttypes.h>\n\n#import \"MTLogging.h\"\n#import \"MTTimer.h\"\n#import \"MTQueue.h\"\n#import \"MTKe"
  },
  {
    "path": "MTProtoKit/MTDatacenterAddress.h",
    "chars": 793,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTDatacenterAddress : NSObject <NSCoding, NSCopying>\n\n@property (nonatom"
  },
  {
    "path": "MTProtoKit/MTDatacenterAddress.m",
    "chars": 3320,
    "preview": "\n\n#import \"MTDatacenterAddress.h\"\n\n#import <netinet/in.h>\n#import <arpa/inet.h>\n\n@implementation MTDatacenterAddress\n\n- "
  },
  {
    "path": "MTProtoKit/MTDatacenterAddressListData.h",
    "chars": 271,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTDatacenterAddressListData : NSObject\n\n@property (nonatomic, strong, read"
  },
  {
    "path": "MTProtoKit/MTDatacenterAddressListData.m",
    "chars": 362,
    "preview": "#import \"MTDatacenterAddressListData.h\"\n\n@implementation MTDatacenterAddressListData\n\n- (instancetype)initWithAddressLis"
  },
  {
    "path": "MTProtoKit/MTDatacenterAddressSet.h",
    "chars": 292,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTDatacenterAddress;\n\n@interface MTDatacenterAddressSet : NSObject <NSCoding"
  },
  {
    "path": "MTProtoKit/MTDatacenterAddressSet.m",
    "chars": 1486,
    "preview": "\n\n#import \"MTDatacenterAddressSet.h\"\n\n#import \"MTDatacenterAddress.h\"\n\n@implementation MTDatacenterAddressSet\n\n- (instan"
  },
  {
    "path": "MTProtoKit/MTDatacenterAuthAction.h",
    "chars": 853,
    "preview": "#import <Foundation/Foundation.h>\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTDatacenterAut"
  },
  {
    "path": "MTProtoKit/MTDatacenterAuthAction.m",
    "chars": 5136,
    "preview": "#import \"MTDatacenterAuthAction.h\"\n\n#import \"MTLogging.h\"\n#import \"MTContext.h\"\n#import \"MTProto.h\"\n#import \"MTRequest.h"
  },
  {
    "path": "MTProtoKit/MTDatacenterAuthInfo.h",
    "chars": 1758,
    "preview": "#import <Foundation/Foundation.h>\n\ntypedef NS_ENUM(NSUInteger, MTDatacenterAuthTempKeyType) {\n    MTDatacenterAuthTempKe"
  },
  {
    "path": "MTProtoKit/MTDatacenterAuthInfo.m",
    "chars": 5844,
    "preview": "#import \"MTDatacenterAuthInfo.h\"\n#import \"MTDatacenterSaltInfo.h\"\n\n@implementation MTDatacenterAuthKey\n\n- (instancetype)"
  },
  {
    "path": "MTProtoKit/MTDatacenterAuthMessageService.h",
    "chars": 734,
    "preview": "\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTMessageService.h>\n#elif defined(MtProtoKitMacF"
  },
  {
    "path": "MTProtoKit/MTDatacenterAuthMessageService.m",
    "chars": 37859,
    "preview": "#import \"MTDatacenterAuthMessageService.h\"\n\n#import \"MTLogging.h\"\n#import \"MTContext.h\"\n#import \"MTProto.h\"\n#import \"MTS"
  },
  {
    "path": "MTProtoKit/MTDatacenterSaltInfo.h",
    "chars": 521,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTDatacenterSaltInfo : NSObject <NSCoding>\n\n@property (nonatomic, readon"
  },
  {
    "path": "MTProtoKit/MTDatacenterSaltInfo.m",
    "chars": 1301,
    "preview": "\n\n#import \"MTDatacenterSaltInfo.h\"\n\n@implementation MTDatacenterSaltInfo\n\n- (instancetype)initWithSalt:(int64_t)salt fir"
  },
  {
    "path": "MTProtoKit/MTDatacenterTransferAuthAction.h",
    "chars": 580,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTContext;\n\n@class MTDatacenterTransferAuthAction;\n\n@protocol MTDatacenterTr"
  },
  {
    "path": "MTProtoKit/MTDatacenterTransferAuthAction.m",
    "chars": 5756,
    "preview": "\n\n#import \"MTDatacenterTransferAuthAction.h\"\n\n#import \"MTContext.h\"\n#import \"MTSerialization.h\"\n#import \"MTProto.h\"\n#imp"
  },
  {
    "path": "MTProtoKit/MTDestroySessionResponseMessage.h",
    "chars": 699,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTDestroySessionResponseMessage : NSObject\n\n@end\n\n@interface MTDestroySess"
  },
  {
    "path": "MTProtoKit/MTDestroySessionResponseMessage.m",
    "chars": 786,
    "preview": "#import \"MTDestroySessionResponseMessage.h\"\n\n@implementation MTDestroySessionResponseMessage\n\n@end\n\n@implementation MTDe"
  },
  {
    "path": "MTProtoKit/MTDiscoverDatacenterAddressAction.h",
    "chars": 502,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTContext;\n@class MTDiscoverDatacenterAddressAction;\n\n@protocol MTDiscoverDa"
  },
  {
    "path": "MTProtoKit/MTDiscoverDatacenterAddressAction.m",
    "chars": 5537,
    "preview": "\n\n#import \"MTDiscoverDatacenterAddressAction.h\"\n\n#import \"MTContext.h\"\n#import \"MTSerialization.h\"\n#import \"MTProto.h\"\n#"
  },
  {
    "path": "MTProtoKit/MTDropResponseContext.h",
    "chars": 291,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTDropResponseContext : NSObject\n\n@property (nonatomic, readonly) int64_"
  },
  {
    "path": "MTProtoKit/MTDropResponseContext.m",
    "chars": 262,
    "preview": "\n\n#import \"MTDropResponseContext.h\"\n\n@implementation MTDropResponseContext\n\n- (instancetype)initWithDropMessageId:(int64"
  },
  {
    "path": "MTProtoKit/MTDropRpcResultMessage.h",
    "chars": 552,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTDropRpcResultMessage : NSObject\n\n@end\n\n\n@interface MTDropRpcResultUnknow"
  },
  {
    "path": "MTProtoKit/MTDropRpcResultMessage.m",
    "chars": 498,
    "preview": "#import \"MTDropRpcResultMessage.h\"\n\n@implementation MTDropRpcResultMessage\n\n@end\n\n@implementation MTDropRpcResultUnknown"
  },
  {
    "path": "MTProtoKit/MTEncryption.h",
    "chars": 3314,
    "preview": "\n\n#ifndef MTEncryption_H\n#define MTEncryption_H\n\n#import <Foundation/Foundation.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#end"
  },
  {
    "path": "MTProtoKit/MTEncryption.m",
    "chars": 34064,
    "preview": "\n\n#import \"MTEncryption.h\"\n\n#import \"MTLogging.h\"\n#import \"MTKeychain.h\"\n\n#import <CommonCrypto/CommonCrypto.h>\n#import "
  },
  {
    "path": "MTProtoKit/MTExportedAuthorizationData.h",
    "chars": 332,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTExportedAuthorizationData : NSObject\n\n@property (nonatomic, strong, read"
  },
  {
    "path": "MTProtoKit/MTExportedAuthorizationData.m",
    "chars": 378,
    "preview": "#import \"MTExportedAuthorizationData.h\"\n\n@implementation MTExportedAuthorizationData\n\n- (instancetype)initWithAuthorizat"
  },
  {
    "path": "MTProtoKit/MTFileBasedKeychain.h",
    "chars": 652,
    "preview": "#import <Foundation/Foundation.h>\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTKeychain.h>\n#"
  },
  {
    "path": "MTProtoKit/MTFileBasedKeychain.m",
    "chars": 16396,
    "preview": "#import \"MTFileBasedKeychain.h\"\n\n#import \"MTLogging.h\"\n\n#import <pthread.h>\n\n#define TG_SYNCHRONIZED_DEFINE(lock) pthrea"
  },
  {
    "path": "MTProtoKit/MTFutureSaltsMessage.h",
    "chars": 653,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTFutureSalt : NSObject\n\n@property (nonatomic, readonly) int32_t validSinc"
  },
  {
    "path": "MTProtoKit/MTFutureSaltsMessage.m",
    "chars": 665,
    "preview": "#import \"MTFutureSaltsMessage.h\"\n\n@implementation MTFutureSalt\n\n- (instancetype)initWithValidSince:(int32_t)validSince v"
  },
  {
    "path": "MTProtoKit/MTHttpRequestOperation.h",
    "chars": 223,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTSignal;\n\n@interface MTHttpRequestOperation : NSObject\n\n+ (MTSignal *)dataFor"
  },
  {
    "path": "MTProtoKit/MTHttpRequestOperation.m",
    "chars": 2052,
    "preview": "#import \"MTHttpRequestOperation.h\"\n\n#import \"../thirdparty/AFNetworking/AFHTTPRequestOperation.h\"\n\n#if defined(MtProtoKi"
  },
  {
    "path": "MTProtoKit/MTIncomingMessage.h",
    "chars": 709,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTIncomingMessage : NSObject\n\n@property (nonatomic, readonly) int64_t me"
  },
  {
    "path": "MTProtoKit/MTIncomingMessage.m",
    "chars": 593,
    "preview": "\n\n#import \"MTIncomingMessage.h\"\n\n@implementation MTIncomingMessage\n\n- (instancetype)initWithMessageId:(int64_t)messageId"
  },
  {
    "path": "MTProtoKit/MTInputStream.h",
    "chars": 888,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTInputStream : NSObject\n\n- (instancetype)initWithData:(NSData *)data;\n-"
  },
  {
    "path": "MTProtoKit/MTInputStream.m",
    "chars": 9731,
    "preview": "\n\n#import <Foundation/Foundation.h>\n#import \"MTLogging.h\"\n#import \"MTInputStream.h\"\n\n#if TARGET_OS_IPHONE\n#   import <en"
  },
  {
    "path": "MTProtoKit/MTInternalId.h",
    "chars": 984,
    "preview": "\n\n#ifndef MtProtoKit_MTInternalId_h\n#define MtProtoKit_MTInternalId_h\n\n#import <libkern/OSAtomic.h>\n\n#define MTInternalI"
  },
  {
    "path": "MTProtoKit/MTInternalMessageParser.h",
    "chars": 159,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTInternalMessageParser : NSObject\n\n+ (id)parseMessage:(NSData *)data;\n+ ("
  },
  {
    "path": "MTProtoKit/MTInternalMessageParser.m",
    "chars": 28777,
    "preview": "#import \"MTInternalMessageParser.h\"\n\n#import \"MTBufferReader.h\"\n\n#import \"MTResPqMessage.h\"\n#import \"MTRpcResultMessage."
  },
  {
    "path": "MTProtoKit/MTKeychain.h",
    "chars": 322,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@protocol MTKeychain <NSObject>\n\n- (void)setObject:(id)object forKey:(NSString *)aK"
  },
  {
    "path": "MTProtoKit/MTKeychain.m",
    "chars": 25,
    "preview": "\n\n#import \"MTKeychain.h\"\n"
  },
  {
    "path": "MTProtoKit/MTLogging.h",
    "chars": 432,
    "preview": "\n\n#ifndef MTLogging_H\n#define MTLogging_H\n\n#import <Foundation/Foundation.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbo"
  },
  {
    "path": "MTProtoKit/MTLogging.m",
    "chars": 940,
    "preview": "\n\n#import \"MTLogging.h\"\n\nstatic void (*loggingFunction)(NSString *, va_list args) = NULL;\nstatic void (*shortLoggingFunc"
  },
  {
    "path": "MTProtoKit/MTMessage.h",
    "chars": 322,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMessage : NSObject\n\n@property (nonatomic, readonly) int64_t messageId;\n@"
  },
  {
    "path": "MTProtoKit/MTMessage.m",
    "chars": 307,
    "preview": "#import \"MTMessage.h\"\n\n@implementation MTMessage\n\n- (instancetype)initWithMessageId:(int64_t)messageId seqNo:(int32_t)se"
  },
  {
    "path": "MTProtoKit/MTMessageEncryptionKey.h",
    "chars": 501,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTMessageEncryptionKey : NSObject\n\n@property (nonatomic, strong, readonl"
  },
  {
    "path": "MTProtoKit/MTMessageEncryptionKey.m",
    "chars": 4128,
    "preview": "\n\n#import \"MTMessageEncryptionKey.h\"\n\n#import \"MTEncryption.h\"\n\n@implementation MTMessageEncryptionKey\n\n+ (instancetype)"
  },
  {
    "path": "MTProtoKit/MTMessageService.h",
    "chars": 2321,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTProto;\n@class MTIncomingMessage;\n@class MTMessageTransaction;\n@class MTApi"
  },
  {
    "path": "MTProtoKit/MTMessageTransaction.h",
    "chars": 1088,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTMessageTransaction : NSObject\n\n@property (nonatomic, strong, readonly)"
  },
  {
    "path": "MTProtoKit/MTMessageTransaction.m",
    "chars": 805,
    "preview": "\n\n#import \"MTMessageTransaction.h\"\n\n#import \"MTInternalId.h\"\n\nMTInternalIdClass(MTMessageTransaction)\n\n@implementation M"
  },
  {
    "path": "MTProtoKit/MTMsgAllInfoMessage.h",
    "chars": 278,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgAllInfoMessage : NSObject\n\n@property (nonatomic, strong, readonly) NS"
  },
  {
    "path": "MTProtoKit/MTMsgAllInfoMessage.m",
    "chars": 288,
    "preview": "#import \"MTMsgAllInfoMessage.h\"\n\n@implementation MTMsgAllInfoMessage\n\n- (instancetype)initWithMessageIds:(NSArray *)mess"
  },
  {
    "path": "MTProtoKit/MTMsgContainerMessage.h",
    "chars": 200,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgContainerMessage : NSObject\n\n@property (nonatomic, strong, readonly) "
  },
  {
    "path": "MTProtoKit/MTMsgContainerMessage.m",
    "chars": 242,
    "preview": "#import \"MTMsgContainerMessage.h\"\n\n@implementation MTMsgContainerMessage\n\n- (instancetype)initWithMessages:(NSArray *)me"
  },
  {
    "path": "MTProtoKit/MTMsgDetailedInfoMessage.h",
    "chars": 698,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgDetailedInfoMessage : NSObject\n\n@property (nonatomic, readonly) int64"
  },
  {
    "path": "MTProtoKit/MTMsgDetailedInfoMessage.m",
    "chars": 850,
    "preview": "#import \"MTMsgDetailedInfoMessage.h\"\n\n@implementation MTMsgDetailedInfoMessage\n\n- (instancetype)initWithResponseMessageI"
  },
  {
    "path": "MTProtoKit/MTMsgResendReqMessage.h",
    "chars": 206,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgResendReqMessage : NSObject\n\n@property (nonatomic, strong, readonly) "
  },
  {
    "path": "MTProtoKit/MTMsgResendReqMessage.m",
    "chars": 250,
    "preview": "#import \"MTMsgResendReqMessage.h\"\n\n@implementation MTMsgResendReqMessage\n\n- (instancetype)initWithMessageIds:(NSArray *)"
  },
  {
    "path": "MTProtoKit/MTMsgsAckMessage.h",
    "chars": 201,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgsAckMessage : NSObject\n\n@property (nonatomic, strong, readonly) NSArr"
  },
  {
    "path": "MTProtoKit/MTMsgsAckMessage.m",
    "chars": 240,
    "preview": "#import \"MTMsgsAckMessage.h\"\n\n@implementation MTMsgsAckMessage\n\n- (instancetype)initWithMessageIds:(NSArray *)messageIds"
  },
  {
    "path": "MTProtoKit/MTMsgsStateInfoMessage.h",
    "chars": 288,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgsStateInfoMessage : NSObject\n\n@property (nonatomic, readonly) int64_t"
  },
  {
    "path": "MTProtoKit/MTMsgsStateInfoMessage.m",
    "chars": 316,
    "preview": "#import \"MTMsgsStateInfoMessage.h\"\n\n@implementation MTMsgsStateInfoMessage\n\n- (instancetype)initWithRequestMessageId:(in"
  },
  {
    "path": "MTProtoKit/MTMsgsStateReqMessage.h",
    "chars": 206,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTMsgsStateReqMessage : NSObject\n\n@property (nonatomic, strong, readonly) "
  },
  {
    "path": "MTProtoKit/MTMsgsStateReqMessage.m",
    "chars": 250,
    "preview": "#import \"MTMsgsStateReqMessage.h\"\n\n@implementation MTMsgsStateReqMessage\n\n- (instancetype)initWithMessageIds:(NSArray *)"
  },
  {
    "path": "MTProtoKit/MTNetworkAvailability.h",
    "chars": 465,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTNetworkAvailability;\n\n@protocol MTNetworkAvailabilityDelegate <NSObject>\n\n"
  },
  {
    "path": "MTProtoKit/MTNetworkAvailability.m",
    "chars": 6506,
    "preview": "\n\n#import \"MTNetworkAvailability.h\"\n\n#import \"MTLogging.h\"\n#import \"MTQueue.h\"\n#import \"MTTimer.h\"\n\n#import <sys/socket."
  },
  {
    "path": "MTProtoKit/MTNewSessionCreatedMessage.h",
    "chars": 372,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTNewSessionCreatedMessage : NSObject\n\n@property (nonatomic, readonly) int"
  },
  {
    "path": "MTProtoKit/MTNewSessionCreatedMessage.m",
    "chars": 605,
    "preview": "#import \"MTNewSessionCreatedMessage.h\"\n\n@implementation MTNewSessionCreatedMessage\n\n- (instancetype)initWithFirstMessage"
  },
  {
    "path": "MTProtoKit/MTOutgoingMessage.h",
    "chars": 991,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTOutgoingMessage : NSObject\n\n@property (nonatomic, strong, readonly) id"
  },
  {
    "path": "MTProtoKit/MTOutgoingMessage.m",
    "chars": 1590,
    "preview": "\n\n#import \"MTOutgoingMessage.h\"\n\n#import <libkern/OSAtomic.h>\n\n@interface MTOutgoingMessageInternalId : NSObject <NSCopy"
  },
  {
    "path": "MTProtoKit/MTOutputStream.h",
    "chars": 432,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTOutputStream : NSObject\n\n- (NSOutputStream *)wrappedOutputStream;\n\n- ("
  },
  {
    "path": "MTProtoKit/MTOutputStream.m",
    "chars": 3974,
    "preview": "\n\n#import \"MTOutputStream.h\"\n\n#if TARGET_OS_IPHONE\n#   import <endian.h>\n#endif\n\nstatic inline int roundUp(int numToRoun"
  },
  {
    "path": "MTProtoKit/MTPingMessage.h",
    "chars": 175,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTPingMessage : NSObject\n\n@property (nonatomic, readonly) int64_t pingId;\n"
  },
  {
    "path": "MTProtoKit/MTPingMessage.m",
    "chars": 216,
    "preview": "#import \"MTPingMessage.h\"\n\n@implementation MTPingMessage\n\n- (instancetype)initWithPingId:(int64_t)pingId\n{\n    self = [s"
  },
  {
    "path": "MTProtoKit/MTPongMessage.h",
    "chars": 255,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTPongMessage : NSObject\n\n@property (nonatomic, readonly) int64_t messageI"
  },
  {
    "path": "MTProtoKit/MTPongMessage.m",
    "chars": 277,
    "preview": "#import \"MTPongMessage.h\"\n\n@implementation MTPongMessage\n\n- (instancetype)initWithMessageId:(int64_t)messageId pingId:(i"
  },
  {
    "path": "MTProtoKit/MTPreparedMessage.h",
    "chars": 968,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTPreparedMessage : NSObject\n\n@property (nonatomic, strong, readonly) id"
  },
  {
    "path": "MTProtoKit/MTPreparedMessage.m",
    "chars": 1183,
    "preview": "\n\n#import \"MTPreparedMessage.h\"\n\n#import \"MTInternalId.h\"\n\nMTInternalIdClass(MTPreparedMessage)\n\n@implementation MTPrepa"
  },
  {
    "path": "MTProtoKit/MTProto.h",
    "chars": 2300,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@protocol MTMessageService;\n@class MTQueue;\n@class MTContext;\n@class MTNetworkUsage"
  },
  {
    "path": "MTProtoKit/MTProto.m",
    "chars": 124466,
    "preview": "\n\n#import \"MTProto.h\"\n\n#import <inttypes.h>\n\n#import \"MTLogging.h\"\n#import \"MTQueue.h\"\n#import \"MTOutputStream.h\"\n#impor"
  },
  {
    "path": "MTProtoKit/MTProtoKit.h",
    "chars": 2816,
    "preview": "//\n//  MtProtoKit.h\n//  MtProtoKit\n//\n//  Created by Peter on 13/04/15.\n//  Copyright (c) 2015 Telegram. All rights rese"
  },
  {
    "path": "MTProtoKit/MTQueue.h",
    "chars": 405,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTQueue : NSObject\n\n- (instancetype)initWithName:(const char *)name;\n\n+ "
  },
  {
    "path": "MTProtoKit/MTQueue.m",
    "chars": 2759,
    "preview": "\n\n#import \"MTQueue.h\"\n\n@interface MTQueue ()\n{\n    bool _isMainQueue;\n    dispatch_queue_t _queue;\n    \n    const char *"
  },
  {
    "path": "MTProtoKit/MTRequest.h",
    "chars": 1456,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTRequestContext;\n@class MTRequestErrorContext;\n@class MTRpcError;\n\n@interfa"
  },
  {
    "path": "MTProtoKit/MTRequest.m",
    "chars": 1303,
    "preview": "\n\n#import \"MTRequest.h\"\n\n#import \"MTRpcError.h\"\n\n#import <libkern/OSAtomic.h>\n\n@interface MTRequestInternalId : NSObject"
  },
  {
    "path": "MTProtoKit/MTRequestContext.h",
    "chars": 612,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTRequestContext : NSObject\n\n@property (nonatomic, readonly) int64_t mes"
  },
  {
    "path": "MTProtoKit/MTRequestContext.m",
    "chars": 446,
    "preview": "\n\n#import \"MTRequestContext.h\"\n\n@implementation MTRequestContext\n\n- (instancetype)initWithMessageId:(int64_t)messageId m"
  },
  {
    "path": "MTProtoKit/MTRequestErrorContext.h",
    "chars": 307,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTRequestErrorContext : NSObject\n\n@property (nonatomic) CFAbsoluteTime m"
  },
  {
    "path": "MTProtoKit/MTRequestErrorContext.m",
    "chars": 222,
    "preview": "//\n//  MTRequestErrorContext.m\n//  MtProtoKit\n//\n//  Created by Admin on 18/02/2014.\n//  Copyright (c) 2014 Telegram. Al"
  },
  {
    "path": "MTProtoKit/MTRequestMessageService.h",
    "chars": 1265,
    "preview": "\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTMessageService.h>\n#elif defined(MtProtoKitMacF"
  },
  {
    "path": "MTProtoKit/MTRequestMessageService.m",
    "chars": 40291,
    "preview": "\n\n#import \"MTRequestMessageService.h\"\n\n#import \"MTLogging.h\"\n#import \"MTTime.h\"\n#import \"MTTimer.h\"\n#import \"MTContext.h"
  },
  {
    "path": "MTProtoKit/MTResPqMessage.h",
    "chars": 490,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTResPqMessage : NSObject\n\n@property (nonatomic, strong, readonly) NSData "
  },
  {
    "path": "MTProtoKit/MTResPqMessage.m",
    "chars": 638,
    "preview": "#import \"MTResPqMessage.h\"\n\n@implementation MTResPqMessage\n\n- (instancetype)initWithNonce:(NSData *)nonce serverNonce:(N"
  },
  {
    "path": "MTProtoKit/MTResendMessageService.h",
    "chars": 665,
    "preview": "\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTMessageService.h>\n#elif defined(MtProtoKitMacF"
  },
  {
    "path": "MTProtoKit/MTResendMessageService.m",
    "chars": 5058,
    "preview": "\n\n#import <Foundation/Foundation.h>\n#import \"MTLogging.h\"\n#import \"MTResendMessageService.h\"\n\n#import \"MTProto.h\"\n#impor"
  },
  {
    "path": "MTProtoKit/MTRpcError.h",
    "chars": 295,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTRpcError : NSObject\n\n@property (nonatomic, readonly) int32_t errorCode;\n"
  },
  {
    "path": "MTProtoKit/MTRpcError.m",
    "chars": 432,
    "preview": "#import \"MTRpcError.h\"\n\n@implementation MTRpcError\n\n- (instancetype)initWithErrorCode:(int32_t)errorCode errorDescriptio"
  },
  {
    "path": "MTProtoKit/MTRpcResultMessage.h",
    "chars": 303,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTRpcError;\n\n@interface MTRpcResultMessage : NSObject\n\n@property (nonatomic, r"
  },
  {
    "path": "MTProtoKit/MTRpcResultMessage.m",
    "chars": 306,
    "preview": "#import \"MTRpcResultMessage.h\"\n\n@implementation MTRpcResultMessage\n\n- (instancetype)initWithRequestMessageId:(int64_t)re"
  },
  {
    "path": "MTProtoKit/MTSerialization.h",
    "chars": 1429,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTExportedAut"
  },
  {
    "path": "MTProtoKit/MTServerDhInnerDataMessage.h",
    "chars": 579,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTServerDhInnerDataMessage : NSObject\n\n@property (nonatomic, strong, reado"
  },
  {
    "path": "MTProtoKit/MTServerDhInnerDataMessage.m",
    "chars": 491,
    "preview": "#import \"MTServerDhInnerDataMessage.h\"\n\n@implementation MTServerDhInnerDataMessage\n\n- (instancetype)initWithNonce:(NSDat"
  },
  {
    "path": "MTProtoKit/MTServerDhParamsMessage.h",
    "chars": 726,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTServerDhParamsMessage : NSObject\n\n@property (nonatomic, strong, readonly"
  },
  {
    "path": "MTProtoKit/MTServerDhParamsMessage.m",
    "chars": 973,
    "preview": "#import \"MTServerDhParamsMessage.h\"\n\n@implementation MTServerDhParamsMessage\n\n- (instancetype)initWithNonce:(NSData *)no"
  },
  {
    "path": "MTProtoKit/MTSessionInfo.h",
    "chars": 1384,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTContext;\n\n@interface MTSessionInfo : NSObject\n\n@property (nonatomic) bool "
  },
  {
    "path": "MTProtoKit/MTSessionInfo.m",
    "chars": 7401,
    "preview": "\n\n#import \"MTSessionInfo.h\"\n\n#import \"MTLogging.h\"\n#import \"MTContext.h\"\n\n@interface MTScheduledMessageConfirmation : NS"
  },
  {
    "path": "MTProtoKit/MTSetClientDhParamsResponseMessage.h",
    "chars": 1054,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTSetClientDhParamsResponseMessage : NSObject\n\n@property (nonatomic, stron"
  },
  {
    "path": "MTProtoKit/MTSetClientDhParamsResponseMessage.m",
    "chars": 1353,
    "preview": "#import \"MTSetClientDhParamsResponseMessage.h\"\n\n@implementation MTSetClientDhParamsResponseMessage\n\n- (instancetype)init"
  },
  {
    "path": "MTProtoKit/MTTcpConnection.h",
    "chars": 1955,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTDatacenterAddress;\n@class MTContext;\n@class MTQueue;\n@class MTTcpConnection;"
  },
  {
    "path": "MTProtoKit/MTTcpConnection.m",
    "chars": 41927,
    "preview": "#import \"MTTcpConnection.h\"\n\n#import \"MTLogging.h\"\n#import \"MTQueue.h\"\n#import \"MTTimer.h\"\n\n#import \"GCDAsyncSocket.h\"\n#"
  },
  {
    "path": "MTProtoKit/MTTcpConnectionBehaviour.h",
    "chars": 688,
    "preview": "\n\n@class MTQueue;\n@class MTTcpConnectionBehaviour;\n\n#import <Foundation/Foundation.h>\n\n@protocol MTTcpConnectionBehaviou"
  },
  {
    "path": "MTProtoKit/MTTcpConnectionBehaviour.m",
    "chars": 2367,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n#import \"MTTcpConnectionBehaviour.h\"\n\n#import \"MTTimer.h\"\n#import \"MTQueue.h\"\n\n@int"
  },
  {
    "path": "MTProtoKit/MTTcpTransport.h",
    "chars": 264,
    "preview": "\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTTransport.h>\n#elif defined(MtProtoKitMacFramew"
  },
  {
    "path": "MTProtoKit/MTTcpTransport.m",
    "chars": 33261,
    "preview": "\n\n#import \"MTTcpTransport.h\"\n\n#import \"MTLogging.h\"\n#import \"MTQueue.h\"\n#import \"MTTimer.h\"\n#import \"MTTime.h\"\n\n#import "
  },
  {
    "path": "MTProtoKit/MTTime.h",
    "chars": 218,
    "preview": "\n\n#ifndef MtProtoKit_MTTime_h\n#define MtProtoKit_MTTime_h\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n    \n#import <Foundati"
  },
  {
    "path": "MTProtoKit/MTTime.m",
    "chars": 351,
    "preview": "\n\n#import \"MTTime.h\"\n\n#import <mach/mach_time.h>\n\nCFAbsoluteTime MTAbsoluteSystemTime()\n{\n    static mach_timebase_info_"
  },
  {
    "path": "MTProtoKit/MTTimeFixContext.h",
    "chars": 505,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTTimeFixContext : NSObject\n\n@property (nonatomic, readonly) int64_t messa"
  },
  {
    "path": "MTProtoKit/MTTimeFixContext.m",
    "chars": 507,
    "preview": "#import \"MTTimeFixContext.h\"\n\n@implementation MTTimeFixContext\n\n- (instancetype)initWithMessageId:(int64_t)messageId mes"
  },
  {
    "path": "MTProtoKit/MTTimeSyncMessageService.h",
    "chars": 641,
    "preview": "\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTMessageService.h>\n#elif defined(MtProtoKitMacF"
  },
  {
    "path": "MTProtoKit/MTTimeSyncMessageService.m",
    "chars": 7558,
    "preview": "\n\n#import \"MTTimeSyncMessageService.h\"\n\n#import \"MTTime.h\"\n#import \"MTContext.h\"\n#import \"MTProto.h\"\n#import \"MTSerializ"
  },
  {
    "path": "MTProtoKit/MTTimer.h",
    "chars": 424,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTTimer : NSObject\n\n@property (nonatomic) NSTimeInterval timeoutDate;\n\n-"
  },
  {
    "path": "MTProtoKit/MTTimer.m",
    "chars": 2546,
    "preview": "\n\n#import \"MTTimer.h\"\n\n@interface MTTimer ()\n\n#if OS_OBJECT_USE_OBJC\n@property (nonatomic, strong) dispatch_source_t tim"
  },
  {
    "path": "MTProtoKit/MTTransport.h",
    "chars": 3858,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTContext;\n@class MTTransportScheme;\n@class MTTransport;\n@class MTTransportT"
  },
  {
    "path": "MTProtoKit/MTTransport.m",
    "chars": 2825,
    "preview": "\n\n#import \"MTTransport.h\"\n\n#import \"MTContext.h\"\n#import \"MTNetworkAvailability.h\"\n\n@interface MTTransport () <MTNetwork"
  },
  {
    "path": "MTProtoKit/MTTransportScheme.h",
    "chars": 665,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@class MTContext;\n@class MTTransport;\n@class MTDatacenterAddress;\n@protocol MTTrans"
  },
  {
    "path": "MTProtoKit/MTTransportScheme.m",
    "chars": 2207,
    "preview": "#import \"MTTransportScheme.h\"\n\n#import \"MTTransport.h\"\n#import \"MTDatacenterAddress.h\"\n\n#import \"MTTcpTransport.h\"\n\n#imp"
  },
  {
    "path": "MTProtoKit/MTTransportTransaction.h",
    "chars": 663,
    "preview": "\n\n#import <Foundation/Foundation.h>\n\n@interface MTTransportTransaction : NSObject\n\n@property (nonatomic, copy, readonly)"
  },
  {
    "path": "MTProtoKit/MTTransportTransaction.m",
    "chars": 751,
    "preview": "\n\n#import \"MTTransportTransaction.h\"\n\n@implementation MTTransportTransaction\n\n- (instancetype)initWithPayload:(NSData *)"
  },
  {
    "path": "MTProxyConnectivity.h",
    "chars": 451,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTSignal;\n@class MTContext;\n@class MTSocksProxySettings;\n\n@interface MTProxyCo"
  },
  {
    "path": "MTProxyConnectivity.m",
    "chars": 7338,
    "preview": "#import \"MTProxyConnectivity.h\"\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTSignal.h>\n#   i"
  },
  {
    "path": "MTRsa.h",
    "chars": 877,
    "preview": "#import <Foundation/Foundation.h>\n\n@interface MTRsa : NSObject\n\n// return base64 encoded string\n+ (NSString *)encryptStr"
  },
  {
    "path": "MTRsa.m",
    "chars": 15803,
    "preview": "#import \"MTRsa.h\"\n\n/*\n @author: ideawu\n @link: https://github.com/ideawu/Objective-C-RSA\n */\n\n#import <Security/Security"
  },
  {
    "path": "MTSignal.h",
    "chars": 1631,
    "preview": "#import <Foundation/Foundation.h>\n\n#import \"MTSubscriber.h\"\n\n@class MTQueue;\n\n@interface MTSignal : NSObject\n{\n@public\n "
  },
  {
    "path": "MTSignal.m",
    "chars": 26902,
    "preview": "#import \"MTSignal.h\"\n\n#if defined(MtProtoKitDynamicFramework)\n#   import <MTProtoKitDynamic/MTProtoKitDynamic.h>\n#elif d"
  },
  {
    "path": "MTSubscriber.h",
    "chars": 404,
    "preview": "#import <Foundation/Foundation.h>\n\n#import \"MTDisposable.h\"\n\n@interface MTSubscriber : NSObject <MTDisposable>\n{\n}\n\n- (i"
  },
  {
    "path": "MTSubscriber.m",
    "chars": 2828,
    "preview": "#import \"MTSubscriber.h\"\n\n#import <libkern/OSAtomic.h>\n\n@interface MTSubscriberBlocks : NSObject {\n@public\n    void (^_n"
  },
  {
    "path": "MTTransportSchemeStats.h",
    "chars": 683,
    "preview": "#import <Foundation/Foundation.h>\n\n@class MTDatacenterAddress;\n\n@interface MTTransportSchemeStats : NSObject<NSCoding>\n\n"
  },
  {
    "path": "MTTransportSchemeStats.m",
    "chars": 2528,
    "preview": "#import \"MTTransportSchemeStats.h\"\n\n#import \"MTDatacenterAddress.h\"\n\n@implementation MTTransportSchemeStats\n\n- (instance"
  },
  {
    "path": "MtProtoKit.xcodeproj/project.pbxproj",
    "chars": 334198,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "MtProtoKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 155,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:MtProtoKit.xcod"
  },
  {
    "path": "MtProtoKitDynamic/Info.plist",
    "chars": 806,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitDynamic/MtProtoKitDynamic.h",
    "chars": 3239,
    "preview": "//\n//  MtProtoKitDynamic.h\n//  MtProtoKitDynamic\n//\n//  Created by Peter on 08/07/15.\n//  Copyright (c) 2015 Telegram. A"
  },
  {
    "path": "MtProtoKitDynamicTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitDynamicTests/MtProtoKitDynamicTests.m",
    "chars": 893,
    "preview": "//\n//  MtProtoKitDynamicTests.m\n//  MtProtoKitDynamicTests\n//\n//  Created by Peter on 08/07/15.\n//  Copyright (c) 2015 T"
  },
  {
    "path": "MtProtoKitMac/Info.plist",
    "chars": 909,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitMac/MtProtoKitMac.h",
    "chars": 2942,
    "preview": "//\n//  MtProtoKitMac.h\n//  MtProtoKitMac\n//\n//  Created by Peter on 01/05/15.\n//  Copyright (c) 2015 Telegram. All right"
  },
  {
    "path": "MtProtoKitMacTests/Info.plist",
    "chars": 751,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitMacTests/MtProtoKitMacTests.m",
    "chars": 877,
    "preview": "//\n//  MtProtoKitMacTests.m\n//  MtProtoKitMacTests\n//\n//  Created by Peter on 01/05/15.\n//  Copyright (c) 2015 Telegram."
  },
  {
    "path": "MtProtoKitOSX/MtProtoKitOSX-Info.plist",
    "chars": 955,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitOSX/MtProtoKitOSX-Prefix.pch",
    "chars": 173,
    "preview": "//\n//  Prefix header\n//\n//  The contents of this file are implicitly included at the beginning of every source file.\n//\n"
  },
  {
    "path": "MtProtoKitOSX/en.lproj/InfoPlist.strings",
    "chars": 45,
    "preview": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "MtProtoKitStabilityTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitStabilityTests/MtProtoKitStabilityTests.m",
    "chars": 2329,
    "preview": "#import <XCTest/XCTest.h>\n\n#import <MTProtoKit/MTProtoKit.h>\n#import <MTProtoKit/MTTcpTransport.h>\n#import <MTProtoKit/M"
  },
  {
    "path": "MtProtoKitTests/Info.plist",
    "chars": 751,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitTests/MtProtoKitTests-Info.plist",
    "chars": 692,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "MtProtoKitTests/MtProtoKitTests.m",
    "chars": 865,
    "preview": "//\n//  MtProtoKitTests.m\n//  MtProtoKitTests\n//\n//  Created by Peter on 13/04/15.\n//  Copyright (c) 2015 Telegram. All r"
  },
  {
    "path": "MtProtoKitTests/en.lproj/InfoPlist.strings",
    "chars": 45,
    "preview": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "MtProtoKitiOS/MtProtoKit-Info.plist",
    "chars": 852,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  }
]

// ... and 98 more files (download for full content)

About this extraction

This page contains the full source code of the peter-iakovlev/MtProtoKit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 298 files (31.5 MB), approximately 831.3k tokens, and a symbol index with 474 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!