2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-31 22:55:11 +00:00

initial commit for 0.4.18 version of Telegram Desktop

This commit is contained in:
John Preston
2014-05-30 12:53:19 +04:00
parent 6d9ac2c475
commit 4221fe666f
1933 changed files with 137552 additions and 3 deletions

View File

@@ -0,0 +1,605 @@
'''
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
'''
import glob
import re
funcs = 0
types = 0;
consts = 0
funcsNow = 0
enums = [];
funcsDict = {};
typesDict = {};
TypesDict = {};
typesList = [];
boxed = {};
funcsText = '';
typesText = '';
dataTexts = '';
inlineMethods = '';
textSerialize = '';
forwards = '';
forwTypedefs = '';
out = open('mtpScheme.h', 'w')
out.write('/*\n');
out.write('Created from \'/SourceFiles/mtproto/scheme.tl\' by \'/SourceFiles/mtproto/generate.py\' script\n\n');
out.write('WARNING! All changes made in this file will be lost!\n\n');
out.write('This file is part of Telegram Desktop,\n');
out.write('an unofficial desktop messaging app, see https://telegram.org\n');
out.write('\n');
out.write('Telegram Desktop is free software: you can redistribute it and/or modify\n');
out.write('it under the terms of the GNU General Public License as published by\n');
out.write('the Free Software Foundation, either version 3 of the License, or\n');
out.write('(at your option) any later version.\n');
out.write('\n');
out.write('It is distributed in the hope that it will be useful,\n');
out.write('but WITHOUT ANY WARRANTY; without even the implied warranty of\n');
out.write('MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n');
out.write('GNU General Public License for more details.\n');
out.write('\n');
out.write('Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n');
out.write('Copyright (c) 2014 John Preston, https://tdesktop.com\n');
out.write('*/\n');
out.write('#pragma once\n\n#include "mtpCoreTypes.h"\n');
with open('scheme.tl') as f:
for line in f:
nocomment = re.match(r'^(.*?)//', line)
if (nocomment):
line = nocomment.group(1);
if (re.match(r'\-\-\-functions\-\-\-', line)):
funcsNow = 1;
continue;
if (re.match(r'\-\-\-types\-\-\-', line)):
funcsNow = 0;
continue;
if (re.match(r'^\s*$', line)):
continue;
nametype = re.match(r'([a-zA-Z\.0-9_]+)#([0-9a-f]+)([^=]*)=\s*([a-zA-Z\.<>0-9_]+);', line);
if (not nametype):
print('Bad line found: ' + line);
continue;
name = nametype.group(1);
nameInd = name.find('.');
if (nameInd >= 0):
Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:];
name = name.replace('.', '_');
else:
Name = name[0:1].upper() + name[1:];
typeid = nametype.group(2);
params = nametype.group(3);
restype = nametype.group(4);
if (restype.find('<') >= 0):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', restype);
if (templ):
restype = templ.group(1) + 'MTP' + templ.group(2).replace('.', '_') + '>';
else:
print('Bad template type: ' + restype);
continue;
resType = restype.replace('.', '_');
if (restype.find('.') >= 0):
parts = re.match(r'([a-z]+)\.([A-Z][A-Za-z0-9<>\._]+)', restype)
if (parts):
restype = parts.group(1) + '_' + parts.group(2)[0:1].lower() + parts.group(2)[1:];
else:
print('Bad result type name with dot: ' + restype);
continue;
else:
if (re.match(r'^[A-Z]', restype)):
restype = restype[:1].lower() + restype[1:];
else:
print('Bad result type name: ' + restype);
continue;
boxed[resType] = restype;
boxed[Name] = name;
enums.append('\tmtpc_' + name + ' = 0x' + typeid);
paramsList = params.strip().split(' ');
prms = {};
prmsList = [];
isTemplate = '';
for param in paramsList:
if (re.match(r'^\s*$', param)):
continue;
pnametype = re.match(r'([a-z_][a-z0-9_]*):([A-Za-z0-9<>\._]+)', param);
if (not pnametype):
pnametypeX = re.match(r'([a-z_][a-z0-9_]*):!X', param);
if (not pnametypeX or isTemplate != ''):
print('Bad param found: "' + param + '" in line: ' + line);
continue;
else:
pname = isTemplate = pnametypeX.group(1);
ptype = 'TQueryType';
else:
pname = pnametype.group(1);
ptype = pnametype.group(2);
if (ptype.find('<') >= 0):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype);
if (templ):
ptype = templ.group(1) + 'MTP' + templ.group(2).replace('.', '_') + '>';
else:
print('Bad template type: ' + ptype);
continue;
prmsList.append(pname);
prms[pname] = ptype.replace('.', '_');
if (isTemplate == '' and resType == 'X'):
print('Bad response type "X" in "' + name +'" in line: ' + line);
continue;
if funcsNow:
if (isTemplate != ''):
funcsText += '\ntemplate <class TQueryType>';
funcsText += '\nclass MTP' + name + ' { // RPC method \'' + nametype.group(1) + '\'\n'; # class
funcsText += 'public:\n';
prmsStr = [];
prmsInit = [];
prmsNames = [];
if (len(prms)):
for paramName in prmsList:
paramType = prms[paramName];
prmsInit.append('v' + paramName + '(_' + paramName + ')');
prmsNames.append('_' + paramName);
if (paramName == isTemplate):
ptypeFull = paramType;
else:
ptypeFull = 'MTP' + paramType;
funcsText += '\t' + ptypeFull + ' v' + paramName + ';\n';
if (paramType in ['int', 'Int', 'bool', 'Bool']):
prmsStr.append(ptypeFull + ' _' + paramName);
else:
prmsStr.append('const ' + ptypeFull + ' &_' + paramName);
funcsText += '\n';
funcsText += '\tMTP' + name + '() {\n\t}\n'; # constructor
funcsText += '\tMTP' + name + '(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ') {\n\t\tread(from, end, cons);\n\t}\n'; # stream constructor
if (len(prms)):
funcsText += '\tMTP' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n\t}\n';
funcsText += '\n';
funcsText += '\tuint32 size() const {\n'; # count size
size = [];
for k in prmsList:
v = prms[k];
size.append('v' + k + '.size()');
if (not len(size)):
size.append('0');
funcsText += '\t\treturn ' + ' + '.join(size) + ';\n';
funcsText += '\t}\n';
funcsText += '\tmtpTypeId type() const {\n\t\treturn mtpc_' + name + ';\n\t}\n'; # type id
funcsText += '\tvoid read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ') {\n'; # read method
for k in prmsList:
v = prms[k];
funcsText += '\t\tv' + k + '.read(from, end);\n';
funcsText += '\t}\n';
funcsText += '\tvoid write(mtpBuffer &to) const {\n'; # write method
for k in prmsList:
v = prms[k];
funcsText += '\t\tv' + k + '.write(to);\n';
funcsText += '\t}\n';
if (isTemplate != ''):
funcsText += '\n\ttypedef typename TQueryType::ResponseType ResponseType;\n';
else:
funcsText += '\n\ttypedef MTP' + resType + ' ResponseType;\n'; # method return type
funcsText += '};\n'; # class ending
if (isTemplate != ''):
funcsText += 'template <typename TQueryType>\n';
funcsText += 'class MTP' + Name + ' : public MTPBoxed<MTP' + name + '<TQueryType> > {\n';
funcsText += 'public:\n';
funcsText += '\tMTP' + Name + '() {\n\t}\n';
funcsText += '\tMTP' + Name + '(const MTP' + name + '<TQueryType> &v) : MTPBoxed<MTP' + name + '<TQueryType> >(v) {\n\t}\n';
if (len(prms)):
funcsText += '\tMTP' + Name + '(' + ', '.join(prmsStr) + ') : MTPBoxed<MTP' + name + '<TQueryType> >(MTP' + name + '<TQueryType>(' + ', '.join(prmsNames) + ')) {\n\t}\n';
funcsText += '};\n';
else:
funcsText += 'class MTP' + Name + ' : public MTPBoxed<MTP' + name + '> {\n';
funcsText += 'public:\n';
funcsText += '\tMTP' + Name + '() {\n\t}\n';
funcsText += '\tMTP' + Name + '(const MTP' + name + ' &v) : MTPBoxed<MTP' + name + '>(v) {\n\t}\n';
funcsText += '\tMTP' + Name + '(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTP' + name + '>(from, end, cons) {\n\t}\n';
if (len(prms)):
funcsText += '\tMTP' + Name + '(' + ', '.join(prmsStr) + ') : MTPBoxed<MTP' + name + '>(MTP' + name + '(' + ', '.join(prmsNames) + ')) {\n\t}\n';
funcsText += '};\n';
funcs = funcs + 1;
if (not restype in funcsDict):
funcsDict[restype] = [];
# TypesDict[restype] = resType;
funcsDict[restype].append([name, typeid, prmsList, prms]);
else:
if (isTemplate != ''):
print('Template types not allowed: "' + resType + '" in line: ' + line);
continue;
if (not restype in typesDict):
typesList.append(restype);
typesDict[restype] = [];
TypesDict[restype] = resType;
typesDict[restype].append([name, typeid, prmsList, prms]);
consts = consts + 1;
# text serialization: types and funcs
def addTextSerialize(dct):
result = '';
for restype in dct:
v = dct[restype];
for data in v:
name = data[0];
prmsList = data[2];
prms = data[3];
if len(result):
result += '\n';
result += '\t\tcase mtpc_' + name + ':\n';
if (len(prms)):
result += '\t\t\tresult += "\\n" + add;\n';
for k in prmsList:
v = prms[k];
result += '\t\t\tresult += " ' + k + ': " + mtpTextSerialize(from, end';
vtypeget = re.match(r'^[Vv]ector<MTP([A-Za-z0-9\._]+)>', v);
if (vtypeget):
if (not re.match(r'^[A-Z]', v)):
result += ', mtpc_vector';
else:
result += ', 0';
result += ', level + 1';
restype = vtypeget.group(1);
try:
if boxed[restype]:
restype = 0;
except KeyError:
if re.match(r'^[A-Z]', restype):
restype = 0;
else:
restype = v;
try:
if boxed[restype]:
restype = 0;
except KeyError:
if re.match(r'^[A-Z]', restype):
restype = 0;
if (restype):
try:
conses = typesDict[restype];
if (len(conses) > 1):
print('Complex bare type found: "' + restype + '" trying to serialize "' + k + '" of type "' + v + '"');
continue;
result += ', mtpc_' + conses[0][0];
except KeyError:
result += ', mtpc_' + restype;
if (not vtypeget):
result += ', level + 1';
else:
if (not vtypeget):
result += ', 0, level + 1';
result += ') + ",\\n" + add;\n';
else:
result += '\t\t\tresult = " ";\n';
result += '\t\treturn "{ ' + name + '" + result + "}";\n';
return result;
textSerialize += addTextSerialize(typesDict) + '\n';
textSerialize += addTextSerialize(funcsDict);
for restype in typesList:
v = typesDict[restype];
resType = TypesDict[restype];
withData = 0;
creatorsText = '';
constructsText = '';
constructsInline = '';
forwards += 'class MTP' + restype + ';\n';
forwTypedefs += 'typedef MTPBoxed<MTP' + restype + '> MTP' + resType + ';\n';
withType = (len(v) > 1);
switchLines = '';
friendDecl = '';
getters = '';
reader = '';
writer = '';
sizeList = [];
sizeFast = '';
newFast = '';
sizeCases = '';
for data in v:
name = data[0];
typeid = data[1];
prmsList = data[2];
prms = data[3];
dataText = '';
dataText += '\nclass MTPD' + name + ' : public mtpDataImpl<MTPD' + name + '> {\n'; # data class
dataText += 'public:\n';
sizeList = [];
creatorParams = [];
creatorParamsList = [];
readText = '';
writeText = '';
dataText += '\tMTPD' + name + '() {\n\t}\n'; # default constructor
switchLines += '\t\tcase mtpc_' + name + ': '; # for by-type-id type constructor
if (len(prms)):
switchLines += 'setData(new MTPD' + name + '()); ';
withData = 1;
getters += '\n\tMTPD' + name + ' &_' + name + '() {\n'; # splitting getter
getters += '\t\tif (!data) throw mtpErrorUninitialized();\n';
if (withType):
getters += '\t\tif (_type != mtpc_' + name + ') throw mtpErrorWrongTypeId(_type, mtpc_' + name + ');\n';
getters += '\t\tsplit();\n';
getters += '\t\treturn *(MTPD' + name + '*)data;\n';
getters += '\t}\n';
getters += '\tconst MTPD' + name + ' &c_' + name + '() const {\n'; # const getter
getters += '\t\tif (!data) throw mtpErrorUninitialized();\n';
if (withType):
getters += '\t\tif (_type != mtpc_' + name + ') throw mtpErrorWrongTypeId(_type, mtpc_' + name + ');\n';
getters += '\t\treturn *(const MTPD' + name + '*)data;\n';
getters += '\t}\n';
constructsText += '\texplicit MTP' + restype + '(MTPD' + name + ' *_data);\n'; # by-data type constructor
constructsInline += 'inline MTP' + restype + '::MTP' + restype + '(MTPD' + name + ' *_data) : ';
if (withType):
constructsInline += '_type(mtpc_' + name + '), ';
constructsInline += 'mtpDataOwner(_data) {\n}\n';
dataText += '\tMTPD' + name + '('; # params constructor
prmsStr = [];
prmsInit = [];
for paramName in prmsList:
paramType = prms[paramName];
if (paramType in ['int', 'Int', 'bool', 'Bool']):
prmsStr.append('MTP' + paramType + ' _' + paramName);
creatorParams.append('MTP' + paramType + ' _' + paramName);
else:
prmsStr.append('const MTP' + paramType + ' &_' + paramName);
creatorParams.append('const MTP' + paramType + ' &_' + paramName);
creatorParamsList.append('_' + paramName);
prmsInit.append('v' + paramName + '(_' + paramName + ')');
if (withType):
readText += '\t\t';
writeText += '\t\t';
readText += '\tv.v' + paramName + '.read(from, end);\n';
writeText += '\tv.v' + paramName + '.write(to);\n';
sizeList.append('v.v' + paramName + '.size()');
forwards += 'class MTPD' + name + ';\n'; # data class forward declaration
dataText += ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n\t}\n';
dataText += '\n';
for paramName in prmsList: # fields declaration
paramType = prms[paramName];
dataText += '\tMTP' + paramType + ' v' + paramName + ';\n';
sizeCases += '\t\tcase mtpc_' + name + ': {\n';
sizeCases += '\t\t\tconst MTPD' + name + ' &v(c_' + name + '());\n';
sizeCases += '\t\t\treturn ' + ' + '.join(sizeList) + ';\n';
sizeCases += '\t\t}\n';
sizeFast = '\tconst MTPD' + name + ' &v(c_' + name + '());\n\treturn ' + ' + '.join(sizeList) + ';\n';
newFast = 'new MTPD' + name + '()';
else:
sizeFast = '\treturn 0;\n';
switchLines += 'break;\n';
dataText += '};\n'; # class ending
if (len(prms)):
dataTexts += dataText; # add data class
friendDecl += '\tfriend MTP' + restype + ' MTP_' + name + '(' + ', '.join(creatorParams) + ');\n';
creatorsText += 'inline MTP' + restype + ' MTP_' + name + '(' + ', '.join(creatorParams) + ') {\n';
if (len(prms)): # creator with params
creatorsText += '\treturn MTP' + restype + '(new MTPD' + name + '(' + ', '.join(creatorParamsList) + '));\n';
else:
if (withType): # creator by type
creatorsText += '\treturn MTP' + restype + '(mtpc_' + name + ');\n';
else: # single creator
creatorsText += '\treturn MTP' + restype + '();\n';
creatorsText += '}\n';
if (withType):
reader += '\t\tcase mtpc_' + name + ': _type = cons; '; # read switch line
if (len(prms)):
reader += '{\n';
reader += '\t\t\tif (!data) setData(new MTPD' + name + '());\n';
reader += '\t\t\tMTPD' + name + ' &v(_' + name + '());\n';
reader += readText;
reader += '\t\t} break;\n';
writer += '\t\tcase mtpc_' + name + ': {\n'; # write switch line
writer += '\t\t\tconst MTPD' + name + ' &v(c_' + name + '());\n';
writer += writeText;
writer += '\t\t} break;\n';
else:
reader += 'break;\n';
else:
if (len(prms)):
reader += '\n\tif (!data) setData(new MTPD' + name + '());\n';
reader += '\tMTPD' + name + ' &v(_' + name + '());\n';
reader += readText;
writer += '\tconst MTPD' + name + ' &v(c_' + name + '());\n';
writer += writeText;
forwards += '\n';
typesText += '\nclass MTP' + restype; # type class declaration
if (withData):
typesText += ' : private mtpDataOwner'; # if has data fields
typesText += ' {\n';
typesText += 'public:\n';
typesText += '\tMTP' + restype + '()'; # default constructor
inits = [];
if (withType):
if (withData):
inits.append('mtpDataOwner(0)');
inits.append('_type(0)');
else:
if (withData):
inits.append('mtpDataOwner(' + newFast + ')');
if (withData and not withType):
typesText += ';\n';
inlineMethods += '\ninline MTP' + restype + '::MTP' + restype + '()';
if (inits):
inlineMethods += ' : ' + ', '.join(inits);
inlineMethods += ' {\n}\n';
else:
if (inits):
typesText += ' : ' + ', '.join(inits);
typesText += ' {\n\t}\n';
inits = [];
if (withData):
inits.append('mtpDataOwner(0)');
if (withType):
inits.append('_type(0)');
typesText += '\tMTP' + restype + '(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons';
if (not withType):
typesText += ' = mtpc_' + name;
typesText += ')'; # read constructor
if (inits):
typesText += ' : ' + ', '.join(inits);
typesText += ' {\n\t\tread(from, end, cons);\n\t}\n';
if (withData):
typesText += getters;
typesText += '\n\tuint32 size() const;\n'; # size method
inlineMethods += '\ninline uint32 MTP' + restype + '::size() const {\n';
if (withType and sizeCases):
inlineMethods += '\tswitch (_type) {\n';
inlineMethods += sizeCases;
inlineMethods += '\t}\n';
inlineMethods += '\treturn 0;\n';
else:
inlineMethods += sizeFast;
inlineMethods += '}\n';
typesText += '\tmtpTypeId type() const;\n'; # type id method
inlineMethods += 'inline mtpTypeId MTP' + restype + '::type() const {\n';
if (withType):
inlineMethods += '\tif (!_type) throw mtpErrorUninitialized();\n';
inlineMethods += '\treturn _type;\n';
else:
inlineMethods += '\treturn mtpc_' + v[0][0] + ';\n';
inlineMethods += '}\n';
typesText += '\tvoid read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons'; # read method
if (not withType):
typesText += ' = mtpc_' + name;
typesText += ');\n';
inlineMethods += 'inline void MTP' + restype + '::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {\n';
if (withData):
if (withType):
inlineMethods += '\tif (cons != _type) setData(0);\n';
else:
inlineMethods += '\tif (cons != mtpc_' + v[0][0] + ') throw mtpErrorUnexpected(cons, "MTP' + restype + '");\n';
if (withType):
inlineMethods += '\tswitch (cons) {\n'
inlineMethods += reader;
inlineMethods += '\t\tdefault: throw mtpErrorUnexpected(cons, "MTP' + restype + '");\n';
inlineMethods += '\t}\n';
else:
inlineMethods += reader;
inlineMethods += '}\n';
typesText += '\tvoid write(mtpBuffer &to) const;\n'; # write method
inlineMethods += 'inline void MTP' + restype + '::write(mtpBuffer &to) const {\n'
if (withType):
inlineMethods += '\tswitch (_type) {\n';
inlineMethods += writer;
inlineMethods += '\t}\n';
else:
inlineMethods += writer;
inlineMethods += '}\n';
typesText += '\n\ttypedef void ResponseType;\n'; # no response types declared
typesText += '\nprivate:\n'; # private constructors
if (withType): # by-type-id constructor
typesText += '\texplicit MTP' + restype + '(mtpTypeId type);\n';
inlineMethods += 'inline MTP' + restype + '::MTP' + restype + '(mtpTypeId type) : _type(type)';
if (withData):
inlineMethods += ', mtpDataOwner(0)';
inlineMethods += ' {\n';
inlineMethods += '\tswitch (type) {\n'; # type id check
inlineMethods += switchLines;
inlineMethods += '\t\tdefault: throw mtpErrorBadTypeId(type, "MTP' + restype + '");\n\t}\n';
inlineMethods += '}\n'; # by-type-id constructor end
if (withData):
typesText += constructsText;
inlineMethods += constructsInline;
if (friendDecl):
typesText += '\n' + friendDecl;
if (withType):
typesText += '\n\tmtpTypeId _type;\n'; # type field var
typesText += '};\n'; # type class ended
inlineMethods += creatorsText;
typesText += 'typedef MTPBoxed<MTP' + restype + '> MTP' + resType + ';\n'; # boxed type definition
textSerializeFull = '\ninline QString mtpTextSerialize(const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons) {\n';
textSerializeFull += '\tQString add = QString(" ").repeated(level * 2);\n\n';
textSerializeFull += '\tconst mtpPrime *start = from;\n';
textSerializeFull += '\ttry {\n';
textSerializeFull += '\t\tif (!cons) {\n';
textSerializeFull += '\t\t\tif (from >= end) {\n';
textSerializeFull += '\t\t\t\tthrow Exception("from >= 2");\n';
textSerializeFull += '\t\t\t}\n';
textSerializeFull += '\t\t\tcons = *from;\n';
textSerializeFull += '\t\t\t++from;\n';
textSerializeFull += '\t\t\t++start;\n';
textSerializeFull += '\t\t}\n\n';
textSerializeFull += '\t\tQString result;\n';
textSerializeFull += '\t\tswitch (cons) {\n' + textSerialize + '\t\t}\n\n';
textSerializeFull += '\t\treturn mtpTextSerializeCore(from, end, cons, level, vcons);\n';
textSerializeFull += '\t} catch (Exception &e) {\n';
textSerializeFull += '\t\tQString result = "(" + QString(e.what()) + QString("), cons: %1").arg(cons);\n';
textSerializeFull += '\t\tif (vcons) result += QString(", vcons: %1").arg(vcons);\n';
textSerializeFull += '\t\tresult += ", " + mb(start, (end - start) * sizeof(mtpPrime)).str();\n';
textSerializeFull += '\t\treturn "[ERROR] " + result;\n';
textSerializeFull += '\t}\n';
textSerializeFull += '}\n';
out.write('\n// Type id constants\nenum {\n' + ',\n'.join(enums) + '\n};\n');
out.write('\n// Type forward declarations\n' + forwards);
out.write('\n// Boxed types definitions\n' + forwTypedefs);
out.write('\n// Type classes definitions\n' + typesText);
out.write('\n// Type constructors with data\n' + dataTexts);
out.write('\n// RPC methods\n' + funcsText);
out.write('\n// Inline methods definition\n' + inlineMethods);
out.write('\n// Human-readable text serialization\n#if (defined _DEBUG || defined _WITH_DEBUG)\n' + textSerializeFull + '\n#endif\n');
print('Done, written {0} constructors, {1} functions.'.format(consts, funcs));

