mirror of
				https://github.com/fabianonline/telegram_backup.git
				synced 2025-11-04 01:27:47 +00:00 
			
		
		
		
	WIP: Lots and lots of rewriting.
This commit is contained in:
		@@ -22,120 +22,64 @@ import com.github.badoualy.telegram.mtproto.auth.AuthKey
 | 
			
		||||
import com.github.badoualy.telegram.mtproto.model.MTSession
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FileNotFoundException
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
 | 
			
		||||
internal class ApiStorage(prefix: String?) : TelegramApiStorage {
 | 
			
		||||
	private var prefix: String? = null
 | 
			
		||||
	private var do_save = false
 | 
			
		||||
	private var auth_key: AuthKey? = null
 | 
			
		||||
	private var dc: DataCenter? = null
 | 
			
		||||
	private var file_auth_key: File? = null
 | 
			
		||||
	private var file_dc: File? = null
 | 
			
		||||
internal class ApiStorage(val base_dir: String) : TelegramApiStorage {
 | 
			
		||||
	var auth_key: AuthKey? = null
 | 
			
		||||
	var dc: DataCenter? = null
 | 
			
		||||
	val file_auth_key: File
 | 
			
		||||
	val file_dc: File
 | 
			
		||||
	val logger = LoggerFactory.getLogger(ApiStorage::class.java)
 | 
			
		||||
	                                   
 | 
			
		||||
	
 | 
			
