From db3405699f8fc3ae28a58d2348b7d13a43c0590a Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 16 Sep 2026 21:39:41 +0400
Subject: [PATCH] Remove legacy interpret path helper.

---
 Telegram/SourceFiles/core/application.cpp     |   9 +-
 Telegram/SourceFiles/core/sandbox.cpp         | 139 +++++++++---
 Telegram/SourceFiles/core/sandbox.h           |   6 +-
 Telegram/SourceFiles/mainwidget.cpp           |  13 +-
 Telegram/SourceFiles/mainwidget.h             |   2 +-
 Telegram/SourceFiles/mainwindow.cpp           |   8 +-
 Telegram/SourceFiles/mainwindow.h             |   2 +-
 .../SourceFiles/support/support_helper.cpp    |  85 -------
 Telegram/SourceFiles/support/support_helper.h |   4 -
 Telegram/build/updates.py                     | 208 ------------------
 Telegram/build/updates.sh                     |  22 --
 11 files changed, 126 insertions(+), 372 deletions(-)
 delete mode 100644 Telegram/build/updates.py
 delete mode 100755 Telegram/build/updates.sh

diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp
index acfdcfdfb1743..d1cf3cdc395c1 100644
--- a/Telegram/SourceFiles/core/application.cpp
+++ b/Telegram/SourceFiles/core/application.cpp
@@ -1225,23 +1225,18 @@ void Application::checkStartUrls() {
 	if (!cRefStartUrls().isEmpty()
 		&& _lastActivePrimaryWindow
 		&& !_lastActivePrimaryWindow->locked()) {
-		auto interprets = QStringList();
 		auto paths = QStringList();
 		cRefStartUrls() = ranges::views::all(
 			cRefStartUrls()
 		) | ranges::views::filter([&](const QUrl &url) {
-			if (url.scheme() == u"interpret"_q) {
-				interprets.append(url.path());
-				return false;
-			} else if (url.isLocalFile()) {
+			if (url.isLocalFile()) {
 				paths.append(url.toLocalFile());
 				return false;
 			}
 			return true;
 		}) | ranges::to<QList<QUrl>>;
-		if (!interprets.isEmpty() || !paths.isEmpty()) {
+		if (!paths.isEmpty()) {
 			_lastActivePrimaryWindow->widget()->handleStartFiles(
-				std::move(interprets),
 				std::move(paths));
 		}
 	}
diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp
index ce256a2d403e5..a382d70acff54 100644
--- a/Telegram/SourceFiles/core/sandbox.cpp
+++ b/Telegram/SourceFiles/core/sandbox.cpp
@@ -52,6 +52,58 @@ base::options::toggle OptionDeadlockDetector({
 constexpr auto kCleanupIpcTimeout = 10 * crl::time(1000);
 constexpr auto kCleanupQuitTimeout = 30 * crl::time(1000);
 
+[[nodiscard]] QChar HexDigit(ushort value) {
+	value &= 0x000F;
+	return QChar::fromLatin1((value >= 10) ? ('a' + (value - 10)) : ('0' + value));
+}
+
+[[nodiscard]] ushort HexDigitValue(QChar ch) {
+	const auto code = ch.unicode();
+	return ((code >= uchar('a'))
+		? (code - uchar('a') + 10)
+		: (code - uchar('0'))) & 0x000F;
+}
+
+[[nodiscard]] QString EscapeTo7bit(const QString &value) {
+	auto result = QString();
+	result.reserve(value.size() * 2);
+	for (const auto ch : value) {
+		const auto code = ch.unicode();
+		if (code < 32
+			|| code > 127
+			|| ch == QChar('%')
+			|| ch == QChar(';')) {
+			result.append('%');
+			result.append(HexDigit(code >> 12));
+			result.append(HexDigit(code >> 8));
+			result.append(HexDigit(code >> 4));
+			result.append(HexDigit(code));
+		} else {
+			result.append(ch);
+		}
+	}
+	return result;
+}
+
+[[nodiscard]] QString EscapeFrom7bit(const QString &value) {
+	auto result = QString();
+	result.reserve(value.size());
+	for (auto i = 0; i != value.size(); ++i) {
+		const auto ch = value.at(i);
+		if (ch == QChar('%') && (i + 4 < value.size())) {
+			result.append(QChar(ushort(
+				(HexDigitValue(value.at(i + 1)) << 12)
+				| (HexDigitValue(value.at(i + 2)) << 8)
+				| (HexDigitValue(value.at(i + 3)) << 4)
+				| HexDigitValue(value.at(i + 4)))));
+			i += 4;
+		} else {
+			result.append(ch);
+		}
+	}
+	return result;
+}
+
 } // namespace
 
 const char kOptionDeadlockDetector[] = "deadlock-detector";
@@ -360,7 +412,9 @@ void Sandbox::socketConnected() {
 		commands += u"XDG_ACTIVATION_TOKEN:"_q + qgetenv("XDG_ACTIVATION_TOKEN").toBase64() + ';';
 	}
 	for (const auto &url : cRefStartUrls()) {
-		commands += u"OPEN:"_q + url.toString(QUrl::FullyEncoded) + ';';
+		commands += u"OPEN:"_q
+			+ EscapeTo7bit(url.toString(QUrl::FullyEncoded))
+			+ ';';
 	}
 	if (cQuit()) {
 		commands += u"CMD:quit;"_q;
@@ -496,7 +550,7 @@ void Sandbox::socketDisconnected() {
 void Sandbox::newInstanceConnected() {
 	DEBUG_LOG(("Sandbox Info: new local socket connected"));
 	for (auto client = _localServer.nextPendingConnection(); client; client = _localServer.nextPendingConnection()) {
-		_localClients.push_back(LocalClient(client, QByteArray()));
+		_localClients.push_back(LocalClient{ .socket = client });
 		connect(
 			client,
 			&QLocalSocket::readyRead,
@@ -511,47 +565,82 @@ void Sandbox::newInstanceConnected() {
 void Sandbox::readClients() {
 	// This method can be called before Application is constructed.
 	QList<QUrl> startUrls;
-	for (LocalClients::iterator i = _localClients.begin(), e = _localClients.end(); i != e; ++i) {
-		i->second.append(i->first->readAll());
-		if (i->second.size()) {
-			bool activationRequired = false;
-			QString cmds(QString::fromLatin1(i->second));
+	for (auto i = _localClients.begin(), e = _localClients.end(); i != e; ++i) {
+		i->buffer.append(i->socket->readAll());
+		if (i->buffer.size()) {
+			QString cmds(QString::fromLatin1(i->buffer));
 			int32 from = 0, l = cmds.length();
+			auto records = QStringList();
 			for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) {
-				auto cmd = base::StringViewMid(cmds, from, to - from);
+				records.push_back(cmds.mid(from, to - from));
+				from = to + 1;
+			}
+			if (from > 0) {
+				i->buffer = i->buffer.mid(from);
+			}
+			auto hasOpen = false;
+			for (const auto &cmd : records) {
+				if (cmd.startsWith(u"OPEN:"_q)) {
+					hasOpen = true;
+					break;
+				}
+			}
+			auto urls = QList<QUrl>();
+			for (const auto &cmd : records) {
 				if (cmd.startsWith(u"CMD:"_q)) {
+					if (hasOpen) {
+						continue;
+					}
 					const auto processId = QApplication::applicationPid();
-					const auto windowId = execExternal(cmds.mid(from + 4, to - from - 4));
+					const auto windowId = execExternal(cmd.mid(4));
 					const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
-					i->first->write(response.data(), response.size());
+					i->socket->write(response.data(), response.size());
 				} else if (cmd.startsWith(u"XDG_ACTIVATION_TOKEN:"_q)) {
-					qputenv("XDG_ACTIVATION_TOKEN", QByteArray::fromBase64(cmds.mid(from + 21, to - from - 21).toLatin1()));
+					qputenv("XDG_ACTIVATION_TOKEN", QByteArray::fromBase64(cmd.mid(21).toLatin1()));
 				} else if (cmd.startsWith(u"OPEN:"_q)) {
-					startUrls.append(cmds.mid(from + 5, to - from - 5).mid(0, 8192));
-					if (!activationRequired) {
-						activationRequired = StartUrlRequiresActivate(startUrls.back().toString());
-					}
+					urls.append(EscapeFrom7bit(cmd.mid(5)).mid(0, 8192));
 				} else if (cmd.startsWith(u"CTRL:"_q)) {
-					const auto payload = HandleExternalControl(
-						cmds.mid(from + 5, to - from - 5));
+					if (hasOpen) {
+						continue;
+					}
+					const auto payload = HandleExternalControl(cmd.mid(5));
 					const auto response = QByteArray("DATA:")
 						+ payload.toBase64()
 						+ ';';
-					i->first->write(response);
+					i->socket->write(response);
 				} else {
-					LOG(("Sandbox Error: unknown command %1 passed in local socket").arg(cmd.toString()));
+					LOG(("Sandbox Error: unknown command %1 passed in local socket").arg(cmd));
 				}
-				from = to + 1;
 			}
-			if (from > 0) {
-				i->second = i->second.mid(from);
+			// A link launch carries a single non-file url and a send-files
+			// launch carries only local paths, so a connection mixing both
+			// means the sender failed to escape the record separator and a
+			// crafted url smuggled extra records. Once such a connection
+			// shows a non-file url its local paths are dropped for good.
+			for (const auto &url : urls) {
+				if (!url.isLocalFile()) {
+					i->externalUrlReceived = true;
+				}
+			}
+			auto activationRequired = false;
+			for (const auto &url : urls) {
+				if (i->externalUrlReceived && url.isLocalFile()) {
+					LOG(("Sandbox Warning: local file dropped, "
+						"the same launch carries an external url: %1"
+						).arg(url.toString()));
+					continue;
+				}
+				startUrls.append(url);
+				if (!activationRequired) {
+					activationRequired = StartUrlRequiresActivate(url.toString());
+				}
 			}
 			const auto processId = QApplication::applicationPid();
 			const auto windowId = activationRequired
 				? execExternal("show")
 				: 0;
 			const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
-			i->first->write(response.data(), response.size());
+			i->socket->write(response.data(), response.size());
 		}
 	}
 	cRefStartUrls() << base::take(startUrls);
@@ -564,7 +653,7 @@ void Sandbox::removeClients() {
 	DEBUG_LOG(("Sandbox Info: remove clients slot called, clients %1"
 		).arg(_localClients.size()));
 	for (auto i = _localClients.begin(), e = _localClients.end(); i != e;) {
-		if (i->first->state() != QLocalSocket::ConnectedState) {
+		if (i->socket->state() != QLocalSocket::ConnectedState) {
 			DEBUG_LOG(("Sandbox Info: removing client"));
 			i = _localClients.erase(i);
 			e = _localClients.end();
@@ -736,7 +825,7 @@ void Sandbox::closeApplication() {
 
 	_localServer.close();
 	for (const auto &localClient : base::take(_localClients)) {
-		localClient.first->close();
+		localClient.socket->close();
 	}
 	_localClients.clear();
 
diff --git a/Telegram/SourceFiles/core/sandbox.h b/Telegram/SourceFiles/core/sandbox.h
index a1f7b4cfb4f8d..a30391d5ee891 100644
--- a/Telegram/SourceFiles/core/sandbox.h
+++ b/Telegram/SourceFiles/core/sandbox.h
@@ -81,7 +81,11 @@ class Sandbox final
 	bool event(QEvent *e) override;
 
 private:
-	typedef QPair<QLocalSocket*, QByteArray> LocalClient;
+	struct LocalClient {
+		QLocalSocket *socket = nullptr;
+		QByteArray buffer;
+		bool externalUrlReceived = false;
+	};
 	typedef QList<LocalClient> LocalClients;
 
 	struct PostponedCall {
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index e3930bc3ee592..c6405cb08dc0d 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -95,7 +95,6 @@ For license and copyright information please follow this link:
 #include "main/main_session_settings.h"
 #include "main/main_app_config.h"
 #include "settings/sections/settings_premium.h"
-#include "support/support_helper.h"
 #include "storage/storage_user_photos.h"
 #include "styles/style_dialogs.h"
 #include "styles/style_chat.h"
@@ -3247,17 +3246,7 @@ void MainWidget::activate() {
 	_controller->widget()->fixOrder();
 }
 
-void MainWidget::handleStartFiles(
-		QStringList interprets,
-		QStringList paths) {
-	for (const auto &interpret : interprets) {
-		const auto error = Support::InterpretSendPath(
-			_controller,
-			interpret);
-		if (!error.isEmpty()) {
-			_controller->show(Ui::MakeInformBox(error));
-		}
-	}
+void MainWidget::handleStartFiles(QStringList paths) {
 	if (!paths.isEmpty()) {
 		const auto chosen = [=](not_null<Data::Thread*> thread) {
 			return sendPaths(thread, paths);
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 060280fe61160..086a6992cb1cd 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -129,7 +129,7 @@ class MainWidget final
 	void showAnimated(QPixmap oldContentCache, bool back = false);
 
 	void activate();
-	void handleStartFiles(QStringList interprets, QStringList paths);
+	void handleStartFiles(QStringList paths);
 
 	void windowShown();
 
diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp
index 9cb9910a88ddb..03389031028f9 100644
--- a/Telegram/SourceFiles/mainwindow.cpp
+++ b/Telegram/SourceFiles/mainwindow.cpp
@@ -770,18 +770,14 @@ void MainWindow::updateControlsGeometry() {
 	if (_main) _main->checkMainSectionToLayer();
 }
 
-void MainWindow::handleStartFiles(
-		QStringList interprets,
-		QStringList paths) {
+void MainWindow::handleStartFiles(QStringList paths) {
 	if (controller().locked()) {
 		return;
 	}
 	Core::App().hideMediaView();
 	ui_hideSettingsAndLayer(anim::type::instant);
 	if (_main) {
-		_main->handleStartFiles(
-			std::move(interprets),
-			std::move(paths));
+		_main->handleStartFiles(std::move(paths));
 	}
 }
 
diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h
index 9290e00694e6e..3843e3b389196 100644
--- a/Telegram/SourceFiles/mainwindow.h
+++ b/Telegram/SourceFiles/mainwindow.h
@@ -75,7 +75,7 @@ class MainWindow : public Platform::MainWindow {
 
 	bool takeThirdSectionFromLayer();
 
-	void handleStartFiles(QStringList interprets, QStringList paths);
+	void handleStartFiles(QStringList paths);
 
 	[[nodiscard]] bool contentOverlapped(const QRect &globalRect);
 	[[nodiscard]] bool contentOverlapped(QWidget *w, QPaintEvent *e) {
diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp
index e31e770866007..ea802971b4626 100644
--- a/Telegram/SourceFiles/support/support_helper.cpp
+++ b/Telegram/SourceFiles/support/support_helper.cpp
@@ -9,8 +9,6 @@ For license and copyright information please follow this link:
 
 #include "dialogs/dialogs_key.h"
 #include "data/data_drafts.h"
-#include "data/data_forum.h"
-#include "data/data_forum_topic.h"
 #include "data/data_user.h"
 #include "data/data_session.h"
 #include "data/data_changes.h"
@@ -18,7 +16,6 @@ For license and copyright information please follow this link:
 #include "history/history.h"
 #include "boxes/abstract_box.h"
 #include "ui/boxes/confirm_box.h"
-#include "ui/chat/attach/attach_prepare.h"
 #include "ui/text/format_values.h"
 #include "ui/text/text_entity.h"
 #include "ui/text/text_options.h"
@@ -30,8 +27,6 @@ For license and copyright information please follow this link:
 #include "lang/lang_keys.h"
 #include "window/window_session_controller.h"
 #include "storage/storage_account.h"
-#include "storage/storage_media_prepare.h"
-#include "storage/localimageloader.h"
 #include "core/launcher.h"
 #include "core/application.h"
 #include "core/core_settings.h"
@@ -670,84 +665,4 @@ QString ChatOccupiedString(not_null<History*> history) {
 		: hand + ' ' + name + " is here";
 }
 
-QString InterpretSendPath(
-		not_null<Window::SessionController*> window,
-		const QString &path) {
-	QFile f(path);
-	if (!f.open(QIODevice::ReadOnly)) {
-		return "App Error: Could not open interpret file: " + path;
-	}
-	const auto content = QString::fromUtf8(f.readAll());
-	f.close();
-	const auto lines = content.split('\n');
-	auto toId = PeerId(0);
-	auto topicRootId = MsgId(0);
-	auto filePath = QString();
-	auto caption = QString();
-	for (const auto &line : lines) {
-		if (line.startsWith(u"from: "_q)) {
-			if (window->session().userId().bare
-				!= base::StringViewMid(
-					line,
-					u"from: "_q.size()).toULongLong()) {
-				return "App Error: Wrong current user.";
-			}
-		} else if (line.startsWith(u"channel: "_q)) {
-			const auto channelId = base::StringViewMid(
-				line,
-				u"channel: "_q.size()).toULongLong();
-			toId = peerFromChannel(channelId);
-		} else if (line.startsWith(u"topic: "_q)) {
-			const auto topicId = base::StringViewMid(
-				line,
-				u"topic: "_q.size()).toULongLong();
-			topicRootId = MsgId(topicId);
-		} else if (line.startsWith(u"file: "_q)) {
-			const auto path = line.mid(u"file: "_q.size());
-			if (!QFile(path).exists()) {
-				return "App Error: Could not find file with path: " + path;
-			}
-			filePath = path;
-		} else if (line.startsWith(u"caption: "_q)) {
-			caption = line.mid(u"caption: "_q.size());
-		} else if (!caption.isEmpty()) {
-			caption += '\n' + line;
-		} else {
-			return "App Error: Invalid command: " + line;
-		}
-	}
-	const auto history = window->session().data().historyLoaded(toId);
-	const auto sendTo = [=](not_null<Data::Thread*> thread) {
-		window->showThread(thread);
-		const auto premium = thread->session().user()->isPremium();
-		auto list = Storage::PrepareMediaList(
-			QStringList(filePath),
-			st::sendMediaPreviewSize,
-			premium);
-		if (!list.files.empty()) {
-			list.files.back().caption.text = caption;
-			thread->session().api().sendFiles(
-				std::move(list),
-				SendMediaType::File,
-				nullptr,
-				Api::SendAction(thread));
-		}
-	};
-	if (!history) {
-		return "App Error: Could not find channel with id: "
-			+ QString::number(peerToChannel(toId).bare);
-	} else if (const auto forum = history->asForum()) {
-		forum->requestTopic(topicRootId, [=] {
-			if (const auto forum = history->asForum()) {
-				if (const auto topic = forum->topicFor(topicRootId)) {
-					sendTo(topic);
-				}
-			}
-		});
-	} else if (!topicRootId) {
-		sendTo(history);
-	}
-	return QString();
-}
-
 } // namespace Support
diff --git a/Telegram/SourceFiles/support/support_helper.h b/Telegram/SourceFiles/support/support_helper.h
index 4094d0aa62e60..28c92b648086d 100644
--- a/Telegram/SourceFiles/support/support_helper.h
+++ b/Telegram/SourceFiles/support/support_helper.h
@@ -139,8 +139,4 @@ class FastButtonsBots final {
 
 QString ChatOccupiedString(not_null<History*> history);
 
-QString InterpretSendPath(
-	not_null<Window::SessionController*> window,
-	const QString &path);
-
 } // namespace Support
diff --git a/Telegram/build/updates.py b/Telegram/build/updates.py
deleted file mode 100644
index 2fef05505efba..0000000000000
--- a/Telegram/build/updates.py
+++ /dev/null
@@ -1,208 +0,0 @@
-import os, sys, re, subprocess, datetime, time
-
-executePath = os.getcwd()
-scriptPath = os.path.dirname(os.path.realpath(__file__))
-
-lastCommit = ''
-today = ''
-uuid = ''
-nextLast = False
-nextDate = False
-nextUuid = False
-building = True
-composing = False
-conf = 'Release'
-for arg in sys.argv:
-    if nextLast:
-        lastCommit = arg
-        nextLast = False
-    elif nextDate:
-        today = arg
-        nextDate = False
-    elif nextUuid:
-        uuid = arg
-        nextUuid = False
-    elif arg == 'send':
-        building = False
-        composing = False
-    elif arg == 'from':
-        nextLast = True
-        building = False
-        composing = True
-    elif arg == 'date':
-        nextDate = True
-    elif arg == 'request_uuid':
-        nextUuid = True
-    elif arg == 'debug':
-        conf = 'Debug'
-
-def finish(code, error = ''):
-    if error != '':
-        print('[ERROR] ' + error)
-    global executePath
-    os.chdir(executePath)
-    sys.exit(code)
-
-os.chdir(scriptPath + '/..')
-
-if 'AC_USERNAME' not in os.environ:
-    finish(1, 'AC_USERNAME not found!')
-username = os.environ['AC_USERNAME']
-
-if today == '':
-    today = datetime.datetime.now().strftime("%d_%m_%y")
-outputFolder = 'updates/' + today
-
-archive = 'tdesktop_macOS_' + today + '.zip'
-
-if building:
-    print('Building ' + conf + ' version for OS X 10.13+..')
-
-    if os.path.exists('../out/' + conf + '/' + outputFolder):
-        finish(1, 'Todays updates version exists.')
-
-    if uuid == '':
-        result = subprocess.call('./configure.sh', shell=True)
-        if result != 0:
-            finish(1, 'While calling GYP.')
-
-    os.chdir('../out')
-    if uuid == '':
-        result = subprocess.call('cmake --build . --config ' + conf + ' --target Telegram', shell=True)
-        if result != 0:
-            finish(1, 'While building Telegram.')
-
-    os.chdir(conf);
-    if uuid == '':
-        if not os.path.exists('Telegram.app'):
-            finish(1, 'Telegram.app not found.')
-
-        result = subprocess.call('strip Telegram.app/Contents/MacOS/Telegram', shell=True)
-        if result != 0:
-            finish(1, 'While stripping Telegram.')
-
-        result = subprocess.call('codesign --force --deep --timestamp --options runtime --sign "Developer ID Application: Telegram FZ-LLC (C67CF9S4VU)" Telegram.app --entitlements "../../Telegram/Telegram/Telegram.entitlements"', shell=True)
-        if result != 0:
-            finish(1, 'While signing Telegram.')
-
-        if not os.path.exists('Telegram.app/Contents/Frameworks/Updater'):
-            finish(1, 'Updater not found.')
-        elif not os.path.exists('Telegram.app/Contents/Helpers/crashpad_handler'):
-            finish(1, 'crashpad_handler not found.')
-        elif not os.path.exists('Telegram.app/Contents/_CodeSignature'):
-            finish(1, 'Signature not found.')
-
-        if os.path.exists(today):
-            subprocess.call('rm -rf ' + today, shell=True)
-        result = subprocess.call('mkdir -p ' + today + '/TelegramForcePortable', shell=True)
-        if result != 0:
-            finish(1, 'Creating folder ' + today + '/TelegramForcePortable')
-
-        result = subprocess.call('cp -r Telegram.app ' + today + '/', shell=True)
-        if result != 0:
-            finish(1, 'Cloning Telegram.app to ' + today + '.')
-
-        result = subprocess.call('zip -r ' + archive + ' ' + today, shell=True)
-        if result != 0:
-            finish(1, 'Adding tdesktop to archive.')
-
-        print('Beginning notarization process.')
-        result = subprocess.call('xcrun notarytool submit "' + archive + '" --keychain-profile "preston" --wait', shell=True)
-        if result != 0:
-            finish(1, 'Notarizing the archive.')
-    result = subprocess.call('xcrun stapler staple Telegram.app', shell=True)
-    if result != 0:
-        finish(1, 'Error calling stapler')
-
-    subprocess.call('rm -rf ' + today + '/Telegram.app', shell=True)
-    subprocess.call('rm ' + archive, shell=True)
-    result = subprocess.call('cp -r Telegram.app ' + today + '/', shell=True)
-    if result != 0:
-        finish(1, 'Re-Cloning Telegram.app to ' + today + '.')
-
-    result = subprocess.call('zip -r ' + archive + ' ' + today, shell=True)
-    if result != 0:
-        finish(1, 'Re-Adding tdesktop to archive.')
-    print('Re-Archived.')
-
-    subprocess.call('mkdir -p ' + outputFolder, shell=True)
-    subprocess.call('mv ' + archive + ' ' + outputFolder + '/', shell=True)
-    subprocess.call('rm -rf ' + today, shell=True)
-    print('Finished.')
-    finish(0)
-
-commandPath = scriptPath + '/../../out/' + conf + '/' + outputFolder + '/command.txt'
-
-if composing:
-    templatePath = scriptPath + '/../../../DesktopPrivate/updates_template.txt'
-    if not os.path.exists(templatePath):
-        finish(1, 'Template file "' + templatePath + '" not found.')
-
-    if not re.match(r'^[a-f0-9]{9,40}$', lastCommit):
-        finish(1, 'Wrong last commit: ' + lastCommit)
-
-    log = subprocess.check_output(['git', 'log', lastCommit+'..HEAD']).decode('utf-8')
-    logLines = log.split('\n')
-    firstCommit = ''
-    commits = []
-    for line in logLines:
-        if line.startswith('commit '):
-            commit = line.split(' ')[1]
-            if not len(firstCommit):
-                firstCommit = commit
-            commits.append('')
-        elif line.startswith('    '):
-            stripped = line[4:]
-            if not len(stripped):
-                continue
-            elif not len(commits):
-                print(log)
-                finish(1, 'Bad git log output.')
-            if len(commits[len(commits) - 1]):
-                commits[len(commits) - 1] += '\n' + stripped
-            else:
-                commits[len(commits) - 1] = '- ' + stripped
-    commits.reverse()
-    if not len(commits):
-        finish(1, 'No commits since last build :(')
-
-    changelog = '\n'.join(commits)
-    print('\n\nReady! File: ' + archive + '\nChangelog:\n' + changelog)
-    with open(templatePath, 'r') as template:
-        with open(commandPath, 'w') as f:
-            for line in template:
-                if line.startswith('//'):
-                    continue
-                line = line.replace('{path}', scriptPath + '/../../out/' + conf + '/' + outputFolder + '/' + archive)
-                line = line.replace('{caption}', 'TDesktop at ' + today.replace('_', '.') + ':\n\n' + changelog)
-                f.write(line)
-    print('\n\nEdit:\n')
-    print('vi ' + commandPath)
-    finish(0)
-
-if not os.path.exists(commandPath):
-    finish(1, 'Command file not found.')
-
-readingCaption = False
-caption = ''
-with open(commandPath, 'r') as f:
-    for line in f:
-        if readingCaption:
-            caption = caption + line
-        elif line.startswith('caption: '):
-            readingCaption = True
-            caption = line[len('caption: '):]
-            if not caption.startswith('TDesktop at ' + today.replace('_', '.') + ':'):
-                finish(1, 'Wrong caption start.')
-print('\n\nSending! File: ' + archive + '\nChangelog:\n' + caption)
-if len(caption) > 1024:
-    print('Length: ' + str(len(caption)))
-    print('vi ' + commandPath)
-    finish(1, 'Too large.')
-
-if not os.path.exists('../out/' + conf + '/' + outputFolder + '/' + archive):
-    finish(1, 'Not built yet.')
-
-subprocess.call(scriptPath + '/../../out/' + conf + '/Telegram.app/Contents/MacOS/Telegram -sendpath interpret://' + scriptPath + '/../../out/' + conf + '/' + outputFolder + '/command.txt', shell=True)
-
-finish(0)
diff --git a/Telegram/build/updates.sh b/Telegram/build/updates.sh
deleted file mode 100755
index 0e00178e044ac..0000000000000
--- a/Telegram/build/updates.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-set -e
-FullExecPath=$PWD
-pushd `dirname $0` > /dev/null
-FullScriptPath=`pwd`
-popd > /dev/null
-
-if [ ! -d "$FullScriptPath/../../../DesktopPrivate" ]; then
-  echo ""
-  echo "This script is for building the production version of Telegram Desktop."
-  echo ""
-  echo "For building custom versions please visit the build instructions page at:"
-  echo "https://github.com/telegramdesktop/tdesktop/#build-instructions"
-  exit
-fi
-
-pushd `dirname $0` > /dev/null
-FullScriptPath=`pwd`
-popd > /dev/null
-
-python3 $FullScriptPath/updates.py $1 $2 $3 $4 $5 $6
-
-exit