View File

@@ -0,0 +1,594 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "mtp.h"
namespace {
typedef QMap<int32, MTProtoSessionPtr> Sessions;
Sessions sessions;
MTProtoSessionPtr mainSession;
typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dc for request to this dc or -dc for request to main dc
RequestsByDC requestsByDC;
QMutex requestByDCLock;
typedef QMap<mtpRequestId, int32> AuthExportRequests; // holds target dc for auth export request
AuthExportRequests authExportRequests;
bool started = false;
bool loadingConfig = false;
uint32 layer;
typedef QMap<mtpRequestId, RPCResponseHandler> ParserMap;
ParserMap parserMap;
typedef QMap<mtpRequestId, mtpRequest> RequestMap;
RequestMap requestMap;
typedef QPair<mtpRequestId, uint64> DelayedRequest;
typedef QList<DelayedRequest> DelayedRequestsList;
DelayedRequestsList delayedRequests;
typedef QVector<mtpRequestId> DCAuthWaiters;
typedef QMap<int32, DCAuthWaiters> AuthWaiters;
AuthWaiters authWaiters;
QMutex toClearLock;
RPCCallbackClears toClear;
RPCResponseHandler globalHandler;
MTPStateChangedHandler stateChangedHandler = 0;
_mtp_internal::RequestResender *resender = 0;
mtpAuthKey _localKey;
void importDone(const MTPauth_Authorization &result, mtpRequestId req) {
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(req);
if (i == requestsByDC.end()) {
LOG(("MTP Error: auth import request not found in requestsByDC, requestId: %1").arg(req));
RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find import request in requestsByDC, request %1").arg(req)));
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
return;
}
int32 newdc = i.value();
DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
DCAuthWaiters &waiters(authWaiters[newdc]);
MTProtoSessionPtr session(_mtp_internal::getSession(newdc));
if (waiters.size()) {
for (DCAuthWaiters::iterator i = waiters.begin(), e = waiters.end(); i != e; ++i) {
mtpRequestId requestId = *i;
RequestMap::const_iterator j = requestMap.constFind(requestId);
if (j == requestMap.cend()) {
LOG(("MTP Error: could not find request %1 for resending").arg(requestId));
continue;
}
{
RequestsByDC::iterator k = requestsByDC.find(requestId);
if (k == requestsByDC.cend()) {
LOG(("MTP Error: could not find request %1 by dc for resending").arg(requestId));
continue;
}
if (k.value() < 0) {
MTP::setdc(newdc);
k.value() = -newdc;
} else {
k.value() = k.value() - (k.value() % _mtp_internal::dcShift) + newdc;
}
DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(requestId).arg(k.value()));
}
session->sendPrepared(j.value());
}
waiters.clear();
}
}
bool importFail(const RPCError &error, mtpRequestId req) {
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth import failed
return true;
}
void exportDone(const MTPauth_ExportedAuthorization &result, mtpRequestId req) {
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
if (i == authExportRequests.cend()) {
LOG(("MTP Error: auth export request target dc not found, requestId: %1").arg(req));
RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dc, request %1").arg(req)));
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
return;
}
const MTPDauth_exportedAuthorization &data(result.c_auth_exportedAuthorization());
MTP::send(MTPauth_ImportAuthorization(data.vid, data.vbytes), rpcDone(importDone), rpcFail(importFail), i.value());
authExportRequests.remove(req);
}
bool exportFail(const RPCError &error, mtpRequestId req) {
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
if (i != authExportRequests.cend()) {
authWaiters[i.value()].clear();
}
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
return true;
}
bool onErrorDefault(mtpRequestId requestId, const RPCError &error) {
const QString &err(error.type());
QRegularExpressionMatch m;;
if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) {
if (!requestId) return false;
int32 dc = 0, newdc = m.captured(2).toInt();
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i == requestsByDC.end()) {
LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdc));
} else {
dc = i.value();
}
}
if (!dc || !newdc) return false;
DEBUG_LOG(("MTP Info: changing request %1 dc%2 to %3").arg(requestId).arg((dc > 0) ? "" : " and main dc").arg(newdc));
if (dc < 0) {
if (MTP::authedId()) { // import auth, set dc and resend
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdc));
DCAuthWaiters &waiters(authWaiters[newdc]);
if (!waiters.size()) {
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc);
}
waiters.push_back(requestId);
return true;
} else {
MTP::setdc(newdc);
}
}
RequestMap::const_iterator i = requestMap.constFind(requestId);
if (i == requestMap.cend()) {
LOG(("MTP Error: could not find request %1").arg(requestId));
return false;
}
_mtp_internal::registerRequest(requestId, (dc < 0) ? -newdc : newdc);
_mtp_internal::getSession(newdc)->sendPrepared(i.value());
return true;
} else if ((m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) {
if (!requestId) return false;
int32 secs = m.captured(1).toInt();
uint64 sendAt = getms() + secs * 1000 + 10;
DelayedRequestsList::iterator i = delayedRequests.begin(), e = delayedRequests.end();
for (; i != e; ++i) {
if (i->first == requestId) return true;
if (i->second > sendAt) break;
}
delayedRequests.insert(i, DelayedRequest(requestId, sendAt));
if (resender) resender->checkDelayed();
return true;
} else if (error.code() == 401) {
int32 dc = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
dc = i.value();
} else {
LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
}
}
int32 newdc = abs(dc) % _mtp_internal::dcShift;
if (!newdc || newdc == mtpMainDC() || !MTP::authedId()) {
if (globalHandler.onFail) (*globalHandler.onFail)(requestId, error); // auth failed in main dc
return false;
}
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(dc));
DCAuthWaiters &waiters(authWaiters[newdc]);
if (!waiters.size()) {
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc);
}
waiters.push_back(requestId);
return true;
} else if (err == qsl("CONNECTION_NOT_INITED") || err == qsl("CONNECTION_LAYER_INVALID")) {
RequestMap::const_iterator i = requestMap.constFind(requestId);
if (i == requestMap.cend()) {
LOG(("MTP Error: could not find request %1").arg(requestId));
return false;
}
int32 dc = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i == requestsByDC.end()) {
LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId));
} else {
dc = i.value();
}
}
if (!dc) return false;
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(i.value());
return true;
}
return false;
}
}
namespace _mtp_internal {
MTProtoSessionPtr getSession(int32 dc) {
if (!started) return MTProtoSessionPtr();
if (!dc) return mainSession;
if (!(dc % _mtp_internal::dcShift)) {
dc += mainSession->getDC();
}
Sessions::const_iterator i = sessions.constFind(dc);
if (i != sessions.cend()) return *i;
MTProtoSessionPtr result(new MTProtoSession());
result->start(dc);
sessions.insert(dc, result);
return result;
}
void registerRequest(mtpRequestId requestId, int32 dc) {
{
QMutexLocker locker(&requestByDCLock);
requestsByDC.insert(requestId, dc);
}
_mtp_internal::performDelayedClear(); // need to do it somewhere..
}
void unregisterRequest(mtpRequestId requestId) {
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
requestsByDC.erase(i);
}
}
uint32 getLayer() {
return layer;
}
mtpRequestId storeRequest(mtpRequest &request, const RPCResponseHandler &parser) {
mtpRequestId res = reqid();
request->requestId = res;
if (parser.onDone || parser.onFail) {
parserMap.insert(res, parser);
requestMap.insert(res, request);
}
return res;
}
void replaceRequest(mtpRequest &newRequest, const mtpRequest &oldRequest) {
newRequest->requestId = oldRequest->requestId;
RequestMap::iterator i = requestMap.find(oldRequest->requestId);
if (i != requestMap.cend()) {
i.value() = newRequest;
}
}
void clearCallbacks(mtpRequestId requestId, int32 errorCode) {
ParserMap::iterator i = parserMap.find(requestId);
if (i != parserMap.end()) {
if (errorCode) {
rpcErrorOccured(requestId, i.value(), rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode)));
}
parserMap.erase(i);
}
requestMap.remove(requestId);
_mtp_internal::unregisterRequest(requestId);
}
void clearCallbacksDelayed(const RPCCallbackClears &requestIds) {
uint32 idsCount = requestIds.size();
if (!idsCount) return;
if (cDebug()) {
QString idsStr = QString("%1").arg(requestIds[0].requestId);
for (uint32 i = 1; i < idsCount; ++i) {
idsStr += QString(", %1").arg(requestIds[i].requestId);
}
DEBUG_LOG(("RPC Info: clear callbacks delayed, msgIds: %1").arg(idsStr));
}
QMutexLocker lock(&toClearLock);
uint32 toClearNow = toClear.size();
if (toClearNow) {
toClear.resize(toClearNow + idsCount);
memcpy(toClear.data() + toClearNow, requestIds.constData(), idsCount * sizeof(RPCCallbackClear));
} else {
toClear = requestIds;
}
}
void performDelayedClear() {
QMutexLocker lock(&toClearLock);
if (!toClear.isEmpty()) {
for (RPCCallbackClears::iterator i = toClear.begin(), e = toClear.end(); i != e; ++i) {
if (cDebug()) {
if (parserMap.find(i->requestId) != parserMap.end()) {
DEBUG_LOG(("RPC Info: clearing delayed callback %1, error code %2").arg(i->requestId).arg(i->errorCode));
}
}
clearCallbacks(i->requestId, i->errorCode);
}
toClear.clear();
}
}
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) {
ParserMap::iterator i = parserMap.find(requestId);
if (i != parserMap.cend()) {
DEBUG_LOG(("RPC Info: found parser for request %1, trying to parse response..").arg(requestId));
try {
if (from >= end) throw mtpErrorInsufficient();
if (*from == mtpc_rpc_error) {
RPCError err(MTPRpcError(from, end));
DEBUG_LOG(("RPC Info: error received, code %1, type %2, description: %3").arg(err.code()).arg(err.type()).arg(err.description()));
if (!rpcErrorOccured(requestId, i.value(), err)) {
return;
}
} else {
if (i.value().onDone) (*i.value().onDone)(requestId, from, end);
}
} catch (Exception &e) {
if (!rpcErrorOccured(requestId, i.value(), rpcClientError("RESPONSE_PARSE_FAILED", QString("exception text: ") + e.what()))) {
return;
}
}
parserMap.erase(i);
requestMap.remove(requestId);
} else {
DEBUG_LOG(("RPC Info: parser not found for %1").arg(requestId));
}
unregisterRequest(requestId);
}
void globalCallback(const mtpPrime *from, const mtpPrime *end) {
if (globalHandler.onDone) (*globalHandler.onDone)(0, from, end); // some updates were received
}
void onStateChange(int32 dc, int32 state) {
if (stateChangedHandler) stateChangedHandler(dc, state);
}
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data
if (onErrorDefault(requestId, err)) {
return false;
}
LOG(("RPC Error: request %1 got fail with code %2, error %3%4").arg(requestId).arg(err.code()).arg(err.type()).arg(err.description().isEmpty() ? QString() : QString(": %1").arg(err.description())));
onFail && (*onFail)(requestId, err);
return true;
}
void RequestResender::checkDelayed() {
uint64 now = getms();
while (!delayedRequests.isEmpty() && now >= delayedRequests.front().second) {
mtpRequestId requestId = delayedRequests.front().first;
delayedRequests.pop_front();
int32 dc = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::const_iterator i = requestsByDC.constFind(requestId);
if (i != requestsByDC.cend()) {
dc = i.value();
} else {
LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId));
continue;
}
}
RequestMap::const_iterator j = requestMap.constFind(requestId);
if (j == requestMap.cend()) {
DEBUG_LOG(("MTP Error: could not find request %1").arg(requestId));
continue;
}
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(j.value(), 0, false);
}
if (!delayedRequests.isEmpty()) {
QTimer::singleShot(delayedRequests.front().second - now, this, SLOT(checkDelayed()));
}
}
};
namespace MTP {
mtpAuthKey &localKey() {
return _localKey;
}
void createLocalKey(const QByteArray &pass, QByteArray *salt) {
uchar key[LocalEncryptKeySize] = { 0 };
int32 iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password
QByteArray newSalt;
if (!salt) {
newSalt.resize(LocalEncryptSaltSize);
memset_rand(newSalt.data(), newSalt.size());
salt = &newSalt;
cSetLocalSalt(newSalt);
}
PKCS5_PBKDF2_HMAC_SHA1(pass.constData(), pass.size(), (uchar*)salt->data(), salt->size(), iterCount, LocalEncryptKeySize, key);
_localKey.setKey(key);
}
void start() {
if (!localKey().created()) {
LOG(("App Error: trying to start MTP without local key!"));
return;
}
mtpLoadData();
MTProtoDCMap &dcs(mtpDCMap());
mainSession = MTProtoSessionPtr(new MTProtoSession());
mainSession->start(mtpMainDC());
sessions[mainSession->getDC()] = mainSession;
started = true;
resender = new _mtp_internal::RequestResender();
if (mtpNeedConfig()) {
mtpConfigLoader()->load();
}
}
void restart() {
if (!started) return;
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
(*i)->restart();
}
}
void setLayer(uint32 l) {
if (l > mtpLayerMax) {
l = mtpLayerMax;
} else if (!l) {
l = 1;
}
layer = l - 1;
}
void setdc(int32 dc, bool fromZeroOnly) {
if (!started) return;
int32 m = mainSession->getDC();
if (!dc || m == dc || m && fromZeroOnly) return;
mtpSetDC(dc);
mainSession = _mtp_internal::getSession(dc);
}
int32 maindc() {
if (!started) return 0;
return mainSession->getDC();
}
int32 dcstate(int32 dc) {
if (!started) return 0;
if (!dc) return mainSession->getState();
if (!(dc % _mtp_internal::dcShift)) {
dc += mainSession->getDC();
}
Sessions::const_iterator i = sessions.constFind(dc);
if (i != sessions.cend()) return (*i)->getState();
return MTProtoConnection::Disconnected;
}
QString dctransport(int32 dc) {
if (!started) return QString();
if (!dc) return mainSession->transport();
if (!(dc % _mtp_internal::dcShift)) {
dc += mainSession->getDC();
}
Sessions::const_iterator i = sessions.constFind(dc);
if (i != sessions.cend()) return (*i)->transport();
return QString();
}
void initdc(int32 dc) {
if (!started) return;
_mtp_internal::getSession(dc);
}
void cancel(mtpRequestId requestId) {
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
_mtp_internal::getSession(abs(i.value()))->cancel(requestId);
}
}
_mtp_internal::clearCallbacks(requestId);
}
int32 state(mtpRequestId requestId) {
if (requestId > 0) {
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
return _mtp_internal::getSession(abs(i.value()))->requestState(requestId);
}
return MTP::RequestSent;
}
return _mtp_internal::getSession(-requestId)->requestState(0);
}
void stop() {
for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) {
i.value()->stop();
}
delete resender;
resender = 0;
}
void authed(int32 uid) {
mtpAuthed(uid);
}
int32 authedId() {
return mtpAuthed();
}
void setGlobalDoneHandler(RPCDoneHandlerPtr handler) {
globalHandler.onDone = handler;
}
void setGlobalFailHandler(RPCFailHandlerPtr handler) {
globalHandler.onFail = handler;
}
void setStateChangedHandler(MTPStateChangedHandler handler) {
stateChangedHandler = handler;
}
void clearGlobalHandlers() {
setGlobalDoneHandler(RPCDoneHandlerPtr());
setGlobalFailHandler(RPCFailHandlerPtr());
setStateChangedHandler(0);
}
void writeConfig(QDataStream &stream) {
return mtpWriteConfig(stream);
}
bool readConfigElem(int32 blockId, QDataStream &stream) {
return mtpReadConfigElem(blockId, stream);
}
};

