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:
605
Telegram/SourceFiles/mtproto/generate.py
Normal file
605
Telegram/SourceFiles/mtproto/generate.py
Normal 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));
|
594
Telegram/SourceFiles/mtproto/mtp.cpp
Normal file
594
Telegram/SourceFiles/mtproto/mtp.cpp
Normal 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);
|
||||
}
|
||||
|
||||
};
|
114
Telegram/SourceFiles/mtproto/mtp.h
Normal file
114
Telegram/SourceFiles/mtproto/mtp.h
Normal 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"
|
151
Telegram/SourceFiles/mtproto/mtpAuthKey.h
Normal file
151
Telegram/SourceFiles/mtproto/mtpAuthKey.h
Normal 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);
|
||||
}
|
2941
Telegram/SourceFiles/mtproto/mtpConnection.cpp
Normal file
2941
Telegram/SourceFiles/mtproto/mtpConnection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
432
Telegram/SourceFiles/mtproto/mtpConnection.h
Normal file
432
Telegram/SourceFiles/mtproto/mtpConnection.h
Normal 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;
|
||||
|
||||
};
|
1146
Telegram/SourceFiles/mtproto/mtpCoreTypes.h
Normal file
1146
Telegram/SourceFiles/mtproto/mtpCoreTypes.h
Normal file
File diff suppressed because it is too large
Load Diff
475
Telegram/SourceFiles/mtproto/mtpDC.cpp
Normal file
475
Telegram/SourceFiles/mtproto/mtpDC.cpp
Normal 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;
|
||||
}
|
109
Telegram/SourceFiles/mtproto/mtpDC.h
Normal file
109
Telegram/SourceFiles/mtproto/mtpDC.h
Normal 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);
|
333
Telegram/SourceFiles/mtproto/mtpFileLoader.cpp
Normal file
333
Telegram/SourceFiles/mtproto/mtpFileLoader.cpp
Normal 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;
|
||||
}
|
||||
}
|
88
Telegram/SourceFiles/mtproto/mtpFileLoader.h
Normal file
88
Telegram/SourceFiles/mtproto/mtpFileLoader.h
Normal 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;
|
||||
|
||||
};
|
82
Telegram/SourceFiles/mtproto/mtpPublicRSA.h
Normal file
82
Telegram/SourceFiles/mtproto/mtpPublicRSA.h
Normal 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;
|
||||
};
|
35
Telegram/SourceFiles/mtproto/mtpRPC.cpp
Normal file
35
Telegram/SourceFiles/mtproto/mtpRPC.cpp
Normal 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);
|
||||
}
|
790
Telegram/SourceFiles/mtproto/mtpRPC.h
Normal file
790
Telegram/SourceFiles/mtproto/mtpRPC.h
Normal 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);
|
24565
Telegram/SourceFiles/mtproto/mtpScheme.h
Normal file
24565
Telegram/SourceFiles/mtproto/mtpScheme.h
Normal file
File diff suppressed because it is too large
Load Diff
439
Telegram/SourceFiles/mtproto/mtpSession.cpp
Normal file
439
Telegram/SourceFiles/mtproto/mtpSession.cpp
Normal 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()));
|
||||
}
|
272
Telegram/SourceFiles/mtproto/mtpSession.h
Normal file
272
Telegram/SourceFiles/mtproto/mtpSession.h
Normal 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;
|
86
Telegram/SourceFiles/mtproto/mtpSessionImpl.h
Normal file
86
Telegram/SourceFiles/mtproto/mtpSessionImpl.h
Normal 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;
|
||||
}
|
625
Telegram/SourceFiles/mtproto/scheme.tl
Normal file
625
Telegram/SourceFiles/mtproto/scheme.tl
Normal 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;
|
Reference in New Issue
Block a user