2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-09-02 23:55:12 +00:00

initial commit for 0.4.18 version of Telegram Desktop

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
/*
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 <QtCore/QMap>
#include <QtCore/QVector>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QBuffer>
#include <QtCore/QDir>
#include <QtCore/QRegularExpression>
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtGui/QPainter>
#include <iostream>
#include <exception>
#include <QtCore/QTextStream>
#include <QtCore/QString>
#include <QtCore/QCoreApplication>
#include <QtGui/QGuiApplication>
using std::string;
using std::cout;
using std::cerr;
using std::exception;
bool genEmoji(QString emoji_in, const QString &emoji_out);
class GenEmoji : public QObject {
Q_OBJECT
public:
GenEmoji(const QString &emoji_in, const QString &emoji_out) : QObject(0),
_emoji_in(emoji_in), _emoji_out(emoji_out) {
}
public slots :
void run() {
if (genEmoji(_emoji_in, _emoji_out)) {
emit finished();
}
}
signals:
void finished();
private:
QString _emoji_in, _emoji_out;
};

View File

@@ -0,0 +1,454 @@
/*
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 "genlang.h"
#include <QtCore/QtPlugin>
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
typedef unsigned int uint32;
QString layoutDirection;
typedef QMap<QString, QString> LangKeys;
LangKeys keys;
typedef QVector<QString> KeysOrder;
KeysOrder keysOrder;
bool skipWhitespaces(const char *&from, const char *end) {
while (from < end && (*from == ' ' || *from == '\n' || *from == '\t' || *from == '\r')) {
++from;
}
return (from < end);
}
bool skipComment(const char *&from, const char *end) {
if (from >= end) return false;
if (*from == '/') {
if (from + 1 >= end) return true;
if (*(from + 1) == '*') {
from += 2;
while (from + 1 < end && (*from != '*' || *(from + 1) != '/')) {
++from;
}
from += 2;
return (from < end);
} else if (*(from + 1) == '/') {
from += 2;
while (from < end && *from != '\n' && *from != '\r') {
++from;
}
++from;
return (from < end);
} else {
return true;
}
}
return true;
}
bool skipJunk(const char *&from, const char *end) {
const char *start;
do {
start = from;
if (!skipWhitespaces(from, end)) return false;
if (!skipComment(from, end)) throw exception("Unexpected end of comment!");
} while (start != from);
return true;
}
void readKeyValue(const char *&from, const char *end) {
if (!skipJunk(from, end)) return;
const char *nameStart = from;
while (from < end && (*from >= 'a' && *from <= 'z' || *from >= 'A' && *from <= 'Z' || *from == '_' || *from >= '0' && *from <= '9')) {
++from;
}
QString varName = QString::fromUtf8(nameStart, from - nameStart);
if (!skipJunk(from, end)) throw exception("Unexpected end of file!");
if (*from != ':') throw exception(QString("':' expected after '%1'").arg(varName).toUtf8().constData());
if (!skipJunk(++from, end)) throw exception("Unexpected end of file!");
if (*from != '"') throw exception(QString("Expected string after '%1:'").arg(varName).toUtf8().constData());
QByteArray varValue;
const char *start = ++from;
while (from < end && *from != '"') {
if (*from == '\\') {
if (from + 1 >= end) throw exception("Unexpected end of file!");
if (*(from + 1) == '"' || *(from + 1) == '\\') {
if (from > start) varValue.append(start, from - start);
start = ++from;
}
}
++from;
}
if (from >= end) throw exception("Unexpected end of file!");
if (from > start) varValue.append(start, from - start);
if (!skipJunk(++from, end)) throw exception("Unexpected end of file!");
if (*from != ';') throw exception(QString("';' expected after '%1: \"value\"'").arg(varName).toUtf8().constData());
skipJunk(++from, end);
if (varName == "direction") {
if (varValue == "LTR" || varValue == "RTL") {
layoutDirection = QString::fromUtf8(varValue);
} else {
throw exception(QString("Unexpected value for 'direction' key: '%1'").arg(QString::fromUtf8(varValue)).toUtf8().constData());
}
} else if (varName.midRef(0, 4) != "lng_") {
throw exception(QString("Bad key '%1'").arg(varName).toUtf8().constData());
} else if (keys.constFind(varName) != keys.cend()) {
throw exception(QString("Key doubled '%1'").arg(varName).toUtf8().constData());
} else {
keys.insert(varName, QString::fromUtf8(varValue));
keysOrder.push_back(varName);
}
}
QString escapeCpp(const QString &key, QString value, bool wideChar) {
if (value.isEmpty()) return "QString()";
value = value.replace('\\', "\\\\").replace('\n', "\\n").replace('\r', "").replace('"', "\\\"");
QString res;
res.reserve(value.size() * 10);
bool instr = false;
for (const QChar *ch = value.constData(), *e = value.constData() + value.size(); ch != e; ++ch) {
if (ch->unicode() < 32) {
throw exception(QString("Bad value for key '%1'").arg(key).toUtf8().constData());
} else if (ch->unicode() > 127) {
if (instr) {
res.append('"');
instr = false;
}
res.append(' ');
if (wideChar) {
res.append('L').append('"').append('\\').append('x').append(QString("%1").arg(ch->unicode(), 4, 16, QChar('0'))).append('"');
} else {
res.append('"');
QByteArray utf(QString(*ch).toUtf8());
for (const unsigned char *uch = (const unsigned char *)utf.constData(), *ue = (const unsigned char *)utf.constData() + utf.size(); uch != ue; ++uch) {
res.append('\\').append('x').append(QString("%1").arg(ushort(*uch), 2, 16, QChar('0')));
}
res.append('"');
}
} else {
if (!instr) {
res.append(' ');
if (wideChar) res.append('L');
res.append('"');
instr = true;
}
res.append(*ch);
}
}
if (instr) res.append('"');
return (wideChar ? "qsl(" : "QString::fromUtf8(") + res.mid(wideChar ? 2 : 1) + ")";
}
void writeCppKey(QTextStream &tcpp, const QString &key, const QString &val) {
QString wide = escapeCpp(key, val, true), utf = escapeCpp(key, val, false);
if (wide.indexOf(" L\"") < 0) {
tcpp << "\t\t\tset(" << key << ", " << wide << ");\n";
} else {
tcpp << "#ifdef Q_OS_WIN\n";
tcpp << "\t\t\tset(" << key << ", " << wide << ");\n";
tcpp << "#else\n";
tcpp << "\t\t\tset(" << key << ", " << utf << ");\n";
tcpp << "#endif\n";
}
}
bool genLang(const QString &lang_in, const QString &lang_out) {
QString lang_cpp = lang_out + ".cpp", lang_h = lang_out + ".h";
QFile f(lang_in);
if (!f.open(QIODevice::ReadOnly)) {
cout << "Could not open styles input file '" << lang_in.toUtf8().constData() << "'!\n";
QCoreApplication::exit(1);
return false;
}
QByteArray blob = f.readAll();
const char *text = blob.constData(), *end = blob.constData() + blob.size();
f.close();
try {
while (text != end) {
readKeyValue(text, end);
}
QByteArray cppText, hText;
{
QTextStream tcpp(&cppText), th(&hText);
tcpp.setCodec("ISO 8859-1");
th.setCodec("ISO 8859-1");
th << "\
/*\n\
Created from \'/Resources/lang.txt\' by \'/MetaLang\' project\n\
\n\
WARNING! All changes made in this file will be lost!\n\
\n\
This file is part of Telegram Desktop,\n\
an unofficial desktop messaging app, see https://telegram.org\n\
\n\
Telegram Desktop is free software: you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation, either version 3 of the License, or\n\
(at your option) any later version.\n\
\n\
It is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\
\n\
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
Copyright (c) 2014 John Preston, https://tdesktop.com\n\
*/\n";
th << "#pragma once\n\n";
th << "enum LangKey {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
th << "\t" << keysOrder[i] << (i ? "" : " = 0") << ",\n";
}
th << "\n\tlng_keys_cnt\n";
th << "};\n\n";
th << "QString lang(LangKey key);\n";
th << "inline QString langDayOfMonth(const QDate &date) {\n";
th << "\tint32 month = date.month(), day = date.day();\n";
th << "\treturn (month > 0 && month <= 12) ? lang(lng_month_day).replace(qsl(\"{month}\"), lang(LangKey(lng_month1 + month - 1))).replace(qsl(\"{day}\"), QString::number(day)) : qsl(\"{err}\");\n";
th << "}\n\n";
th << "inline QString langDayOfWeek(const QDate &date) {\n";
th << "\tint32 day = date.dayOfWeek();\n";
th << "\treturn (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1 + day - 1)) : qsl(\"{err}\");\n";
th << "}\n\n";
th << "Qt::LayoutDirection langDir();\n\n";
th << "class LangLoader {\n";
th << "public:\n";
th << "\tconst QString &errors() const;\n";
th << "\tconst QString &warnings() const;\n\n";
th << "protected:\n";
th << "\tLangLoader() : _checked(false) {\n";
th << "\t\tmemset(_found, 0, sizeof(_found));\n";
th << "\t}\n\n";
th << "\tbool feedKeyValue(const QString &key, const QString &value);\n\n";
th << "\tvoid error(const QString &text) {\n";
th << "\t\t_err.push_back(text);\n";
th << "\t}\n";
th << "\tvoid warning(const QString &text) {\n";
th << "\t\t_warn.push_back(text);\n";
th << "\t}\n\n";
th << "private:\n";
th << "\tmutable QStringList _err, _warn;\n";
th << "\tmutable QString _errors, _warnings;\n";
th << "\tmutable bool _checked;\n";
th << "\tbool _found[lng_keys_cnt];\n\n";
th << "\tLangLoader(const LangLoader &);\n";
th << "\tLangLoader &operator=(const LangLoader &);\n";
th << "};\n";
tcpp << "\
/*\n\
Created from \'/Resources/lang.txt\' by \'/MetaLang\' project\n\
\n\
WARNING! All changes made in this file will be lost!\n\
\n\
This file is part of Telegram Desktop,\n\
an unofficial desktop messaging app, see https://telegram.org\n\
\n\
Telegram Desktop is free software: you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation, either version 3 of the License, or\n\
(at your option) any later version.\n\
\n\
It is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\
\n\
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
Copyright (c) 2014 John Preston, https://tdesktop.com\n\
*/\n";
tcpp << "#include \"stdafx.h\"\n#include \"lang.h\"\n\n";
tcpp << "namespace {\n";
tcpp << "\tQt::LayoutDirection _langDir = Qt::" << (layoutDirection == "LTR" ? "LeftToRight" : "RightToLeft") << ";\n";
tcpp << "\tconst char *_langKeyNames[lng_keys_cnt + 1] = {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
tcpp << "\t\t\"" << keysOrder[i] << "\",\n";
}
tcpp << "\t\t\"lng_keys_cnt\"\n";
tcpp << "\t};\n\n";
tcpp << "\tQString _langValues[lng_keys_cnt + 1];\n\n";
tcpp << "\tvoid set(LangKey key, const QString &val) {\n";
tcpp << "\t\t_langValues[key] = val;\n";
tcpp << "\t}\n\n";
tcpp << "\tclass LangInit {\n";
tcpp << "\tpublic:\n";
tcpp << "\t\tLangInit() {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
writeCppKey(tcpp, keysOrder[i], keys[keysOrder[i]]);
}
tcpp << "\t\t\tset(lng_keys_cnt, QString());\n";
tcpp << "\t\t}\n";
tcpp << "\t};\n\n";
tcpp << "\tLangInit _langInit;\n";
tcpp << "}\n\n";
tcpp << "QString lang(LangKey key) {\n";
tcpp << "\treturn _langValues[(key < 0 || key > lng_keys_cnt) ? lng_keys_cnt : key];\n";
tcpp << "}\n\n";
tcpp << "Qt::LayoutDirection langDir() {\n";
tcpp << "\treturn _langDir;\n";
tcpp << "}\n\n";
tcpp << "bool LangLoader::feedKeyValue(const QString &key, const QString &value) {\n";
tcpp << "\tif (key == qsl(\"direction\")) {\n";
tcpp << "\t\tif (value == qsl(\"LTR\")) {\n";
tcpp << "\t\t\t_langDir = Qt::LeftToRight;\n";
tcpp << "\t\t\treturn true;\n";
tcpp << "\t\t} else if (value == qsl(\"RTL\")) {\n";
tcpp << "\t\t\t_langDir = Qt::RightToLeft;\n";
tcpp << "\t\t\treturn true;\n";
tcpp << "\t\t} else {\n";
tcpp << "\t\t\t_err.push_back(qsl(\"Bad value for 'direction' key.\"));\n";
tcpp << "\t\t\treturn false;\n";
tcpp << "\t\t}\n";
tcpp << "\t}\n";
tcpp << "\tif (key.size() < 5 || key.midRef(0, 4) != qsl(\"lng_\")) {\n";
tcpp << "\t\t_err.push_back(qsl(\"Bad key name '%1'\").arg(key));\n";
tcpp << "\t\treturn false;\n";
tcpp << "\t}\n\n";
if (!keys.isEmpty()) {
QString tab("\t");
tcpp << "\tLangKey keyIndex = lng_keys_cnt;\n";
tcpp << "\tconst QChar *ch = key.constData(), *e = key.constData() + key.size();\n";
QString current("lng_");
int depth = current.size();
tcpp << "\tswitch ((ch + " << depth << ")->unicode()) {\n";
for (LangKeys::const_iterator i = keys.cbegin(), j = i + 1, e = keys.cend(); i != e; ++i) {
QString key = i.key();
while (key.midRef(0, depth) != current) {
tcpp << tab.repeated(depth - 3) << "}\n";
current.chop(1);
--depth;
tcpp << tab.repeated(depth - 3) << "break;\n";
}
do {
if (key == current) break;
QChar ich = i.key().at(current.size());
tcpp << tab.repeated(current.size() - 3) << "case '" << ich << "':\n";
if (j == e || ich != ((j.key().size() > depth) ? j.key().at(depth) : 0)) {
if (key == current + ich) {
tcpp << tab.repeated(depth - 3) << "\tif (ch + " << (depth + 1) << " == e) keyIndex = " << key << ";\n";
} else {
tcpp << tab.repeated(depth - 3) << "\tif (key.midRef(" << (depth + 1) << ") == qsl(\"" << i.key().mid(depth + 1) << "\")) keyIndex = " << key << ";\n";
}
tcpp << tab.repeated(depth - 3) << "break;\n";
break;
}
++depth;
current += ich;
if (key == current) {
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " == e) {\n";
tcpp << tab.repeated(depth - 3) << "\tkeyIndex = " << key << ";\n";
tcpp << tab.repeated(depth - 3) << "}\n";
}
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " < e) switch ((ch + " << depth << ")->unicode()) {\n";
} while (true);
++j;
}
while (QString("lng_") != current) {
tcpp << tab.repeated(depth - 3) << "}\n";
current.chop(1);
--depth;
tcpp << tab.repeated(depth - 3) << "break;\n";
}
tcpp << "\t}\n\n";
tcpp << "\tif (keyIndex < lng_keys_cnt) {\n";
tcpp << "\t\t_found[keyIndex] = 1;\n";
tcpp << "\t\t_langValues[keyIndex] = value;\n";
tcpp << "\t\treturn true;\n";
tcpp << "\t}\n\n";
}
tcpp << "\t_err.push_back(qsl(\"Unknown key name '%1'\").arg(key));\n";
tcpp << "\treturn false;\n";
tcpp << "}\n\n";
tcpp << "const QString &LangLoader::errors() const {\n";
tcpp << "\tif (_errors.isEmpty() && !_err.isEmpty()) {\n";
tcpp << "\t\t_errors = _err.join('\\n');\n";
tcpp << "\t}\n";
tcpp << "\treturn _errors;\n";
tcpp << "}\n\n";
tcpp << "const QString &LangLoader::warnings() const {\n";
tcpp << "\tif (!_checked) {\n";
tcpp << "\t\tfor (int32 i = 0; i < lng_keys_cnt; ++i) {\n";
tcpp << "\t\t\tif (!_found[i]) {\n";
tcpp << "\t\t\t\t_warn.push_back(qsl(\"No value found for key '%1'\").arg(_langKeyNames[i]));\n";
tcpp << "\t\t\t}\n";
tcpp << "\t\t}\n";
tcpp << "\t\t_checked = true;\n";
tcpp << "\t}\n";
tcpp << "\tif (_warnings.isEmpty() && !_warn.isEmpty()) {\n";
tcpp << "\t\t_warnings = _warn.join('\\n');\n";
tcpp << "\t}\n";
tcpp << "\treturn _warnings;\n";
tcpp << "}\n";
}
QFile cpp(lang_cpp), h(lang_h);
bool write_cpp = true, write_h = true;
if (cpp.open(QIODevice::ReadOnly)) {
QByteArray wasCpp = cpp.readAll();
if (wasCpp.size() == cppText.size()) {
if (!memcmp(wasCpp.constData(), cppText.constData(), cppText.size())) {
write_cpp = false;
}
}
cpp.close();
}
if (write_cpp) {
cout << "lang.cpp updated, writing " << keysOrder.size() << " rows.\n";
if (!cpp.open(QIODevice::WriteOnly)) throw exception("Could not open lang.cpp for writing!");
if (cpp.write(cppText) != cppText.size()) throw exception("Could not open lang.cpp for writing!");
}
if (h.open(QIODevice::ReadOnly)) {
QByteArray wasH = h.readAll();
if (wasH.size() == hText.size()) {
if (!memcmp(wasH.constData(), hText.constData(), hText.size())) {
write_h = false;
}
}
h.close();
}
if (write_h) {
cout << "lang.h updated, writing " << keysOrder.size() << " rows.\n";
if (!h.open(QIODevice::WriteOnly)) throw exception("Could not open lang.h for writing!");
if (h.write(hText) != hText.size()) throw exception("Could not open lang.h for writing!");
}
} catch (exception &e) {
cout << e.what() << "\n";
QCoreApplication::exit(1);
return false;
}
return true;
}