View File

@@ -0,0 +1,114 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
#include "mtproto/mtpSession.h"
#include "mtproto/mtpFileLoader.h"
namespace _mtp_internal {
MTProtoSessionPtr getSession(int32 dc = 0); // 0 - current set dc
void registerRequest(mtpRequestId requestId, int32 dc);
void unregisterRequest(mtpRequestId requestId);
uint32 getLayer();
static const uint32 dcShift = 10000;
mtpRequestId storeRequest(mtpRequest &request, const RPCResponseHandler &parser);
void replaceRequest(mtpRequest &newRequest, const mtpRequest &oldRequest);
void clearCallbacks(mtpRequestId requestId, int32 errorCode = RPCError::NoError); // 0 - do not toggle onError callback
void clearCallbacksDelayed(const RPCCallbackClears &requestIds);
void performDelayedClear();
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end);
void globalCallback(const mtpPrime *from, const mtpPrime *end);
void onStateChange(int32 dc, int32 state);
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); // return true if need to clean request data
inline bool rpcErrorOccured(mtpRequestId requestId, const RPCResponseHandler &handler, const RPCError &err) {
return rpcErrorOccured(requestId, handler.onFail, err);
}
class RequestResender : public QObject {
Q_OBJECT
public slots:
void checkDelayed();
};
};
namespace MTP {
mtpAuthKey &localKey();
void createLocalKey(const QByteArray &pass, QByteArray *salt = 0);
static const uint32 dld = 1 * _mtp_internal::dcShift; // send(req, callbacks, MTP::dld + dc) - for download
static const uint32 upl = 2 * _mtp_internal::dcShift; // send(req, callbacks, MTP::upl + dc) - for upload
void start();
void restart();
void setLayer(uint32 layer);
void setdc(int32 dc, bool fromZeroOnly = false);
int32 maindc();
int32 dcstate(int32 dc = 0);
QString dctransport(int32 dc = 0);
void initdc(int32 dc);
template <typename TRequest>
inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0) {
return _mtp_internal::getSession(dc)->send(request, callbacks, msCanWait, _mtp_internal::getLayer(), !dc);
}
template <typename TRequest>
inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0) {
return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait);
}
void cancel(mtpRequestId req);
enum {
RequestSent = 0,
RequestConnecting = 1,
RequestSending = 2
};
int32 state(mtpRequestId req); // < 0 means waiting for such count of ms
void defOnError(const RPCError &err);
void stop();
void authed(int32 uid);
int32 authedId();
void setGlobalDoneHandler(RPCDoneHandlerPtr handler);
void setGlobalFailHandler(RPCFailHandlerPtr handler);
void setStateChangedHandler(MTPStateChangedHandler handler);
void clearGlobalHandlers();
template <typename T>
T nonce() {
T result;
memset_rand(&result, sizeof(T));
return result;
}
void writeConfig(QDataStream &stream);
bool readConfigElem(int32 blockId, QDataStream &stream);
};
#include "mtproto/mtpSessionImpl.h"

View File

@@ -0,0 +1,151 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
class mtpAuthKey {
public:
mtpAuthKey() : _isset(false), _dc(0) {
}
bool created() const {
return _isset;
}
void setKey(const void *from) {
memcpy(_key, from, 256);
uchar sha1Buffer[20];
_keyId = *(uint64*)(hashSha1(_key, 256, sha1Buffer) + 3);
_isset = true;
}
void setDC(uint32 dc) {
_dc = dc;
}
uint32 getDC() const {
if (!_isset) throw mtpErrorKeyNotReady("getDC()");
return _dc;
}
uint64 keyId() const {
if (!_isset) throw mtpErrorKeyNotReady("keyId()");
return _keyId;
}
void prepareAES(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send = true) {
if (!_isset) throw mtpErrorKeyNotReady(QString("prepareAES(.., %1)").arg(logBool(send)));
uint32 x = send ? 0 : 8;
uchar data_a[16 + 32], sha1_a[20];
memcpy(data_a, &msgKey, 16);
memcpy(data_a + 16, _key + x, 32);
hashSha1(data_a, 16 + 32, sha1_a);
uchar data_b[16 + 16 + 16], sha1_b[20];
memcpy(data_b, _key + 32 + x, 16);
memcpy(data_b + 16, &msgKey, 16);
memcpy(data_b + 32, _key + 48 + x, 16);
hashSha1(data_b, 16 + 16 + 16, sha1_b);
uchar data_c[32 + 16], sha1_c[20];
memcpy(data_c, _key + 64 + x, 32);
memcpy(data_c + 32, &msgKey, 16);
hashSha1(data_c, 32 + 16, sha1_c);
uchar data_d[16 + 32], sha1_d[20];
memcpy(data_d, &msgKey, 16);
memcpy(data_d + 16, _key + 96 + x, 32);
hashSha1(data_d, 16 + 32, sha1_d);
uchar *key((uchar*)&aesKey), *iv((uchar*)&aesIV);
memcpy(key, sha1_a, 8);
memcpy(key + 8, sha1_b + 8, 12);
memcpy(key + 8 + 12, sha1_c + 4, 12);
memcpy(iv, sha1_a + 8, 12);
memcpy(iv + 12, sha1_b, 8);
memcpy(iv + 12 + 8, sha1_c + 16, 4);
memcpy(iv + 12 + 8 + 4, sha1_d, 8);
}
void write(QDataStream &to) const {
if (!_isset) throw mtpErrorKeyNotReady("write(..)");
to.writeRawData(_key, 256);
}
static const uint64 RecreateKeyId = 0xFFFFFFFFFFFFFFFFL;
private:
char _key[256];
uint64 _keyId;
bool _isset;
uint32 _dc;
};
typedef QSharedPointer<mtpAuthKey> mtpAuthKeyPtr;
inline void aesEncrypt(const void *src, void *dst, uint32 len, void *key, void *iv) {
uchar aes_key[32], aes_iv[32];
memcpy(aes_key, key, 32);
memcpy(aes_iv, iv, 32);
AES_KEY aes;
AES_set_encrypt_key(aes_key, 256, &aes);
AES_ige_encrypt((const uchar*)src, (uchar*)dst, len, &aes, aes_iv, AES_ENCRYPT);
}
inline void aesEncrypt(const void *src, void *dst, uint32 len, mtpAuthKeyPtr authKey, const MTPint128 &msgKey) {
MTPint256 aesKey, aesIV;
authKey->prepareAES(msgKey, aesKey, aesIV);
return aesEncrypt(src, dst, len, &aesKey, &aesIV);
}
inline void aesEncryptLocal(const void *src, void *dst, uint32 len, mtpAuthKey *authKey, const void *key128) {
MTPint256 aesKey, aesIV;
authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false);
return aesEncrypt(src, dst, len, &aesKey, &aesIV);
}
inline void aesDecrypt(const void *src, void *dst, uint32 len, void *key, void *iv) {
uchar aes_key[32], aes_iv[32];
memcpy(aes_key, key, 32);
memcpy(aes_iv, iv, 32);
AES_KEY aes;
AES_set_decrypt_key(aes_key, 256, &aes);
AES_ige_encrypt((const uchar*)src, (uchar*)dst, len, &aes, aes_iv, AES_DECRYPT);
}
inline void aesDecrypt(const void *src, void *dst, uint32 len, mtpAuthKeyPtr authKey, const MTPint128 &msgKey) {
MTPint256 aesKey, aesIV;
authKey->prepareAES(msgKey, aesKey, aesIV, false);
return aesDecrypt(src, dst, len, &aesKey, &aesIV);
}
inline void aesDecryptLocal(const void *src, void *dst, uint32 len, mtpAuthKey *authKey, const void *key128) {
MTPint256 aesKey, aesIV;
authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false);
return aesDecrypt(src, dst, len, &aesKey, &aesIV);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,432 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
#include "mtproto/mtpCoreTypes.h"
#include "mtproto/mtpScheme.h"
#include "mtproto/mtpPublicRSA.h"
#include "mtproto/mtpAuthKey.h"
inline bool mtpRequestData::isSentContainer(const mtpRequest &request) { // "request-like" wrap for msgIds vector
if (request->size() < 9) return false;
return (!request->msDate && !(*request)[6]); // msDate = 0, seqNo = 0
}
inline bool mtpRequestData::isStateRequest(const mtpRequest &request) {
if (request->size() < 9) return false;
return ((*request)[8] == mtpc_msgs_state_req);
}
inline bool mtpRequestData::needAck(const mtpRequest &request) {
if (request->size() < 9) return false;
return mtpRequestData::needAckByType((*request)[8]);
}
inline bool mtpRequestData::needAckByType(mtpTypeId type) {
switch (type) {
case mtpc_msg_container:
case mtpc_msgs_ack:
case mtpc_http_wait:
case mtpc_bad_msg_notification:
case mtpc_msgs_all_info:
case mtpc_msg_detailed_info:
case mtpc_msg_new_detailed_info:
return false;
}
return true;
}
class MTProtoConnectionPrivate;
class MTPSessionData;
class MTPThread : public QThread {
public:
MTPThread(QObject *parent = 0);
uint32 getThreadId() const;
private:
uint32 threadId;
};
class MTProtoConnection {
public:
enum ConnectionType {
TcpConnection,
HttpConnection
};
MTProtoConnection();
int32 start(MTPSessionData *data, int32 dc = 0); // return dc
void restart();
void stop();
void stopped();
~MTProtoConnection();
enum {
Disconnected = 0,
Connecting = 1,
Connected = 2,
UpdateAlways = 666
};
int32 state() const;
QString transport() const;
/*template <typename TRequest> // not used
uint64 sendAsync(const TRequest &request) {
return data->sendAsync(request);
}*/
private:
QThread *thread;
MTProtoConnectionPrivate *data;
};
class MTPabstractConnection : public QObject {
Q_OBJECT
typedef QList<mtpBuffer> BuffersQueue;
public:
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
virtual void disconnectFromServer() = 0;
virtual void connectToServer(const QString &addr, int32 port) = 0;
virtual bool isConnected() = 0;
virtual bool needHttpWait() {
return false;
}
virtual int32 debugState() const = 0;
virtual QString transport() const = 0;
BuffersQueue &received() {
return receivedQueue;
}
signals:
void receivedData();
void receivedSome(); // to stop restart timer
void error(bool maybeBadKey = false);
void connected();
void disconnected();
protected:
BuffersQueue receivedQueue; // list of received packets, not processed yet
};
class MTPabstractTcpConnection : public MTPabstractConnection {
Q_OBJECT
public:
MTPabstractTcpConnection();
public slots:
void socketRead();
protected:
QTcpSocket sock;
uint32 packetNum; // sent packet number
uint32 packetRead, packetLeft; // reading from socket
bool readingToShort;
char *currentPos;
mtpBuffer longBuffer;
mtpPrime shortBuffer[MTPShortBufferSize];
virtual void socketPacket(mtpPrime *packet, uint32 packetSize) = 0;
};
class MTPautoConnection : public MTPabstractTcpConnection {
Q_OBJECT
public:
MTPautoConnection(QThread *thread);
void sendData(mtpBuffer &buffer);
void disconnectFromServer();
void connectToServer(const QString &addr, int32 port);
bool isConnected();
bool needHttpWait();
int32 debugState() const;
QString transport() const;
public slots:
void socketError(QAbstractSocket::SocketError e);
void requestFinished(QNetworkReply *reply);
void onSocketConnected();
void onSocketDisconnected();
void onHttpStart();
protected:
void socketPacket(mtpPrime *packet, uint32 packetSize);
private:
void tcpSend(mtpBuffer &buffer);
void httpSend(mtpBuffer &buffer);
enum Status {
WaitingBoth = 0,
WaitingHttp,
WaitingTcp,
HttpReady,
UsingHttp,
UsingTcp,
};
Status status;
MTPint128 tcpNonce, httpNonce;
QTimer httpStartTimer;
QNetworkAccessManager manager;
QUrl address;
typedef QSet<QNetworkReply*> Requests;
Requests requests;
};
class MTPtcpConnection : public MTPabstractTcpConnection {
Q_OBJECT
public:
MTPtcpConnection(QThread *thread);
void sendData(mtpBuffer &buffer);
void disconnectFromServer();
void connectToServer(const QString &addr, int32 port);
bool isConnected();
int32 debugState() const;
QString transport() const;
public slots:
void socketError(QAbstractSocket::SocketError e);
protected:
void socketPacket(mtpPrime *packet, uint32 packetSize);
};
class MTPhttpConnection : public MTPabstractConnection {
Q_OBJECT
public:
MTPhttpConnection(QThread *thread);
void sendData(mtpBuffer &buffer);
void disconnectFromServer();
void connectToServer(const QString &addr, int32 port);
bool isConnected();
bool needHttpWait();
int32 debugState() const;
QString transport() const;
public slots:
void requestFinished(QNetworkReply *reply);
private:
QNetworkAccessManager manager;
QUrl address;
typedef QSet<QNetworkReply*> Requests;
Requests requests;
};
class MTProtoConnectionPrivate : public QObject {
Q_OBJECT
public:
MTProtoConnectionPrivate(QThread *thread, MTProtoConnection *owner, MTPSessionData *data, uint32 dc);
~MTProtoConnectionPrivate();
int32 getDC() const;
int32 getState() const;
QString transport() const;
signals:
void needToReceive();
void needToRestart();
void stateChanged(qint32 newState);
public slots:
void retryByTimer();
void restartNow();
void restart(bool maybeBadKey = false);
void onBadConnection();
void onOldConnection();
void onSentSome(uint64 size);
void onReceivedSome();
void onError(bool maybeBadKey = false);
void onReadyData();
void socketStart(bool afterConfig = false);
void onConnected();
void doDisconnect();
void doFinish();
// Auth key creation packet receive slots
void pqAnswered();
void dhParamsAnswered();
void dhClientParamsAnswered();
// General packet receive slot, connected to conn->receivedData signal
void handleReceived();
// Sessions signals, when we need to send something
void tryToSend();
bool updateAuthKey();
void sendPing();
void onConfigLoaded();
private:
void createConn();
mtpMsgId prepareToSend(mtpRequest &request);
bool sendRequest(mtpRequest &request, bool needAnyResponse);
mtpRequestId wasSent(mtpMsgId msgId) const;
int32 handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime);
mtpBuffer ungzip(const mtpPrime *from, const mtpPrime *end) const;
void handleMsgsStates(const QVector<MTPlong> &ids, const string &states, QVector<MTPlong> &acked);
void clearMessages();
bool setState(int32 state, int32 ifState = MTProtoConnection::UpdateAlways);
mutable QReadWriteLock stateMutex;
int32 _state;
uint32 dc;
MTProtoConnection *_owner;
MTPabstractConnection *conn;
QTimer retryTimer; // exp retry timer
uint32 retryTimeout;
quint64 retryWillFinish;
QTimer oldConnectionTimer;
bool oldConnection;
QTimer connCheckTimer;
uint32 receiveDelay;
int64 firstSentAt;
MTPMsgsAck ackRequest;
QVector<MTPlong> *ackRequestData;
// if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found
bool requestsFixTimeSalt(const QVector<MTPlong> &ids, int32 serverTime, uint64 serverSalt);
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
void requestsAcked(const QVector<MTPlong> &ids);
mtpPingId pingId, toSendPingId;
mtpMsgId pingMsgId;
mtpRequestId resend(mtpMsgId msgId, uint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
template <typename TRequest>
void sendRequestNotSecure(const TRequest &request);
template <typename TResponse>
bool readResponseNotSecure(TResponse &response);
bool restarted;
uint64 keyId;
MTPSessionData *sessionData;
bool myKeyLock;
void lockKey();
void unlockKey();
// Auth key creation fields and methods
struct AuthKeyCreateData {
AuthKeyCreateData()
: retries(0)
, g(0)
, new_nonce(*(MTPint256*)((uchar*)new_nonce_buf))
, auth_key_aux_hash(*(MTPlong*)((uchar*)new_nonce_buf + 33))
, req_num(0)
, msgs_sent(0) {
memset(new_nonce_buf, 0, sizeof(new_nonce_buf));
memset(aesKey, 0, sizeof(aesKey));
memset(aesIV, 0, sizeof(aesIV));
memset(auth_key, 0, sizeof(auth_key));
}
MTPint128 nonce, server_nonce;
uchar new_nonce_buf[41]; // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash
MTPint256 &new_nonce;
MTPlong &auth_key_aux_hash;
uint32 retries;
MTPlong retry_id;
string dh_prime;
int32 g;
string g_a;
uchar aesKey[32], aesIV[32];
uint32 auth_key[64];
MTPlong auth_key_hash;
uint32 req_num; // sent not encrypted request number
uint32 msgs_sent;
};
AuthKeyCreateData *authKeyData;
void dhClientParamsSend();
void authKeyCreated();
void clearAuthKeyData();
QTimer pinger;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,475 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "mtpDC.h"
#include "mtp.h"
namespace {
MTProtoDCMap gDCs;
bool configLoadedOnce = false;
int32 mainDC = 1;
int userId = 0;
mtpDcOptions gDCOptions;
typedef QMap<int32, mtpAuthKeyPtr> _KeysMapForWrite;
_KeysMapForWrite _keysMapForWrite;
QMutex _keysMapForWriteMutex;
int32 readAuthKeysFields(QIODevice *io) {
if (!io->isOpen()) io->open(QIODevice::ReadOnly);
QDataStream stream(io);
stream.setVersion(QDataStream::Qt_5_1);
int32 oldFound = 0;
while (true) {
quint32 blockId;
stream >> blockId;
if (stream.status() == QDataStream::ReadPastEnd) {
DEBUG_LOG(("MTP Info: keys file read end"));
break;
} else if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: could not read block id, status: %1 - keys file is corrupted?..").arg(stream.status()));
break;
}
if (blockId == dbiVersion) {
qint32 keysVersion;
stream >> keysVersion;
continue; // should not be in encrypted part, just ignore
}
if (blockId != dbiEncrypted && blockId != dbiKey) {
oldFound = 2;
}
switch (blockId) {
case dbiEncrypted: {
QByteArray data, decrypted;
stream >> data;
if (!MTP::localKey().created()) {
LOG(("MTP Error: reading encrypted keys without local key!"));
continue;
}
if (data.size() <= 16 || (data.size() & 0x0F)) {
LOG(("MTP Error: bad encrypted part size: %1").arg(data.size()));
continue;
}
uint32 fullDataLen = data.size() - 16;
decrypted.resize(fullDataLen);
const char *dataKey = data.constData(), *encrypted = data.constData() + 16;
aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &MTP::localKey(), dataKey);
uchar sha1Buffer[20];
if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) {
LOG(("MTP Error: bad decrypt key, data from user-config not decrypted"));
continue;
}
uint32 dataLen = *(const uint32*)decrypted.constData();
if (dataLen > decrypted.size() || dataLen <= fullDataLen - 16 || dataLen < 4) {
LOG(("MTP Error: bad decrypted part size: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size()));
continue;
}
decrypted.resize(dataLen);
QBuffer decryptedStream(&decrypted);
decryptedStream.open(QIODevice::ReadOnly);
decryptedStream.seek(4); // skip size
readAuthKeysFields(&decryptedStream);
} break;
case dbiKey: {
qint32 dcId;
quint32 key[64];
stream >> dcId;
stream.readRawData((char*)key, 256);
if (stream.status() == QDataStream::Ok) {
DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(mb(key, 256).str()));
dcId = dcId % _mtp_internal::dcShift;
mtpAuthKeyPtr keyPtr(new mtpAuthKey());
keyPtr->setKey(key);
keyPtr->setDC(dcId);
MTProtoDCPtr dc(new MTProtoDC(dcId, keyPtr));
gDCs.insert(dcId, dc);
}
} break;
case dbiUser: {
quint32 dcId;
qint32 uid;
stream >> uid >> dcId;
if (stream.status() == QDataStream::Ok) {
DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(uid));
userId = uid;
mainDC = dcId;
}
} break;
case dbiDcOption: {
quint32 dcId, port;
QString host, ip;
stream >> dcId >> host >> ip >> port;
if (stream.status() == QDataStream::Ok) {
gDCOptions.insert(dcId, mtpDcOption(dcId, host.toUtf8().constData(), ip.toUtf8().constData(), port));
}
} break;
case dbiConfig1: {
quint32 maxSize;
stream >> maxSize;
if (stream.status() == QDataStream::Ok) {
cSetMaxGroupCount(maxSize);
}
} break;
}
if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: could not read data, status: %1 - keys file is corrupted?..").arg(stream.status()));
break;
}
}
return oldFound;
}
int32 readAuthKeys(QFile &file) {
QDataStream stream(&file);
stream.setVersion(QDataStream::Qt_5_1);
int32 oldFound = 0;
quint32 blockId;
stream >> blockId;
if (stream.status() == QDataStream::ReadPastEnd) {
DEBUG_LOG(("MTP Info: keys file read end"));
return oldFound;
} else if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: could not read block id, status: %1 - keys file is corrupted?..").arg(stream.status()));
return oldFound;
}
if (blockId == dbiVersion) {
qint32 keysVersion;
stream >> keysVersion;
if (keysVersion > AppVersion) return oldFound;
stream >> blockId;
if (stream.status() == QDataStream::ReadPastEnd) {
DEBUG_LOG(("MTP Info: keys file read end"));
return oldFound;
} else if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: could not read block id, status: %1 - keys file is corrupted?..").arg(stream.status()));
return oldFound;
}
if (blockId != dbiEncrypted) {
oldFound = (blockId != dbiKey) ? 2 : 1;
}
} else {
oldFound = 2;
}
file.reset();
oldFound = qMax(oldFound, readAuthKeysFields(&file));
return oldFound;
}
void writeAuthKeys();
void readAuthKeys() {
QFile keysFile(cWorkingDir() + cDataFile());
if (keysFile.open(QIODevice::ReadOnly)) {
DEBUG_LOG(("MTP Info: keys file opened for reading"));
int32 oldFound = readAuthKeys(keysFile);
if (gDCOptions.isEmpty() || mainDC && gDCOptions.find(mainDC) == gDCOptions.cend()) { // load first dc info
gDCOptions.insert(1, mtpDcOption(1, "", cFirstDCIp(), cFirstDCPort()));
userId = 0;
mainDC = 0;
DEBUG_LOG(("MTP Info: first DC connect options: %1:%2").arg(cFirstDCIp()).arg(cFirstDCPort()));
} else {
configLoadedOnce = true;
DEBUG_LOG(("MTP Info: config loaded, dc option count: %1").arg(gDCOptions.size()));
}
if (oldFound > 0) {
writeAuthKeys();
if (oldFound > 1) {
App::writeUserConfig();
}
DEBUG_LOG(("MTP Info: rewritten old data / config to new data and config"));
}
} else {
DEBUG_LOG(("MTP Info: could not open keys file for reading"));
gDCOptions.insert(1, mtpDcOption(1, "", cFirstDCIp(), cFirstDCPort()));
DEBUG_LOG(("MTP Info: first DC connect options: %1:%2").arg(cFirstDCIp()).arg(cFirstDCPort()));
}
}
typedef QVector<mtpAuthKeyPtr> _KeysToWrite;
void writeAuthKeys() {
_KeysToWrite keysToWrite;
{
QMutexLocker lock(&_keysMapForWriteMutex);
for (_KeysMapForWrite::const_iterator i = _keysMapForWrite.cbegin(), e = _keysMapForWrite.cend(); i != e; ++i) {
keysToWrite.push_back(i.value());
}
}
QFile keysFile(cWorkingDir() + cDataFile());
if (keysFile.open(QIODevice::WriteOnly)) {
DEBUG_LOG(("MTP Info: writing keys data for encrypt"));
QByteArray toEncrypt;
toEncrypt.reserve(65536);
toEncrypt.resize(4);
{
QBuffer buffer(&toEncrypt);
buffer.open(QIODevice::Append);
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
for (_KeysToWrite::const_iterator i = keysToWrite.cbegin(), e = keysToWrite.cend(); i != e; ++i) {
stream << quint32(dbiKey) << quint32((*i)->getDC());
(*i)->write(stream);
}
if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: could not write keys to memory buf, status: %1").arg(stream.status()));
}
}
*(uint32*)(toEncrypt.data()) = toEncrypt.size();
uint32 size = toEncrypt.size(), fullSize = size;
if (fullSize & 0x0F) {
fullSize += 0x10 - (fullSize & 0x0F);
toEncrypt.resize(fullSize);
memset_rand(toEncrypt.data() + size, fullSize - size);
}
QByteArray encrypted(16 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data
hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data());
aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 16, fullSize, &MTP::localKey(), encrypted.constData());
DEBUG_LOG(("MTP Info: keys file opened for writing %1 keys").arg(keysToWrite.size()));
QDataStream keysStream(&keysFile);
keysStream.setVersion(QDataStream::Qt_5_1);
keysStream << quint32(dbiVersion) << qint32(AppVersion);
keysStream << quint32(dbiEncrypted) << encrypted; // write all encrypted data
if (keysStream.status() != QDataStream::Ok) {
LOG(("MTP Error: could not write keys, status: %1").arg(keysStream.status()));
}
} else {
LOG(("MTP Error: could not open keys file for writing"));
}
}
class _KeysReader {
public:
_KeysReader() {
readAuthKeys();
}
};
}
void mtpLoadData() {
static _KeysReader keysReader;
}
int32 mtpAuthed() {
return userId;
}
void mtpAuthed(int32 uid) {
if (userId != uid && mainDC) {
userId = uid;
App::writeUserConfig();
}
}
MTProtoDCMap &mtpDCMap() {
return gDCs;
}
const mtpDcOptions &mtpDCOptions() {
return gDCOptions;
}
bool mtpNeedConfig() {
return !configLoadedOnce;
}
int32 mtpMainDC() {
return mainDC;
}
void mtpSetDC(int32 dc) {
if (dc != mainDC) {
mainDC = dc;
if (userId) {
App::writeUserConfig();
}
}
}
MTProtoDC::MTProtoDC(int32 id, const mtpAuthKeyPtr &key) : _id(id), _key(key), _connectionInited(false), _connectionInitSent(false) {
connect(this, SIGNAL(authKeyCreated()), this, SLOT(authKeyWrite()), Qt::QueuedConnection);
QMutexLocker lock(&_keysMapForWriteMutex);
if (_key) {
_keysMapForWrite[_id] = _key;
} else {
_keysMapForWrite.remove(_id);
}
}
void MTProtoDC::authKeyWrite() {
DEBUG_LOG(("AuthKey Info: MTProtoDC::authKeyWrite() slot, dc %1").arg(_id));
if (_key) {
writeAuthKeys();
}
}
void MTProtoDC::setKey(const mtpAuthKeyPtr &key) {
DEBUG_LOG(("AuthKey Info: MTProtoDC::setKey(%1), emitting authKeyCreated, dc %2").arg(key ? key->keyId() : 0).arg(_id));
_key = key;
emit authKeyCreated();
QMutexLocker lock(&_keysMapForWriteMutex);
if (_key) {
_keysMapForWrite[_id] = _key;
} else {
_keysMapForWrite.remove(_id);
}
}
QReadWriteLock *MTProtoDC::keyMutex() const {
return &keyLock;
}
const mtpAuthKeyPtr &MTProtoDC::getKey() const {
return _key;
}
void MTProtoDC::destroyKey() {
setKey(mtpAuthKeyPtr());
QMutexLocker lock(&_keysMapForWriteMutex);
_keysMapForWrite.remove(_id);
}
namespace {
MTProtoConfigLoader configLoader;
bool loadingConfig = false;
void configLoaded(const MTPConfig &result) {
loadingConfig = false;
const MTPDconfig &data(result.c_config());
DEBUG_LOG(("MTP Info: got config, chat_size_max: %1, date: %2, test_mode: %3, this_dc: %4, dc_options.length: %5").arg(data.vchat_size_max.v).arg(data.vdate.v).arg(data.vtest_mode.v).arg(data.vthis_dc.v).arg(data.vdc_options.c_vector().v.size()));
QSet<int32> already;
const QVector<MTPDcOption> &options(data.vdc_options.c_vector().v);
for (QVector<MTPDcOption>::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) {
const MTPDdcOption &optData(i->c_dcOption());
if (already.constFind(optData.vid.v) == already.cend()) {
already.insert(optData.vid.v);
gDCOptions.insert(optData.vid.v, mtpDcOption(optData.vid.v, optData.vhostname.c_string().v, optData.vip_address.c_string().v, optData.vport.v));
}
}
cSetMaxGroupCount(data.vchat_size_max.v);
configLoadedOnce = true;
App::writeUserConfig();
emit mtpConfigLoader()->loaded();
}
bool configFailed(const RPCError &err) {
loadingConfig = false;
LOG(("MTP Error: failed to get config!"));
return false;
}
};
void MTProtoConfigLoader::load() {
if (loadingConfig) return;
loadingConfig = true;
MTPhelp_GetConfig request;
MTP::send(request, rpcDone(configLoaded), rpcFail(configFailed));
}
MTProtoConfigLoader *mtpConfigLoader() {
return &configLoader;
}
void mtpWriteConfig(QDataStream &stream) {
if (userId) {
stream << quint32(dbiUser) << qint32(userId) << quint32(mainDC);
}
if (configLoadedOnce) {
for (mtpDcOptions::const_iterator i = gDCOptions.cbegin(), e = gDCOptions.cend(); i != e; ++i) {
stream << quint32(dbiDcOption) << i->id << QString(i->host.c_str()) << QString(i->ip.c_str()) << i->port;
}
stream << quint32(dbiConfig1) << qint32(cMaxGroupCount());
}
}
bool mtpReadConfigElem(int32 blockId, QDataStream &stream) {
switch (blockId) {
case dbiUser: {
quint32 dcId;
qint32 uid;
stream >> uid >> dcId;
if (stream.status() == QDataStream::Ok) {
DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(uid));
userId = uid;
mainDC = dcId;
return true;
}
} break;
case dbiDcOption: {
quint32 dcId, port;
QString host, ip;
stream >> dcId >> host >> ip >> port;
if (stream.status() == QDataStream::Ok) {
gDCOptions.insert(dcId, mtpDcOption(dcId, host.toUtf8().constData(), ip.toUtf8().constData(), port));
return true;
}
} break;
case dbiConfig1: {
quint32 maxSize;
stream >> maxSize;
if (stream.status() == QDataStream::Ok) {
cSetMaxGroupCount(maxSize);
return true;
}
} break;
}
return false;
}

