initial commit for 0.4.18 version of Telegram Desktop
1280
Telegram/SourceFiles/_other/genemoji.cpp
Normal file
63
Telegram/SourceFiles/_other/genemoji.h
Normal 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;
|
||||
};
|
454
Telegram/SourceFiles/_other/genlang.cpp
Normal 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;
|
||||
}
|
61
Telegram/SourceFiles/_other/genlang.h
Normal 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;
|
||||
};
|
1817
Telegram/SourceFiles/_other/genstyles.cpp
Normal file
60
Telegram/SourceFiles/_other/genstyles.h
Normal 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;
|
||||
};
|
37
Telegram/SourceFiles/_other/memain.cpp
Normal 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();
|
||||
}
|
20
Telegram/SourceFiles/_other/memain.h
Normal 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"
|
37
Telegram/SourceFiles/_other/mlmain.cpp
Normal 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();
|
||||
}
|
20
Telegram/SourceFiles/_other/mlmain.h
Normal 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"
|
41
Telegram/SourceFiles/_other/msmain.cpp
Normal 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();
|
||||
}
|
20
Telegram/SourceFiles/_other/msmain.h
Normal 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"
|
298
Telegram/SourceFiles/_other/packer.cpp
Normal 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;
|
||||
}
|
44
Telegram/SourceFiles/_other/packer.h
Normal 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;
|
99
Telegram/SourceFiles/_other/prepare.cpp
Normal 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;
|
||||
}
|
37
Telegram/SourceFiles/_other/prepare.h
Normal 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;
|
459
Telegram/SourceFiles/_other/updater.cpp
Normal 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
|
40
Telegram/SourceFiles/_other/updater.h
Normal 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
187
Telegram/SourceFiles/app.h
Normal 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);
|
||||
|
||||
};
|
644
Telegram/SourceFiles/application.cpp
Normal 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;
|
||||
}
|
132
Telegram/SourceFiles/application.h
Normal 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;
|
||||
|
||||
};
|
BIN
Telegram/SourceFiles/art/Emoji/002320E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003020E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003120E3.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003220E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003320E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003420E3.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003520E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003620E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003720E3.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003820E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/003920E3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/00A9.png
Normal file
After Width: | Height: | Size: 706 B |
BIN
Telegram/SourceFiles/art/Emoji/00AE.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
Telegram/SourceFiles/art/Emoji/203C.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2049.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2122.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2139.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2194.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2195.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2196.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2197.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2198.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2199.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/21A9.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/21AA.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/231A.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/231B.png
Normal file
After Width: | Height: | Size: 856 B |
BIN
Telegram/SourceFiles/art/Emoji/23E9.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/23EA.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/23EB.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/23EC.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/23F0.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/SourceFiles/art/Emoji/23F3.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/24C2.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/SourceFiles/art/Emoji/25AA.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/SourceFiles/art/Emoji/25AB.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/SourceFiles/art/Emoji/25B6.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/25C0.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/25FB.png
Normal file
After Width: | Height: | Size: 515 B |
BIN
Telegram/SourceFiles/art/Emoji/25FC.png
Normal file
After Width: | Height: | Size: 560 B |
BIN
Telegram/SourceFiles/art/Emoji/25FD.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/SourceFiles/art/Emoji/25FE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2600.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2601.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/260E.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2611.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2614.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2615.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/SourceFiles/art/Emoji/261D.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/263A.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2648.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2649.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/264A.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/264B.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/SourceFiles/art/Emoji/264C.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/264D.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/264E.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/264F.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2650.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2651.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2652.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2653.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2660.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2663.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2665.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2666.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2668.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/SourceFiles/art/Emoji/267B.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/SourceFiles/art/Emoji/267F.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/Emoji/2693.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26A0.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26A1.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26AA.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26AB.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26BD.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26BE.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26C4.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/SourceFiles/art/Emoji/26C5.png
Normal file
After Width: | Height: | Size: 1.5 KiB |