		||||
	init {
 | 
			
		||||
		this.setPrefix(prefix)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun setPrefix(prefix: String?) {
 | 
			
		||||
		this.prefix = prefix
 | 
			
		||||
		this.do_save = this.prefix != null
 | 
			
		||||
		if (this.do_save) {
 | 
			
		||||
			val base = Config.FILE_BASE +
 | 
			
		||||
				File.separatorChar +
 | 
			
		||||
				this.prefix +
 | 
			
		||||
				File.separatorChar
 | 
			
		||||
			this.file_auth_key = File(base + Config.FILE_NAME_AUTH_KEY)
 | 
			
		||||
			this.file_dc = File(base + Config.FILE_NAME_DC)
 | 
			
		||||
			this._saveAuthKey()
 | 
			
		||||
			this._saveDc()
 | 
			
		||||
		} else {
 | 
			
		||||
			this.file_auth_key = null
 | 
			
		||||
			this.file_dc = null
 | 
			
		||||
		}
 | 
			
		||||
		file_auth_key = File(base_dir + Config.FILE_NAME_AUTH_KEY)
 | 
			
		||||
		file_dc = File(base_dir + Config.FILE_NAME_DC)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override fun saveAuthKey(authKey: AuthKey) {
 | 
			
		||||
		this.auth_key = authKey
 | 
			
		||||
		this._saveAuthKey()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun _saveAuthKey() {
 | 
			
		||||
		if (this.do_save && this.auth_key != null) {
 | 
			
		||||
			try {
 | 
			
		||||
				FileUtils.writeByteArrayToFile(this.file_auth_key, this.auth_key!!.key)
 | 
			
		||||
			} catch (e: IOException) {
 | 
			
		||||
				e.printStackTrace()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		FileUtils.writeByteArrayToFile(file_auth_key, authKey.key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override fun loadAuthKey(): AuthKey? {
 | 
			
		||||
		if (this.auth_key != null) return this.auth_key
 | 
			
		||||
		if (this.file_auth_key != null) {
 | 
			
		||||
		try {
 | 
			
		||||
				return AuthKey(FileUtils.readFileToByteArray(this.file_auth_key))
 | 
			
		||||
			} catch (e: IOException) {
 | 
			
		||||
				if (e !is FileNotFoundException) e.printStackTrace()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			return AuthKey(FileUtils.readFileToByteArray(file_auth_key))
 | 
			
		||||
		} catch (e: FileNotFoundException) {
 | 
			
		||||
			return null
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override fun saveDc(dataCenter: DataCenter) {
 | 
			
		||||
		this.dc = dataCenter
 | 
			
		||||
		this._saveDc()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun _saveDc() {
 | 
			
		||||
		if (this.do_save && this.dc != null) {
 | 
			
		||||
			try {
 | 
			
		||||
				FileUtils.write(this.file_dc, this.dc!!.toString())
 | 
			
		||||
			} catch (e: IOException) {
 | 
			
		||||
				e.printStackTrace()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		FileUtils.write(file_dc, dataCenter.toString())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override fun loadDc(): DataCenter? {
 | 
			
		||||
		if (this.dc != null) return this.dc
 | 
			
		||||
		if (this.file_dc != null) {
 | 
			
		||||
		try {
 | 
			
		||||
			val infos = FileUtils.readFileToString(this.file_dc).split(":")
 | 
			
		||||
			return DataCenter(infos[0], Integer.parseInt(infos[1]))
 | 
			
		||||
			} catch (e: IOException) {
 | 
			
		||||
				if (e !is FileNotFoundException) e.printStackTrace()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		} catch (e: FileNotFoundException) {
 | 
			
		||||
			return null
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override fun deleteAuthKey() {
 | 
			
		||||
		if (this.do_save) {
 | 
			
		||||
		try {
 | 
			
		||||
				FileUtils.forceDelete(this.file_auth_key)
 | 
			
		||||
			FileUtils.forceDelete(file_auth_key)
 | 
			
		||||
		} catch (e: IOException) {
 | 
			
		||||
				e.printStackTrace()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			logger.warn("Exception in deleteAuthKey(): {}", e)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	override fun deleteDc() {
 | 
			
		||||
		if (this.do_save) {
 | 
			
		||||
		try {
 | 
			
		||||
				FileUtils.forceDelete(this.file_dc)
 | 
			
		||||
			FileUtils.forceDelete(file_dc)
 | 
			
		||||
		} catch (e: IOException) {
 | 
			
		||||
				e.printStackTrace()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			logger.warn("Exception in deleteDc(): {}", e)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,149 +29,136 @@ import java.util.HashMap
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
 | 
			
		||||
class CommandLineController {
 | 
			
		||||
	private val storage: ApiStorage
 | 
			
		||||
class CommandLineController(val options: CommandLineOptions) {
 | 
			
		||||
	init {
 | 
			
		||||
		val storage: ApiStorage
 | 
			
		||||
		val app: TelegramApp
 | 
			
		||||
		val target_dir: String
 | 
			
		||||
		val file_base: String
 | 
			
		||||
		val phone_number: String
 | 
			
		||||
	val account_file_base: String
 | 
			
		||||
 | 
			
		||||
	private fun getLine(): String {
 | 
			
		||||
		if (System.console() != null) {
 | 
			
		||||
			return System.console().readLine("> ")
 | 
			
		||||
		} else {
 | 
			
		||||
			print("> ")
 | 
			
		||||
			return Scanner(System.`in`).nextLine()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun getPassword(): String {
 | 
			
		||||
		if (System.console() != null) {
 | 
			
		||||
			return String(System.console().readPassword("> "))
 | 
			
		||||
		} else {
 | 
			
		||||
			return getLine()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	init {
 | 
			
		||||
		val handler: TelegramUpdateHandler
 | 
			
		||||
		val client: TelegramClient
 | 
			
		||||
		val user_manager: UserManager
 | 
			
		||||
		val inisettings: IniSettings
 | 
			
		||||
		val database: Database
 | 
			
		||||
		logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
 | 
			
		||||
		
 | 
			
		||||
		printHeader()
 | 
			
		||||
		if (CommandLineOptions.cmd_version) {
 | 
			
		||||
		if (options.cmd_version) {
 | 
			
		||||
			System.exit(0)
 | 
			
		||||
		} else if (CommandLineOptions.cmd_help) {
 | 
			
		||||
		} else if (options.cmd_help) {
 | 
			
		||||
			show_help()
 | 
			
		||||
			System.exit(0)
 | 
			
		||||
		} else if (CommandLineOptions.cmd_license) {
 | 
			
		||||
		} else if (options.cmd_license) {
 | 
			
		||||
			show_license()
 | 
			
		||||
			System.exit(0)
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		// Setup TelegramApp
 | 
			
		||||
		logger.debug("Initializing TelegramApp")
 | 
			
		||||
		app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG)
 | 
			
		||||
 | 
			
		||||
		// Setup file_base
 | 
			
		||||
		logger.debug("Target dir from Config: {}", Config.TARGET_DIR.anonymize())
 | 
			
		||||
		target_dir = CommandLineOptions.val_target ?: Config.TARGET_DIR
 | 
			
		||||
		target_dir = options.val_target ?: Config.TARGET_DIR
 | 
			
		||||
		logger.debug("Target dir after options: {}", target_dir)
 | 
			
		||||
		println("Base directory for files: ${target_dir.anonymize()}")
 | 
			
		||||
 | 
			
		||||
		if (CommandLineOptions.cmd_list_accounts) {
 | 
			
		||||
		if (options.cmd_list_accounts) {
 | 
			
		||||
			Utils.print_accounts(target_dir)
 | 
			
		||||
			System.exit(0)
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if (CommandLineOptions.cmd_login) {
 | 
			
		||||
			cmd_login(target_dir, CommandLineOptions.val_account)
 | 
			
		||||
		if (options.cmd_login) {
 | 
			
		||||
			cmd_login(app, target_dir, options.val_account)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.trace("Checking accounts")
 | 
			
		||||
		try {
 | 
			
		||||
			phone_number = selectAccount(target_dir, CommandLineOptions.val_account)
 | 
			
		||||
		phone_number = try { selectAccount(target_dir, options.val_account)
 | 
			
		||||
		} catch(e: AccountNotFoundException) {
 | 
			
		||||
			show_error("The specified account could not be found.")
 | 
			
		||||
		} catch(e: NoAccountsException) {
 | 
			
		||||
			println("No accounts found. Starting login process...")
 | 
			
		||||
			cmd_login(target_dir, CommandLineOptions.val_account)
 | 
			
		||||
			cmd_login(app, target_dir, options.val_account)
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		file_base = target_dir + File.separatorChar + account_to_use
 | 
			
		||||
		// TODO: Create a new TelegramApp if the user set his/her own TelegramApp credentials
 | 
			
		||||
 | 
			
		||||
		account = Account(file_base, account_to_use)
 | 
			
		||||
		// At this point we can assume that the selected user account ("phone_number") exists.
 | 
			
		||||
		// So we can create some objects:
 | 
			
		||||
		file_base = build_file_base(target_dir, phone_number)
 | 
			
		||||
 | 
			
		||||
		logger.debug("Initializing TelegramApp")
 | 
			
		||||
		app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG)
 | 
			
		||||
 | 
			
		||||
		logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
 | 
			
		||||
		logger.info("Initializing ApiStorage")
 | 
			
		||||
		storage = ApiStorage(account)
 | 
			
		||||
		storage = ApiStorage(file_base)
 | 
			
		||||
		
 | 
			
		||||
		logger.info("Initializing TelegramUpdateHandler")
 | 
			
		||||
		val handler = TelegramUpdateHandler()
 | 
			
		||||
		handler = TelegramUpdateHandler()
 | 
			
		||||
		
 | 
			
		||||
		logger.info("Creating Client")
 | 
			
		||||
		val client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler)
 | 
			
		||||
		client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler)
 | 
			
		||||
		
 | 
			
		||||
		// From now on we have a new catch-all-block that will terminate it's TelegramClient when an exception happens.
 | 
			
		||||
		try {
 | 
			
		||||
			logger.info("Initializing UserManager")
 | 
			
		||||
			UserManager.init(client)
 | 
			
		||||
			val user = UserManager.getInstance()
 | 
			
		||||
			if (!CommandLineOptions.cmd_login && !user.loggedIn) {
 | 
			
		||||
			user_manager = UserManager(client)
 | 
			
		||||
		
 | 
			
		||||
			// TODO
 | 
			
		||||
			/*if (!options.cmd_login && !user.loggedIn) {
 | 
			
		||||
				println("Your authorization data is invalid or missing. You will have to login with Telegram again.")
 | 
			
		||||
				CommandLineOptions.cmd_login = true
 | 
			
		||||
			}
 | 
			
		||||
			if (account != null && user.loggedIn) {
 | 
			
		||||
				if (account != "+" + user.user!!.getPhone()) {
 | 
			
		||||
					logger.error("Account: {}, user.user!!.getPhone(): +{}", account.anonymize(), user.user!!.getPhone().anonymize())
 | 
			
		||||
					throw RuntimeException("Account / User mismatch")
 | 
			
		||||
				}
 | 
			
		||||
				options.cmd_login = true
 | 
			
		||||
			}*/
 | 
			
		||||
			
 | 
			
		||||
			if (phone_number != user_manager.phone) {
 | 
			
		||||
				logger.error("phone_number: {}, user_manager.phone: {}", phone_number.anonymize(), user_manager.phone.anonymize())
 | 
			
		||||
				show_error("Account / User mismatch")
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			// Load the ini file.
 | 
			
		||||
			IniSettings.load()
 | 
			
		||||
			inisettings = IniSettings(file_base)
 | 
			
		||||
			
 | 
			
		||||
			logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
 | 
			
		||||
			if (CommandLineOptions.cmd_login) {
 | 
			
		||||
				cmd_login(CommandLineOptions.val_account)
 | 
			
		||||
				System.exit(0)
 | 
			
		||||
			}
 | 
			
		||||
			// If we reach this point, we can assume that there is an account and a database can be loaded / created.
 | 
			
		||||
			Database.init(client)
 | 
			
		||||
			if (CommandLineOptions.cmd_stats) {
 | 
			
		||||
				cmd_stats()
 | 
			
		||||
			database = Database(file_base, user_manager)
 | 
			
		||||
			
 | 
			
		||||
			if (options.cmd_stats) {
 | 
			
		||||
				cmd_stats(file_base, database)
 | 
			
		||||
				System.exit(0)
 | 
			
		||||
			}
 | 
			
		||||
			if (CommandLineOptions.val_test != null) {
 | 
			
		||||
				if (CommandLineOptions.val_test == 1) {
 | 
			
		||||
			
 | 
			
		||||
			if (options.val_test != null) {
 | 
			
		||||
				if (options.val_test == 1) {
 | 
			
		||||
					TestFeatures.test1()
 | 
			
		||||
				} else if (CommandLineOptions.val_test == 2) {
 | 
			
		||||
				} else if (options.val_test == 2) {
 | 
			
		||||
					TestFeatures.test2()
 | 
			
		||||
				} else {
 | 
			
		||||
					System.out.println("Unknown test " + CommandLineOptions.val_test)
 | 
			
		||||
					System.out.println("Unknown test " + options.val_test)
 | 
			
		||||
				}
 | 
			
		||||
				System.exit(1)
 | 
			
		||||
			}
 | 
			
		||||
			logger.debug("CommandLineOptions.val_export: {}", CommandLineOptions.val_export)
 | 
			
		||||
			if (CommandLineOptions.val_export != null) {
 | 
			
		||||
				if (CommandLineOptions.val_export!!.toLowerCase().equals("html")) {
 | 
			
		||||
					(HTMLExporter()).export()
 | 
			
		||||
			
 | 
			
		||||
			val export = options.val_export
 | 
			
		||||
			logger.debug("options.val_export: {}", export)
 | 
			
		||||
			if (export != null) {
 | 
			
		||||
				if (export.toLowerCase().equals("html")) {
 | 
			
		||||
					HTMLExporter().export()
 | 
			
		||||
					System.exit(0)
 | 
			
		||||
				} else {
 | 
			
		||||
					show_error("Unknown export format.")
 | 
			
		||||
					show_error("Unknown export format '${export}'.")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (user.loggedIn) {
 | 
			
		||||
				System.out.println("You are logged in as ${user.userString.anonymize()}")
 | 
			
		||||
			} else {
 | 
			
		||||
				println("You are not logged in.")
 | 
			
		||||
				System.exit(1)
 | 
			
		||||
			}
 | 
			
		||||
			logger.info("Initializing Download Manager")
 | 
			
		||||
			val d = DownloadManager(client, CommandLineDownloadProgress())
 | 
			
		||||
			
 | 
			
		||||
			if (CommandLineOptions.cmd_list_channels) {
 | 
			
		||||
			println("You are logged in as ${user_manager.toString().anonymize()}")
 | 
			
		||||
 | 
			
		||||
			logger.info("Initializing Download Manager")
 | 
			
		||||
			val d = DownloadManager(client, CommandLineDownloadProgress(), database, user_manager, inisettings)
 | 
			
		||||
			
 | 
			
		||||
			if (options.cmd_list_channels) {
 | 
			
		||||
				val chats = d.getChats()
 | 
			
		||||
				val print_header = {download: Boolean -> println("%-15s %-40s %s".format("ID", "Title", if (download) "Download" else "")); println("-".repeat(65)) }
 | 
			
		||||
				val format = {c: DownloadManager.Channel, download: Boolean -> "%-15s %-40s %s".format(c.id.toString().anonymize(), c.title.anonymize(), if (download) (if(c.download) "YES" else "no") else "")}
 | 
			
		||||
				var download: Boolean
 | 
			
		||||
 | 
			
		||||
				println("Channels:")
 | 
			
		||||
				download = IniSettings.download_channels
 | 
			
		||||
				download = inisettings.download_channels
 | 
			
		||||
				if (!download) println("Download of channels is disabled - see download_channels in config.ini")
 | 
			
		||||
				print_header(download)
 | 
			
		||||
				for (c in chats.channels) {
 | 
			
		||||
@@ -179,7 +166,7 @@ class CommandLineController {
 | 
			
		||||
				}
 | 
			
		||||
				println()
 | 
			
		||||
				println("Supergroups:")
 | 
			
		||||
				download = IniSettings.download_supergroups
 | 
			
		||||
				download = inisettings.download_supergroups
 | 
			
		||||
				if (!download) println("Download of supergroups is disabled - see download_supergroups in config.ini")
 | 
			
		||||
				print_header(download)
 | 
			
		||||
				for (c in chats.supergroups) {
 | 
			
		||||
@@ -188,10 +175,10 @@ class CommandLineController {
 | 
			
		||||
				System.exit(0)
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			logger.debug("Calling DownloadManager.downloadMessages with limit {}", CommandLineOptions.val_limit_messages)
 | 
			
		||||
			d.downloadMessages(CommandLineOptions.val_limit_messages)
 | 
			
		||||
			logger.debug("IniSettings.download_media: {}", IniSettings.download_media)
 | 
			
		||||
			if (IniSettings.download_media) {
 | 
			
		||||
			logger.debug("Calling DownloadManager.downloadMessages with limit {}", options.val_limit_messages)
 | 
			
		||||
			d.downloadMessages(options.val_limit_messages)
 | 
			
		||||
			logger.debug("IniSettings#download_media: {}", inisettings.download_media)
 | 
			
		||||
			if (inisettings.download_media) {
 | 
			
		||||
				logger.debug("Calling DownloadManager.downloadMedia")
 | 
			
		||||
				d.downloadMedia()
 | 
			
		||||
			} else {
 | 
			
		||||
@@ -202,9 +189,9 @@ class CommandLineController {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			logger.error("Exception caught!", e)
 | 
			
		||||
			// If we encountered an exception, we definitely don't want to start the daemon mode now.
 | 
			
		||||
			CommandLineOptions.cmd_daemon = false
 | 
			
		||||
			options.cmd_daemon = false
 | 
			
		||||
		} finally {
 | 
			
		||||
			if (CommandLineOptions.cmd_daemon) {
 | 
			
		||||
			if (options.cmd_daemon) {
 | 
			
		||||
				handler.activate()
 | 
			
		||||
				println("DAEMON mode requested - keeping running.")
 | 
			
		||||
			} else {
 | 
			
		||||
@@ -225,79 +212,63 @@ class CommandLineController {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun selectAccount(file_base: String, requested_account: String?): String {
 | 
			
		||||
		val found_account: String?
 | 
			
		||||
		var found_account: String? = null
 | 
			
		||||
		val accounts = Utils.getAccounts(file_base)
 | 
			
		||||
		if (requested_account != null) {
 | 
			
		||||
			logger.debug("Account requested: {}", requested_account.anonymize())
 | 
			
		||||
			logger.trace("Checking accounts for match.")
 | 
			
		||||
			found_account = accounts.find{it == requested_account}
 | 
			
		||||
 | 
			
		||||
			if (found_account == null) {
 | 
			
		||||
				throw AccountNotFoundException()
 | 
			
		||||
			}
 | 
			
		||||
		} else if (accounts.size == 0) {
 | 
			
		||||
			throw NoAccountsException()
 | 
			
		||||
		} else if (accounts.size == 1) {
 | 
			
		||||
			found_account = accounts.firstElement()
 | 
			
		||||
			println("Using only available account: " + account.anonymize())
 | 
			
		||||
			println("Using only available account: " + found_account.anonymize())
 | 
			
		||||
		} else {
 | 
			
		||||
			show_error(("You didn't specify which account to use.\n" +
 | 
			
		||||
			show_error(("You have more than one account but didn't specify which one to use.\n" +
 | 
			
		||||
				"Use '--account <x>' to use account <x>.\n" +
 | 
			
		||||
				"Use '--list-accounts' to see all available accounts."))
 | 
			
		||||
			System.exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		logger.debug("accounts.size: {}", accounts.size)
 | 
			
		||||
		logger.debug("account: {}", found_account.anonymize())
 | 
			
		||||
		return found_account!!
 | 
			
		||||
		
 | 
			
		||||
		if (found_account == null) {
 | 
			
		||||
			throw AccountNotFoundException()
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
	private fun cmd_stats() {
 | 
			
		||||
		logger.debug("accounts.size: {}", accounts.size)
 | 
			
		||||
		logger.debug("account: {}", found_account.anonymize())
 | 
			
		||||
		return found_account
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun cmd_stats(file_base: String, db: Database) {
 | 
			
		||||
		println()
 | 
			
		||||
		println("Stats:")
 | 
			
		||||
		val format = "%40s: %d%n"
 | 
			
		||||
		System.out.format(format, "Number of accounts", Utils.getAccounts().size)
 | 
			
		||||
		System.out.format(format, "Number of messages", Database.getInstance().getMessageCount())
 | 
			
		||||
		System.out.format(format, "Number of chats", Database.getInstance().getChatCount())
 | 
			
		||||
		System.out.format(format, "Number of users", Database.getInstance().getUserCount())
 | 
			
		||||
		System.out.format(format, "Top message ID", Database.getInstance().getTopMessageID())
 | 
			
		||||
		System.out.format(format, "Number of accounts", Utils.getAccounts(file_base).size)
 | 
			
		||||
		System.out.format(format, "Number of messages", db.getMessageCount())
 | 
			
		||||
		System.out.format(format, "Number of chats", db.getChatCount())
 | 
			
		||||
		System.out.format(format, "Number of users", db.getUserCount())
 | 
			
		||||
		System.out.format(format, "Top message ID", db.getTopMessageID())
 | 
			
		||||
		println()
 | 
			
		||||
		println("Media Types:")
 | 
			
		||||
		for ((key, value) in Database.getInstance().getMessageMediaTypesWithCount()) {
 | 
			
		||||
		for ((key, value) in db.getMessageMediaTypesWithCount()) {
 | 
			
		||||
			System.out.format(format, key, value)
 | 
			
		||||
		}
 | 
			
		||||
		println()
 | 
			
		||||
		println("Api layers of messages:")
 | 
			
		||||
		for ((key, value) in Database.getInstance().getMessageApiLayerWithCount()) {
 | 
			
		||||
		for ((key, value) in db.getMessageApiLayerWithCount()) {
 | 
			
		||||
			System.out.format(format, key, value)
 | 
			
		||||
		}
 | 
			
		||||
		println()
 | 
			
		||||
		println("Message source types:")
 | 
			
		||||
		for ((key, value) in Database.getInstance().getMessageSourceTypeWithCount()) {
 | 
			
		||||
		for ((key, value) in db.getMessageSourceTypeWithCount()) {
 | 
			
		||||
			System.out.format(format, key, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun cmd_login(target_dir: String, phoneToUse: String?): Nothing {
 | 
			
		||||
		val phone: String
 | 
			
		||||
		if (phoneToUse == null) {
 | 
			
		||||
			println("Please enter your phone number in international format.")
 | 
			
		||||
			println("Example: +4917077651234")
 | 
			
		||||
			phone = getLine()
 | 
			
		||||
		} else {
 | 
			
		||||
			phone = phoneToUse
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		user.sendCodeToPhoneNumber(phone)
 | 
			
		||||
		println("Telegram sent you a code. Please enter it here.")
 | 
			
		||||
		val code = getLine()
 | 
			
		||||
		user.verifyCode(code)
 | 
			
		||||
		if (user.isPasswordNeeded) {
 | 
			
		||||
			println("We also need your account password. Please enter it now. It should not be printed, so it's okay if you see nothing while typing it.")
 | 
			
		||||
			val pw = getPassword()
 | 
			
		||||
			user.verifyPassword(pw)
 | 
			
		||||
		}
 | 
			
		||||
		storage.setPrefix("+" + user.user!!.getPhone())
 | 
			
		||||
		System.out.println("Everything seems fine. Please run this tool again with '--account +" + user.user!!.getPhone().anonymize() + " to use this account.")
 | 
			
		||||
	private fun cmd_login(app: TelegramApp, target_dir: String, phoneToUse: String?): Nothing {
 | 
			
		||||
		LoginManager(app, target_dir, phoneToUse).run()
 | 
			
		||||
		System.exit(0)
 | 
			
		||||
		throw RuntimeException("Code never reaches this. This exists just to keep the Kotlin compiler happy.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun show_help() {
 | 
			
		||||
@@ -323,15 +294,18 @@ class CommandLineController {
 | 
			
		||||
	companion object {
 | 
			
		||||
		private val logger = LoggerFactory.getLogger(CommandLineController::class.java)
 | 
			
		||||
 | 
			
		||||
		public fun show_error(error: String) {
 | 
			
		||||
		public fun show_error(error: String): Nothing {
 | 
			
		||||
			logger.error(error)
 | 
			
		||||
			println("ERROR: " + error)
 | 
			
		||||
			System.exit(1)
 | 
			
		||||
			throw RuntimeException("Code never reaches this. This exists just to keep the Kotlin compiler happy.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fun show_license() {
 | 
			
		||||
			println("TODO: Print the GPL.")
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		fun build_file_base(target_dir: String, account_to_use: String) = target_dir + File.separatorChar + account_to_use + File.separatorChar
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	class AccountNotFoundException() : Exception("Account not found") {}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,10 @@
 | 
			
		||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
 | 
			
		||||
package de.fabianonline.telegram_backup
 | 
			
		||||
 | 
			
		||||
internal object CommandLineOptions {
 | 
			
		||||
	public var cmd_console = false
 | 
			
		||||
	public var cmd_help = false
 | 
			
		||||
	public var cmd_login = false
 | 
			
		||||
class CommandLineOptions(args: Array<String>) {
 | 
			
		||||
	var cmd_console = false
 | 
			
		||||
	var cmd_help = false
 | 
			
		||||
	var cmd_login = false
 | 
			
		||||
	var cmd_debug = false
 | 
			
		||||
	var cmd_trace = false
 | 
			
		||||
	var cmd_trace_telegram = false
 | 
			
		||||
@@ -34,8 +34,8 @@ internal object CommandLineOptions {
 | 
			
		||||
	var val_target: String? = null
 | 
			
		||||
	var val_export: String? = null
 | 
			
		||||
	var val_test: Int? = null
 | 
			
		||||
	@JvmStatic
 | 
			
		||||
	fun parseOptions(args: Array<String>) {
 | 
			
		||||
 | 
			
		||||
	init {
 | 
			
		||||
		var last_cmd: String? = null
 | 
			
		||||
		loop@ for (arg in args) {
 | 
			
		||||
			if (last_cmd != null) {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,24 +29,30 @@ import ch.qos.logback.core.ConsoleAppender
 | 
			
		||||
import ch.qos.logback.classic.Level
 | 
			
		||||
 | 
			
		||||
fun main(args: Array<String>) {
 | 
			
		||||
	CommandLineOptions.parseOptions(args)
 | 
			
		||||
	val clr = CommandLineRunner(args)
 | 
			
		||||
	
 | 
			
		||||
	CommandLineRunner.setupLogging()
 | 
			
		||||
	CommandLineRunner.checkVersion()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (true || CommandLineOptions.cmd_console) {
 | 
			
		||||
		// Always use the console for now.
 | 
			
		||||
		CommandLineController()
 | 
			
		||||
	} else {
 | 
			
		||||
		GUIController()
 | 
			
		||||
	}
 | 
			
		||||
	clr.setupLogging()
 | 
			
		||||
	clr.checkVersion()
 | 
			
		||||
	clr.run()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object CommandLineRunner {
 | 
			
		||||
	fun setupLogging() {
 | 
			
		||||
class CommandLineRunner(args: Array<String>) {
 | 
			
		||||
	val logger = LoggerFactory.getLogger(CommandLineRunner::class.java) as Logger
 | 
			
		||||
	val options = CommandLineOptions(args)
 | 
			
		||||
	
 | 
			
		||||
	fun run() {
 | 
			
		||||
		// Always use the console for now.
 | 
			
		||||
		try {
 | 
			
		||||
			CommandLineController(options)
 | 
			
		||||
		} catch (e: Throwable) {
 | 
			
		||||
			println("An error occured!")
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			logger.error("Exception caught!", e)
 | 
			
		||||
			System.exit(1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	fun setupLogging() {
 | 
			
		||||
		logger.trace("Setting up Loggers...")
 | 
			
		||||
		val rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger
 | 
			
		||||
		val rootContext = rootLogger.getLoggerContext()
 | 
			
		||||
@@ -65,13 +71,13 @@ object CommandLineRunner {
 | 
			
		||||
		rootLogger.addAppender(appender)
 | 
			
		||||
		rootLogger.setLevel(Level.OFF)
 | 
			
		||||
 | 
			
		||||
		if (CommandLineOptions.cmd_trace) {
 | 
			
		||||
		if (options.cmd_trace) {
 | 
			
		||||
			(LoggerFactory.getLogger("de.fabianonline.telegram_backup") as Logger).setLevel(Level.TRACE)
 | 
			
		||||
		} else if (CommandLineOptions.cmd_debug) {
 | 
			
		||||
		} else if (options.cmd_debug) {
 | 
			
		||||
			(LoggerFactory.getLogger("de.fabianonline.telegram_backup") as Logger).setLevel(Level.DEBUG)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (CommandLineOptions.cmd_trace_telegram) {
 | 
			
		||||
		if (options.cmd_trace_telegram) {
 | 
			
		||||
			(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import com.github.badoualy.telegram.tl.core.TLVector
 | 
			
		||||
import com.github.badoualy.telegram.api.TelegramClient
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
 | 
			
		||||
import java.sql.Connection
 | 
			
		||||
import java.sql.DriverManager
 | 
			
		||||
import java.sql.Statement
 | 
			
		||||
@@ -47,60 +46,45 @@ import java.text.SimpleDateFormat
 | 
			
		||||
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
 | 
			
		||||
import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory
 | 
			
		||||
 | 
			
		||||
class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	private var conn: Connection? = null
 | 
			
		||||
	private var stmt: Statement? = null
 | 
			
		||||
	var user_manager: UserManager
 | 
			
		||||
class Database constructor(val file_base: String, val user_manager: UserManager) {
 | 
			
		||||
	val conn: Connection
 | 
			
		||||
	val stmt: Statement
 | 
			
		||||
	val logger = LoggerFactory.getLogger(Database::class.java)
 | 
			
		||||
	
 | 
			
		||||
	init {
 | 
			
		||||
		private val logger = LoggerFactory.getLogger(Database::class.java)
 | 
			
		||||
		
 | 
			
		||||
		this.user_manager = UserManager.getInstance()
 | 
			
		||||
		System.out.println("Opening database...")
 | 
			
		||||
		println("Opening database...")
 | 
			
		||||
		try {
 | 
			
		||||
			Class.forName("org.sqlite.JDBC")
 | 
			
		||||
		} catch (e: ClassNotFoundException) {
 | 
			
		||||
			CommandLineController.show_error("Could not load jdbc-sqlite class.")
 | 
			
		||||
			throw RuntimeException("Could not load jdbc-sqlite class.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		val path = "jdbc:sqlite:${user_manager.fileBase}${Config.FILE_NAME_DB}"
 | 
			
		||||
		val path = "jdbc:sqlite:${file_base}${Config.FILE_NAME_DB}"
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			conn = DriverManager.getConnection(path)
 | 
			
		||||
			stmt = conn!!.createStatement()
 | 
			
		||||
			conn = DriverManager.getConnection(path)!!
 | 
			
		||||
			stmt = conn.createStatement()
 | 
			
		||||
		} catch (e: SQLException) {
 | 
			
		||||
			CommandLineController.show_error("Could not connect to SQLITE database.")
 | 
			
		||||
			throw RuntimeException("Could not connect to SQLITE database.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Run updates
 | 
			
		||||
		val updates = DatabaseUpdates(conn!!, this)
 | 
			
		||||
		updates.doUpdates()
 | 
			
		||||
 | 
			
		||||
		System.out.println("Database is ready.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getTopMessageID(): Int {
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT MAX(message_id) FROM messages WHERE source_type IN ('group', 'dialog')")
 | 
			
		||||
			rs.next()
 | 
			
		||||
			val result = rs.getInt(1)
 | 
			
		||||
			rs.close()
 | 
			
		||||
			return result
 | 
			
		||||
		} catch (e: SQLException) {
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		println("Database is ready.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getTopMessageID(): Int = queryInt("SELECT MAX(message_id) FROM messages WHERE source_type IN ('group', 'dialog')")
 | 
			
		||||
	fun getMessageCount(): Int = queryInt("SELECT COUNT(*) FROM messages")
 | 
			
		||||
	fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats")
 | 
			
		||||
	fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users")
 | 
			
		||||
 | 
			
		||||
	fun getMissingIDs: LinkedList<Int>()
 | 
			
		||||
	fun getMissingIDs(): LinkedList<Int> {
 | 
			
		||||
		try {
 | 
			
		||||
			val missing = LinkedList<Int>()
 | 
			
		||||
			val max = getTopMessageID()
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id")
 | 
			
		||||
			val rs = stmt.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id")
 | 
			
		||||
			rs.next()
 | 
			
		||||
			var id = rs.getInt(1)
 | 
			
		||||
			for (i in 1..max) {
 | 
			
		||||
@@ -126,7 +110,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	fun getMessagesWithMedia(): LinkedList<TLMessage?> {
 | 
			
		||||
		try {
 | 
			
		||||
			val list = LinkedList<TLMessage?>()
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT data FROM messages WHERE has_media=1")
 | 
			
		||||
			val rs = stmt.executeQuery("SELECT data FROM messages WHERE has_media=1")
 | 
			
		||||
			while (rs.next()) {
 | 
			
		||||
				list.add(bytesToTLMessage(rs.getBytes(1)))
 | 
			
		||||
			}
 | 
			
		||||
@@ -139,7 +123,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getMessagesFromUserCount() = queryInt("SELECT COUNT(*) FROM messages WHERE sender_id=" + user_manager.user!!.getId())
 | 
			
		||||
	fun getMessagesFromUserCount() = queryInt("SELECT COUNT(*) FROM messages WHERE sender_id=" + user_manager.id)
 | 
			
		||||
 | 
			
		||||
	fun getMessageTypesWithCount(): HashMap<String, Int> = getMessageTypesWithCount(GlobalChat())
 | 
			
		||||
 | 
			
		||||
@@ -148,7 +132,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	fun getMessageSourceTypeWithCount(): HashMap<String, Int> {
 | 
			
		||||
		val map = HashMap<String, Int>()
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT COUNT(id), source_type FROM messages GROUP BY source_type ORDER BY source_type")
 | 
			
		||||
			val rs = stmt.executeQuery("SELECT COUNT(id), source_type FROM messages GROUP BY source_type ORDER BY source_type")
 | 
			
		||||
			while (rs.next()) {
 | 
			
		||||
				val source_type = rs.getString(2) ?: "null"
 | 
			
		||||
				map.put("count.messages.source_type.${source_type}", rs.getInt(1))
 | 
			
		||||
@@ -163,7 +147,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	fun getMessageApiLayerWithCount(): HashMap<String, Int> {
 | 
			
		||||
		val map = HashMap<String, Int>()
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT COUNT(id), api_layer FROM messages GROUP BY api_layer ORDER BY api_layer")
 | 
			
		||||
			val rs = stmt.executeQuery("SELECT COUNT(id), api_layer FROM messages GROUP BY api_layer ORDER BY api_layer")
 | 
			
		||||
			while (rs.next()) {
 | 
			
		||||
				var layer = rs.getInt(2)
 | 
			
		||||
				map.put("count.messages.api_layer.$layer", rs.getInt(1))
 | 
			
		||||
@@ -179,25 +163,12 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
 | 
			
		||||
	fun getMessageTimesMatrix(): Array<IntArray> = getMessageTimesMatrix(GlobalChat())
 | 
			
		||||
 | 
			
		||||
	fun getEncoding(): String {
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("PRAGMA encoding")
 | 
			
		||||
			rs.next()
 | 
			
		||||
			val result = rs.getString(1)
 | 
			
		||||
			rs.close()
 | 
			
		||||
			return result
 | 
			
		||||
		} catch (e: SQLException) {
 | 
			
		||||
			logger.debug("SQLException: {}", e)
 | 
			
		||||
			return "unknown"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getEncoding(): String = queryString("PRAGMA encoding")
 | 
			
		||||
 | 
			
		||||
	fun getListOfChatsForExport(): LinkedList<Chat> {
 | 
			
		||||
		val list = LinkedList<Chat>()
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT chats.id, chats.name, COUNT(messages.id) as c " +
 | 
			
		||||
 | 
			
		||||
		val rs = stmt.executeQuery("SELECT chats.id, chats.name, COUNT(messages.id) as c " +
 | 
			
		||||
			"FROM chats, messages WHERE messages.source_type IN('group', 'supergroup', 'channel') AND messages.source_id=chats.id " +
 | 
			
		||||
			"GROUP BY chats.id ORDER BY c DESC")
 | 
			
		||||
		while (rs.next()) {
 | 
			
		||||
@@ -205,18 +176,12 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		}
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return list
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			throw RuntimeException("Exception above!")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	fun getListOfDialogsForExport(): LinkedList<Dialog> {
 | 
			
		||||
		val list = LinkedList<Dialog>()
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery(
 | 
			
		||||
		val rs = stmt.executeQuery(
 | 
			
		||||
			"SELECT users.id, first_name, last_name, username, COUNT(messages.id) as c " +
 | 
			
		||||
				"FROM users, messages WHERE messages.source_type='dialog' AND messages.source_id=users.id " +
 | 
			
		||||
				"GROUP BY users.id ORDER BY c DESC")
 | 
			
		||||
@@ -225,21 +190,14 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		}
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return list
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			throw RuntimeException("Exception above!")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	fun backupDatabase(currentVersion: Int) {
 | 
			
		||||
		val filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion)
 | 
			
		||||
		System.out.println("  Creating a backup of your database as " + filename)
 | 
			
		||||
		try {
 | 
			
		||||
			val src = user_manager.fileBase + Config.FILE_NAME_DB
 | 
			
		||||
			val dst = user_manager.fileBase + filename
 | 
			
		||||
			val src = file_base + Config.FILE_NAME_DB
 | 
			
		||||
			val dst = file_base + filename
 | 
			
		||||
			logger.debug("Copying {} to {}", src.anonymize(), dst.anonymize())
 | 
			
		||||
			Files.copy(
 | 
			
		||||
				File(src).toPath(),
 | 
			
		||||
@@ -256,8 +214,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	fun getTopMessageIDForChannel(id: Int): Int = queryInt("SELECT MAX(message_id) FROM messages WHERE source_id=$id AND source_type IN('channel', 'supergroup')")
 | 
			
		||||
 | 
			
		||||
	fun logRun(start_id: Int, end_id: Int, count: Int) {
 | 
			
		||||
		try {
 | 
			
		||||
			val ps = conn!!.prepareStatement("INSERT INTO runs " +
 | 
			
		||||
		val ps = conn.prepareStatement("INSERT INTO runs " +
 | 
			
		||||
			"(time,        start_id, end_id, count_missing) " +
 | 
			
		||||
			"VALUES " +
 | 
			
		||||
			"(DateTime('now'),    ?,        ?,      ?            )")
 | 
			
		||||
@@ -266,27 +223,26 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		ps.setInt(3, count)
 | 
			
		||||
		ps.execute()
 | 
			
		||||
		ps.close()
 | 
			
		||||
		} catch (e: SQLException) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun queryInt(query: String): Int {
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery(query)
 | 
			
		||||
		val rs = stmt.executeQuery(query)
 | 
			
		||||
		rs.next()
 | 
			
		||||
		val result = rs.getInt(1)
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return result
 | 
			
		||||
		} catch (e: SQLException) {
 | 
			
		||||
			throw RuntimeException("Could not get count of messages.")
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	fun queryString(query: String): String {
 | 
			
		||||
		val rs = stmt.executeQuery(query)
 | 
			
		||||
		rs.next()
 | 
			
		||||
		val result = rs.getString(1)
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return result
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Synchronized
 | 
			
		||||
	fun saveMessages(all: TLVector<TLAbsMessage>, api_layer: Int, source_type: MessageSource = MessageSource.NORMAL) {
 | 
			
		||||
		try {
 | 
			
		||||
		//"(id, dialog_id, from_id, from_type, text, time, has_media, data, sticker, type) " +
 | 
			
		||||
		//"VALUES " +
 | 
			
		||||
		//"(?,  ?,         ?,       ?,         ?,    ?,    ?,         ?,    ?,       ?)");
 | 
			
		||||
@@ -294,8 +250,8 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
			"VALUES " +
 | 
			
		||||
			"(?,          ?,            ?,           ?,         ?,         ?,           ?,    ?,    ?,         ?,          ?,          ?,          ?,    ?)"
 | 
			
		||||
		//1           2             3            4          5          6            7     8     9          10          11          12          13    14
 | 
			
		||||
			val ps = conn!!.prepareStatement("INSERT OR REPLACE INTO messages " + columns)
 | 
			
		||||
			val ps_insert_or_ignore = conn!!.prepareStatement("INSERT OR IGNORE INTO messages " + columns)
 | 
			
		||||
		val ps = conn.prepareStatement("INSERT OR REPLACE INTO messages " + columns)
 | 
			
		||||
		val ps_insert_or_ignore = conn.prepareStatement("INSERT OR IGNORE INTO messages " + columns)
 | 
			
		||||
 | 
			
		||||
		for (msg in all) {
 | 
			
		||||
			if (msg is TLMessage) {
 | 
			
		||||
@@ -307,7 +263,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
					ps.setInt(4, peer.getChatId())
 | 
			
		||||
				} else if (peer is TLPeerUser) {
 | 
			
		||||
					var id = peer.getUserId()
 | 
			
		||||
						if (id == this.user_manager.user!!.getId()) {
 | 
			
		||||
					if (id == user_manager.id) {
 | 
			
		||||
						id = msg.getFromId()
 | 
			
		||||
					}
 | 
			
		||||
					ps.setString(3, "dialog")
 | 
			
		||||
@@ -348,7 +304,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
				}
 | 
			
		||||
				ps.setString(7, text)
 | 
			
		||||
				ps.setString(8, "" + msg.getDate())
 | 
			
		||||
					val f = FileManagerFactory.getFileManager(msg, user_manager, client)
 | 
			
		||||
				val f = FileManagerFactory.getFileManager(msg, user_manager)
 | 
			
		||||
				if (f == null) {
 | 
			
		||||
					ps.setNull(9, Types.BOOLEAN)
 | 
			
		||||
					ps.setNull(10, Types.VARCHAR)
 | 
			
		||||
@@ -375,7 +331,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
					ps.setInt(4, peer.getChatId())
 | 
			
		||||
				} else if (peer is TLPeerUser) {
 | 
			
		||||
					var id = peer.getUserId()
 | 
			
		||||
						if (id == this.user_manager.user!!.getId()) {
 | 
			
		||||
					if (id == user_manager.id) {
 | 
			
		||||
						id = msg.getFromId()
 | 
			
		||||
					}
 | 
			
		||||
					ps.setString(3, "dialog")
 | 
			
		||||
@@ -423,32 +379,26 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
				throw RuntimeException("Unexpected Message type: " + msg.javaClass)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
			conn!!.setAutoCommit(false)
 | 
			
		||||
		conn.setAutoCommit(false)
 | 
			
		||||
		ps.executeBatch()
 | 
			
		||||
		ps.clearBatch()
 | 
			
		||||
		ps_insert_or_ignore.executeBatch()
 | 
			
		||||
		ps_insert_or_ignore.clearBatch()
 | 
			
		||||
			conn!!.commit()
 | 
			
		||||
			conn!!.setAutoCommit(true)
 | 
			
		||||
		conn.commit()
 | 
			
		||||
		conn.setAutoCommit(true)
 | 
			
		||||
		
 | 
			
		||||
		ps.close()
 | 
			
		||||
		ps_insert_or_ignore.close()
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			throw RuntimeException("Exception shown above happened.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Synchronized
 | 
			
		||||
	fun saveChats(all: TLVector<TLAbsChat>) {
 | 
			
		||||
		try {
 | 
			
		||||
			val ps_insert_or_replace = conn!!.prepareStatement(
 | 
			
		||||
		val ps_insert_or_replace = conn.prepareStatement(
 | 
			
		||||
			"INSERT OR REPLACE INTO chats " +
 | 
			
		||||
				"(id, name, type) " +
 | 
			
		||||
				"VALUES " +
 | 
			
		||||
				"(?,  ?,    ?)")
 | 
			
		||||
			val ps_insert_or_ignore = conn!!.prepareStatement(
 | 
			
		||||
		val ps_insert_or_ignore = conn.prepareStatement(
 | 
			
		||||
			"INSERT OR IGNORE INTO chats " +
 | 
			
		||||
				"(id, name, type) " +
 | 
			
		||||
				"VALUES " +
 | 
			
		||||
@@ -481,32 +431,26 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
				throw RuntimeException("Unexpected " + abs.javaClass)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
			conn!!.setAutoCommit(false)
 | 
			
		||||
		conn.setAutoCommit(false)
 | 
			
		||||
		ps_insert_or_ignore.executeBatch()
 | 
			
		||||
		ps_insert_or_ignore.clearBatch()
 | 
			
		||||
		ps_insert_or_replace.executeBatch()
 | 
			
		||||
		ps_insert_or_replace.clearBatch()
 | 
			
		||||
			conn!!.commit()
 | 
			
		||||
			conn!!.setAutoCommit(true)
 | 
			
		||||
		conn.commit()
 | 
			
		||||
		conn.setAutoCommit(true)
 | 
			
		||||
		
 | 
			
		||||
		ps_insert_or_ignore.close()
 | 
			
		||||
		ps_insert_or_replace.close()
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			throw RuntimeException("Exception shown above happened.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Synchronized
 | 
			
		||||
	fun saveUsers(all: TLVector<TLAbsUser>) {
 | 
			
		||||
		try {
 | 
			
		||||
			val ps_insert_or_replace = conn!!.prepareStatement(
 | 
			
		||||
		val ps_insert_or_replace = conn.prepareStatement(
 | 
			
		||||
			"INSERT OR REPLACE INTO users " +
 | 
			
		||||
				"(id, first_name, last_name, username, type, phone) " +
 | 
			
		||||
				"VALUES " +
 | 
			
		||||
				"(?,  ?,          ?,         ?,        ?,    ?)")
 | 
			
		||||
			val ps_insert_or_ignore = conn!!.prepareStatement(
 | 
			
		||||
		val ps_insert_or_ignore = conn.prepareStatement(
 | 
			
		||||
			"INSERT OR IGNORE INTO users " +
 | 
			
		||||
				"(id, first_name, last_name, username, type, phone) " +
 | 
			
		||||
				"VALUES " +
 | 
			
		||||
@@ -533,24 +477,20 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
				throw RuntimeException("Unexpected " + abs.javaClass)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
			conn!!.setAutoCommit(false)
 | 
			
		||||
		conn.setAutoCommit(false)
 | 
			
		||||
		ps_insert_or_ignore.executeBatch()
 | 
			
		||||
		ps_insert_or_ignore.clearBatch()
 | 
			
		||||
		ps_insert_or_replace.executeBatch()
 | 
			
		||||
		ps_insert_or_replace.clearBatch()
 | 
			
		||||
			conn!!.commit()
 | 
			
		||||
			conn!!.setAutoCommit(true)
 | 
			
		||||
		conn.commit()
 | 
			
		||||
		conn.setAutoCommit(true)
 | 
			
		||||
		
 | 
			
		||||
		ps_insert_or_ignore.close()
 | 
			
		||||
		ps_insert_or_replace.close()
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			throw RuntimeException("Exception shown above happened.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	fun fetchSetting(key: String): String? {
 | 
			
		||||
		val rs = stmt!!.executeQuery("SELECT value FROM settings WHERE key='${key}'")
 | 
			
		||||
		val rs = stmt.executeQuery("SELECT value FROM settings WHERE key='${key}'")
 | 
			
		||||
		rs.next()
 | 
			
		||||
		val result = rs.getString(1)
 | 
			
		||||
		rs.close()
 | 
			
		||||
@@ -558,7 +498,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	fun saveSetting(key: String, value: String?) {
 | 
			
		||||
		val ps = conn!!.prepareStatement("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")
 | 
			
		||||
		val ps = conn.prepareStatement("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")
 | 
			
		||||
		ps.setString(1, key)
 | 
			
		||||
		if (value==null) {
 | 
			
		||||
			ps.setNull(2, Types.VARCHAR)
 | 
			
		||||
@@ -570,56 +510,37 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	fun getIdsFromQuery(query: String): LinkedList<Int> {
 | 
			
		||||
		try {
 | 
			
		||||
		val list = LinkedList<Int>()
 | 
			
		||||
			val rs = stmt!!.executeQuery(query)
 | 
			
		||||
		val rs = stmt.executeQuery(query)
 | 
			
		||||
		while (rs.next()) {
 | 
			
		||||
			list.add(rs.getInt(1))
 | 
			
		||||
		}
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return list
 | 
			
		||||
		} catch (e: SQLException) {
 | 
			
		||||
			throw RuntimeException(e)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getMessageTypesWithCount(c: AbstractChat): HashMap<String, Int> {
 | 
			
		||||
		val map = HashMap<String, Int>()
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT message_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY message_type")
 | 
			
		||||
		val rs = stmt.executeQuery("SELECT message_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY message_type")
 | 
			
		||||
		while (rs.next()) {
 | 
			
		||||
			map.put("count.messages.type." + rs.getString(1), rs.getInt(2))
 | 
			
		||||
		}
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return map
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			throw RuntimeException(e)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getMessageMediaTypesWithCount(c: AbstractChat): HashMap<String, Int> {
 | 
			
		||||
		val map = HashMap<String, Int>()
 | 
			
		||||
		try {
 | 
			
		||||
		var count = 0
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT media_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY media_type")
 | 
			
		||||
		val rs = stmt.executeQuery("SELECT media_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY media_type")
 | 
			
		||||
		while (rs.next()) {
 | 
			
		||||
				var s = rs.getString(1)
 | 
			
		||||
				if (s == null) {
 | 
			
		||||
					s = "null"
 | 
			
		||||
				} else {
 | 
			
		||||
					count += rs.getInt(2)
 | 
			
		||||
				}
 | 
			
		||||
				map.put("count.messages.media_type.$s", rs.getInt(2))
 | 
			
		||||
			var type = rs.getString(1) ?: "null"
 | 
			
		||||
			if (type != "null") count += rs.getInt(2)
 | 
			
		||||
			map.put("count.messages.media_type.${type}", rs.getInt(2))
 | 
			
		||||
		}
 | 
			
		||||
		map.put("count.messages.media_type.any", count)
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return map
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			throw RuntimeException(e)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getMessageAuthorsWithCount(c: AbstractChat): HashMap<String, Any> {
 | 
			
		||||
@@ -629,8 +550,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		// Set a default value for 'me' to fix the charts for channels - cause I
 | 
			
		||||
		// possibly didn't send any messages there.
 | 
			
		||||
		map.put("authors.count.me", 0)
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT users.id, users.first_name, users.last_name, users.username, COUNT(messages.id) " +
 | 
			
		||||
		val rs = stmt.executeQuery("SELECT users.id, users.first_name, users.last_name, users.username, COUNT(messages.id) " +
 | 
			
		||||
			"FROM messages " +
 | 
			
		||||
			"LEFT JOIN users ON users.id=messages.sender_id " +
 | 
			
		||||
			"WHERE " + c.query + " GROUP BY sender_id")
 | 
			
		||||
@@ -656,23 +576,13 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		map.put("authors.all", all_data)
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return map
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			throw RuntimeException(e)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	fun getMessageCountForExport(c: AbstractChat): Int {
 | 
			
		||||
		val rs = stmt!!.executeQuery("SELECT COUNT(*) FROM messages WHERE " + c.query);
 | 
			
		||||
		rs.next()
 | 
			
		||||
		val result = rs.getInt(1)
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return result
 | 
			
		||||
	}
 | 
			
		||||
	fun getMessageCountForExport(c: AbstractChat): Int = queryInt("SELECT COUNT(*) FROM messages WHERE " + c.query)
 | 
			
		||||
 | 
			
		||||
	fun getMessageTimesMatrix(c: AbstractChat): Array<IntArray> {
 | 
			
		||||
		val result = Array(7) { IntArray(24) }
 | 
			
		||||
		try {
 | 
			
		||||
			val rs = stmt!!.executeQuery("SELECT STRFTIME('%w', time, 'unixepoch') as DAY, " +
 | 
			
		||||
		val rs = stmt.executeQuery("SELECT STRFTIME('%w', time, 'unixepoch') as DAY, " +
 | 
			
		||||
			"STRFTIME('%H', time, 'unixepoch') AS hour, " +
 | 
			
		||||
			"COUNT(id) FROM messages WHERE " + c.query + " GROUP BY hour, day " +
 | 
			
		||||
			"ORDER BY hour, day")
 | 
			
		||||
@@ -681,14 +591,9 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		}
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return result
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			throw RuntimeException(e)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fun getMessagesForExport(c: AbstractChat, limit: Int=-1, offset: Int=0): LinkedList<HashMap<String, Any>> {
 | 
			
		||||
		try {
 | 
			
		||||
		var query = "SELECT messages.message_id as message_id, text, time*1000 as time, has_media, " +
 | 
			
		||||
			"media_type, media_file, media_size, users.first_name as user_first_name, users.last_name as user_last_name, " +
 | 
			
		||||
			"users.username as user_username, users.id as user_id, " +
 | 
			
		||||
@@ -703,7 +608,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
			query = query + " LIMIT ${limit} OFFSET ${offset}"
 | 
			
		||||
		}	
 | 
			
		||||
		
 | 
			
		||||
			val rs = stmt!!.executeQuery(query)
 | 
			
		||||
		val rs = stmt.executeQuery(query)
 | 
			
		||||
		
 | 
			
		||||
		val format_time = SimpleDateFormat("HH:mm:ss")
 | 
			
		||||
		val format_date = SimpleDateFormat("d MMM yy")
 | 
			
		||||
@@ -727,7 +632,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
			if (rs.getString("media_type") != null) {
 | 
			
		||||
				h.put("media_" + rs.getString("media_type"), true)
 | 
			
		||||
			}
 | 
			
		||||
				h.put("from_me", rs.getInt("user_id") == user_manager.user!!.getId())
 | 
			
		||||
			h.put("from_me", rs.getInt("user_id") == user_manager.id)
 | 
			
		||||
			h.put("is_new_date", !date.equals(old_date))
 | 
			
		||||
			h.put("odd_even", if (count % 2 == 0) "even" else "odd")
 | 
			
		||||
			h.put("same_user", old_user != 0 && rs.getInt("user_id") == old_user)
 | 
			
		||||
@@ -739,11 +644,6 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		}
 | 
			
		||||
		rs.close()
 | 
			
		||||
		return list
 | 
			
		||||
		} catch (e: Exception) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			throw RuntimeException("Exception above!")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -753,21 +653,13 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inner class Dialog(var id: Int, var first_name: String?, var last_name: String?, var username: String?, var count: Int?) : AbstractChat() {
 | 
			
		||||
 | 
			
		||||
		override val query: String
 | 
			
		||||
			get() = "source_type='dialog' AND source_id=" + id
 | 
			
		||||
		
 | 
			
		||||
		override val type: String
 | 
			
		||||
			get() = "dialog"
 | 
			
		||||
		override val query = "source_type='dialog' AND source_id=" + id
 | 
			
		||||
		override val type = "dialog"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inner class Chat(var id: Int, var name: String?, var count: Int?) : AbstractChat() {
 | 
			
		||||
 | 
			
		||||
		override val query: String
 | 
			
		||||
			get() = "source_type IN('group', 'supergroup', 'channel') AND source_id=" + id
 | 
			
		||||
		
 | 
			
		||||
		override val type: String
 | 
			
		||||
			get() = "chat"
 | 
			
		||||
		override val query = "source_type IN('group', 'supergroup', 'channel') AND source_id=" + id
 | 
			
		||||
		override val type = "chat"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inner class User(id: Int, first_name: String?, last_name: String?) {
 | 
			
		||||
@@ -775,7 +667,7 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
		var isMe: Boolean = false
 | 
			
		||||
 | 
			
		||||
		init {
 | 
			
		||||
			isMe = id == user_manager.user!!.getId()
 | 
			
		||||
			isMe = id == user_manager.id
 | 
			
		||||
			val s = StringBuilder()
 | 
			
		||||
			if (first_name != null) s.append(first_name + " ")
 | 
			
		||||
			if (last_name != null) s.append(last_name)
 | 
			
		||||
@@ -784,11 +676,8 @@ class Database private constructor(var client: TelegramClient) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inner class GlobalChat : AbstractChat() {
 | 
			
		||||
		override val query: String
 | 
			
		||||
			get() = "1=1"
 | 
			
		||||
		
 | 
			
		||||
		override val type: String
 | 
			
		||||
			get() = "GlobalChat"
 | 
			
		||||
		override val query = "1=1"
 | 
			
		||||
		override val type = "GlobalChat"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -313,7 +313,7 @@ internal class DB_Update_6(conn: Connection, db: Database) : DatabaseUpdate(conn
 | 
			
		||||
			} else {
 | 
			
		||||
				ps.setInt(1, msg.getFwdFrom().getFromId())
 | 
			
		||||
			}
 | 
			
		||||
			val f = FileManagerFactory.getFileManager(msg, db.user_manager, db.client)
 | 
			
		||||
			val f = FileManagerFactory.getFileManager(msg, db.user_manager)
 | 
			
		||||
			if (f == null) {
 | 
			
		||||
				ps.setNull(2, Types.VARCHAR)
 | 
			
		||||
				ps.setNull(3, Types.VARCHAR)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package de.fabianonline.telegram_backup
 | 
			
		||||
 | 
			
		||||
class DbSettings() {
 | 
			
		||||
	private fun fetchValue(name: String): String? = Database.getInstance().fetchSetting(name)
 | 
			
		||||
	private fun saveValue(name: String, value: String?) = Database.getInstance().saveSetting(name, value)
 | 
			
		||||
class DbSettings(val database: Database) {
 | 
			
		||||
	private fun fetchValue(name: String): String? = database.fetchSetting(name)
 | 
			
		||||
	private fun saveValue(name: String, value: String?) = database.saveSetting(name, value)
 | 
			
		||||
	
 | 
			
		||||
	var pts: String?
 | 
			
		||||
		get() = fetchValue("pts")
 | 
			
		||||
 
 | 
			
		||||
@@ -61,18 +61,9 @@ enum class MessageSource(val descr: String) {
 | 
			
		||||
	SUPERGROUP("supergroup")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressInterface) {
 | 
			
		||||
	internal var user: UserManager? = null
 | 
			
		||||
	internal var db: Database? = null
 | 
			
		||||
	internal var prog: DownloadProgressInterface? = null
 | 
			
		||||
class DownloadManager(val client: TelegramClient, val prog: DownloadProgressInterface, val db: Database, val user_manager: UserManager, val inisettings: IniSettings) {
 | 
			
		||||
	internal var has_seen_flood_wait_message = false
 | 
			
		||||
 | 
			
		||||
	init {
 | 
			
		||||
		this.user = UserManager.getInstance()
 | 
			
		||||
		this.prog = p
 | 
			
		||||
		this.db = Database.getInstance()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Throws(RpcErrorException::class, IOException::class)
 | 
			
		||||
	fun downloadMessages(limit: Int?) {
 | 
			
		||||
		var completed: Boolean
 | 
			
		||||
@@ -173,20 +164,16 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
 | 
			
		||||
		}
 | 
			
		||||
		*/
 | 
			
		||||
 | 
			
		||||
		if (IniSettings.download_channels || IniSettings.download_supergroups) {
 | 
			
		||||
			// TODO Add chat title (and other stuff?) to the database
 | 
			
		||||
			
 | 
			
		||||
			if (IniSettings.download_channels) {
 | 
			
		||||
		if (inisettings.download_channels) {
 | 
			
		||||
			println("Checking channels...")
 | 
			
		||||
			for (channel in chats.channels) { if (channel.download) downloadMessagesFromChannel(channel) }
 | 
			
		||||
		}
 | 
			
		||||
			
 | 
			
		||||
			if (IniSettings.download_supergroups) {
 | 
			
		||||
		if (inisettings.download_supergroups) {
 | 
			
		||||
			println("Checking supergroups...")
 | 
			
		||||
			for (supergroup in chats.supergroups) { if (supergroup.download) downloadMessagesFromChannel(supergroup) }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private fun downloadMessagesFromChannel(channel: Channel) {
 | 
			
		||||
		val obj = channel.obj
 | 
			
		||||
@@ -315,7 +302,7 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
 | 
			
		||||
		prog!!.onMediaDownloadStart(messages.size)
 | 
			
		||||
		for (msg in messages) {
 | 
			
		||||
			if (msg == null) continue
 | 
			
		||||
			val m = FileManagerFactory.getFileManager(msg, user!!, client!!)
 | 
			
		||||
			val m = FileManagerFactory.getFileManager(msg, user_manager)
 | 
			
		||||
			logger.trace("message {}, {}, {}, {}, {}",
 | 
			
		||||
				msg.getId(),
 | 
			
		||||
				msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
 | 
			
		||||
@@ -366,10 +353,10 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
 | 
			
		||||
			if (tl_peer_channel == null) continue
 | 
			
		||||
			
 | 
			
		||||
			var download = true
 | 
			
		||||
			if (IniSettings.whitelist_channels != null) {
 | 
			
		||||
				download = IniSettings.whitelist_channels!!.contains(tl_channel.getId().toString())
 | 
			
		||||
			} else if (IniSettings.blacklist_channels != null) {
 | 
			
		||||
				download = !IniSettings.blacklist_channels!!.contains(tl_channel.getId().toString())
 | 
			
		||||
			if (inisettings.whitelist_channels != null) {
 | 
			
		||||
				download = inisettings.whitelist_channels.contains(tl_channel.getId().toString())
 | 
			
		||||
			} else if (inisettings.blacklist_channels != null) {
 | 
			
		||||
				download = !inisettings.blacklist_channels.contains(tl_channel.getId().toString())
 | 
			
		||||
			}
 | 
			
		||||
			val channel = Channel(id=tl_channel.getId(), access_hash=tl_channel.getAccessHash(), title=tl_channel.getTitle(), obj=tl_peer_channel, download=download)
 | 
			
		||||
			if (tl_channel.getMegagroup()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,85 +0,0 @@
 | 
			
		||||
/* Telegram_Backup
 | 
			
		||||
 * Copyright (C) 2016 Fabian Schlenz
 | 
			
		||||
 * 
 | 
			
		||||
 * This program 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * This program 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 | 
			
		||||
 | 
			
		||||
package de.fabianonline.telegram_backup
 | 
			
		||||
 | 
			
		||||
import javax.swing.*
 | 
			
		||||
import javax.swing.event.ListSelectionEvent
 | 
			
		||||
import javax.swing.event.ListSelectionListener
 | 
			
		||||
import java.awt.*
 | 
			
		||||
import java.awt.event.ActionEvent
 | 
			
		||||
import java.awt.event.ActionListener
 | 
			
		||||
import java.util.Vector
 | 
			
		||||
 | 
			
		||||
class GUIController() {
 | 
			
		||||
	init {
 | 
			
		||||
		showAccountChooserDialog()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun showAccountChooserDialog() {
 | 
			
		||||
		val accountChooser = JDialog()
 | 
			
		||||
		accountChooser.setTitle("Choose account")
 | 
			
		||||
		accountChooser.setSize(400, 200)
 | 
			
		||||
		val vert = JPanel()
 | 
			
		||||
		vert.setLayout(BorderLayout())
 | 
			
		||||
		vert.add(JLabel("Please select the account to use or create a new one."), BorderLayout.NORTH)
 | 
			
		||||
		val accounts = Utils.getAccounts()
 | 
			
		||||
		val list = JList<String>(accounts)
 | 
			
		||||
		list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
 | 
			
		||||
		vert.add(list, BorderLayout.CENTER)
 | 
			
		||||
		val bottom = JPanel(GridLayout(1, 2))
 | 
			
		||||
		val btnAddAccount = JButton("Add account")
 | 
			
		||||
		bottom.add(btnAddAccount)
 | 
			
		||||
		val btnLogin = JButton("Login")
 | 
			
		||||
		btnLogin.setEnabled(false)
 | 
			
		||||
		bottom.add(btnLogin)
 | 
			
		||||
		vert.add(bottom, BorderLayout.SOUTH)
 | 
			
		||||
		accountChooser.add(vert)
 | 
			
		||||
		accountChooser.setVisible(true)
 | 
			
		||||
		accountChooser.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun addAccountDialog() {
 | 
			
		||||
		val loginDialog = JDialog()
 | 
			
		||||
		loginDialog.setTitle("Add an account")
 | 
			
		||||
		loginDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
 | 
			
		||||
 | 
			
		||||
		val sections = JPanel()
 | 
			
		||||
		sections.setLayout(BoxLayout(sections, BoxLayout.Y_AXIS))
 | 
			
		||||
 | 
			
		||||
		val top = JPanel()
 | 
			
		||||
		top.setLayout(BoxLayout(top, BoxLayout.Y_AXIS))
 | 
			
		||||
		top.add(JLabel("Please enter your phone number in international format:"))
 | 
			
		||||
		top.add(JTextField("+49123773212"))
 | 
			
		||||
 | 
			
		||||
		sections.add(top)
 | 
			
		||||
		sections.add(Box.createVerticalStrut(5))
 | 
			
		||||
		sections.add(JSeparator(SwingConstants.HORIZONTAL))
 | 
			
		||||
 | 
			
		||||
		val middle = JPanel()
 | 
			
		||||
		middle.setLayout(BoxLayout(middle, BoxLayout.Y_AXIS))
 | 
			
		||||
		middle.add(JLabel("Telegram sent you a code. Enter it here:"))
 | 
			
		||||
		middle.add(JTextField())
 | 
			
		||||
		middle.setEnabled(false)
 | 
			
		||||
 | 
			
		||||
		sections.add(middle)
 | 
			
		||||
		sections.add(Box.createVerticalStrut(5))
 | 
			
		||||
		sections.add(JSeparator(SwingConstants.HORIZONTAL))
 | 
			
		||||
 | 
			
		||||
		loginDialog.add(sections)
 | 
			
		||||
		loginDialog.setVisible(true)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -4,19 +4,14 @@ import java.io.File
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
 | 
			
		||||
object IniSettings {
 | 
			
		||||
class IniSettings(val file_base: String) {
 | 
			
		||||
	val logger = LoggerFactory.getLogger(IniSettings::class.java)
 | 
			
		||||
	var settings = mutableMapOf<String, MutableList<String>>()
 | 
			
		||||
	
 | 
			
		||||
	init {
 | 
			
		||||
		if (UserManager.getInstance().user != null) {
 | 
			
		||||
			loadIni(UserManager.getInstance().fileBase + "config.ini")
 | 
			
		||||
			copySampleIni(UserManager.getInstance().fileBase + "config.sample.ini")
 | 
			
		||||
		loadIni(file_base + "config.ini")
 | 
			
		||||
		copySampleIni(file_base + "config.sample.ini")
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Dummy function that can be called to force this object to run its init-code.
 | 
			
		||||
	fun load() { }
 | 
			
		||||
	
 | 
			
		||||
	private fun loadIni(filename: String) {
 | 
			
		||||
		val file = File(filename)
 | 
			
		||||
@@ -66,20 +61,12 @@ object IniSettings {
 | 
			
		||||
	}
 | 
			
		||||
	fun getArray(key: String): List<String> = settings.get(key) ?: listOf<String>()
 | 
			
		||||
	
 | 
			
		||||
	val gmaps_key: String
 | 
			
		||||
		get() = getString("gmaps_key", default=Config.SECRET_GMAPS)!!
 | 
			
		||||
	val pagination: Boolean
 | 
			
		||||
		get() = getBoolean("pagination", default=true)
 | 
			
		||||
	val pagination_size: Int
 | 
			
		||||
		get() = getInt("pagination_size", default=Config.DEFAULT_PAGINATION)!!
 | 
			
		||||
	val download_media: Boolean
 | 
			
		||||
		get() = getBoolean("download_media", default=true)
 | 
			
		||||
	val download_channels: Boolean
 | 
			
		||||
		get() = getBoolean("download_channels", default=false)
 | 
			
		||||
	val download_supergroups: Boolean
 | 
			
		||||
		get() = getBoolean("download_supergroups", default=false)
 | 
			
		||||
	val whitelist_channels: List<String>?
 | 
			
		||||
		get() = getStringList("whitelist_channels")
 | 
			
		||||
	val blacklist_channels: List<String>?
 | 
			
		||||
		get() = getStringList("blacklist_channels")
 | 
			
		||||
	val gmaps_key = getString("gmaps_key", default=Config.SECRET_GMAPS)!!
 | 
			
		||||
	val pagination = getBoolean("pagination", default=true)
 | 
			
		||||
	val pagination_size = getInt("pagination_size", default=Config.DEFAULT_PAGINATION)!!
 | 
			
		||||
	val download_media = getBoolean("download_media", default=true)
 | 
			
		||||
	val download_channels = getBoolean("download_channels", default=false)
 | 
			
		||||
	val download_supergroups = getBoolean("download_supergroups", default=false)
 | 
			
		||||
	val whitelist_channels = getStringList("whitelist_channels")
 | 
			
		||||
	val blacklist_channels = getStringList("blacklist_channels")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,87 @@
 | 
			
		||||
package de.fabianonline.telegram_backup
 | 
			
		||||
 | 
			
		||||
import com.github.badoualy.telegram.tl.api.auth.TLSentCode
 | 
			
		||||
 | 
			
		||||
class LoginManager(val app: TelegramApp, val target_dir: String, val phoneToUse: String?) {
 | 
			
		||||
	fun run() {
 | 
			
		||||
		var phone: String
 | 
			
		||||
		
 | 
			
		||||
		if (phoneToUse == null) {
 | 
			
		||||
			println("Please enter your phone number in international format.")
 | 
			
		||||
			println("Example: +4917077651234")
 | 
			
		||||
			phone = getLine()
 | 
			
		||||
		} else {
 | 
			
		||||
			phone = phoneToUse
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		val file_base = CommandLineController.build_file_base(target_dir, phone)
 | 
			
		||||
		
 | 
			
		||||
		// We now have an account, so we can create an ApiStorage and TelegramClient.
 | 
			
		||||
		val storage = ApiStorage(file_base)
 | 
			
		||||
		val client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, null)
 | 
			
		||||
		
 | 
			
		||||
		val sent_code = send_code_to_phone_number(client, phone)
 | 
			
		||||
		println("Telegram sent you a code. Please enter it here.")
 | 
			
		||||
		val code = getLine()
 | 
			
		||||
		
 | 
			
		||||
		try {
 | 
			
		||||
			verify_code(client, phone, sent_code, code)
 | 
			
		||||
		} catch(e: PasswordNeededException) {
 | 
			
		||||
			println("We also need your account password. Please enter it now. It should not be printed, so it's okay if you see nothing while typing it.")
 | 
			
		||||
			val pw = getPassword()
 | 
			
		||||
			verify_password(client, pw)
 | 
			
		||||
		}
 | 
			
		||||
		System.out.println("Everything seems fine. Please run this tool again with '--account +" + user.user!!.getPhone().anonymize() + " to use this account.")
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private fun send_code_to_phone_number(client: TelegramClient, phone: String): TLSentCode {
 | 
			
		||||
		return client.authSendCode(false, phone, true)
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private fun verify_code(client: TelegramClient, phone: String, sent_code: TLSentCode, code: String): TLUser {
 | 
			
		||||
		try {
 | 
			
		||||
			val auth = client.authSignIn(phone, sent_code.getPhoneCodeHash(), code)
 | 
			
		||||
			return auth.getUser().getAsUser()
 | 
			
		||||
		} catch (e: RpcErrorException) {
 | 
			
		||||
			if (e.getCode() == 401 && e.getTag()=="SESSION_PASSWORD_NEEDED") {
 | 
			
		||||
				throw PasswordNeededException()
 | 
			
		||||
			} else {
 | 
			
		||||
				throw e
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private fun verify_password(client: TelegramClient, password: String): TLUser {
 | 
			
		||||
		val pw = password.toByteArray(charset = Charsets.UTF_8)
 | 
			
		||||
		val salt = (client.accountGetPassword() as TLPassword).getCurrentSalt().getData()
 | 
			
		||||
		val md = MessageDigest.getInstance("SHA-256")
 | 
			
		||||
		
 | 
			
		||||
		val salted = ByteArray(2 * salt.size + pw.size)
 | 
			
		||||
		System.arraycopy(salt, 0, salted, 0, salt.size)
 | 
			
		||||
		System.arraycopy(pw, 0, salted, salt.size, pw.size)
 | 
			
		||||
		System.arraycopy(salt, 0, salted, salt.size + pw.size, salt.size)
 | 
			
		||||
		val hash = md.digest(salted)
 | 
			
		||||
		val auth = client.authCheckPassword(TLBytes(hash))
 | 
			
		||||
		return auth.getUser().getAsUser()
 | 
			
		||||
	}
 | 
			
		||||
			
 | 
			
		||||
	
 | 
			
		||||
	private fun getLine(): String {
 | 
			
		||||
		if (System.console() != null) {
 | 
			
		||||
			return System.console().readLine("> ")
 | 
			
		||||
		} else {
 | 
			
		||||
			print("> ")
 | 
			
		||||
			return Scanner(System.`in`).nextLine()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private fun getPassword(): String {
 | 
			
		||||
		if (System.console() != null) {
 | 
			
		||||
			return String(System.console().readPassword("> "))
 | 
			
		||||
		} else {
 | 
			
		||||
			return getLine()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PasswordNeededException: Exception("A password is needed to be able to login to this account.") {}
 | 
			
		||||
@@ -34,107 +34,34 @@ import java.io.File
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
 | 
			
		||||
class UserManager @Throws(IOException::class)
 | 
			
		||||
private constructor(c: TelegramClient) {
 | 
			
		||||
	var user: TLUser? = null
 | 
			
		||||
	var phone: String? = null
 | 
			
		||||
	private var code: String? = null
 | 
			
		||||
	private val client: TelegramClient
 | 
			
		||||
	private var sent_code: TLSentCode? = null
 | 
			
		||||
	private var auth: TLAuthorization? = null
 | 
			
		||||
	var isPasswordNeeded = false
 | 
			
		||||
		private set
 | 
			
		||||
class UserManager(val client: TelegramClient) {
 | 
			
		||||
	val tl_user: TLUser
 | 
			
		||||
	val logger = LoggerFactory.getLogger(UserManager::class.java)
 | 
			
		||||
	val phone: String
 | 
			
		||||
		get() = "+" + tl_user.getPhone()
 | 
			
		||||
	val id: Int
 | 
			
		||||
		get() = tl_user.getId()
 | 
			
		||||
	
 | 
			
		||||
	val loggedIn: Boolean
 | 
			
		||||
		get() = user != null
 | 
			
		||||
	init {
 | 
			
		||||
		logger.debug("Calling getFullUser")
 | 
			
		||||
		val full_user = client.usersGetFullUser(TLInputUserSelf())
 | 
			
		||||
		tl_user = full_user.getUser().getAsUser()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val userString: String
 | 
			
		||||
		get() {
 | 
			
		||||
			if (this.user == null) return "Not logged in"
 | 
			
		||||
	fun toString(): String {
 | 
			
		||||
		val sb = StringBuilder()
 | 
			
		||||
			if (this.user!!.getFirstName() != null) {
 | 
			
		||||
				sb.append(this.user!!.getFirstName())
 | 
			
		||||
			}
 | 
			
		||||
			if (this.user!!.getLastName() != null) {
 | 
			
		||||
		sb.append(tl_user.getFirstName() ?: "")
 | 
			
		||||
		if (tl_user.getLastName() != null) {
 | 
			
		||||
			sb.append(" ")
 | 
			
		||||
				sb.append(this.user!!.getLastName())
 | 
			
		||||
			sb.append(tl_user.getLastName())
 | 
			
		||||
		}
 | 
			
		||||
			if (this.user!!.getUsername() != null) {
 | 
			
		||||
		if (tl_user.getUsername() != null) {
 | 
			
		||||
			sb.append(" (@")
 | 
			
		||||
				sb.append(this.user!!.getUsername())
 | 
			
		||||
			sb.append(tl_user.getUsername())
 | 
			
		||||
			sb.append(")")
 | 
			
		||||
		}
 | 
			
		||||
		return sb.toString()
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	val fileBase: String
 | 
			
		||||
		get() = Config.FILE_BASE + File.separatorChar + "+" + this.user!!.getPhone() + File.separatorChar
 | 
			
		||||
	
 | 
			
		||||
	init {
 | 
			
		||||
		this.client = c
 | 
			
		||||
		logger.debug("Calling getFullUser")
 | 
			
		||||
		try {
 | 
			
		||||
			val full_user = this.client.usersGetFullUser(TLInputUserSelf())
 | 
			
		||||
			this.user = full_user.getUser().getAsUser()
 | 
			
		||||
		} catch (e: RpcErrorException) {
 | 
			
		||||
			// This may happen. Ignoring it.
 | 
			
		||||
			logger.debug("Ignoring exception:", e)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Throws(RpcErrorException::class, IOException::class)
 | 
			
		||||
	fun sendCodeToPhoneNumber(number: String) {
 | 
			
		||||
		this.phone = number
 | 
			
		||||
		this.sent_code = this.client.authSendCode(false, number, true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Throws(RpcErrorException::class, IOException::class)
 | 
			
		||||
	fun verifyCode(code: String) {
 | 
			
		||||
		this.code = code
 | 
			
		||||
		try {
 | 
			
		||||
			this.auth = client.authSignIn(phone, this.sent_code!!.getPhoneCodeHash(), this.code)
 | 
			
		||||
			this.user = auth!!.getUser().getAsUser()
 | 
			
		||||
		} catch (e: RpcErrorException) {
 | 
			
		||||
			if (e.getCode() != 401 || !e.getTag().equals("SESSION_PASSWORD_NEEDED")) throw e
 | 
			
		||||
			this.isPasswordNeeded = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Throws(RpcErrorException::class, IOException::class)
 | 
			
		||||
	fun verifyPassword(pw: String) {
 | 
			
		||||
		val password = pw.toByteArray(charset = Charsets.UTF_8)
 | 
			
		||||
		val salt = (client.accountGetPassword() as TLPassword).getCurrentSalt().getData()
 | 
			
		||||
		var md: MessageDigest
 | 
			
		||||
		try {
 | 
			
		||||
			md = MessageDigest.getInstance("SHA-256")
 | 
			
		||||
		} catch (e: NoSuchAlgorithmException) {
 | 
			
		||||
			e.printStackTrace()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		val salted = ByteArray(2 * salt.size + password.size)
 | 
			
		||||
		System.arraycopy(salt, 0, salted, 0, salt.size)
 | 
			
		||||
		System.arraycopy(password, 0, salted, salt.size, password.size)
 | 
			
		||||
		System.arraycopy(salt, 0, salted, salt.size + password.size, salt.size)
 | 
			
		||||
		val hash = md.digest(salted)
 | 
			
		||||
		auth = client.authCheckPassword(TLBytes(hash))
 | 
			
		||||
		this.user = auth!!.getUser().getAsUser()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	companion object {
 | 
			
		||||
		private val logger = LoggerFactory.getLogger(UserManager::class.java)
 | 
			
		||||
		internal var instance: UserManager? = null
 | 
			
		||||
 | 
			
		||||
		@Throws(IOException::class)
 | 
			
		||||
		fun init(c: TelegramClient) {
 | 
			
		||||
			instance = UserManager(c)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fun getInstance(): UserManager {
 | 
			
		||||
			if (instance == null) throw RuntimeException("UserManager is not yet initialized.")
 | 
			
		||||
			return instance!!
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager, protected var client: TelegramClient) {
 | 
			
		||||
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager) {
 | 
			
		||||
	open var isEmpty = false
 | 
			
		||||
	abstract val size: Int
 | 
			
		||||
	abstract val extension: String
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
open class DocumentFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
 | 
			
		||||
open class DocumentFileManager(msg: TLMessage, user: UserManager) : AbstractMediaFileManager(msg, user) {
 | 
			
		||||
	protected var doc: TLDocument? = null
 | 
			
		||||
	override lateinit var extension: String
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,29 +42,29 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
object FileManagerFactory {
 | 
			
		||||
	fun getFileManager(m: TLMessage?, u: UserManager, c: TelegramClient): AbstractMediaFileManager? {
 | 
			
		||||
	fun getFileManager(m: TLMessage?, u: UserManager): AbstractMediaFileManager? {
 | 
			
		||||
		if (m == null) return null
 | 
			
		||||
		val media = m.getMedia() ?: return null
 | 
			
		||||
 | 
			
		||||
		if (media is TLMessageMediaPhoto) {
 | 
			
		||||
			return PhotoFileManager(m, u, c)
 | 
			
		||||
			return PhotoFileManager(m, u)
 | 
			
		||||
		} else if (media is TLMessageMediaDocument) {
 | 
			
		||||
			val d = DocumentFileManager(m, u, c)
 | 
			
		||||
			val d = DocumentFileManager(m, u)
 | 
			
		||||
			return if (d.isSticker) {
 | 
			
		||||
				StickerFileManager(m, u, c)
 | 
			
		||||
				StickerFileManager(m, u)
 | 
			
		||||
			} else d
 | 
			
		||||
		} else if (media is TLMessageMediaGeo) {
 | 
			
		||||
			return GeoFileManager(m, u, c)
 | 
			
		||||
			return GeoFileManager(m, u)
 | 
			
		||||
		} else if (media is TLMessageMediaEmpty) {
 | 
			
		||||
			return UnsupportedFileManager(m, u, c, "empty")
 | 
			
		||||
			return UnsupportedFileManager(m, u, "empty")
 | 
			
		||||
		} else if (media is TLMessageMediaUnsupported) {
 | 
			
		||||
			return UnsupportedFileManager(m, u, c, "unsupported")
 | 
			
		||||
			return UnsupportedFileManager(m, u, "unsupported")
 | 
			
		||||
		} else if (media is TLMessageMediaWebPage) {
 | 
			
		||||
			return UnsupportedFileManager(m, u, c, "webpage")
 | 
			
		||||
			return UnsupportedFileManager(m, u, "webpage")
 | 
			
		||||
		} else if (media is TLMessageMediaContact) {
 | 
			
		||||
			return UnsupportedFileManager(m, u, c, "contact")
 | 
			
		||||
			return UnsupportedFileManager(m, u, "contact")
 | 
			
		||||
		} else if (media is TLMessageMediaVenue) {
 | 
			
		||||
			return UnsupportedFileManager(m, u, c, "venue")
 | 
			
		||||
			return UnsupportedFileManager(m, u, "venue")
 | 
			
		||||
		} else {
 | 
			
		||||
			AbstractMediaFileManager.throwUnexpectedObjectError(media)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
class GeoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
 | 
			
		||||
class GeoFileManager(msg: TLMessage, user: UserManager) : AbstractMediaFileManager(msg, user) {
 | 
			
		||||
	protected lateinit var geo: TLGeoPoint
 | 
			
		||||
 | 
			
		||||
	// We don't know the size, so we just guess.
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
class PhotoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
 | 
			
		||||
class PhotoFileManager(msg: TLMessage, user: UserManager) : AbstractMediaFileManager(msg, user) {
 | 
			
		||||
	private lateinit var photo: TLPhoto
 | 
			
		||||
	override var size = 0
 | 
			
		||||
	private lateinit var photo_size: TLPhotoSize
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
class StickerFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : DocumentFileManager(msg, user, client) {
 | 
			
		||||
class StickerFileManager(msg: TLMessage, user: UserManager) : DocumentFileManager(msg, user) {
 | 
			
		||||
 | 
			
		||||
	override val isSticker = true
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ import java.util.concurrent.TimeoutException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.io.FileUtils
 | 
			
		||||
 | 
			
		||||
class UnsupportedFileManager(msg: TLMessage, user: UserManager, client: TelegramClient, type: String) : AbstractMediaFileManager(msg, user, client) {
 | 
			
		||||
class UnsupportedFileManager(msg: TLMessage, user: UserManager, type: String) : AbstractMediaFileManager(msg, user) {
 | 
			
		||||
	override var name = type
 | 
			
		||||
	override val targetFilename = ""
 | 
			
		||||
	override val targetPath = ""
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user