View File

@@ -0,0 +1,109 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
class MTProtoDC : public QObject {
Q_OBJECT
public:
MTProtoDC(int32 id, const mtpAuthKeyPtr &key);
QReadWriteLock *keyMutex() const;
const mtpAuthKeyPtr &getKey() const;
void setKey(const mtpAuthKeyPtr &key);
void destroyKey();
bool needConnectionInit() {
QMutexLocker lock(&initLock);
if (_connectionInited || _connectionInitSent) return false;
_connectionInitSent = true;
return true;
}
bool connectionInited() const {
QMutexLocker lock(&initLock);
bool res = _connectionInited;
return res;
}
void setConnectionInited(bool connectionInited = true) {
QMutexLocker lock(&initLock);
_connectionInited = connectionInited;
}
signals:
void authKeyCreated();
private slots:
void authKeyWrite();
private:
mutable QReadWriteLock keyLock;
mutable QMutex initLock;
int32 _id;
mtpAuthKeyPtr _key;
bool _connectionInited;
bool _connectionInitSent;
};
typedef QSharedPointer<MTProtoDC> MTProtoDCPtr;
typedef QMap<uint32, MTProtoDCPtr> MTProtoDCMap;
struct mtpDcOption {
mtpDcOption(int _id, const string &_host, const string &_ip, int _port) : id(_id), host(_host), ip(_ip), port(_port) {
}
int id;
string host;
string ip;
int port;
};
typedef QMap<int, mtpDcOption> mtpDcOptions;
class MTProtoConfigLoader : public QObject {
Q_OBJECT
public:
void load();
signals:
void loaded();
};
MTProtoConfigLoader *mtpConfigLoader();
const mtpDcOptions &mtpDCOptions();
MTProtoDCMap &mtpDCMap();
bool mtpNeedConfig();
int32 mtpMainDC();
void mtpSetDC(int32 dc);
uint32 mtpMaxChatSize();
void mtpWriteAuthKeys();
void mtpLoadData();
int32 mtpAuthed();
void mtpAuthed(int32 uid);
void mtpWriteConfig(QDataStream &stream);
bool mtpReadConfigElem(int32 blockId, QDataStream &stream);

View File