View File

@@ -0,0 +1,61 @@
/*
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 <QtCore/QMap>
#include <QtCore/QVector>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QRegularExpression>
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtGui/QPainter>
#include <iostream>
#include <exception>
#include <QtCore/QTextStream>
#include <QtCore/QString>
#include <QtCore/QCoreApplication>
#include <QtGui/QGuiApplication>
using std::string;
using std::cout;
using std::cerr;
using std::exception;
bool genLang(const QString &lang_in, const QString &lang_out);
class GenLang : public QObject {
Q_OBJECT
public:
GenLang(const QString &lang_in, const QString &lang_out) : QObject(0),
_lang_in(lang_in), _lang_out(lang_out) {
}
public slots :
void run() {
if (genLang(_lang_in, _lang_out)) {
emit finished();
}
}
signals:
void finished();
private:
QString _lang_in, _lang_out;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/*
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 <QtCore/QMap>
#include <QtCore/QVector>
#include <QtGui/QBitmap>
#include <QtCore/QBuffer>
#include <QtCore/QFileInfo>
#include <QtCore/QFile>
#include <iostream>
#include <exception>
#include <QtCore/QTextStream>
#include <QtCore/QString>
#include <QtCore/QCoreApplication>
#include <QtGui/QGuiApplication>
#include <QtGui/QPainter>
using std::string;
using std::cout;
using std::cerr;
using std::exception;
bool genStyles(const QString &classes_in, const QString &classes_out, const QString &styles_in, const QString &styles_out);
class GenStyles : public QObject {
Q_OBJECT
public:
GenStyles(const QString &classes_in, const QString &classes_out, const QString &styles_in, const QString styles_out) : QObject(0),
_classes_in(classes_in), _classes_out(classes_out), _styles_in(styles_in), _styles_out(styles_out) {
}
public slots:
void run() {
if (genStyles(_classes_in, _classes_out, _styles_in, _styles_out)) {
emit finished();
}
}
signals:
void finished();
private:
QString _classes_in, _classes_out, _styles_in, _styles_out;
};

View File

@@ -0,0 +1,37 @@
/*
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 "memain.h"
int main(int argc, char *argv[]) {
QString emoji_in("."), emoji_out("emoji_config.cpp");
for (int i = 0; i < argc; ++i) {
if (string("-emoji_in") == argv[i]) {
if (++i < argc) emoji_in = argv[i];
} else if (string("-emoji_out") == argv[i]) {
if (++i < argc) emoji_out = argv[i];
}
}
QObject *taskImpl = new GenEmoji(emoji_in, emoji_out);
QGuiApplication a(argc, argv);
QObject::connect(taskImpl, SIGNAL(finished()), &a, SLOT(quit()));
QTimer::singleShot(0, taskImpl, SLOT(run()));
return a.exec();
}

View File

@@ -0,0 +1,20 @@
/*
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 <QtCore/QTimer>
#include "genemoji.h"

View File

@@ -0,0 +1,37 @@
/*
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 "mlmain.h"
int main(int argc, char *argv[]) {
QString lang_in("lang.txt"), lang_out("lang");
for (int i = 0; i < argc; ++i) {
if (string("-lang_in") == argv[i]) {
if (++i < argc) lang_in = argv[i];
} else if (string("-lang_out") == argv[i]) {
if (++i < argc) lang_out = argv[i];
}
}
QObject *taskImpl = new GenLang(lang_in, lang_out);
QCoreApplication a(argc, argv);
QObject::connect(taskImpl, SIGNAL(finished()), &a, SLOT(quit()));
QTimer::singleShot(0, taskImpl, SLOT(run()));
return a.exec();
}

View File

@@ -0,0 +1,20 @@
/*
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 <QtCore/QTimer>
#include "genlang.h"

View File

@@ -0,0 +1,41 @@
/*
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 "msmain.h"
int main(int argc, char *argv[]) {
QString classes_in("style_classes.txt"), classes_out("style_classes.h"), styles_in("style.txt"), styles_out("style_auto.h");
for (int i = 0; i < argc; ++i) {
if (string("-classes_in") == argv[i]) {
if (++i < argc) classes_in = argv[i];
} else if (string("-classes_out") == argv[i]) {
if (++i < argc) classes_out = argv[i];
} else if (string("-styles_in") == argv[i]) {
if (++i < argc) styles_in = argv[i];
} else if (string("-styles_out") == argv[i]) {
if (++i < argc) styles_out = argv[i];
}
}
QObject *taskImpl = new GenStyles(classes_in, classes_out, styles_in, styles_out);
QGuiApplication a(argc, argv);
QObject::connect(taskImpl, SIGNAL(finished()), &a, SLOT(quit()));
QTimer::singleShot(0, taskImpl, SLOT(run()));
return a.exec();
}

View File

@@ -0,0 +1,20 @@
/*
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 <QtCore/QTimer>
#include "genstyles.h"

View File

@@ -0,0 +1,298 @@
/*
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 "packer.h"
const char *publicKey = "\
-----BEGIN RSA PUBLIC KEY-----\n\
MIGJAoGBAMA4ViQrjkPZ9xj0lrer3r23JvxOnrtE8nI69XLGSr+sRERz9YnUptnU\n\
BZpkIfKaRcl6XzNJiN28cVwO1Ui5JSa814UAiDHzWUqCaXUiUEQ6NmNTneiGx2sQ\n\
+9PKKlb8mmr3BB9A45ZNwLT6G9AK3+qkZLHojeSA+m84/a6GP4svAgMBAAE=\n\
-----END RSA PUBLIC KEY-----\
";
extern const char *privateKey;
#include "../../../../TelegramPrivate/packer_private.h" // RSA PRIVATE KEY for update signing
// sha1 hash
typedef unsigned char uchar;
typedef unsigned int uint32;
typedef signed int int32;
namespace{
inline uint32 sha1Shift(uint32 v, uint32 shift) {
return ((v << shift) | (v >> (32 - shift)));
}
void sha1PartHash(uint32 *sha, uint32 *temp)
{
uint32 a = sha[0], b = sha[1], c = sha[2], d = sha[3], e = sha[4], round = 0;
#define _shiftswap(f, v) { \
uint32 t = sha1Shift(a, 5) + (f) + e + v + temp[round]; \
e = d; \
d = c; \
c = sha1Shift(b, 30); \
b = a; \
a = t; \
++round; \
}
#define _shiftshiftswap(f, v) { \
temp[round] = sha1Shift((temp[round - 3] ^ temp[round - 8] ^ temp[round - 14] ^ temp[round - 16]), 1); \
_shiftswap(f, v) \
}
while (round < 16) _shiftswap((b & c) | (~b & d), 0x5a827999)
while (round < 20) _shiftshiftswap((b & c) | (~b & d), 0x5a827999)
while (round < 40) _shiftshiftswap(b ^ c ^ d, 0x6ed9eba1)
while (round < 60) _shiftshiftswap((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
while (round < 80) _shiftshiftswap(b ^ c ^ d, 0xca62c1d6)
#undef _shiftshiftswap
#undef _shiftswap
sha[0] += a;
sha[1] += b;
sha[2] += c;
sha[3] += d;
sha[4] += e;
}
}
int32 *hashSha1(const void *data, uint32 len, void *dest) {
const uchar *buf = (const uchar *)data;
uint32 temp[80], block = 0, end;
uint32 sha[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
for (end = block + 64; block + 64 <= len; end = block + 64) {
for (uint32 i = 0; block < end; block += 4) {
temp[i++] = (uint32) buf[block + 3]
| (((uint32) buf[block + 2]) << 8)
| (((uint32) buf[block + 1]) << 16)
| (((uint32) buf[block]) << 24);
}
sha1PartHash(sha, temp);
}
end = len - block;
memset(temp, 0, sizeof(uint32) * 16);
uint32 last = 0;
for (; last < end; ++last) {
temp[last >> 2] |= (uint32)buf[last + block] << ((3 - (last & 0x03)) << 3);
}
temp[last >> 2] |= 0x80 << ((3 - (last & 3)) << 3);
if (end >= 56) {
sha1PartHash(sha, temp);
memset(temp, 0, sizeof(uint32) * 16);
}
temp[15] = len << 3;
sha1PartHash(sha, temp);
uchar *sha1To = (uchar*)dest;
for (int32 i = 19; i >= 0; --i) {
sha1To[i] = (sha[i >> 2] >> (((3 - i) & 0x03) << 3)) & 0xFF;
}
return (int32*)sha1To;
}
int main(int argc, char *argv[])
{
QString remove;
int version = 0;
QFileInfoList files;
for (int i = 0; i < argc; ++i) {
if (string("-path") == argv[i] && i + 1 < argc) {
QString path = QString(argv[i + 1]);
QFileInfo info(path);
files.push_back(info);
if (remove.isEmpty()) remove = info.canonicalPath() + "/";
} else if (string("-version") == argv[i] && i + 1 < argc) {
version = QString(argv[i + 1]).toInt();
}
}
if (files.isEmpty() || remove.isEmpty() || version <= 1016 || version > 999999) { // not for release =)
cout << "Usage: Packer.exe -path {file} -version {version} OR Packer.exe -path {dir} -version {version}\n";
return 0;
}
bool hasDirs = true;
while (hasDirs) {
hasDirs = false;
for (QFileInfoList::iterator i = files.begin(); i != files.end(); ++i) {
QFileInfo info(*i);
QString fullPath = info.canonicalFilePath();
if (info.isDir()) {
hasDirs = true;
files.erase(i);
QDir d = QDir(info.absoluteFilePath());
QString fullDir = d.canonicalPath();
QStringList entries = d.entryList(QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
files.append(d.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot));
break;
} else if (!info.isReadable()) {
cout << "Can't read: " << info.absoluteFilePath().toUtf8().constData() << "\n";
return -1;
}
}
}
for (QFileInfoList::iterator i = files.begin(); i != files.end(); ++i) {
QFileInfo info(*i);
if (info.canonicalFilePath().indexOf(remove) != 0) {
cout << "Can't find '" << remove.toUtf8().constData() << "' in file '" << info.canonicalFilePath().toUtf8().constData() << "' :(\n";
return -1;
}
}
QByteArray result;
{
QBuffer buffer(&result);
buffer.open(QIODevice::WriteOnly);
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
stream << quint32(version);
stream << quint32(files.size());
cout << "Found " << files.size() << " file" << (files.size() == 1 ? "" : "s") << "..\n";
for (QFileInfoList::iterator i = files.begin(); i != files.end(); ++i) {
QFileInfo info(*i);
QString fullName = info.canonicalFilePath();
QString name = fullName.mid(remove.length());
cout << name.toUtf8().constData() << " (" << info.size() << ")\n";
QFile f(fullName);
if (!f.open(QIODevice::ReadOnly)) {
cout << "Can't open '" << fullName.toUtf8().constData() << "' for read..\n";
return -1;
}
QByteArray inner = f.readAll();
stream << name << quint32(inner.size()) << inner;
}
if (stream.status() != QDataStream::Ok) {
cout << "Stream status is bad: " << stream.status() << "\n";
return -1;
}
}
int32 resultSize = result.size();
cout << "Compression start, size: " << resultSize << "\n";
QByteArray compressed, resultCheck;
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
size_t compressedLen = compressed.size() - hSize;
uchar outProps[LZMA_PROPS_SIZE];
size_t outPropsSize = LZMA_PROPS_SIZE;
int res = LzmaCompress((uchar*)(compressed.data() + hSize), &compressedLen, (const uchar*)(result.constData()), result.size(), (uchar*)(compressed.data() + hSigLen + hShaLen), &outPropsSize, 9, 64 * 1024 * 1024, 0, 0, 0, 0, 0);
if (res != SZ_OK) {
cout << "Error in compression: " << res << "\n";
return -1;
}
compressed.resize(hSize + compressedLen);
memcpy(compressed.data() + hSigLen + hShaLen + hPropsLen, &resultSize, hOriginalSizeLen);
cout << "Compressed to size: " << compressedLen << "\n";
cout << "Checking uncompressed..\n";
int32 resultCheckLen;
memcpy(&resultCheckLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
if (resultCheckLen <= 0 || resultCheckLen > 1024 * 1024 * 1024) {
cout << "Bad result len: " << resultCheckLen << "\n";
return -1;
}
resultCheck.resize(resultCheckLen);
size_t resultLen = resultCheck.size();
SizeT srcLen = compressedLen;
int uncompressRes = LzmaUncompress((uchar*)resultCheck.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
if (uncompressRes != SZ_OK) {
cout << "Uncompress failed: " << uncompressRes << "\n";
return -1;
}
if (resultLen != result.size()) {
cout << "Uncompress bad size: " << resultLen << ", was: " << result.size() << "\n";
return -1;
}
if (memcmp(result.constData(), resultCheck.constData(), resultLen)) {
cout << "Data differ :(\n";
return -1;
}
/**/
result = resultCheck = QByteArray();
cout << "Counting SHA1 hash..\n";
uchar sha1Buffer[20];
memcpy(compressed.data() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen); // count sha1
uint32 siglen = 0;
cout << "Signing..\n";
RSA *prKey = PEM_read_bio_RSAPrivateKey(BIO_new_mem_buf(const_cast<char*>(privateKey), -1), 0, 0, 0);
if (!prKey) {
cout << "Could not read RSA private key!\n";
return -1;
}
if (RSA_size(prKey) != hSigLen) {
RSA_free(prKey);
cout << "Bad private key, size: " << RSA_size(prKey) << "\n";
return -1;
}
if (RSA_sign(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (uchar*)(compressed.data()), &siglen, prKey) != 1) { // count signature
RSA_free(prKey);
cout << "Signing failed!\n";
return -1;
}
RSA_free(prKey);
if (siglen != hSigLen) {
cout << "Bad signature length: " << siglen << "\n";
return -1;
}
cout << "Checking signature..\n";
RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(publicKey), -1), 0, 0, 0);
if (!pbKey) {
cout << "Could not read RSA public key!\n";
return -1;
}
if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), siglen, pbKey) != 1) { // verify signature
RSA_free(pbKey);
cout << "Signature verification failed!\n";
return -1;
}
cout << "Signature verified!\n";
RSA_free(pbKey);
QString outName(QString("tupdate%1").arg(version));
QFile out(outName);
if (!out.open(QIODevice::WriteOnly)) {
cout << "Can't open '" << outName.toUtf8().constData() << "' for write..\n";
return -1;
}
out.write(compressed);
out.close();
cout << "Update file '" << outName.toUtf8().constData() << "' written successfully!\n";
return 0;
}