@@ -0,0 +1,333 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "mainwidget.h"
#include "window.h"
namespace {
int32 _priority = 1;
}
struct mtpFileLoaderQueue {
mtpFileLoaderQueue() : queries(0), start(0), end(0) {
}
int32 queries;
mtpFileLoader *start, *end;
};
namespace {
typedef QMap<int32, mtpFileLoaderQueue> LoaderQueues;
LoaderQueues queues;
}
mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret) : next(0), prev(0), inQueue(false), complete(false), requestId(0), priority(0), initialSize(0),
dc(dc), volume(volume), local(local), secret(secret), size(0), type(MTP_storage_fileUnknown()), locationType(0), id(0), access(0) {
LoaderQueues::iterator i = queues.find(dc);
if (i == queues.cend()) {
i = queues.insert(dc, mtpFileLoaderQueue());
}
queue = &i.value();
}
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size) : next(0), prev(0), inQueue(false), complete(false), requestId(0), priority(0),
dc(dc), id(id), access(access), type(MTP_storage_fileUnknown()), locationType(locType), file(to), initialSize(size) {
LoaderQueues::iterator i = queues.find(MTP::dld + dc);
if (i == queues.cend()) {
i = queues.insert(MTP::dld + dc, mtpFileLoaderQueue());
}
queue = &i.value();
}
QString mtpFileLoader::fileName() const {
return file.fileName();
}
bool mtpFileLoader::done() const {
return complete;
}
mtpTypeId mtpFileLoader::fileType() const {
return type.type();
}
const QByteArray &mtpFileLoader::bytes() const {
return data;
}
float64 mtpFileLoader::currentProgress() const {
if (complete) return 1;
if (!fullSize()) return 0;
return float64(currentOffset()) / fullSize();
}
int32 mtpFileLoader::currentOffset() const {
return file.isOpen() ? file.size() : data.size();
}
int32 mtpFileLoader::fullSize() const {
return size;
}
uint64 mtpFileLoader::objId() const {
return id;
}
void mtpFileLoader::loadNext() {
if (queue->queries >= MaxFileQueries) return;
for (mtpFileLoader *i = queue->start; i; i = i->next) {
if (i->loadPart() && queue->queries >= MaxFileQueries) return;
}
}
void mtpFileLoader::finishFail() {
bool started = currentOffset() > 0;
if (requestId) {
requestId = 0;
--queue->queries;
}
type = MTP_storage_fileUnknown();
complete = true;
if (file.isOpen()) {
file.close();
file.remove();
}
data = QByteArray();
emit failed(this, started);
file.setFileName(QString());
loadNext();
}
bool mtpFileLoader::loadPart() {
if (complete || requestId) return false;
int32 limit = DocumentDownloadPartSize;
MTPInputFileLocation loc;
switch (locationType) {
case 0: loc = MTP_inputFileLocation(MTP_long(volume), MTP_int(local), MTP_long(secret)); limit = DownloadPartSize; break;
case mtpc_inputVideoFileLocation: loc = MTP_inputVideoFileLocation(MTP_long(id), MTP_long(access)); break;
case mtpc_inputAudioFileLocation: loc = MTP_inputAudioFileLocation(MTP_long(id), MTP_long(access)); break;
case mtpc_inputDocumentFileLocation: loc = MTP_inputDocumentFileLocation(MTP_long(id), MTP_long(access)); break;
default:
finishFail();
return false;
break;
}
++queue->queries;
int32 offset = currentOffset();
MTPupload_GetFile request(MTPupload_getFile(loc, MTP_int(offset), MTP_int(limit)));
requestId = MTP::send(request, rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld + dc, 50);
return true;
}
void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result) {
if (requestId) {
--queue->queries;
requestId = 0;
}
if (offset == currentOffset()) {
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
const MTPDupload_file &d(result.c_upload_file());
const string &bytes(d.vbytes.c_string().v);
if (bytes.size()) {
if (file.isOpen()) {
if (file.write(bytes.data(), bytes.size()) != bytes.size()) {
return finishFail();
}
} else {
data.append(bytes.data(), bytes.size());
}
}
if (bytes.size() && !(bytes.size() % 1024)) { // good next offset
// offset += bytes.size();
} else {
type = d.vtype;
complete = true;
if (file.isOpen()) {
file.close();
psPostprocessFile(QFileInfo(file).absoluteFilePath());
}
removeFromQueue();
App::wnd()->update();
App::wnd()->psUpdateNotifies();
}
emit progress(this);
}
loadNext();
}
bool mtpFileLoader::partFailed(const RPCError &error) {
finishFail();
return true;
}
void mtpFileLoader::removeFromQueue() {
if (!inQueue) return;
if (next) {
next->prev = prev;
}
if (prev) {
prev->next = next;
}
if (queue->end == this) {
queue->end = prev;
}
if (queue->start == this) {
queue->start = next;
}
next = prev = 0;
inQueue = false;
}
void mtpFileLoader::pause() {
removeFromQueue();
}
void mtpFileLoader::start(bool loadFirst, bool prior) {
if (complete) return;
if (!file.fileName().isEmpty()) {
if (!file.open(QIODevice::WriteOnly)) {
finishFail();
return;
}
}
mtpFileLoader *before = 0, *after = 0;
if (prior) {
if (inQueue && priority == _priority) {
if (loadFirst) {
if (!prev) return started(loadFirst, prior);
before = queue->start;
} else {
if (!next || next->priority < _priority) return started(loadFirst, prior);
after = next;
while (after->next && after->next->priority == _priority) {
after = after->next;
}
}
} else {
priority = _priority;
if (loadFirst) {
if (inQueue && !prev) return started(loadFirst, prior);
before = queue->start;
} else {
if (inQueue) {
if (next && next->priority == _priority) {
after = next;
} else if (prev && prev->priority < _priority) {
before = prev;
while (before->prev && before->prev->priority < _priority) {
before = before->prev;
}
} else {
return started(loadFirst, prior);
}
} else {
if (queue->start && queue->start->priority == _priority) {
after = queue->start;
} else {
before = queue->start;
}
}
if (after) {
while (after->next && after->next->priority == _priority) {
after = after->next;
}
}
}
}
} else {
if (loadFirst) {
if (inQueue && (!prev || prev->priority == _priority)) return started(loadFirst, prior);
before = prev;
while (before->prev && before->prev->priority != _priority) {
before = before->prev;
}
} else {
if (inQueue && !next) return started(loadFirst, prior);
after = queue->end;
}
}
removeFromQueue();
inQueue = true;
if (!queue->start) {
queue->start = queue->end = this;
} else if (before) {
if (before != next) {
prev = before->prev;
next = before;
next->prev = this;
if (prev) {
prev->next = this;
}
if (queue->start->prev) queue->start = queue->start->prev;
}
} else if (after) {
if (after != prev) {
next = after->next;
prev = after;
after->next = this;
if (next) {
next->prev = this;
}
if (queue->end->next) queue->end = queue->end->next;
}
} else {
LOG(("Queue Error: _start && !before && !after"));
}
return started(loadFirst, prior);
}
void mtpFileLoader::cancel() {
bool started = currentOffset() > 0;
if (requestId) {
requestId = 0;
--queue->queries;
}
type = MTP_storage_fileUnknown();
complete = true;
if (file.isOpen()) {
file.close();
file.remove();
}
data = QByteArray();
file.setFileName(QString());
emit progress(this);
loadNext();
}
bool mtpFileLoader::loading() const {
return inQueue;
}
void mtpFileLoader::started(bool loadFirst, bool prior) {
if (queue->queries >= MaxFileQueries && (!loadFirst || !prior) || complete) return;
loadPart();
}
mtpFileLoader::~mtpFileLoader() {
removeFromQueue();
}
namespace MTP {
void clearLoaderPriorities() {
++_priority;
}
}

View File

@@ -0,0 +1,88 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
namespace MTP {
void clearLoaderPriorities();
}
struct mtpFileLoaderQueue;
class mtpFileLoader : public QObject, public RPCSender {
Q_OBJECT
public:
mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret);
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size);
bool done() const;
mtpTypeId fileType() const;
const QByteArray &bytes() const;
QString fileName() const;
float64 currentProgress() const;
int32 currentOffset() const;
int32 fullSize() const;
void pause();
void start(bool loadFirst = false, bool prior = true);
void cancel();
bool loading() const;
uint64 objId() const;
~mtpFileLoader();
mtpFileLoader *prev, *next;
int32 priority;
signals:
void progress(mtpFileLoader *loader);
void failed(mtpFileLoader *loader, bool started);
private:
mtpFileLoaderQueue *queue;
bool inQueue, complete;
int32 requestId;
void started(bool loadFirst, bool prior);
void removeFromQueue();
void loadNext();
void finishFail();
bool loadPart();
void partLoaded(int32 offset, const MTPupload_File &result);
bool partFailed(const RPCError &error);
int32 dc;
mtpTypeId locationType; // 0 or mtpc_inputVideoFileLocation / mtpc_inputAudioFileLocation / mtpc_inputDocumentFileLocation
int64 volume; // for photo locations
int32 local;
int64 secret;
uint64 id; // for other locations
uint64 access;
QFile file;
int32 initialSize;
QByteArray data;
int32 size;
MTPstorage_FileType type;
};

View File

@@ -0,0 +1,82 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
class mtpPublicRSA {
public:
mtpPublicRSA(const char *key) : data(new mtpPublicRSAInner(PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(key), -1), 0, 0, 0), 0)) {
if (!data->prsa) return;
int32 nBytes = BN_num_bytes(data->prsa->n);
int32 eBytes = BN_num_bytes(data->prsa->e);
string nStr(nBytes, 0), eStr(eBytes, 0);
BN_bn2bin(data->prsa->n, (uchar*)&nStr[0]);
BN_bn2bin(data->prsa->e, (uchar*)&eStr[0]);
mtpBuffer tmp;
MTP_string(nStr).write(tmp);
MTP_string(eStr).write(tmp);
uchar sha1Buffer[20];
data->fp = *(uint64*)(hashSha1(&tmp[0], tmp.size() * sizeof(mtpPrime), sha1Buffer) + 3);
}
mtpPublicRSA(const mtpPublicRSA &v) : data(v.data) {
++data->cnt;
}
mtpPublicRSA &operator=(const mtpPublicRSA &v) {
if (data != v.data) {
destroy();
data = v.data;
++data->cnt;
}
return *this;
}
uint64 fingerPrint() const {
return data->fp;
}
RSA *key() {
return data->prsa;
}
~mtpPublicRSA() {
destroy();
}
private:
void destroy() {
if (!--data->cnt) {
delete data;
}
}
struct mtpPublicRSAInner {
mtpPublicRSAInner(RSA *_prsa, uint64 _fp) : prsa(_prsa), fp(_fp), cnt(1) {
}
~mtpPublicRSAInner() {
RSA_free(prsa);
}
RSA *prsa;
uint32 cnt;
uint64 fp;
};
mtpPublicRSAInner *data;
};

View File

@@ -0,0 +1,35 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "mtproto/mtpRPC.h"
RPCOwnedDoneHandler::RPCOwnedDoneHandler(RPCSender *owner) : _owner(owner) {
_owner->regHandler(this);
}
RPCOwnedDoneHandler::~RPCOwnedDoneHandler() {
if (_owner) _owner->unregHandler(this);
}
RPCOwnedFailHandler::RPCOwnedFailHandler(RPCSender *owner) : _owner(owner) {
_owner->regHandler(this);
}
RPCOwnedFailHandler::~RPCOwnedFailHandler() {
if (_owner) _owner->unregHandler(this);
}

View File

@@ -0,0 +1,790 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
class RPCError {
public:
RPCError(const MTPrpcError &error) : _code(error.c_rpc_error().verror_code.v) {
const string &msg(error.c_rpc_error().verror_message.c_string().v);
const QString &text(QString::fromUtf8(msg.c_str(), msg.length()));
QRegularExpressionMatch m = QRegularExpression("^([A-Z0-9_]+)(: .*)?$", reMultiline).match(text);
if (m.hasMatch()) {
_type = m.captured(1);
_description = m.captured(2).mid(2);
} else {
_type = "CLIENT_BAD_RPC_ERROR";
_description = "Bad rpc error received, text = '" + text + "'";
}
}
int32 code() const {
return _code;
}
const QString &type() const {
return _type;
}
const QString &description() const {
return _description;
}
enum {
NoError,
TimeoutError
};
private:
int32 _code;
QString _type, _description;
};
class RPCAbstractDoneHandler { // abstract done
public:
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const = 0;
virtual ~RPCAbstractDoneHandler() {
}
};
typedef QSharedPointer<RPCAbstractDoneHandler> RPCDoneHandlerPtr;
class RPCAbstractFailHandler { // abstract fail
public:
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const = 0;
virtual ~RPCAbstractFailHandler() {
}
};
typedef QSharedPointer<RPCAbstractFailHandler> RPCFailHandlerPtr;
struct RPCResponseHandler {
RPCResponseHandler() {
}
RPCResponseHandler(const RPCDoneHandlerPtr &ondone, const RPCFailHandlerPtr &onfail) : onDone(ondone), onFail(onfail) {
}
RPCDoneHandlerPtr onDone;
RPCFailHandlerPtr onFail;
};
inline RPCResponseHandler rpcCb(const RPCDoneHandlerPtr &onDone = RPCDoneHandlerPtr(), const RPCFailHandlerPtr &onFail = RPCFailHandlerPtr()) {
return RPCResponseHandler(onDone, onFail);
}
template <typename TReturn>
class RPCDoneHandlerBare : public RPCAbstractDoneHandler { // done(from, end)
typedef TReturn (*CallbackType)(const mtpPrime *, const mtpPrime *);
public:
RPCDoneHandlerBare(CallbackType onDone) : _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
(*_onDone)(from, end);
}
private:
CallbackType _onDone;
};
template <typename TReturn>
class RPCDoneHandlerBareReq : public RPCAbstractDoneHandler { // done(from, end, req_id)
typedef TReturn (*CallbackType)(const mtpPrime *, const mtpPrime *, mtpRequestId);
public:
RPCDoneHandlerBareReq(CallbackType onDone) : _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
(*_onDone)(from, end, requestId);
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TResponse>
class RPCDoneHandlerPlain : public RPCAbstractDoneHandler { // done(result)
typedef TReturn (*CallbackType)(const TResponse &);
public:
RPCDoneHandlerPlain(CallbackType onDone) : _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
(*_onDone)(TResponse(from, end));
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TResponse>
class RPCDoneHandlerReq : public RPCAbstractDoneHandler { // done(result, req_id)
typedef TReturn (*CallbackType)(const TResponse &, mtpRequestId);
public:
RPCDoneHandlerReq(CallbackType onDone) : _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
(*_onDone)(TResponse(from, end), requestId);
}
private:
CallbackType _onDone;
};
template <typename TReturn>
class RPCDoneHandlerNo : public RPCAbstractDoneHandler { // done()
typedef TReturn (*CallbackType)();
public:
RPCDoneHandlerNo(CallbackType onDone) : _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
(*_onDone)();
}
private:
CallbackType _onDone;
};
template <typename TReturn>
class RPCDoneHandlerNoReq : public RPCAbstractDoneHandler { // done(req_id)
typedef TReturn (*CallbackType)(mtpRequestId);
public:
RPCDoneHandlerNoReq(CallbackType onDone) : _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
(*_onDone)(requestId);
}
private:
CallbackType _onDone;
};
class RPCFailHandlerPlain : public RPCAbstractFailHandler { // fail(error)
typedef bool (*CallbackType)(const RPCError &);
public:
RPCFailHandlerPlain(CallbackType onFail) : _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return (*_onFail)(e);
}
private:
CallbackType _onFail;
};
class RPCFailHandlerReq : public RPCAbstractFailHandler { // fail(error, req_id)
typedef bool (*CallbackType)(const RPCError &, mtpRequestId);
public:
RPCFailHandlerReq(CallbackType onFail) : _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return (*_onFail)(e, requestId);
}
private:
CallbackType _onFail;
};
class RPCFailHandlerNo : public RPCAbstractFailHandler { // fail()
typedef bool (*CallbackType)();
public:
RPCFailHandlerNo(CallbackType onFail) : _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return (*_onFail)();
}
private:
CallbackType _onFail;
};
class RPCFailHandlerNoReq : public RPCAbstractFailHandler { // fail(req_id)
typedef bool (*CallbackType)(mtpRequestId);
public:
RPCFailHandlerNoReq(CallbackType onFail) : _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return (*_onFail)(requestId);
}
private:
CallbackType _onFail;
};
struct RPCCallbackClear {
RPCCallbackClear(mtpRequestId id = 0, int32 code = RPCError::NoError) : requestId(id), errorCode(code) {
}
mtpRequestId requestId;
int32 errorCode;
};
typedef QVector<RPCCallbackClear> RPCCallbackClears;
template <typename TReturn>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const mtpPrime *, const mtpPrime *)) { // done(from, end)
return RPCDoneHandlerPtr(new RPCDoneHandlerBare<TReturn>(onDone));
}
template <typename TReturn>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const mtpPrime *, const mtpPrime *, mtpRequestId)) { // done(from, end, req_id)
return RPCDoneHandlerPtr(new RPCDoneHandlerBareReq<TReturn>(onDone));
}
template <typename TReturn, typename TResponse>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const TResponse &)) { // done(result)
return RPCDoneHandlerPtr(new RPCDoneHandlerPlain<TReturn, TResponse>(onDone));
}
template <typename TReturn, typename TResponse>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const TResponse &, mtpRequestId)) { // done(result, req_id)
return RPCDoneHandlerPtr(new RPCDoneHandlerReq<TReturn, TResponse>(onDone));
}
template <typename TReturn>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)()) { // done()
return RPCDoneHandlerPtr(new RPCDoneHandlerNo<TReturn>(onDone));
}
template <typename TReturn>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(mtpRequestId)) { // done(req_id)
return RPCDoneHandlerPtr(new RPCDoneHandlerNoReq<TReturn>(onDone));
}
inline RPCFailHandlerPtr rpcFail(bool (*onFail)(const RPCError &)) { // fail(error)
return RPCFailHandlerPtr(new RPCFailHandlerPlain(onFail));
}
inline RPCFailHandlerPtr rpcFail(bool (*onFail)(const RPCError &, mtpRequestId)) { // fail(error, req_id)
return RPCFailHandlerPtr(new RPCFailHandlerReq(onFail));
}
inline RPCFailHandlerPtr rpcFail(bool (*onFail)()) { // fail()
return RPCFailHandlerPtr(new RPCFailHandlerNo(onFail));
}
inline RPCFailHandlerPtr rpcFail(bool (*onFail)(mtpRequestId)) { // fail(req_id)
return RPCFailHandlerPtr(new RPCFailHandlerNoReq(onFail));
}
class RPCSender;
class RPCOwnedDoneHandler : public RPCAbstractDoneHandler { // abstract done
public:
RPCOwnedDoneHandler(RPCSender *owner);
void invalidate() {
_owner = 0;
}
~RPCOwnedDoneHandler();
protected:
RPCSender *_owner;
};
class RPCOwnedFailHandler : public RPCAbstractFailHandler { // abstract fail
public:
RPCOwnedFailHandler(RPCSender *owner);
void invalidate() {
_owner = 0;
}
~RPCOwnedFailHandler();
protected:
RPCSender *_owner;
};
template <typename TReturn, typename TReceiver>
class RPCDoneHandlerBareOwned : public RPCOwnedDoneHandler { // done(from, end)
typedef TReturn (TReceiver::*CallbackType)(const mtpPrime *, const mtpPrime *);
public:
RPCDoneHandlerBareOwned(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(from, end);
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TReceiver>
class RPCDoneHandlerBareOwnedReq : public RPCOwnedDoneHandler { // done(from, end, req_id)
typedef TReturn (TReceiver::*CallbackType)(const mtpPrime *, const mtpPrime *, mtpRequestId);
public:
RPCDoneHandlerBareOwnedReq(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(from, end, requestId);
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TReceiver, typename TResponse>
class RPCDoneHandlerOwned : public RPCOwnedDoneHandler { // done(result)
typedef TReturn (TReceiver::*CallbackType)(const TResponse &);
public:
RPCDoneHandlerOwned(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(TResponse(from, end));
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TReceiver, typename TResponse>
class RPCDoneHandlerOwnedReq : public RPCOwnedDoneHandler { // done(result, req_id)
typedef TReturn (TReceiver::*CallbackType)(const TResponse &, mtpRequestId);
public:
RPCDoneHandlerOwnedReq(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(TResponse(from, end), requestId);
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TReceiver>
class RPCDoneHandlerOwnedNo : public RPCOwnedDoneHandler { // done()
typedef TReturn (TReceiver::*CallbackType)();
public:
RPCDoneHandlerOwnedNo(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)();
}
private:
CallbackType _onDone;
};
template <typename TReturn, typename TReceiver>
class RPCDoneHandlerOwnedNoReq : public RPCOwnedDoneHandler { // done(req_id)
typedef TReturn (TReceiver::*CallbackType)(mtpRequestId);
public:
RPCDoneHandlerOwnedNoReq(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(requestId);
}
private:
CallbackType _onDone;
};
template <typename T, typename TReturn, typename TReceiver>
class RPCBindedDoneHandlerBareOwned : public RPCOwnedDoneHandler { // done(b, from, end)
typedef TReturn (TReceiver::*CallbackType)(T, const mtpPrime *, const mtpPrime *);
public:
RPCBindedDoneHandlerBareOwned(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b, from, end);
}
private:
CallbackType _onDone;
T _b;
};
template <typename T, typename TReturn, typename TReceiver>
class RPCBindedDoneHandlerBareOwnedReq : public RPCOwnedDoneHandler { // done(b, from, end, req_id)
typedef TReturn (TReceiver::*CallbackType)(T, const mtpPrime *, const mtpPrime *, mtpRequestId);
public:
RPCBindedDoneHandlerBareOwnedReq(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b, from, end, requestId);
}
private:
CallbackType _onDone;
T _b;
};
template <typename T, typename TReturn, typename TReceiver, typename TResponse>
class RPCBindedDoneHandlerOwned : public RPCOwnedDoneHandler { // done(b, result)
typedef TReturn (TReceiver::*CallbackType)(T, const TResponse &);
public:
RPCBindedDoneHandlerOwned(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b, TResponse(from, end));
}
private:
CallbackType _onDone;
T _b;
};
template <typename T, typename TReturn, typename TReceiver, typename TResponse>
class RPCBindedDoneHandlerOwnedReq : public RPCOwnedDoneHandler { // done(b, result, req_id)
typedef TReturn (TReceiver::*CallbackType)(T, const TResponse &, mtpRequestId);
public:
RPCBindedDoneHandlerOwnedReq(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b, TResponse(from, end), requestId);
}
private:
CallbackType _onDone;
T _b;
};
template <typename T, typename TReturn, typename TReceiver>
class RPCBindedDoneHandlerOwnedNo : public RPCOwnedDoneHandler { // done(b)
typedef TReturn (TReceiver::*CallbackType)(T);
public:
RPCBindedDoneHandlerOwnedNo(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b);
}
private:
CallbackType _onDone;
T _b;
};
template <typename T, typename TReturn, typename TReceiver>
class RPCBindedDoneHandlerOwnedNoReq : public RPCOwnedDoneHandler { // done(b, req_id)
typedef TReturn (TReceiver::*CallbackType)(T, mtpRequestId);
public:
RPCBindedDoneHandlerOwnedNoReq(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
}
virtual void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b, requestId);
}
private:
CallbackType _onDone;
T _b;
};
template <typename TReceiver>
class RPCFailHandlerOwned : public RPCOwnedFailHandler { // fail(error)
typedef bool (TReceiver::*CallbackType)(const RPCError &);
public:
RPCFailHandlerOwned(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(e) : true;
}
private:
CallbackType _onFail;
};
template <typename TReceiver>
class RPCFailHandlerOwnedReq : public RPCOwnedFailHandler { // fail(error, req_id)
typedef bool (TReceiver::*CallbackType)(const RPCError &, mtpRequestId);
public:
RPCFailHandlerOwnedReq(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(e, requestId) : true;
}
private:
CallbackType _onFail;
};
template <typename TReceiver>
class RPCFailHandlerOwnedNo : public RPCOwnedFailHandler { // fail()
typedef bool (TReceiver::*CallbackType)();
public:
RPCFailHandlerOwnedNo(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)() : true;
}
private:
CallbackType _onFail;
};
template <typename TReceiver>
class RPCFailHandlerOwnedNoReq : public RPCOwnedFailHandler { // fail(req_id)
typedef bool (TReceiver::*CallbackType)(mtpRequestId);
public:
RPCFailHandlerOwnedNoReq(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(requestId) : true;
}
private:
CallbackType _onFail;
};
template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwned : public RPCOwnedFailHandler { // fail(b, error)
typedef bool (TReceiver::*CallbackType)(T, const RPCError &);
public:
RPCBindedFailHandlerOwned(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _b(b), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b, e) : true;
}
private:
CallbackType _onFail;
T _b;
};
template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwnedReq : public RPCOwnedFailHandler { // fail(b, error, req_id)
typedef bool (TReceiver::*CallbackType)(T, const RPCError &, mtpRequestId);
public:
RPCBindedFailHandlerOwnedReq(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _b(b), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b, e, requestId) : true;
}
private:
CallbackType _onFail;
T _b;
};
template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwnedNo : public RPCOwnedFailHandler { // fail(b)
typedef bool (TReceiver::*CallbackType)(T);
public:
RPCBindedFailHandlerOwnedNo(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _b(b), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b) : true;
}
private:
CallbackType _onFail;
T _b;
};
template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwnedNoReq : public RPCOwnedFailHandler { // fail(b, req_id)
typedef bool (TReceiver::*CallbackType)(T, mtpRequestId);
public:
RPCBindedFailHandlerOwnedNoReq(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _b(b), _onFail(onFail) {
}
virtual bool operator()(mtpRequestId requestId, const RPCError &e) const {
return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b, requestId) : true;
}
private:
CallbackType _onFail;
T _b;
};
class RPCSender {
typedef QSet<RPCOwnedDoneHandler*> DoneHandlers;
DoneHandlers doneHandlers;
typedef QSet<RPCOwnedFailHandler*> FailHandlers;
FailHandlers failHandlers;
public:
template <typename TReturn, typename TReceiver> // done(from, end)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const mtpPrime *, const mtpPrime *)) {
return RPCDoneHandlerPtr(new RPCDoneHandlerBareOwned<TReturn, TReceiver>(static_cast<TReceiver*>(this), onDone));
}
template <typename TReturn, typename TReceiver> // done(from, end, req_id)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const mtpPrime *, const mtpPrime *, mtpRequestId)) {
return RPCDoneHandlerPtr(new RPCDoneHandlerBareOwnedReq<TReturn, TReceiver>(static_cast<TReceiver*>(this), onDone));
}
template <typename TReturn, typename TReceiver, typename TResponse> // done(result)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const TResponse &)) {
return RPCDoneHandlerPtr(new RPCDoneHandlerOwned<TReturn, TReceiver, TResponse>(static_cast<TReceiver*>(this), onDone));
}
template <typename TReturn, typename TReceiver, typename TResponse> // done(result, req_id)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const TResponse &, mtpRequestId)) {
return RPCDoneHandlerPtr(new RPCDoneHandlerOwnedReq<TReturn, TReceiver, TResponse>(static_cast<TReceiver*>(this), onDone));
}
template <typename TReturn, typename TReceiver> // done()
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)()) {
return RPCDoneHandlerPtr(new RPCDoneHandlerOwnedNo<TReturn, TReceiver>(static_cast<TReceiver*>(this), onDone));
}
template <typename TReturn, typename TReceiver> // done(req_id)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(mtpRequestId)) {
return RPCDoneHandlerPtr(new RPCDoneHandlerOwnedNoReq<TReturn, TReceiver>(static_cast<TReceiver*>(this), onDone));
}
template <typename TReceiver> // fail(error)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(const RPCError &)) {
return RPCFailHandlerPtr(new RPCFailHandlerOwned<TReceiver>(static_cast<TReceiver*>(this), onFail));
}
template <typename TReceiver> // fail(error, req_id)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(const RPCError &, mtpRequestId)) {
return RPCFailHandlerPtr(new RPCFailHandlerOwnedReq<TReceiver>(static_cast<TReceiver*>(this), onFail));
}
template <typename TReceiver> // fail()
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)()) {
return RPCFailHandlerPtr(new RPCFailHandlerOwnedNo<TReceiver>(static_cast<TReceiver*>(this), onFail));
}
template <typename TReceiver> // fail(req_id)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(mtpRequestId)) {
return RPCFailHandlerPtr(new RPCFailHandlerOwnedNo<TReceiver>(static_cast<TReceiver*>(this), onFail));
}
template <typename T, typename TReturn, typename TReceiver> // done(b, from, end)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, const mtpPrime *, const mtpPrime *), T b) {
return RPCDoneHandlerPtr(new RPCBindedDoneHandlerBareOwned<T, TReturn, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
}
template <typename T, typename TReturn, typename TReceiver> // done(b, from, end, req_id)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, const mtpPrime *, const mtpPrime *, mtpRequestId), T b) {
return RPCDoneHandlerPtr(new RPCBindedDoneHandlerBareOwnedReq<T, TReturn, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
}
template <typename T, typename TReturn, typename TReceiver, typename TResponse> // done(b, result)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, const TResponse &), T b) {
return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwned<T, TReturn, TReceiver, TResponse>(b, static_cast<TReceiver*>(this), onDone));
}
template <typename T, typename TReturn, typename TReceiver, typename TResponse> // done(b, result, req_id)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, const TResponse &, mtpRequestId), T b) {
return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwnedReq<T, TReturn, TReceiver, TResponse>(b, static_cast<TReceiver*>(this), onDone));
}
template <typename T, typename TReturn, typename TReceiver> // done(b)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T), T b) {
return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwnedNo<T, TReturn, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
}
template <typename T, typename TReturn, typename TReceiver> // done(b, req_id)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, mtpRequestId), T b) {
return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwnedNoReq<T, TReturn, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
}
template <typename T, typename TReceiver> // fail(b, error)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T, const RPCError &), T b) {
return RPCFailHandlerPtr(new RPCBindedFailHandlerOwned<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
}
template <typename T, typename TReceiver> // fail(b, error, req_id)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T, const RPCError &, mtpRequestId), T b) {
return RPCFailHandlerPtr(new RPCBindedFailHandlerOwnedReq<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
}
template <typename T, typename TReceiver> // fail(b)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T), T b) {
return RPCFailHandlerPtr(new RPCBindedFailHandlerOwnedNo<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
}
template <typename T, typename TReceiver> // fail(b, req_id)
RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T, mtpRequestId), T b) {
return RPCFailHandlerPtr(new RPCBindedFailHandlerOwnedNo<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
}
void regHandler(RPCOwnedDoneHandler *handler) {
doneHandlers.insert(handler);
}
void unregHandler(RPCOwnedDoneHandler *handler) {
doneHandlers.remove(handler);
}
void regHandler(RPCOwnedFailHandler *handler) {
failHandlers.insert(handler);
}
void unregHandler(RPCOwnedFailHandler *handler) {
failHandlers.remove(handler);
}
~RPCSender() {
for (DoneHandlers::iterator i = doneHandlers.begin(), e = doneHandlers.end(); i != e; ++i) {
(*i)->invalidate();
}
for (FailHandlers::iterator i = failHandlers.begin(), e = failHandlers.end(); i != e; ++i) {
(*i)->invalidate();
}
}
};
typedef void (*MTPStateChangedHandler)(int32 dcId, int32 state);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,439 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include <QtCore/QSharedPointer>
void MTPSessionData::clear() {
RPCCallbackClears clearCallbacks;
{
QReadLocker locker1(haveSentMutex()), locker2(toResendMutex()), locker3(haveReceivedMutex()), locker4(wereAckedMutex());
mtpResponseMap::const_iterator end = haveReceived.cend();
clearCallbacks.reserve(haveSent.size() + wereAcked.size());
for (mtpRequestMap::const_iterator i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
mtpRequestId requestId = i.value()->requestId;
if (haveReceived.find(requestId) == end) {
clearCallbacks.push_back(requestId);
}
}
for (mtpRequestIdsMap::const_iterator i = toResend.cbegin(), e = toResend.cend(); i != e; ++i) {
mtpRequestId requestId = i.value();
if (haveReceived.find(requestId) == end) {
clearCallbacks.push_back(requestId);
}
}
for (mtpRequestIdsMap::const_iterator i = wereAcked.cbegin(), e = wereAcked.cend(); i != e; ++i) {
mtpRequestId requestId = i.value();
if (haveReceived.find(requestId) == end) {
clearCallbacks.push_back(requestId);
}
}
}
{
QWriteLocker locker(haveSentMutex());
haveSent.clear();
}
{
QWriteLocker locker(toResendMutex());
toResend.clear();
}
{
QWriteLocker locker(wereAckedMutex());
wereAcked.clear();
}
{
QWriteLocker locker(receivedIdsMutex());
receivedIds.clear();
}
_mtp_internal::clearCallbacksDelayed(clearCallbacks);
}
MTProtoSession::MTProtoSession() : data(this), dc(0), dcId(0), msSendCall(0), msWait(0) {
}
void MTProtoSession::start(int32 dcenter, uint32 connects) {
if (dcId) {
DEBUG_LOG(("Session Info: MTProtoSession::start called on already started session"));
return;
}
if (connects < 1) {
connects = cConnectionsInSession();
} else if (connects > 4) {
connects = 4;
}
msSendCall = msWait = 0;
connect(&timeouter, SIGNAL(timeout()), this, SLOT(checkRequestsByTimer()));
timeouter.start(1000);
connect(&sender, SIGNAL(timeout()), this, SIGNAL(needToSend()));
connect(this, SIGNAL(startSendTimer(int)), &sender, SLOT(start(int)));
connect(this, SIGNAL(stopSendTimer()), &sender, SLOT(stop()));
connect(this, SIGNAL(needToSendAsync()), this, SIGNAL(needToSend()));
sender.setSingleShot(true);
MTProtoDCMap &dcs(mtpDCMap());
connections.reserve(connects);
for (uint32 i = 0; i < connects; ++i) {
connections.push_back(new MTProtoConnection());
dcId = connections.back()->start(&data, dcenter);
if (!dcId) {
for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) {
delete *j;
}
connections.clear();
DEBUG_LOG(("Session Info: could not start connection %1 to dc %2").arg(i).arg(dcenter));
return;
}
if (!dc) {
dcenter = dcId;
MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId % _mtp_internal::dcShift);
if (dcIndex == dcs.cend()) {
dc = MTProtoDCPtr(new MTProtoDC(dcId % _mtp_internal::dcShift, mtpAuthKeyPtr()));
dcs.insert(dcId % _mtp_internal::dcShift, dc);
} else {
dc = dcIndex.value();
}
ReadLockerAttempt lock(keyMutex());
data.setKey(lock ? dc->getKey() : mtpAuthKeyPtr(0));
connect(dc.data(), SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()));
}
}
}
void MTProtoSession::restart() {
for (MTProtoConnections::const_iterator i = connections.cbegin(), e = connections.cend(); i != e; ++i) {
(*i)->restart();
}
}
void MTProtoSession::stop() {
while (connections.size()) {
connections.back()->stop();
connections.pop_back();
}
}
void MTProtoSession::checkRequestsByTimer() {
MTPMsgsStateReq stateRequest(MTP_msgs_state_req(MTP_vector<MTPlong>(0)));
QVector<MTPlong> &stateRequestIds(stateRequest._msgs_state_req().vmsg_ids._vector().v);
QVector<mtpMsgId> resendingIds;
QVector<mtpMsgId> removingIds; // remove very old (10 minutes) containers and resend requests
{
QReadLocker locker(data.haveSentMutex());
mtpRequestMap &haveSent(data.haveSentMap());
uint32 haveSentCount(haveSent.size());
uint64 ms = getms();
for (mtpRequestMap::iterator i = haveSent.begin(), e = haveSent.end(); i != e; ++i) {
mtpRequest &req(i.value());
if (req->msDate > 0) {
if (req->msDate + MTPCheckResendTimeout < ms) { // need to resend or check state
if (mtpRequestData::messageSize(req) < MTPResendThreshold) { // resend
resendingIds.reserve(haveSentCount);
resendingIds.push_back(i.key());
} else {
req->msDate = ms;
stateRequestIds.reserve(haveSentCount);
stateRequestIds.push_back(MTP_long(i.key()));
}
}
} else if (unixtime() > (uint32)(i.key() >> 32) + MTPContainerLives) {
removingIds.reserve(haveSentCount);
removingIds.push_back(i.key());
}
}
}
if (stateRequestIds.size()) {
DEBUG_LOG(("MTP Info: requesting state of msgs: %1").arg(logVectorLong(stateRequestIds)));
send(stateRequest, RPCResponseHandler(), MTPCheckResendWaiting);
}
for (uint32 i = 0, l = resendingIds.size(); i < l; ++i) {
DEBUG_LOG(("MTP Info: resending request %1").arg(resendingIds[i]));
resend(resendingIds[i], MTPCheckResendWaiting);
}
uint32 removingIdsCount = removingIds.size();
if (removingIdsCount) {
RPCCallbackClears clearCallbacks;
{
QWriteLocker locker(data.haveSentMutex());
mtpRequestMap &haveSent(data.haveSentMap());
for (uint32 i = 0; i < removingIdsCount; ++i) {
mtpRequestMap::iterator j = haveSent.find(removingIds[i]);
if (j != haveSent.cend()) {
if (j.value()->requestId) {
clearCallbacks.push_back(j.value()->requestId);
}
haveSent.erase(j);
}
}
}
_mtp_internal::clearCallbacksDelayed(clearCallbacks);
}
}
void MTProtoSession::onConnectionStateChange(qint32 newState) {
_mtp_internal::onStateChange(dcId, newState);
}
void MTProtoSession::cancel(mtpRequestId requestId) {
QWriteLocker locker(data.toSendMutex());
mtpPreRequestMap &toSend(data.toSendMap());
mtpPreRequestMap::iterator i = toSend.find(requestId);
if (i != toSend.end()) toSend.erase(i);
}
int32 MTProtoSession::requestState(mtpRequestId requestId) const {
MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend();
int32 result = MTP::RequestSent;
for (; j != e; ++j) {
int32 s = (*j)->state();
if (s == MTProtoConnection::Connected) {
break;
} else if (s == MTProtoConnection::Connecting || s == MTProtoConnection::Disconnected) {
if (result < 0 || result == MTP::RequestSent) {
result = MTP::RequestConnecting;
}
} else if (s < 0) {
if (result < 0 && s > result || result == MTP::RequestSent) {
result = s;
}
}
}
if (j == e) { // no one is connected
return result;
}
if (!requestId) return MTP::RequestSent;
QWriteLocker locker(data.toSendMutex());
const mtpPreRequestMap &toSend(data.toSendMap());
mtpPreRequestMap::const_iterator i = toSend.constFind(requestId);
if (i != toSend.cend()) {
return MTP::RequestSending;
} else {
return MTP::RequestSent;
}
}
int32 MTProtoSession::getState() const {
MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend();
int32 result = -86400000;
for (; j != e; ++j) {
int32 s = (*j)->state();
if (s == MTProtoConnection::Connected) {
return s;
} else if (s == MTProtoConnection::Connecting || s == MTProtoConnection::Disconnected) {
if (result < 0) {
return s;
}
} else if (s < 0) {
if (result < 0 && s > result) {
result = s;
}
}
}
if (result == -86400000) {
result = MTProtoConnection::Disconnected;
}
return result;
}
QString MTProtoSession::transport() const {
MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend();
for (; j != e; ++j) {
QString s = (*j)->transport();
if (!s.isEmpty()) return s;
}
return QString();
}
mtpRequestId MTProtoSession::resend(mtpMsgId msgId, uint64 msCanWait, bool forceContainer, bool sendMsgStateInfo) {
mtpRequest request;
{
QWriteLocker locker(data.haveSentMutex());
mtpRequestMap &haveSent(data.haveSentMap());
mtpRequestMap::iterator i = haveSent.find(msgId);
if (i == haveSent.end()) {
if (sendMsgStateInfo) {
char cantResend[2] = {1, 0};
DEBUG_LOG(("Message Info: cant resend %1, request not found").arg(msgId));
return send(MTP_msgs_state_info(MTP_long(msgId), MTP_string(string(cantResend, cantResend + 1))));
}
return 0;
}
request = i.value();
haveSent.erase(i);
}
if (mtpRequestData::isSentContainer(request)) { // for container just resend all messages we can
DEBUG_LOG(("Message Info: resending container from haveSent, msgId %1").arg(msgId));
const mtpMsgId *ids = (const mtpMsgId *)(request->constData() + 8);
for (uint32 i = 0, l = (request->size() - 8) >> 1; i < l; ++i) {
resend(ids[i], 10, true);
}
return 0xFFFFFFFF;
} else if (!mtpRequestData::isStateRequest(request)) {
request->msDate = forceContainer ? 0 : getms();
sendPrepared(request, msCanWait, false);
{
QWriteLocker locker(data.toResendMutex());
data.toResendMap().insert(msgId, request->requestId);
}
return request->requestId;
} else {
return 0;
}
}
void MTProtoSession::resendAll() {
QVector<mtpMsgId> toResend;
{
QReadLocker locker(data.haveSentMutex());
const mtpRequestMap &haveSent(data.haveSentMap());
toResend.reserve(haveSent.size());
for (mtpRequestMap::const_iterator i = haveSent.cbegin(), e = haveSent.cend(); i != e; ++i) {
if (i.value()->requestId) toResend.push_back(i.key());
}
}
for (uint32 i = 0, l = toResend.size(); i < l; ++i) {
resend(toResend[i], 10, true);
}
}
void MTProtoSession::sendPrepared(const mtpRequest &request, uint64 msCanWait, bool newRequest) { // returns true, if emit of needToSend() is needed
{
QWriteLocker locker(data.toSendMutex());
data.toSendMap().insert(request->requestId, request);
if (newRequest) {
*(mtpMsgId*)(request->data() + 4) = 0;
*(request->data() + 6) = 0;
}
}
DEBUG_LOG(("MTP Info: added, requestId %1").arg(request->requestId));
uint64 ms = getms();
if (msSendCall) {
if (ms > msSendCall + msWait) {
msWait = 0;
} else {
msWait = (msSendCall + msWait) - ms;
if (msWait > msCanWait) {
msWait = msCanWait;
}
}
} else {
msWait = msCanWait;
}
if (msWait) {
msSendCall = ms;
emit startSendTimer(msWait);
DEBUG_LOG(("MTP Info: can wait for %1ms from current %2").arg(msWait).arg(msSendCall));
} else {
emit stopSendTimer();
msSendCall = 0;
emit needToSendAsync();
}
}
void MTProtoSession::sendPreparedWithInit(const mtpRequest &request, uint64 msCanWait) { // returns true, if emit of needToSend() is needed
if (request->size() > 8 && request->at(8) == mtpc_initConnection) {
sendPrepared(request, msCanWait, false);
return;
}
MTPInitConnection<mtpRequest> requestWrap(MTPinitConnection<mtpRequest>(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request));
uint32 requestSize = requestWrap.size() >> 2;
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize));
requestWrap.write(*reqSerialized);
reqSerialized->msDate = getms(); // > 0 - can send without container
_mtp_internal::replaceRequest(reqSerialized, request);
sendPrepared(reqSerialized, msCanWait);
}
QReadWriteLock *MTProtoSession::keyMutex() const {
return dc->keyMutex();
}
void MTProtoSession::authKeyCreatedForDC() {
DEBUG_LOG(("AuthKey Info: MTProtoSession::authKeyCreatedForDC slot, emitting authKeyCreated(), dc %1").arg(dcId));
data.setKey(dc->getKey());
emit authKeyCreated();
}
void MTProtoSession::keyCreated(const mtpAuthKeyPtr &key) {
DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dc %1").arg(dcId));
dc->setKey(key);
}
void MTProtoSession::destroyKey() {
if (!dc) return;
if (data.getKey()) {
DEBUG_LOG(("MTP Info: destroying auth_key for dc %1").arg(dcId));
if (data.getKey() == dc->getKey()) {
dc->destroyKey();
}
data.setKey(mtpAuthKeyPtr(0));
}
}
int32 MTProtoSession::getDC() const {
return dcId;
}
void MTProtoSession::tryToReceive() {
while (true) {
mtpRequestId requestId;
mtpResponse response;
{
QWriteLocker locker(data.haveReceivedMutex());
mtpResponseMap &responses(data.haveReceivedMap());
mtpResponseMap::iterator i = responses.begin();
if (i == responses.end()) return;
requestId = i.key();
response = i.value();
responses.erase(i);
}
if (requestId <= 0) {
_mtp_internal::globalCallback(response.constData(), response.constData() + response.size());
} else {
_mtp_internal::execCallback(requestId, response.constData(), response.constData() + response.size());
}
}
}
MTProtoSession::~MTProtoSession() {
for (MTProtoConnections::const_iterator i = connections.cbegin(), e = connections.cend(); i != e; ++i) {
delete *i;
}
}
MTPrpcError rpcClientError(const QString &type, const QString &description) {
return MTP_rpc_error(MTP_int(0), MTP_string(("CLIENT_" + type + (description.length() ? (": " + description) : "")).toUtf8().constData()));
}