View File

@@ -0,0 +1,44 @@
/*
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 <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QStringList>
#include <QtCore/QBuffer>
#include <zlib.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <LzmaLib.h>
#include <string>
#include <iostream>
#include <exception>
using std::string;
using std::wstring;
using std::cout;

View File

@@ -0,0 +1,99 @@
/*
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 "prepare.h"
int prepare(QFileInfo f, QStringList paths) {
if (paths.isEmpty()) {
cout << "No -path args were passed :(\n";
return -1;
}
int lastVersion = 0;
QString lastVersionStr;
QFileInfo last;
QFileInfoList l = f.absoluteDir().entryInfoList(QDir::Files);
for (QFileInfoList::iterator i = l.begin(), e = l.end(); i != e; ++i) {
QRegularExpressionMatch m = QRegularExpression("/tsetup.((\\d+).(\\d+).(\\d+)).exe$").match(i->absoluteFilePath());
if (!m.hasMatch()) continue;
int version = m.captured(2).toInt() * 1000000 + m.captured(3).toInt() * 1000 + m.captured(4).toInt();
if (version > lastVersion) {
lastVersion = version;
lastVersionStr = m.captured(1);
last = *i;
}
}
if (!lastVersion) {
cout << "No tsetup.X.Y.Z.exe found :(\n";
return -1;
}
cout << "Last version: " << lastVersionStr.toUtf8().constData() << " (" << lastVersion << "), executing packer..\n";
QDir dir("deploy/" + lastVersionStr);
if (dir.exists()) {
cout << "Version " << lastVersionStr.toUtf8().constData() << " already exists in /deploy..\n";
return -1;
}
QString packer = QString("Packer.exe -version %1").arg(lastVersion);
for (QStringList::iterator i = paths.begin(), e = paths.end(); i != e; ++i) {
packer += " -path " + *i;
}
int res = system(packer.toUtf8().constData());
if (res) return res;
dir.mkpath(".");
paths.push_back("Telegram.pdb");
paths.push_back("Updater.pdb");
paths.push_back("tsetup." + lastVersionStr + ".exe");
paths.push_back(QString("tupdate%1").arg(lastVersion));
for (QStringList::iterator i = paths.begin(), e = paths.end(); i != e; ++i) {
if (!QFile::copy(*i, "deploy/" + lastVersionStr + "/" + *i)) {
cout << "Could not copy " << i->toUtf8().constData() << " to deploy/" << lastVersionStr.toUtf8().constData() << "\n";
return -1;
}
cout << "Copied " << i->toUtf8().constData() << "..\n";
}
for (QStringList::iterator i = paths.begin(), e = paths.end(); i != e; ++i) {
QFile::remove(*i);
}
cout << "Update created in deploy/" << lastVersionStr.toUtf8().constData() << "\n";
return 0;
}
int main(int argc, char *argv[])
{
QFileInfo f(argv[0]);
QStringList paths;
for (int i = 1; i < argc; ++i) {
if (string(argv[i]) == "-path" && i + 1 < argc) {
paths.push_back(QString(argv[i + 1]));
}
}
int res = prepare(f, paths);
system("PAUSE");
return res;
}

View File

@@ -0,0 +1,37 @@
/*
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 <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QStringList>
#include <QtCore/QBuffer>
#include <QtCore/QRegularExpression>
#include <QtCore/QVector>
#include <process.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <exception>
using std::string;
using std::wstring;
using std::cout;

View File

@@ -0,0 +1,459 @@
/*
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 "updater.h"
bool _debug = false;
wstring exeName, exeDir;
bool equal(const wstring &a, const wstring &b) {
return !_wcsicmp(a.c_str(), b.c_str());
}
void updateError(const WCHAR *msg, DWORD errorCode) {
WCHAR errMsg[2048];
LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, 0, 0);
if (!errorText) {
errorText = errorTextDefault;
}
wsprintf(errMsg, L"%s, error code: %d\nError message: %s", msg, errorCode, errorText);
MessageBox(0, errMsg, L"Update error!", MB_ICONERROR);
if (errorText != errorTextDefault) {
LocalFree(errorText);
}
}
HANDLE _logFile = 0;
void openLog() {
if (!_debug || _logFile) return;
wstring logPath = L"DebugLogs";
if (!CreateDirectory(logPath.c_str(), NULL)) {
DWORD errorCode = GetLastError();
if (errorCode && errorCode != ERROR_ALREADY_EXISTS) {
updateError(L"Failed to create log directory", errorCode);
return;
}
}
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);
static const int maxFileLen = MAX_PATH * 10;
WCHAR logName[maxFileLen];
wsprintf(logName, L"DebugLogs\\%04d%02d%02d_%02d%02d%02d_upd.txt",
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
_logFile = CreateFile(logName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (_logFile == INVALID_HANDLE_VALUE) { // :(
updateError(L"Failed to create log file", GetLastError());
_logFile = 0;
return;
}
}
void closeLog() {
if (!_logFile) return;
CloseHandle(_logFile);
_logFile = 0;
}
void writeLog(const wstring &msg) {
if (!_logFile) return;
wstring full = msg + L'\n';
DWORD written = 0;
BOOL result = WriteFile(_logFile, full.c_str(), full.size() * sizeof(wchar_t), &written, 0);
if (!result) {
updateError((L"Failed to write log entry '" + msg + L"'").c_str(), GetLastError());
closeLog();
return;
}
BOOL flushr = FlushFileBuffers(_logFile);
if (!flushr) {
updateError((L"Failed to flush log on entry '" + msg + L"'").c_str(), GetLastError());
closeLog();
return;
}
}
void delFolder() {
wstring delPath = L"tupdates\\ready", delFolder = L"tupdates";
WCHAR path[4096];
memcpy(path, delPath.c_str(), (delPath.size() + 1) * sizeof(WCHAR));
path[delPath.size() + 1] = 0;
writeLog(L"Fully clearing path '" + delPath + L"'..");
SHFILEOPSTRUCT file_op = {
NULL,
FO_DELETE,
path,
L"",
FOF_NOCONFIRMATION |
FOF_NOERRORUI |
FOF_SILENT,
false,
0,
L""
};
int res = SHFileOperation(&file_op);
if (res) writeLog(L"Error: failed to clear path! :(");
RemoveDirectory(delFolder.c_str());
}
bool update() {
writeLog(L"Update started..");
wstring updDir = L"tupdates\\ready";
deque<wstring> dirs;
dirs.push_back(updDir);
deque<wstring> from, to, forcedirs;
do {
wstring dir = dirs.front();
dirs.pop_front();
wstring toDir = exeDir;
if (dir.size() > updDir.size() + 1) {
toDir += (dir.substr(updDir.size() + 1) + L"\\");
forcedirs.push_back(toDir);
writeLog(L"Parsing dir '" + toDir + L"' in update tree..");
}
WIN32_FIND_DATA findData;
HANDLE findHandle = FindFirstFileEx((dir + L"\\*").c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, 0, 0);
if (findHandle == INVALID_HANDLE_VALUE) {
DWORD errorCode = GetLastError();
if (errorCode == ERROR_PATH_NOT_FOUND) { // no update is ready
return true;
}
writeLog(L"Error: failed to find update files :(");
updateError(L"Failed to find update files", errorCode);
delFolder();
return false;
}
do {
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (findData.cFileName != wstring(L".") && findData.cFileName != wstring(L"..")) {
dirs.push_back(dir + L"\\" + findData.cFileName);
writeLog(L"Added dir '" + dir + L"\\" + findData.cFileName + L"' in update tree..");
}
} else {
wstring fname = dir + L"\\" + findData.cFileName;
wstring tofname = exeDir + fname.substr(updDir.size() + 1);
if (equal(tofname, exeName)) { // bad update - has Updater.exe - delete all dir
writeLog(L"Error: bad update, has Updater.exe! '" + tofname + L"' equal '" + exeName + L"'");
delFolder();
return false;
}
from.push_back(fname);
to.push_back(tofname);
writeLog(L"Added file '" + fname + L"' to be copied to '" + tofname + L"'");
}
} while (FindNextFile(findHandle, &findData));
DWORD errorCode = GetLastError();
if (errorCode && errorCode != ERROR_NO_MORE_FILES) { // everything is found
writeLog(L"Error: failed to find next update file :(");
updateError(L"Failed to find next update file", errorCode);
delFolder();
return false;
}
FindClose(findHandle);
} while (!dirs.empty());
for (size_t i = 0; i < forcedirs.size(); ++i) {
wstring forcedir = forcedirs[i];
writeLog(L"Forcing dir '" + forcedir + L"'..");
if (!forcedir.empty() && !CreateDirectory(forcedir.c_str(), NULL)) {
DWORD errorCode = GetLastError();
if (errorCode && errorCode != ERROR_ALREADY_EXISTS) {
writeLog(L"Error: failed to create dir '" + forcedir + L"'..");
updateError(L"Failed to create directory", errorCode);
delFolder();
return false;
}
writeLog(L"Already exists!");
}
}
for (size_t i = 0; i < from.size(); ++i) {
wstring fname = from[i], tofname = to[i];
BOOL copyResult;
do {
writeLog(L"Copying file '" + fname + L"' to '" + tofname + L"'..");
int copyTries = 0;
do {
copyResult = CopyFile(fname.c_str(), tofname.c_str(), FALSE);
if (copyResult == FALSE) {
++copyTries;
Sleep(100);
} else {
break;
}
} while (copyTries < 30);
if (copyResult == FALSE) {
writeLog(L"Error: failed to copy, asking to retry..");
WCHAR errMsg[2048];
wsprintf(errMsg, L"Failed to update Telegram :(\n%s is not accessible.", tofname);
if (MessageBox(0, errMsg, L"Update error!", MB_ICONERROR | MB_RETRYCANCEL) != IDRETRY) {
delFolder();
return false;
}
}
} while (copyResult == FALSE);
}
writeLog(L"Update succeed! Clearing folder..");
delFolder();
return true;
}
void updateRegistry() {
writeLog(L"Updating registry..");
HANDLE versionFile = CreateFile(L"tdata\\version", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (versionFile != INVALID_HANDLE_VALUE) {
DWORD versionNum = 0, versionLen = 0, readLen = 0;
WCHAR versionStr[32];
if (ReadFile(versionFile, &versionNum, sizeof(DWORD), &readLen, NULL) != TRUE || readLen != sizeof(DWORD)) {
versionNum = 0;
} else if (ReadFile(versionFile, &versionLen, sizeof(DWORD), &readLen, NULL) != TRUE || readLen != sizeof(DWORD) || versionLen > 63) {
versionNum = 0;
} else if (ReadFile(versionFile, versionStr, versionLen, &readLen, NULL) != TRUE || readLen != versionLen) {
versionNum = 0;
}
CloseHandle(versionFile);
writeLog(L"Version file read.");
if (versionNum) {
versionStr[versionLen / 2] = 0;
HKEY rkey;
LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1", 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &rkey);
if (status == ERROR_SUCCESS) {
writeLog(L"Checking registry install location..");
static const int bufSize = 4096;
DWORD locationType, locationSize = bufSize * 2;
WCHAR locationStr[bufSize], exp[bufSize];
if (RegQueryValueEx(rkey, L"InstallLocation", 0, &locationType, (BYTE*)locationStr, &locationSize) == ERROR_SUCCESS) {
locationSize /= 2;
if (locationStr[locationSize - 1]) {
locationStr[locationSize++] = 0;
}
if (locationType == REG_EXPAND_SZ) {
DWORD copy = ExpandEnvironmentStrings(locationStr, exp, bufSize);
if (copy <= bufSize) {
memcpy(locationStr, exp, copy * sizeof(WCHAR));
}
}
if (locationType == REG_EXPAND_SZ || locationType == REG_SZ) {
if (PathCanonicalize(exp, locationStr) == TRUE) {
memcpy(locationStr, exp, bufSize * sizeof(WCHAR));
if (GetFullPathName(L".", bufSize, exp, 0) < bufSize) {
wstring installpath = locationStr, mypath = exp;
if (installpath == mypath + L"\\" || true) { // always update reg info, if we found it
WCHAR nameStr[bufSize], dateStr[bufSize];
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
wsprintf(nameStr, L"Telegram Win (Unofficial) version %s", versionStr);
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
wsprintf(dateStr, L"%04d%02d%02d", stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay);
RegSetValueEx(rkey, L"InstallDate", 0, REG_SZ, (BYTE*)dateStr, (wcslen(dateStr) + 1) * sizeof(WCHAR));
WCHAR *appURL = L"https://tdesktop.com";
RegSetValueEx(rkey, L"HelpLink", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
RegSetValueEx(rkey, L"URLInfoAbout", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
RegSetValueEx(rkey, L"URLUpdateInfo", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
}
}
}
}
}
RegCloseKey(rkey);
}
}
}
}
#include <ShlObj.h>
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParamarg, int cmdShow) {
openLog();
#ifdef _NEED_WIN_GENERATE_DUMP
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
#endif
writeLog(L"Updaters started..");
LPWSTR *args;
int argsCount;
bool needupdate = false, autostart = false, debug = false;
args = CommandLineToArgvW(GetCommandLine(), &argsCount);
if (args) {
for (int i = 1; i < argsCount; ++i) {
if (equal(args[i], L"-update")) {
needupdate = true;
} else if (equal(args[i], L"-autostart")) {
autostart = true;
} else if (equal(args[i], L"-debug")) {
debug = _debug = true;
openLog();
}
}
if (needupdate) writeLog(L"Need to update!");
if (autostart) writeLog(L"From autostart!");
exeName = args[0];
writeLog(L"Exe name is: " + exeName);
if (exeName.size() > 11) {
if (equal(exeName.substr(exeName.size() - 11), L"Updater.exe")) {
exeDir = exeName.substr(0, exeName.size() - 11);
writeLog(L"Exe dir is: " + exeDir);
if (needupdate && update()) {
updateRegistry();
}
} else {
writeLog(L"Error: bad exe name!");
}
} else {
writeLog(L"Error: short exe name!");
}
LocalFree(args);
} else {
writeLog(L"Error: No command line arguments!");
}
wstring targs = L"-noupdate";
if (autostart) targs += L" -autostart";
if (debug) targs += L" -debug";
ShellExecute(0, 0, (exeDir + L"Telegram.exe").c_str(), targs.c_str(), 0, SW_SHOWNORMAL);
writeLog(L"Executed Telegram.exe, closing log and quiting..");
closeLog();
return 0;
}
#ifdef _NEED_WIN_GENERATE_DUMP
static const WCHAR *_programName = L"Telegram Win (Unofficial)"; // folder in APPDATA, if current path is unavailable for writing
static const WCHAR *_exeName = L"Updater.exe";
LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter = 0;
typedef BOOL (FAR STDAPICALLTYPE *t_miniDumpWriteDump)(
_In_ HANDLE hProcess,
_In_ DWORD ProcessId,
_In_ HANDLE hFile,
_In_ MINIDUMP_TYPE DumpType,
_In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
_In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
_In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
t_miniDumpWriteDump miniDumpWriteDump = 0;
HANDLE _generateDumpFileAtPath(const WCHAR *path) {
static const int maxFileLen = MAX_PATH * 10;
WCHAR szPath[maxFileLen];
wsprintf(szPath, L"%stdumps\\", path);
if (!CreateDirectory(szPath, NULL)) {
if (GetLastError() != ERROR_ALREADY_EXISTS) {
return 0;
}
}
WCHAR szFileName[maxFileLen];
WCHAR szExeName[maxFileLen];
wcscpy_s(szExeName, _exeName);
WCHAR *dotFrom = wcschr(szExeName, WCHAR(L'.'));
if (dotFrom) {
wsprintf(dotFrom, L"");
}
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);
wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
szPath, szExeName, updaterVersionStr,
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
GetCurrentProcessId(), GetCurrentThreadId());
return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
}
void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
static const int maxFileLen = MAX_PATH * 10;
closeLog();
HMODULE hDll = LoadLibrary(L"DBGHELP.DLL");
if (!hDll) return;
miniDumpWriteDump = (t_miniDumpWriteDump)GetProcAddress(hDll, "MiniDumpWriteDump");
if (!miniDumpWriteDump) return;
HANDLE hDumpFile = 0;
WCHAR szPath[maxFileLen];
DWORD len = GetModuleFileName(GetModuleHandle(0), szPath, maxFileLen);
if (!len) return;
WCHAR *pathEnd = szPath + len;
if (!_wcsicmp(pathEnd - wcslen(_exeName), _exeName)) {
wsprintf(pathEnd - wcslen(_exeName), L"");
hDumpFile = _generateDumpFileAtPath(szPath);
}
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
WCHAR wstrPath[maxFileLen];
DWORD wstrPathLen;
if (wstrPathLen = GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
wsprintf(wstrPath + wstrPathLen, L"\\%s\\", _programName);
hDumpFile = _generateDumpFileAtPath(wstrPath);
}
}
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
return;
}
MINIDUMP_EXCEPTION_INFORMATION ExpParam = {0};
ExpParam.ThreadId = GetCurrentThreadId();
ExpParam.ExceptionPointers = pExceptionPointers;
ExpParam.ClientPointers = TRUE;
miniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);
}
LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) {
_generateDump(pExceptionPointers);
return _oldWndExceptionFilter ? (*_oldWndExceptionFilter)(pExceptionPointers) : EXCEPTION_CONTINUE_SEARCH;
}
#endif

View File

@@ -0,0 +1,40 @@
/*
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 <windows.h>
#include <string>
#include <DbgHelp.h>
#include <Shellapi.h>
#include <Shlwapi.h>
#include <deque>
#include <string>
using std::deque;
using std::wstring;
#define _NEED_WIN_GENERATE_DUMP
#ifdef _NEED_WIN_GENERATE_DUMP
extern LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter;
LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers);
#endif _NEED_WIN_GENERATE_DUMP
static int updaterVersion = 1000;
static const WCHAR *updaterVersionStr = L"0.1.0";

1925
Telegram/SourceFiles/app.cpp Normal file

File diff suppressed because it is too large Load Diff

187
Telegram/SourceFiles/app.h Normal file
View File

@@ -0,0 +1,187 @@
/*
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 "types.h"
class Application;
class Window;
class MainWidget;
class Settings;
class Font;
class Color;
class FileUploader;
#include "history.h"
typedef QMap<HistoryItem*, bool> HistoryItemsMap;
typedef QHash<VideoData*, HistoryItemsMap> VideoItems;
typedef QHash<AudioData*, HistoryItemsMap> AudioItems;
typedef QHash<DocumentData*, HistoryItemsMap> DocumentItems;
namespace App {
Application *app();
Window *wnd();
MainWidget *main();
Settings *settings();
FileUploader *uploader();
void showSettings();
void logOut();
bool loggedOut();
QString formatPhone(QString phone);
inline bool isChat(const PeerId &peer) {
return peer & 0x100000000L;
}
PeerId peerFromMTP(const MTPPeer &peer_id);
PeerId peerFromChat(int32 chat_id);
inline PeerId peerFromChat(const MTPint &chat_id) {
return peerFromChat(chat_id.v);
}
PeerId peerFromUser(int32 user_id);
inline PeerId peerFromUser(const MTPint &user_id) {
return peerFromUser(user_id.v);
}
MTPpeer peerToMTP(const PeerId &peer_id);
int32 onlineWillChangeIn(int32 onlineOnServer, int32 nowOnServer);
QString onlineText(int32 onlineOnServer, int32 nowOnServer);
void feedUsers(const MTPVector<MTPUser> &users);
void feedChats(const MTPVector<MTPChat> &chats);
void feedParticipants(const MTPChatParticipants &p);
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
void feedMsgs(const MTPVector<MTPMessage> &msgs, bool newMsgs = false);
void feedWereRead(const QVector<MTPint> &msgsIds);
void feedWereDeleted(const QVector<MTPint> &msgsIds);
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links);
void feedUserLink(MTPint userId, const MTPcontacts_MyLink &myLink, const MTPcontacts_ForeignLink &foreignLink);
void feedMessageMedia(MsgId msgId, const MTPMessage &msg);
int32 maxMsgId();
PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs);
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert = 0);
PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert = 0);
VideoData *feedVideo(const MTPDvideo &video, VideoData *convert = 0);
AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert = 0);
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = 0);
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert = 0);
UserData *userLoaded(const PeerId &user);
ChatData *chatLoaded(const PeerId &chat);
PeerData *peerLoaded(const PeerId &peer);
UserData *userLoaded(int32 user);
ChatData *chatLoaded(int32 chat);
PeerData *peer(const PeerId &peer);
UserData *user(const PeerId &peer);
UserData *user(int32 user);
UserData *self();
ChatData *chat(const PeerId &peer);
ChatData *chat(int32 chat);
QString peerName(const PeerData *peer, bool forDialogs = false);
PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &full = ImagePtr());
void forgetPhotos();
VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void forgetVideos();
AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0);
void forgetAudios();
DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &name = QString(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void forgetDocuments();
MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo);
Histories &histories();
History *history(const PeerId &peer, int32 unreadCnt = 0);
History *historyLoaded(const PeerId &peer);
HistoryItem *histItemById(MsgId itemId);
bool historyRegItem(HistoryItem *item);
void historyUnregItem(HistoryItem *item);
void historyClearMsgs();
void historyClearItems();
// void deleteHistory(const PeerId &peer);
void historyRegRandom(uint64 randomId, MsgId itemId);
void historyUnregRandom(uint64 randomId);
MsgId histItemByRandom(uint64 randomId);
void hoveredItem(HistoryItem *item);
HistoryItem *hoveredItem();
void pressedItem(HistoryItem *item);
HistoryItem *pressedItem();
void hoveredLinkItem(HistoryItem *item);
HistoryItem *hoveredLinkItem();
void pressedLinkItem(HistoryItem *item);
HistoryItem *pressedLinkItem();
void contextItem(HistoryItem *item);
HistoryItem *contextItem();
void mousedItem(HistoryItem *item);
HistoryItem *mousedItem();
QPixmap &sprite();
QPixmap &emojis();
const QPixmap &emojiSingle(const EmojiData *emoji, int32 fontHeight);
void initMedia();
void deinitMedia(bool completely = true);
void playSound();
void writeConfig();
void readConfig();
void writeUserConfig();
void readUserConfig();
void muteHistory(History *history);
void unmuteHistory(History *history);
void writeAllMuted(QDataStream &stream);
void readAllMuted(QDataStream &stream);
void readOneMuted(QDataStream &stream);
bool isPeerMuted(const PeerId &peer);
void checkImageCacheSize();
bool isValidPhone(QString phone);
void quit();
bool quiting();
void setQuiting();
QImage readImage(QByteArray data, QByteArray *format = 0);
QImage readImage(const QString &file, QByteArray *format = 0);
void regVideoItem(VideoData *data, HistoryItem *item);
void unregVideoItem(VideoData *data, HistoryItem *item);
const VideoItems &videoItems();
void regAudioItem(AudioData *data, HistoryItem *item);
void unregAudioItem(AudioData*data, HistoryItem *item);
const AudioItems &audioItems();
void regDocumentItem(DocumentData *data, HistoryItem *item);
void unregDocumentItem(DocumentData *data, HistoryItem *item);
const DocumentItems &documentItems();
void setProxySettings(QNetworkAccessManager &manager);
void setProxySettings(QTcpSocket &socket);
};

View File

@@ -0,0 +1,644 @@
/*
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 "application.h"
#include "style.h"
#include "pspecific.h"
#include "fileuploader.h"
#include "mainwidget.h"
#include "supporttl.h"
#include "lang.h"
#include "boxes/confirmbox.h"
#include "langloaderplain.h"
namespace {
Application *mainApp = 0;
FileUploader *uploader = 0;
QString lng;
void mtpStateChanged(int32 dc, int32 state) {
if (App::wnd()) {
App::wnd()->mtpStateChanged(dc, state);
}
}
class _DebugWaiter : public QObject {
public:
_DebugWaiter(QObject *parent) : QObject(parent), _debugState(0) {
}
bool eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
switch (_debugState) {
case 0: if (ev->key() == Qt::Key_F12) _debugState = 1; break;
case 1: if (ev->key() == Qt::Key_F11) _debugState = 2; else if (ev->key() != Qt::Key_F12) _debugState = 0; break;
case 2: if (ev->key() == Qt::Key_F10) _debugState = 3; else if (ev->key() != Qt::Key_F11) _debugState = 0; break;
case 3: if (ev->key() == Qt::Key_F11) _debugState = 4; else if (ev->key() != Qt::Key_F10) _debugState = 0; break;
case 4: if (ev->key() == Qt::Key_F12) offerDebug(); if (ev->key() != Qt::Key_F11) _debugState = 0; break;
}
}
return QObject::eventFilter(o, e);
}
void offerDebug() {
ConfirmBox *box = new ConfirmBox(lang(lng_sure_enable_debug));
connect(box, SIGNAL(confirmed()), App::app(), SLOT(onEnableDebugMode()));
App::wnd()->showLayer(box);
}
private:
int _debugState;
};
}
Application::Application(int argc, char *argv[]) : PsApplication(argc, argv),
serverName(psServerPrefix() + cGUIDStr()), closing(false),
updateRequestId(0), updateThread(0), updateDownloader(0), updateReply(0) {
if (mainApp) {
DEBUG_LOG(("Application Error: another Application was created, terminating.."));
exit(0);
}
mainApp = this;
installEventFilter(new _DebugWaiter(this));
QFontDatabase::addApplicationFont(qsl(":/gui/art/segoe_ui.ttf"));
QFontDatabase::addApplicationFont(qsl(":/gui/art/segoe_ui_semibold.ttf"));
QFontDatabase::addApplicationFont(qsl(":/gui/art/segoe_wp_semibold.ttf"));
QFontDatabase::addApplicationFont(qsl(":/gui/art/ThoolikaTrditionalUnicode.ttf"));
float64 dpi = primaryScreen()->logicalDotsPerInch();
if (dpi <= 108) { // 0-96-108
cSetScreenScale(dbisOne);
} else if (dpi <= 132) { // 108-120-132
cSetScreenScale(dbisOneAndQuarter);
} else if (dpi <= 168) { // 132-144-168
cSetScreenScale(dbisOneAndHalf);
} else { // 168-192-inf
cSetScreenScale(dbisTwo);
}
if (!cLangFile().isEmpty()) {
LangLoaderPlain loader(cLangFile());
if (!loader.errors().isEmpty()) {
LOG(("Lang load errors: %1").arg(loader.errors()));
} else if (!loader.warnings().isEmpty()) {
LOG(("Lang load warnings: %1").arg(loader.warnings()));
}
}
style::startManager();
anim::startManager();
historyInit();
window = new Window();
psInstallEventFilter();
updateCheckTimer.setSingleShot(true);
connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected()));
connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
connect(&socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(socketError(QLocalSocket::LocalSocketError)));
connect(&socket, SIGNAL(bytesWritten(qint64)), this, SLOT(socketWritten(qint64)));
connect(&socket, SIGNAL(readyRead()), this, SLOT(socketReading()));
connect(&server, SIGNAL(newConnection()), this, SLOT(newInstanceConnected()));
connect(this, SIGNAL(aboutToQuit()), this, SLOT(closeApplication()));
connect(&updateCheckTimer, SIGNAL(timeout()), this, SLOT(startUpdateCheck()));
connect(this, SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
connect(this, SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
connect(&writeUserConfigTimer, SIGNAL(timeout()), this, SLOT(onWriteUserConfig()));
writeUserConfigTimer.setSingleShot(true);
if (cManyInstance()) {
startApp();
} else {
DEBUG_LOG(("Application Info: connecting local socket to %1..").arg(serverName));
socket.connectToServer(serverName);
}
}
void Application::onAppUpdate(const MTPhelp_AppUpdate &response) {
updateRequestId = 0;
cSetLastUpdateCheck(unixtime());
App::writeConfig();
if (response.type() == mtpc_help_noAppUpdate) {
startUpdateCheck();
} else {
updateThread = new QThread();
updateDownloader = new PsUpdateDownloader(updateThread, response.c_help_appUpdate());
updateThread->start();
}
}
bool Application::onAppUpdateFail() {
updateRequestId = 0;
cSetLastUpdateCheck(unixtime());
App::writeConfig();
startUpdateCheck();
return true;
}
void Application::updateGotCurrent() {
if (!updateReply || updateThread) return;
cSetLastUpdateCheck(unixtime());
QRegularExpressionMatch m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromUtf8(updateReply->readAll()));
if (m.hasMatch()) {
int32 currentVersion = m.captured(1).toInt();
if (currentVersion > AppVersion) {
updateThread = new QThread();
updateDownloader = new PsUpdateDownloader(updateThread, m.captured(2));
updateThread->start();
}
}
if (updateReply) updateReply->deleteLater();
updateReply = 0;
if (!updateThread) {
QDir updates(cWorkingDir() + "tupdates");
if (updates.exists()) {
QFileInfoList list = updates.entryInfoList(QDir::Files);
for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
if (QRegularExpression("^tupdate\\d+$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
QFile(i->absoluteFilePath()).remove();
}
}
}
emit updateLatest();
}
startUpdateCheck(true);
}
void Application::updateFailedCurrent(QNetworkReply::NetworkError e) {
LOG(("App Error: could not get current version (update check): %1").arg(e));
if (updateReply) updateReply->deleteLater();
updateReply = 0;
emit updateFailed();
startUpdateCheck(true);
}
void Application::onUpdateReady() {
if (updateDownloader) {
updateDownloader->deleteLater();
updateDownloader = 0;
}
updateCheckTimer.stop();
cSetLastUpdateCheck(unixtime());
App::writeConfig();
}
void Application::onUpdateFailed() {
if (updateDownloader) {
updateDownloader->deleteLater();
updateDownloader = 0;
if (updateThread) updateThread->deleteLater();
updateThread = 0;
}
cSetLastUpdateCheck(unixtime());
App::writeConfig();
}
void Application::regPhotoUpdate(const PeerId &peer, MsgId msgId) {
photoUpdates.insert(msgId, peer);
}
void Application::clearPhotoUpdates() {
photoUpdates.clear();
}
bool Application::isPhotoUpdating(const PeerId &peer) {
for (QMap<MsgId, PeerId>::iterator i = photoUpdates.begin(), e = photoUpdates.end(); i != e; ++i) {
if (i.value() == peer) {
return true;
}
}
return false;
}
void Application::cancelPhotoUpdate(const PeerId &peer) {
for (QMap<MsgId, PeerId>::iterator i = photoUpdates.begin(), e = photoUpdates.end(); i != e;) {
if (i.value() == peer) {
i = photoUpdates.erase(i);
} else {
++i;
}
}
}
void Application::selfPhotoCleared(const MTPUserProfilePhoto &result) {
if (!App::self()) return;
App::self()->setPhoto(result);
emit peerPhotoDone(App::self()->id);
}
void Application::chatPhotoCleared(PeerId peer, const MTPmessages_StatedMessage &result) {
if (App::main()) {
App::main()->sentFullDataReceived(0, result);
}
cancelPhotoUpdate(peer);
emit peerPhotoDone(peer);
}
void Application::selfPhotoDone(const MTPphotos_Photo &result) {
if (!App::self()) return;
const MTPDphotos_photo &photo(result.c_photos_photo());
App::feedPhoto(photo.vphoto);
App::feedUsers(photo.vusers);
cancelPhotoUpdate(App::self()->id);
emit peerPhotoDone(App::self()->id);
}
void Application::chatPhotoDone(PeerId peer, const MTPmessages_StatedMessage &result) {
if (App::main()) {
App::main()->sentFullDataReceived(0, result);
}
cancelPhotoUpdate(peer);
emit peerPhotoDone(peer);
}
bool Application::peerPhotoFail(PeerId peer, const RPCError &e) {
LOG(("Application Error: update photo failed %1: %2").arg(e.type()).arg(e.description()));
cancelPhotoUpdate(peer);
emit peerPhotoFail(peer);
return true;
}
void Application::peerClearPhoto(PeerId peer) {
if (App::self() && App::self()->id == peer) {
MTP::send(MTPphotos_UpdateProfilePhoto(MTP_inputPhotoEmpty(), MTP_inputPhotoCropAuto()), rpcDone(&Application::selfPhotoCleared), rpcFail(&Application::peerPhotoFail, peer));
} else {
MTP::send(MTPmessages_EditChatPhoto(MTP_int(int32(peer & 0xFFFFFFFF)), MTP_inputChatPhotoEmpty()), rpcDone(&Application::chatPhotoCleared, peer), rpcFail(&Application::peerPhotoFail, peer));
}
}
void Application::writeUserConfigIn(uint64 ms) {
if (!writeUserConfigTimer.isActive()) {
writeUserConfigTimer.start(ms);
}
}
void Application::onWriteUserConfig() {
App::writeUserConfig();
}
void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) {
if (!App::self()) return;
QMap<MsgId, PeerId>::iterator i = photoUpdates.find(msgId);
if (i != photoUpdates.end()) {
PeerId peer = i.value();
if (peer == App::self()->id) {
MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&Application::selfPhotoDone), rpcFail(&Application::peerPhotoFail, peer));
} else {
MTP::send(MTPmessages_EditChatPhoto(MTP_int(peer & 0xFFFFFFFF), MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&Application::chatPhotoDone, peer), rpcFail(&Application::peerPhotoFail, peer));
}
}
}
void Application::onEnableDebugMode() {
if (!cDebug()) {
logsInitDebug();
cSetDebug(true);
}
App::wnd()->hideLayer();
}
Application::UpdatingState Application::updatingState() {
if (!updateThread) return Application::UpdatingNone;
if (!updateDownloader) return Application::UpdatingReady;
return Application::UpdatingDownload;
}
int32 Application::updatingSize() {
if (!updateDownloader) return 0;
return updateDownloader->size();
}
int32 Application::updatingReady() {
if (!updateDownloader) return 0;
return updateDownloader->ready();
}
FileUploader *Application::uploader() {
if (!::uploader) ::uploader = new FileUploader();
return ::uploader;
}
void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId) {
PreparedPhotoThumbs photoThumbs;
QVector<MTPPhotoSize> photoSizes;
QPixmap thumb = QPixmap::fromImage(tosend.scaled(160, 160, Qt::KeepAspectRatio, Qt::SmoothTransformation));
photoThumbs.insert('a', thumb);
photoSizes.push_back(MTP_photoSize(MTP_string("a"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
QPixmap full = QPixmap::fromImage(tosend);
photoThumbs.insert('c', full);
photoSizes.push_back(MTP_photoSize(MTP_string("c"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
QByteArray jpeg;
QBuffer jpegBuffer(&jpeg);
full.save(&jpegBuffer, "JPG", 87);
PhotoId id = MTP::nonce<PhotoId>();
MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(MTP::authedId()), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes)));
QString file, filename;
int32 filesize = 0;
QByteArray data;
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg);
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
MsgId newId = clientMsgId();
App::app()->regPhotoUpdate(peerId, newId);
App::uploader()->uploadMedia(newId, ready);
}
void Application::stopUpdate() {
if (updateReply) {
updateReply->abort();
updateReply->deleteLater();
updateReply = 0;
}
if (updateDownloader) {
updateDownloader->deleteLater();
updateDownloader = 0;
if (updateThread) updateThread->deleteLater();
updateThread = 0;
}
}
void Application::startUpdateCheck(bool forceWait) {
updateCheckTimer.stop();
if (updateRequestId || updateThread || updateReply || !cAutoUpdate()) return;
int32 updateInSecs = cLastUpdateCheck() + 3600 + (rand() % 3600) - unixtime();
bool sendRequest = (updateInSecs <= 0 || updateInSecs > 7200);
if (!sendRequest && !forceWait) {
QDir updates(cWorkingDir() + "tupdates");
if (updates.exists()) {
QFileInfoList list = updates.entryInfoList(QDir::Files);
for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
if (QRegularExpression("^tupdate\\d+$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
sendRequest = true;
}
}
}
}
if (cManyInstance() && !cDebug()) return; // only main instance is updating
if (sendRequest) {
QNetworkRequest checkVersion(QUrl(qsl("http://tdesktop.com/win/tupdates/current")));
if (updateReply) updateReply->deleteLater();
App::setProxySettings(updateManager);
updateReply = updateManager.get(checkVersion);
connect(updateReply, SIGNAL(finished()), this, SLOT(updateGotCurrent()));
connect(updateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(updateFailedCurrent(QNetworkReply::NetworkError)));
// updateRequestId = MTP::send(MTPhelp_GetAppUpdate(MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(cApiLang())), rpcDone(&Application::onAppUpdate), rpcFail(&Application::onAppUpdateFail);
emit updateChecking();
} else {
updateCheckTimer.start((updateInSecs + 5) * 1000);
}
}
void Application::socketConnected() {
DEBUG_LOG(("Application Info: socket connected, this is not the first application instance, sending show command.."));
closing = true;
socket.write("CMD:show;");
}
void Application::socketWritten(qint64/* bytes*/) {
if (socket.state() != QLocalSocket::ConnectedState) {
DEBUG_LOG(("Application Error: socket is not connected %1").arg(socket.state()));
return;
}
if (socket.bytesToWrite()) {
return;
}
DEBUG_LOG(("Application Info: show command written, waiting response.."));
}
void Application::socketReading() {
if (socket.state() != QLocalSocket::ConnectedState) {
DEBUG_LOG(("Application Error: socket is not connected %1").arg(socket.state()));
return;
}
socketRead.append(socket.readAll());
if (QRegularExpression("RES:(\\d+);").match(socketRead).hasMatch()) {
uint64 pid = socketRead.mid(4, socketRead.length() - 5).toULongLong();
psActivateProcess(pid);
DEBUG_LOG(("Application Info: show command response received, pid = %1, activating and quiting..").arg(pid));
return App::quit();
}
}
void Application::socketError(QLocalSocket::LocalSocketError e) {
if (closing) {
DEBUG_LOG(("Application Error: could not write show command, error %1, quiting..").arg(e));
return App::quit();
}
if (e == QLocalSocket::ServerNotFoundError) {
DEBUG_LOG(("Application Info: this is the only instance of Telegram, starting server and app.."));
} else {
DEBUG_LOG(("Application Info: socket connect error %1, starting server and app..").arg(e));
}
socket.close();
psCheckLocalSocket(serverName);
if (!server.listen(serverName)) {
DEBUG_LOG(("Application Error: failed to start listening to %1 server").arg(serverName));
return App::quit();
}
if (!cNoStartUpdate() && psCheckReadyUpdate()) {
cSetRestartingUpdate(true);
DEBUG_LOG(("Application Info: installing update instead of starting app.."));
return App::quit();
}
startApp();
}
void Application::startApp() {
App::readUserConfig();
if (!MTP::localKey().created()) {
MTP::createLocalKey(QByteArray());
cSetNeedConfigResave(true);
}
if (cNeedConfigResave()) {
App::writeConfig();
App::writeUserConfig();
cSetNeedConfigResave(false);
}
window->createWinId();
window->init();
readSupportTemplates();
MTP::setLayer(mtpLayerMax);
MTP::start();
MTP::setStateChangedHandler(mtpStateChanged);
App::initMedia();
if (MTP::authedId()) {
window->setupMain(false);
} else {
window->setupIntro(false);
}
window->psFirstShow();
if (cStartToSettings()) {
window->showSettings();
}
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
void Application::socketDisconnected() {
if (closing) {
DEBUG_LOG(("Application Error: socket disconnected before command response received, quiting.."));
return App::quit();
}
}
void Application::newInstanceConnected() {
DEBUG_LOG(("Application Info: new local socket connected"));
for (QLocalSocket *client = server.nextPendingConnection(); client; client = server.nextPendingConnection()) {
clients.push_back(ClientSocket(client, QByteArray()));
connect(client, SIGNAL(readyRead()), this, SLOT(readClients()));
connect(client, SIGNAL(disconnected()), this, SLOT(removeClients()));
}
}
void Application::readClients() {
for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e; ++i) {
i->second.append(i->first->readAll());
if (i->second.size()) {
QString cmds(i->second);
int32 from = 0, l = cmds.length();
for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) {
QStringRef cmd(&cmds, from, to - from);
if (cmd.indexOf("CMD:") == 0) {
execExternal(cmds.mid(from + 4, to - from - 4));
QByteArray response(QString("RES:%1;").arg(QCoreApplication::applicationPid()).toUtf8());
i->first->write(response.data(), response.size());
} else {
LOG(("Application Error: unknown command %1 passed in local socket").arg(QString(cmd.constData(), cmd.length())));
}
from = to + 1;
}
if (from > 0) {
i->second = i->second.mid(from);
}
}
}
}
void Application::removeClients() {
DEBUG_LOG(("Application Info: remove clients slot called, clients %1").arg(clients.size()));
for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e;) {
if (i->first->state() != QLocalSocket::ConnectedState) {
DEBUG_LOG(("Application Info: removing client"));
i = clients.erase(i);
e = clients.end();
} else {
++i;
}
}
}
void Application::execExternal(const QString &cmd) {
DEBUG_LOG(("Application Info: executing external command '%1'").arg(cmd));
if (cmd == "show") {
window->activate();
}
}
void Application::closeApplication() {
// close server
server.close();
for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e; ++i) {
disconnect(i->first, SIGNAL(disconnected()), this, SLOT(removeClients()));
i->first->close();
}
clients.clear();
MTP::stop();
}
Application::~Application() {
App::setQuiting();
window->setParent(0);
anim::stopManager();
socket.close();
closeApplication();
App::deinitMedia();
mainApp = 0;
delete updateReply;
delete ::uploader;
updateReply = 0;
delete updateDownloader;
updateDownloader = 0;
delete updateThread;
updateThread = 0;
delete window;
style::stopManager();
}
Application *Application::app() {
return mainApp;
}
Window *Application::wnd() {
return mainApp ? mainApp->window : 0;
}
QString Application::lang() {
if (!lng.length()) {
lng = psCurrentLanguage();
}
if (!lng.length()) {
lng = "en";
}
return lng;
}
MainWidget *Application::main() {
return mainApp ? mainApp->window->mainWidget() : 0;
}