View File

@@ -0,0 +1,272 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
#include "mtpConnection.h"
#include "mtpDC.h"
#include "mtpRPC.h"
class MTProtoSession;
class MTPSessionData {
public:
MTPSessionData(MTProtoSession *creator)
: _session(0), _salt(0), fakeRequestId(-2000000000)
, _messagesSent(0), keyChecked(false)
, _owner(creator) {
}
void setSession(uint64 session) {
DEBUG_LOG(("MTP Info: setting server_session: %1").arg(session));
QWriteLocker locker(&lock);
if (_session != session) {
_session = session;
_messagesSent = 0;
}
}
uint64 getSession() const {
QReadLocker locker(&lock);
return _session;
}
void setSalt(uint64 salt) {
QWriteLocker locker(&lock);
_salt = salt;
}
uint64 getSalt() const {
QReadLocker locker(&lock);
return _salt;
}
const mtpAuthKeyPtr &getKey() const {
return authKey;
}
void setKey(const mtpAuthKeyPtr &key) {
if (authKey != key) {
uint64 session;
memset_rand(&session, sizeof(uint64));
authKey = key;
DEBUG_LOG(("MTP Info: new auth key set in SessionData, id %1, setting random server_session %2").arg(key ? key->keyId() : 0).arg(session));
setSession(session);
}
}
bool isCheckedKey() const {
QReadLocker locker(&lock);
return keyChecked;
}
void setCheckedKey(bool checked) {
QWriteLocker locker(&lock);
keyChecked = checked;
}
QReadWriteLock *keyMutex() const;
QReadWriteLock *toSendMutex() const {
return &toSendLock;
}
QReadWriteLock *haveSentMutex() const {
return &haveSentLock;
}
QReadWriteLock *toResendMutex() const {
return &toResendLock;
}
QReadWriteLock *wereAckedMutex() const {
return &wereAckedLock;
}
QReadWriteLock *receivedIdsMutex() const {
return &receivedIdsLock;
}
QReadWriteLock *haveReceivedMutex() const {
return &haveReceivedLock;
}
mtpPreRequestMap &toSendMap() {
return toSend;
}
const mtpPreRequestMap &toSendMap() const {
return toSend;
}
mtpRequestMap &haveSentMap() {
return haveSent;
}
const mtpRequestMap &haveSentMap() const {
return haveSent;
}
mtpRequestIdsMap &toResendMap() { // msgId -> requestId, on which toSend: requestId -> request for resended requests
return toResend;
}
const mtpRequestIdsMap &toResendMap() const {
return toResend;
}
mtpMsgIdsSet &receivedIdsSet() {
return receivedIds;
}
const mtpMsgIdsSet &receivedIdsSet() const {
return receivedIds;
}
mtpRequestIdsMap &wereAckedMap() {
return wereAcked;
}
const mtpRequestIdsMap &wereAckedMap() const {
return wereAcked;
}
mtpResponseMap &haveReceivedMap() {
return haveReceived;
}
const mtpResponseMap &haveReceivedMap() const {
return haveReceived;
}
mtpRequestId nextFakeRequestId() { // must be locked by haveReceivedMutex()
if (haveReceived.isEmpty() || haveReceived.cbegin().key() > 0) {
fakeRequestId = -2000000000;
} else {
++fakeRequestId;
}
return fakeRequestId;
}
MTProtoSession *owner() {
return _owner;
}
const MTProtoSession *owner() const {
return _owner;
}
uint32 nextRequestSeqNumber(bool needAck = true) {
QWriteLocker locker(&lock);
uint32 result(_messagesSent);
_messagesSent += (needAck ? 1 : 0);
return result * 2 + (needAck ? 1 : 0);
}
void clear();
private:
uint64 _session, _salt;
uint32 _messagesSent;
mtpRequestId fakeRequestId;
MTProtoSession *_owner;
mtpAuthKeyPtr authKey;
bool keyChecked;
mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent
mtpRequestMap haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers
mtpRequestIdsMap toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent
mtpMsgIdsSet receivedIds; // set of received msg_id's, for checking new msg_ids
mtpRequestIdsMap wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack
mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread
// mutexes
mutable QReadWriteLock lock;
mutable QReadWriteLock toSendLock;
mutable QReadWriteLock haveSentLock;
mutable QReadWriteLock toResendLock;
mutable QReadWriteLock receivedIdsLock;
mutable QReadWriteLock wereAckedLock;
mutable QReadWriteLock haveReceivedLock;
};
class MTProtoSession : public QObject {
Q_OBJECT
public:
MTProtoSession();
void start(int32 dcenter = 0, uint32 connects = 0);
void restart();
void stop();
int32 getDC() const;
~MTProtoSession();
QReadWriteLock *keyMutex() const;
void keyCreated(const mtpAuthKeyPtr &key);
void destroyKey();
template <typename TRequest>
mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false); // send mtp request
void cancel(mtpRequestId requestId);
int32 requestState(mtpRequestId requestId) const;
int32 getState() const;
QString transport() const;
mtpRequestId resend(mtpMsgId msgId, uint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
void resendAll(); // after connection restart
void sendPrepared(const mtpRequest &request, uint64 msCanWait = 0, bool newRequest = true); // nulls msgId and seqNo in request, if newRequest = true
void sendPreparedWithInit(const mtpRequest &request, uint64 msCanWait = 0);
signals:
void authKeyCreated();
void needToSend();
void needToSendAsync(); // emit this signal, to emit needToSend() in MTProtoSession thread
void startSendTimer(int msec); // manipulating timer from all threads
void stopSendTimer();
public slots:
void authKeyCreatedForDC();
void tryToReceive();
void checkRequestsByTimer();
void onConnectionStateChange(qint32 newState);
private:
template <typename TRequest>
mtpRequestId sendFirst(const MTPInitConnection<TRequest> &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false); // send first mtp request
typedef QList<MTProtoConnection*> MTProtoConnections;
MTProtoConnections connections;
MTPSessionData data;
int32 dcId;
MTProtoDCPtr dc;
uint64 msSendCall, msWait;
QTimer timeouter;
QTimer sender;
};
inline QReadWriteLock *MTPSessionData::keyMutex() const {
return _owner->keyMutex();
}
MTPrpcError rpcClientError(const QString &type, const QString &description = QString());
// here
typedef QSharedPointer<MTProtoSession> MTProtoSessionPtr;

View File

@@ -0,0 +1,86 @@
/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
template <typename TRequest>
mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC) {
mtpRequestId requestId = 0;
if (layer && dc->needConnectionInit()) {
MTPInitConnection<TRequest> requestWrap(MTPinitConnection<TRequest>(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request));
return sendFirst(requestWrap, callbacks, msCanWait, layer, toMainDC);
}
try {
uint32 requestSize = request.size() >> 2;
if (dc->connectionInited()) layer = 0;
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize + (layer ? 1 : 0)));
if (layer) reqSerialized->push_back(mtpLayers[layer]);
request.write(*reqSerialized);
DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1").arg(msCanWait));
reqSerialized->msDate = getms(); // > 0 - can send without container
requestId = _mtp_internal::storeRequest(reqSerialized, callbacks);
sendPrepared(reqSerialized, msCanWait);
} catch (Exception &e) {
requestId = 0;
_mtp_internal::rpcErrorOccured(requestId, callbacks, rpcClientError("NO_REQUEST_ID", QString("send() failed to queue request, exception: %1").arg(e.what())));
}
if (requestId) _mtp_internal::registerRequest(requestId, toMainDC ? -getDC() : getDC());
return requestId;
}
class RPCWrappedDcDoneHandler : public RPCAbstractDoneHandler {
public:
RPCWrappedDcDoneHandler(const MTProtoDCPtr &dc, const RPCDoneHandlerPtr &ondone) : _dc(dc), _ondone(ondone) {
}
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
_dc->setConnectionInited();
return (*_ondone)(requestId, from, end);
}
private:
MTProtoDCPtr _dc;
RPCDoneHandlerPtr _ondone;
};
template <typename TRequest>
mtpRequestId MTProtoSession::sendFirst(const MTPInitConnection<TRequest> &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC) {
mtpRequestId requestId = 0;
try {
uint32 requestSize = request.size() >> 2;
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize + (layer ? 1 : 0)));
if (layer) reqSerialized->push_back(mtpLayers[layer]);
request.write(*reqSerialized);
DEBUG_LOG(("MTP Info: adding wrapped to init connection request to toSendMap, msCanWait %1").arg(msCanWait));
callbacks.onDone = RPCDoneHandlerPtr(new RPCWrappedDcDoneHandler(dc, callbacks.onDone));
reqSerialized->msDate = getms(); // > 0 - can send without container
requestId = _mtp_internal::storeRequest(reqSerialized, callbacks);
sendPrepared(reqSerialized, msCanWait);
} catch (Exception &e) {
requestId = 0;
_mtp_internal::rpcErrorOccured(requestId, callbacks, rpcClientError("NO_REQUEST_ID", QString("sendFirst() failed to queue request, exception: %1").arg(e.what())));
}
if (requestId) {
_mtp_internal::registerRequest(requestId, toMainDC ? -getDC() : getDC());
}
return requestId;
}

View File

@@ -0,0 +1,625 @@
// Core types (no need to gen)
//boolFalse#bc799737 = Bool;
//boolTrue#997275b5 = Bool;
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
//error#c4b9f9bb code:int text:string = Error;
//null#56730bcc = Null;
///////////////////////////////
/////////////////// Layer cons
///////////////////////////////
//invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
//invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X;
//invokeWithLayer1#53835315 query:!X = X;
//invokeWithLayer2#289dd1f6 query:!X = X;
//invokeWithLayer3#b7475268 query:!X = X;
//invokeWithLayer4#dea0d430 query:!X = X;
//invokeWithLayer5#417a57ae query:!X = X;
//invokeWithLayer6#3a64d54d query:!X = X;
//invokeWithLayer7#a5be56d3 query:!X = X;
//invokeWithLayer8#e9abd9fd query:!X = X;
//invokeWithLayer9#76715a63 query:!X = X;
//invokeWithLayer10#39620c41 query:!X = X;
//invokeWithLayer11#a6b88fdf query:!X = X;
//invokeWithLayer12#dda60d3c query:!X = X;
//invokeWithLayer13#427c8ea2 query:!X = X;
//invokeWithLayer14#2b9b08fa query:!X = X;
///////////////////////////////
/// Authorization key creation
///////////////////////////////
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
---functions---
req_pq#60469778 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
///////////////////////////////
////////////// System messages
///////////////////////////////
---types---
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
//rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
future_salts#ae500895 req_msg_id:long now:int salts:vector<futureSalt> = FutureSalts;
pong#347773c5 msg_id:long ping_id:long = Pong;
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
//message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually
//msg_container#73f1f8dc messages:vector<message> = MessageContainer; // parsed manually
//msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container
//gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
---functions---
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
get_future_salts#b921bd04 num:int = FutureSalts;
ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
destroy_session#e7512126 session_id:long = DestroySessionRes;
register.saveDeveloperInfo#9a5f6e95 name:string email:string phone_number:string age:int city:string = Bool;
///////////////////////////////
///////// Main application API
///////////////////////////////
---types---
inputPeerEmpty#7f3b18ea = InputPeer;
inputPeerSelf#7da07ec9 = InputPeer;
inputPeerContact#1023dbe8 user_id:int = InputPeer;
inputPeerForeign#9b447325 user_id:int access_hash:long = InputPeer;
inputPeerChat#179be863 chat_id:int = InputPeer;
inputUserEmpty#b98886cf = InputUser;
inputUserSelf#f7c1b13f = InputUser;
inputUserContact#86e94f65 user_id:int = InputUser;
inputUserForeign#655e74ff user_id:int access_hash:long = InputUser;
inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_name:string = InputContact;
inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#2dc53a7d file:InputFile = InputMedia;
inputMediaPhoto#8f2ab2ec id:InputPhoto = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
inputMediaUploadedVideo#133ad6f6 file:InputFile duration:int w:int h:int mime_type:string = InputMedia;
inputMediaUploadedThumbVideo#9912dabf file:InputFile thumb:InputFile duration:int w:int h:int mime_type:string = InputMedia;
inputMediaVideo#7f023ae6 id:InputVideo = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#94254732 file:InputFile crop:InputPhotoCrop = InputChatPhoto;
inputChatPhoto#b2e1bf08 id:InputPhoto crop:InputPhotoCrop = InputChatPhoto;
inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
inputPhotoEmpty#1cd7bf0d = InputPhoto;
inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto;
inputVideoEmpty#5508ec75 = InputVideo;
inputVideo#ee579652 id:long access_hash:long = InputVideo;
inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation;
inputVideoFileLocation#3d0364ec id:long access_hash:long = InputFileLocation;
inputPhotoCropAuto#ade6b004 = InputPhotoCrop;
inputPhotoCrop#d9915325 crop_left:double crop_top:double crop_width:double = InputPhotoCrop;
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
peerUser#9db1bc6d user_id:int = Peer;
peerChat#bad0e5bb chat_id:int = Peer;
storage.fileUnknown#aa963b05 = storage.FileType;
storage.fileJpeg#7efe0e = storage.FileType;
storage.fileGif#cae1aadf = storage.FileType;
storage.filePng#a4f63c0 = storage.FileType;
storage.filePdf#ae1e508d = storage.FileType;
storage.fileMp3#528a0677 = storage.FileType;
storage.fileMov#4b09ebbc = storage.FileType;
storage.filePartial#40bc6f52 = storage.FileType;
storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
userEmpty#200250ba id:int = User;
userSelf#720535ec id:int first_name:string last_name:string phone:string photo:UserProfilePhoto status:UserStatus inactive:Bool = User;
userContact#f2fb8319 id:int first_name:string last_name:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
userRequest#22e8ceb0 id:int first_name:string last_name:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
userForeign#5214c89d id:int first_name:string last_name:string access_hash:long photo:UserProfilePhoto status:UserStatus = User;
userDeleted#b29ad7cc id:int first_name:string last_name:string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
userStatusEmpty#9d05049 = UserStatus;
userStatusOnline#edb93949 expires:int = UserStatus;
userStatusOffline#8c703f was_online:int = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#6e9c9bc7 id:int title:string photo:ChatPhoto participants_count:int date:int left:Bool version:int = Chat;
chatForbidden#fb0ccc41 id:int title:string date:int = Chat;
chatFull#630e61be id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantsForbidden#fd2bb8a chat_id:int = ChatParticipants;
chatParticipants#7841b415 chat_id:int admin_id:int participants:Vector<ChatParticipant> version:int = ChatParticipants;
chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#22eb6aba id:int from_id:int to_id:Peer out:Bool unread:Bool date:int message:string media:MessageMedia = Message;
messageForwarded#5f46804 id:int fwd_from_id:int fwd_date:int from_id:int to_id:Peer out:Bool unread:Bool date:int message:string media:MessageMedia = Message;
messageService#9f8d60bb id:int from_id:int to_id:Peer out:Bool unread:Bool date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#c8c45a2a photo:Photo = MessageMedia;
messageMediaVideo#a2d24290 video:Video = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
messageMediaUnsupported#29632a36 bytes:bytes = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction;
messageActionChatDeletePhoto#95e3fbef = MessageAction;
messageActionChatAddUser#5e3cfc4b user_id:int = MessageAction;
messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction;
dialog#ab3a99ac peer:Peer top_message:int unread_count:int notify_settings:PeerNotifySettings = Dialog;
photoEmpty#2331b22d id:long = Photo;
photo#22b56751 id:long access_hash:long user_id:int date:int caption:string geo:GeoPoint sizes:Vector<PhotoSize> = Photo;
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
videoEmpty#c10658a8 id:long = Video;
video#388fa391 id:long access_hash:long user_id:int date:int caption:string duration:int mime_type:string size:int thumb:PhotoSize dc_id:int w:int h:int = Video;
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#2049d70c long:double lat:double = GeoPoint;
auth.checkedPhone#e300cc3b phone_registered:Bool phone_invited:Bool = auth.CheckedPhone;
auth.sentCode#efed51d9 phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode;
auth.authorization#f6b673a4 expires:int user:User = auth.Authorization;
auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
inputNotifyUsers#193b4417 = InputNotifyPeer;
inputNotifyChats#4a95e84e = InputNotifyPeer;
inputNotifyAll#a429b886 = InputNotifyPeer;
inputPeerNotifyEventsEmpty#f03064d8 = InputPeerNotifyEvents;
inputPeerNotifyEventsAll#e86a2c74 = InputPeerNotifyEvents;
inputPeerNotifySettings#46a2ce98 mute_until:int sound:string show_previews:Bool events_mask:int = InputPeerNotifySettings;
peerNotifyEventsEmpty#add53cb3 = PeerNotifyEvents;
peerNotifyEventsAll#6d1ded88 = PeerNotifyEvents;
peerNotifySettingsEmpty#70a68512 = PeerNotifySettings;
peerNotifySettings#8d5e11ee mute_until:int sound:string show_previews:Bool events_mask:int = PeerNotifySettings;
wallPaper#ccb03657 id:int title:string sizes:Vector<PhotoSize> color:int = WallPaper;
userFull#771095da user:User link:contacts.Link profile_photo:Photo notify_settings:PeerNotifySettings blocked:Bool real_first_name:string real_last_name:string = UserFull;
contact#f911c994 user_id:int mutual:Bool = Contact;
importedContact#d0028438 user_id:int client_id:long = ImportedContact;
contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
contactFound#ea879f95 user_id:int = ContactFound;
contactSuggested#3de191a1 user_id:int mutual_contacts:int = ContactSuggested;
contactStatus#aa77b873 user_id:int expires:int = ContactStatus;
chatLocated#3631cf4c chat_id:int distance:int = ChatLocated;
contacts.foreignLinkUnknown#133421f8 = contacts.ForeignLink;
contacts.foreignLinkRequested#a7801f47 has_phone:Bool = contacts.ForeignLink;
contacts.foreignLinkMutual#1bea8ce1 = contacts.ForeignLink;
contacts.myLinkEmpty#d22a1c60 = contacts.MyLink;
contacts.myLinkRequested#6c69efee contact:Bool = contacts.MyLink;
contacts.myLinkContact#c240ebd9 = contacts.MyLink;
contacts.link#eccea3f5 my_link:contacts.MyLink foreign_link:contacts.ForeignLink user:User = contacts.Link;
contacts.contacts#6f8b8cb2 contacts:Vector<Contact> users:Vector<User> = contacts.Contacts;
contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
contacts.importedContacts#ad524315 imported:Vector<ImportedContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;
contacts.blocked#1c138d15 blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
contacts.found#566000e results:Vector<ContactFound> users:Vector<User> = contacts.Found;
contacts.suggested#5649dcc5 results:Vector<ContactSuggested> users:Vector<User> = contacts.Suggested;
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesSlice#b446ae3 count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messageEmpty#3f4e0648 = messages.Message;
messages.message#ff90c417 message:Message chats:Vector<Chat> users:Vector<User> = messages.Message;
messages.statedMessages#969478bb messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int seq:int = messages.StatedMessages;
messages.statedMessage#d07ae726 message:Message chats:Vector<Chat> users:Vector<User> pts:int seq:int = messages.StatedMessage;
messages.sentMessage#d1f4d35c id:int date:int pts:int seq:int = messages.SentMessage;
messages.chat#40e9002a chat:Chat users:Vector<User> = messages.Chat;
messages.chats#8150cbd8 chats:Vector<Chat> users:Vector<User> = messages.Chats;
messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector<Chat> users:Vector<User> = messages.ChatFull;
messages.affectedHistory#b7de36f2 pts:int seq:int offset:int = messages.AffectedHistory;
inputMessagesFilterEmpty#57e2f66c = MessagesFilter;
inputMessagesFilterPhotos#9609a51c = MessagesFilter;
inputMessagesFilterVideo#9fc00e65 = MessagesFilter;
inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
inputMessagesFilterDocument#9eddf188 = MessagesFilter;
updateNewMessage#13abdb3 message:Message pts:int = Update;
updateMessageID#4e90bfd6 id:int random_id:long = Update;
updateReadMessages#c6649e31 messages:Vector<int> pts:int = Update;
updateDeleteMessages#a92bfe26 messages:Vector<int> pts:int = Update;
updateRestoreMessages#d15de04d messages:Vector<int> pts:int = Update;
updateUserTyping#6baa8508 user_id:int = Update;
updateChatUserTyping#3c46cfe6 chat_id:int user_id:int = Update;
updateChatParticipants#7761198 participants:ChatParticipants = Update;
updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
updateUserName#da22d9ad user_id:int first_name:string last_name:string = Update;
updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
updateContactRegistered#2575bbb9 user_id:int date:int = Update;
updateContactLink#51a48a9a user_id:int my_link:contacts.MyLink foreign_link:contacts.ForeignLink = Update;
updateActivation#6f690963 user_id:int = Update;
updateNewAuthorization#8f06529a auth_key_id:long date:int device:string location:string = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference;
updatesTooLong#e317af7e = Updates;
updateShortMessage#d3f45784 id:int from_id:int message:string pts:int date:int seq:int = Updates;
updateShortChatMessage#2b2fbd4e id:int from_id:int chat_id:int message:string pts:int date:int seq:int = Updates;
updateShort#78d4dec1 update:Update date:int = Updates;
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
photos.photos#8dca6aa5 photos:Vector<Photo> users:Vector<User> = photos.Photos;
photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> = photos.Photos;
photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
dcOption#2ec2a43c id:int hostname:string ip_address:string port:int = DcOption;
config#2e54dd74 date:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int broadcast_size_max:int = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate;
help.noAppUpdate#c45a6536 = help.AppUpdate;
help.inviteText#18cb9f78 message:string = help.InviteText;
messages.statedMessagesLinks#3e74f5c6 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> links:Vector<contacts.Link> pts:int seq:int = messages.StatedMessages;
messages.statedMessageLink#a9af2881 message:Message chats:Vector<Chat> users:Vector<User> links:Vector<contacts.Link> pts:int seq:int = messages.StatedMessage;
messages.sentMessageLink#e9db4a3f id:int date:int pts:int seq:int links:Vector<contacts.Link> = messages.SentMessage;
inputGeoChat#74d456fa chat_id:int access_hash:long = InputGeoChat;
inputNotifyGeoChatPeer#4d8ddec8 peer:InputGeoChat = InputNotifyPeer;
geoChat#75eaea5a id:int access_hash:long title:string address:string venue:string geo:GeoPoint photo:ChatPhoto participants_count:int date:int checked_in:Bool version:int = Chat;
geoChatMessageEmpty#60311a9b chat_id:int id:int = GeoChatMessage;
geoChatMessage#4505f8e1 chat_id:int id:int from_id:int date:int message:string media:MessageMedia = GeoChatMessage;
geoChatMessageService#d34fa24e chat_id:int id:int from_id:int date:int action:MessageAction = GeoChatMessage;
geochats.statedMessage#17b1578b message:GeoChatMessage chats:Vector<Chat> users:Vector<User> seq:int = geochats.StatedMessage;
geochats.located#48feb267 results:Vector<ChatLocated> messages:Vector<GeoChatMessage> chats:Vector<Chat> users:Vector<User> = geochats.Located;
geochats.messages#d1526db1 messages:Vector<GeoChatMessage> chats:Vector<Chat> users:Vector<User> = geochats.Messages;
geochats.messagesSlice#bc5863e8 count:int messages:Vector<GeoChatMessage> chats:Vector<Chat> users:Vector<User> = geochats.Messages;
messageActionGeoChatCreate#6f038ebc title:string address:string = MessageAction;
messageActionGeoChatCheckin#c7d53de = MessageAction;
updateNewGeoChatMessage#5a68e3f7 message:GeoChatMessage = Update;
wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper;
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update;
updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update;
encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat;
encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat;
encryptedChatRequested#c878527e id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat;
encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat;
encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat;
inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat;
encryptedFileEmpty#c21f497e = EncryptedFile;
encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile;
inputEncryptedFileEmpty#1837c364 = InputEncryptedFile;
inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile;
inputEncryptedFile#5a17b5e5 id:long access_hash:long = InputEncryptedFile;
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
encryptedMessage#ed18c118 random_id:long chat_id:int date:int bytes:bytes file:EncryptedFile = EncryptedMessage;
encryptedMessageService#23734b06 random_id:long chat_id:int date:int bytes:bytes = EncryptedMessage;
decryptedMessageLayer#99a438cf layer:int message:DecryptedMessage = DecryptedMessageLayer;
decryptedMessage#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
decryptedMessageService#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
decryptedMessageMediaPhoto#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
messages.dhConfigNotModified#c0e24635 random:bytes = messages.DhConfig;
messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhConfig;
messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage;
messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage;
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
inputEncryptedFileBigUploaded#2dc173c8 id:long parts:int key_fingerprint:int = InputEncryptedFile;
updateChatParticipantAdd#3a0eeb22 chat_id:int user_id:int inviter_id:int version:int = Update;
updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update;
updateDcOptions#8e5e9873 dc_options:Vector<DcOption> = Update;
inputMediaUploadedAudio#4e498cab file:InputFile duration:int mime_type:string = InputMedia;
inputMediaAudio#89938781 id:InputAudio = InputMedia;
inputMediaUploadedDocument#34e794bd file:InputFile file_name:string mime_type:string = InputMedia;
inputMediaUploadedThumbDocument#3e46de5d file:InputFile thumb:InputFile file_name:string mime_type:string = InputMedia;
inputMediaDocument#d184e841 id:InputDocument = InputMedia;
messageMediaDocument#2fda2204 document:Document = MessageMedia;
messageMediaAudio#c6b68300 audio:Audio = MessageMedia;
inputAudioEmpty#d95adc84 = InputAudio;
inputAudio#77d440ff id:long access_hash:long = InputAudio;
inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#18798952 id:long access_hash:long = InputDocument;
inputAudioFileLocation#74dc404d id:long access_hash:long = InputFileLocation;
inputDocumentFileLocation#4e45abe9 id:long access_hash:long = InputFileLocation;
decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
audioEmpty#586988d8 id:long = Audio;
audio#c7ac6496 id:long access_hash:long user_id:int date:int duration:int mime_type:string size:int dc_id:int = Audio;
documentEmpty#36f8c871 id:long = Document;
document#9efc6326 id:long access_hash:long user_id:int date:int file_name:string mime_type:string size:int thumb:PhotoSize dc_id:int = Document;
help.support#17c6b5f6 phone_number:string user:User = help.Support;
decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction;
decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction;
notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
notifyUsers#b4c83b4c = NotifyPeer;
notifyChats#c007cec3 = NotifyPeer;
notifyAll#74d07c60 = NotifyPeer;
updateUserBlocked#80ece81a user_id:int blocked:Bool = Update;
updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update;
---functions---
invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X;
auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone;
auth.sendCode#768d5f4d phone_number:string sms_type:int api_id:int api_hash:string lang_code:string = auth.SentCode;
auth.sendCall#3c51564 phone_number:string phone_code_hash:string = Bool;
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.logOut#5717da40 = Bool;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.sendInvites#771c1d97 phone_numbers:Vector<string> message:string = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
account.registerDevice#446c712c token_type:int token:string device_model:string system_version:string app_version:string app_sandbox:Bool lang_code:string = Bool;
account.unregisterDevice#65c55b40 token_type:int token:string = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool;
account.updateProfile#f0888d68 first_name:string last_name:string = User;
account.updateStatus#6628562c offline:Bool = Bool;
account.getWallPapers#c04cfac2 = Vector<WallPaper>;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
contacts.getContacts#22c6aa08 hash:string = contacts.Contacts;
contacts.importContacts#da30b32d contacts:Vector<InputContact> replace:Bool = contacts.ImportedContacts;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.getSuggested#cd773428 limit:int = contacts.Suggested;
contacts.deleteContact#8e953744 id:InputUser = contacts.Link;
contacts.deleteContacts#59ab389e id:Vector<InputUser> = Bool;
contacts.block#332b49fc id:InputUser = Bool;
contacts.unblock#e54100bd id:InputUser = Bool;
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
messages.getDialogs#eccf1df6 offset:int max_id:int limit:int = messages.Dialogs;
messages.getHistory#92a1df2f peer:InputPeer offset:int max_id:int limit:int = messages.Messages;
messages.search#7e9f2ab peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages;
messages.readHistory#b04f2510 peer:InputPeer max_id:int offset:int = messages.AffectedHistory;
messages.deleteHistory#f4f8fb61 peer:InputPeer offset:int = messages.AffectedHistory;
messages.deleteMessages#14f2dd0a id:Vector<int> = Vector<int>;
messages.restoreMessages#395f9d7e id:Vector<int> = Vector<int>;
messages.receivedMessages#28abcb68 max_id:int = Vector<int>;
messages.setTyping#719839e9 peer:InputPeer typing:Bool = Bool;
messages.sendMessage#4cde0aab peer:InputPeer message:string random_id:long = messages.SentMessage;
messages.sendMedia#a3c85d76 peer:InputPeer media:InputMedia random_id:long = messages.StatedMessage;
messages.forwardMessages#514cd10f peer:InputPeer id:Vector<int> = messages.StatedMessages;
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
messages.editChatTitle#b4bc68b5 chat_id:int title:string = messages.StatedMessage;
messages.editChatPhoto#d881821d chat_id:int photo:InputChatPhoto = messages.StatedMessage;
messages.addChatUser#2ee9ee9e chat_id:int user_id:InputUser fwd_limit:int = messages.StatedMessage;
messages.deleteChatUser#c3c5cd23 chat_id:int user_id:InputUser = messages.StatedMessage;
messages.createChat#419d9aee users:Vector<InputUser> title:string = messages.StatedMessage;
updates.getState#edd4882a = updates.State;
updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference;
photos.updateProfilePhoto#eef579a0 id:InputPhoto crop:InputPhotoCrop = UserProfilePhoto;
photos.uploadProfilePhoto#d50f9c88 file:InputFile caption:string geo_point:InputGeoPoint crop:InputPhotoCrop = photos.Photo;
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File;
help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
help.getAppUpdate#c812ac7e device_model:string system_version:string app_version:string lang_code:string = help.AppUpdate;
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
help.getInviteText#a4a95186 lang_code:string = help.InviteText;
photos.getUserPhotos#b7ee553c user_id:InputUser offset:int max_id:int limit:int = photos.Photos;
messages.forwardMessage#3f3f4f2 peer:InputPeer id:int random_id:long = messages.StatedMessage;
messages.sendBroadcast#41bb0972 contacts:Vector<InputUser> message:string media:InputMedia = messages.StatedMessages;
geochats.getLocated#7f192d8f geo_point:InputGeoPoint radius:int limit:int = geochats.Located;
geochats.getRecents#e1427e6f offset:int limit:int = geochats.Messages;
geochats.checkin#55b3e8fb peer:InputGeoChat = geochats.StatedMessage;
geochats.getFullChat#6722dd6f peer:InputGeoChat = messages.ChatFull;
geochats.editChatTitle#4c8e2273 peer:InputGeoChat title:string address:string = geochats.StatedMessage;
geochats.editChatPhoto#35d81a95 peer:InputGeoChat photo:InputChatPhoto = geochats.StatedMessage;
geochats.search#cfcdc44d peer:InputGeoChat q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = geochats.Messages;
geochats.getHistory#b53f7a68 peer:InputGeoChat offset:int max_id:int limit:int = geochats.Messages;
geochats.setTyping#8b8a729 peer:InputGeoChat typing:Bool = Bool;
geochats.sendMessage#61b0044 peer:InputGeoChat message:string random_id:long = geochats.StatedMessage;
geochats.sendMedia#b8f0deff peer:InputGeoChat media:InputMedia random_id:long = geochats.StatedMessage;
geochats.createGeoChat#e092e16 title:string geo_point:InputGeoPoint address:string venue:string = geochats.StatedMessage;
messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat;
messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat;
messages.discardEncryption#edd923c5 chat_id:int = Bool;
messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool;
messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool;
messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
initConnection#69796de9 api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X;
help.getSupport#9cdf08cd = help.Support;