View File

@@ -0,0 +1,132 @@
/*
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 <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QNetworkReply>
#include "window.h"
#include "pspecific.h"
class MainWidget;
class FileUploader;
class Application : public PsApplication, public RPCSender {
Q_OBJECT
public:
Application(int argc, char *argv[]);
~Application();
static Application *app();
static Window *wnd();
static QString lang();
static MainWidget *main();
void onAppUpdate(const MTPhelp_AppUpdate &response);
bool onAppUpdateFail();
enum UpdatingState {
UpdatingNone,
UpdatingDownload,
UpdatingReady,
};
UpdatingState updatingState();
int32 updatingSize();
int32 updatingReady();
FileUploader *uploader();
void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId);
void regPhotoUpdate(const PeerId &peer, MsgId msgId);
void clearPhotoUpdates();
bool isPhotoUpdating(const PeerId &peer);
void cancelPhotoUpdate(const PeerId &peer);
void stopUpdate();
void selfPhotoCleared(const MTPUserProfilePhoto &result);
void chatPhotoCleared(PeerId peer, const MTPmessages_StatedMessage &result);
void selfPhotoDone(const MTPphotos_Photo &result);
void chatPhotoDone(PeerId peerId, const MTPmessages_StatedMessage &rersult);
bool peerPhotoFail(PeerId peerId, const RPCError &e);
void peerClearPhoto(PeerId peer);
void writeUserConfigIn(uint64 ms);
signals:
void peerPhotoDone(PeerId peer);
void peerPhotoFail(PeerId peer);
public slots:
void startUpdateCheck(bool forceWait = false);
void socketConnected();
void socketError(QLocalSocket::LocalSocketError e);
void socketDisconnected();
void socketWritten(qint64 bytes);
void socketReading();
void newInstanceConnected();
void closeApplication();
void readClients();
void removeClients();
void updateGotCurrent();
void updateFailedCurrent(QNetworkReply::NetworkError e);
void onUpdateReady();
void onUpdateFailed();
void photoUpdated(MsgId msgId, const MTPInputFile &file);
void onEnableDebugMode();
void onWriteUserConfig();
private:
QMap<MsgId, PeerId> photoUpdates;
void startApp();
typedef QPair<QLocalSocket*, QByteArray> ClientSocket;
typedef QVector<ClientSocket> ClientSockets;
QString serverName;
QLocalSocket socket;
QString socketRead;
QLocalServer server;
ClientSockets clients;
bool closing;
void execExternal(const QString &cmd);
Window *window;
mtpRequestId updateRequestId;
QNetworkAccessManager updateManager;
QNetworkReply *updateReply;
QTimer updateCheckTimer;
QThread *updateThread;
PsUpdateDownloader *updateDownloader;
QTimer writeUserConfigTimer;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More