mirror of
				https://github.com/fabianonline/telegram_backup.git
				synced 2025-10-28 14:40:04 +00:00 
			
		
		
		
	WIP: Started rewriting the code to make it more null-safe.
This commit is contained in:
		| @@ -0,0 +1,5 @@ | ||||
| package de.fabianonline.telegram_backup | ||||
|  | ||||
| class Account(val file_base: String, val phone_number: String) { | ||||
|  | ||||
| } | ||||
| @@ -31,7 +31,11 @@ import org.slf4j.Logger | ||||
|  | ||||
| class CommandLineController { | ||||
| 	private val storage: ApiStorage | ||||
| 	var app: TelegramApp | ||||
| 	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) { | ||||
| @@ -53,25 +57,45 @@ class CommandLineController { | ||||
| 	init { | ||||
| 		logger.info("CommandLineController started. App version {}", Config.APP_APPVER) | ||||
| 		 | ||||
| 		this.printHeader() | ||||
| 		printHeader() | ||||
| 		if (CommandLineOptions.cmd_version) { | ||||
| 			System.exit(0) | ||||
| 		} else if (CommandLineOptions.cmd_help) { | ||||
| 			this.show_help() | ||||
| 			show_help() | ||||
| 			System.exit(0) | ||||
| 		} else if (CommandLineOptions.cmd_license) { | ||||
| 			CommandLineController.show_license() | ||||
| 			show_license() | ||||
| 			System.exit(0) | ||||
| 		} | ||||
| 		this.setupFileBase() | ||||
|  | ||||
| 		// Setup file_base | ||||
| 		logger.debug("Target dir from Config: {}", Config.TARGET_DIR.anonymize()) | ||||
| 		target_dir = CommandLineOptions.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) { | ||||
| 			this.list_accounts() | ||||
| 			Utils.print_accounts(target_dir) | ||||
| 			System.exit(0) | ||||
| 		} | ||||
|  | ||||
| 		logger.trace("Checking accounts") | ||||
| 		try { | ||||
| 			phone_number = selectAccount(target_dir, CommandLineOptions.val_account) | ||||
| 		} catch(e: AccountNotFoundException) { | ||||
| 			show_error("The specified account could not be found.") | ||||
| 		} catch(e: NoAccountsException) { | ||||
| 			println("No accounts found. Starting login process...") | ||||
| 			CommandLineOptions.cmd_login = true | ||||
| 		} | ||||
|  | ||||
| 		file_base = target_dir + File.separatorChar + account_to_use | ||||
|  | ||||
| 		account = Account(file_base, account_to_use) | ||||
|  | ||||
| 		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.trace("Checking accounts") | ||||
| 		val account = this.selectAccount() | ||||
|  | ||||
| 		logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login) | ||||
| 		logger.info("Initializing ApiStorage") | ||||
| 		storage = ApiStorage(account) | ||||
| @@ -196,44 +220,22 @@ class CommandLineController { | ||||
| 		println() | ||||
| 	} | ||||
|  | ||||
| 	private fun setupFileBase() { | ||||
| 		logger.debug("Target dir at startup: {}", Config.FILE_BASE.anonymize()) | ||||
| 		if (CommandLineOptions.val_target != null) { | ||||
| 			Config.FILE_BASE = CommandLineOptions.val_target!! | ||||
| 		} | ||||
| 		logger.debug("Target dir after options: {}", Config.FILE_BASE.anonymize()) | ||||
| 		System.out.println("Base directory for files: " + Config.FILE_BASE.anonymize()) | ||||
| 	} | ||||
|  | ||||
| 	private fun selectAccount(): String? { | ||||
| 		var account = "none" | ||||
| 		val accounts = Utils.getAccounts() | ||||
| 		if (CommandLineOptions.cmd_login) { | ||||
| 			logger.debug("Login requested, doing nothing.") | ||||
| 			// do nothing | ||||
| 		} else if (CommandLineOptions.val_account != null) { | ||||
| 			logger.debug("Account requested: {}", CommandLineOptions.val_account!!.anonymize()) | ||||
| 	private fun selectAccount(file_base: String, requested_account: String?): String { | ||||
| 		val found_account: String? | ||||
| 		val accounts = Utils.getAccounts(file_base) | ||||
| 		if (requested_account != null) { | ||||
| 			logger.debug("Account requested: {}", requested_account.anonymize()) | ||||
| 			logger.trace("Checking accounts for match.") | ||||
| 			var found = false | ||||
| 			for (acc in accounts) { | ||||
| 				logger.trace("Checking {}", acc.anonymize()) | ||||
| 				if (acc == CommandLineOptions.val_account) { | ||||
| 					found = true | ||||
| 					logger.trace("Matches.") | ||||
| 					break | ||||
| 				} | ||||
| 			found_account = accounts.find{it == requested_account} | ||||
|  | ||||
| 			if (found_account == null) { | ||||
| 				throw AccountNotFoundException() | ||||
| 			} | ||||
| 			if (!found) { | ||||
| 				show_error("Couldn't find account '" + CommandLineOptions.val_account!!.anonymize() + "'. Maybe you want to use '--login' first?") | ||||
| 			} | ||||
| 			account = CommandLineOptions.val_account!! | ||||
| 		} else if (accounts.size == 0) { | ||||
| 			println("No accounts found. Starting login process...") | ||||
| 			CommandLineOptions.cmd_login = true | ||||
| 			return null | ||||
| 			throw NoAccountsException() | ||||
| 		} else if (accounts.size == 1) { | ||||
| 			account = accounts.firstElement() | ||||
| 			System.out.println("Using only available account: " + account.anonymize()) | ||||
| 			found_account = accounts.firstElement() | ||||
| 			println("Using only available account: " + account.anonymize()) | ||||
| 		} else { | ||||
| 			show_error(("You didn't specify which account to use.\n" + | ||||
| 				"Use '--account <x>' to use account <x>.\n" + | ||||
| @@ -241,8 +243,8 @@ class CommandLineController { | ||||
| 			System.exit(1) | ||||
| 		} | ||||
| 		logger.debug("accounts.size: {}", accounts.size) | ||||
| 		logger.debug("account: {}", account.anonymize()) | ||||
| 		return account | ||||
| 		logger.debug("account: {}", found_account.anonymize()) | ||||
| 		return found_account!! | ||||
| 	} | ||||
|  | ||||
| 	private fun cmd_stats() { | ||||
| @@ -315,20 +317,6 @@ class CommandLineController { | ||||
| 		println(" --list-channels       Lists all channels together with their ID") | ||||
| 	} | ||||
|  | ||||
| 	private fun list_accounts() { | ||||
| 		println("List of available accounts:") | ||||
| 		val accounts = Utils.getAccounts() | ||||
| 		if (accounts.size > 0) { | ||||
| 			for (str in accounts) { | ||||
| 				System.out.println(" " + str.anonymize()) | ||||
| 			} | ||||
| 			println("Use '--account <x>' to use one of those accounts.") | ||||
| 		} else { | ||||
| 			println("NO ACCOUNTS FOUND") | ||||
| 			println("Use '--login' to login to a telegram account.") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	companion object { | ||||
| 		private val logger = LoggerFactory.getLogger(CommandLineController::class.java) | ||||
|  | ||||
| @@ -342,4 +330,7 @@ class CommandLineController { | ||||
| 			println("TODO: Print the GPL.") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	class AccountNotFoundException() : Exception("Account not found") {} | ||||
| 	class NoAccountsException() : Exception("No accounts found") {} | ||||
| } | ||||
|   | ||||
| @@ -29,7 +29,7 @@ object Config { | ||||
| 	val APP_APPVER: String | ||||
| 	val APP_LANG = "en" | ||||
|  | ||||
| 	var FILE_BASE = System.getProperty("user.home") + File.separatorChar + ".telegram_backup" | ||||
| 	var TARGET_DIR = System.getProperty("user.home") + File.separatorChar + ".telegram_backup" | ||||
| 	val FILE_NAME_AUTH_KEY = "auth.dat" | ||||
| 	val FILE_NAME_DC = "dc.dat" | ||||
| 	val FILE_NAME_DB = "database.sqlite" | ||||
|   | ||||
| @@ -51,6 +51,33 @@ class Database private constructor(var client: TelegramClient) { | ||||
| 	private var conn: Connection? = null | ||||
| 	private var stmt: Statement? = null | ||||
| 	var user_manager: UserManager | ||||
| 	 | ||||
| 	init { | ||||
| 		private val logger = LoggerFactory.getLogger(Database::class.java) | ||||
| 		 | ||||
| 		this.user_manager = UserManager.getInstance() | ||||
| 		System.out.println("Opening database...") | ||||
| 		try { | ||||
| 			Class.forName("org.sqlite.JDBC") | ||||
| 		} catch (e: ClassNotFoundException) { | ||||
| 			CommandLineController.show_error("Could not load jdbc-sqlite class.") | ||||
| 		} | ||||
|  | ||||
| 		val path = "jdbc:sqlite:${user_manager.fileBase}${Config.FILE_NAME_DB}" | ||||
|  | ||||
| 		try { | ||||
| 			conn = DriverManager.getConnection(path) | ||||
| 			stmt = conn!!.createStatement() | ||||
| 		} catch (e: SQLException) { | ||||
| 			CommandLineController.show_error("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 { | ||||
| @@ -69,34 +96,32 @@ class Database private constructor(var client: TelegramClient) { | ||||
| 	fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats") | ||||
| 	fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users") | ||||
|  | ||||
| 	val missingIDs: LinkedList<Int> | ||||
| 		get() { | ||||
| 			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") | ||||
| 				rs.next() | ||||
| 				var id = rs.getInt(1) | ||||
| 				for (i in 1..max) { | ||||
| 					if (i == id) { | ||||
| 						rs.next() | ||||
| 						if (rs.isClosed()) { | ||||
| 							id = Integer.MAX_VALUE | ||||
| 						} else { | ||||
| 							id = rs.getInt(1) | ||||
| 						} | ||||
| 					} else if (i < id) { | ||||
| 						missing.add(i) | ||||
| 	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") | ||||
| 			rs.next() | ||||
| 			var id = rs.getInt(1) | ||||
| 			for (i in 1..max) { | ||||
| 				if (i == id) { | ||||
| 					rs.next() | ||||
| 					if (rs.isClosed()) { | ||||
| 						id = Integer.MAX_VALUE | ||||
| 					} else { | ||||
| 						id = rs.getInt(1) | ||||
| 					} | ||||
| 				} else if (i < id) { | ||||
| 					missing.add(i) | ||||
| 				} | ||||
| 				rs.close() | ||||
| 				return missing | ||||
| 			} catch (e: SQLException) { | ||||
| 				e.printStackTrace() | ||||
| 				throw RuntimeException("Could not get list of ids.") | ||||
| 			} | ||||
|  | ||||
| 			rs.close() | ||||
| 			return missing | ||||
| 		} catch (e: SQLException) { | ||||
| 			e.printStackTrace() | ||||
| 			throw RuntimeException("Could not get list of ids.") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	fun getMessagesWithMedia(): LinkedList<TLMessage?> { | ||||
| 		try { | ||||
| @@ -207,30 +232,7 @@ class Database private constructor(var client: TelegramClient) { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	init { | ||||
| 		this.user_manager = UserManager.getInstance() | ||||
| 		System.out.println("Opening database...") | ||||
| 		try { | ||||
| 			Class.forName("org.sqlite.JDBC") | ||||
| 		} catch (e: ClassNotFoundException) { | ||||
| 			CommandLineController.show_error("Could not load jdbc-sqlite class.") | ||||
| 		} | ||||
|  | ||||
| 		val path = "jdbc:sqlite:${user_manager.fileBase}${Config.FILE_NAME_DB}" | ||||
|  | ||||
| 		try { | ||||
| 			conn = DriverManager.getConnection(path) | ||||
| 			stmt = conn!!.createStatement() | ||||
| 		} catch (e: SQLException) { | ||||
| 			CommandLineController.show_error("Could not connect to SQLITE database.") | ||||
| 		} | ||||
|  | ||||
| 		// Run updates | ||||
| 		val updates = DatabaseUpdates(conn!!, this) | ||||
| 		updates.doUpdates() | ||||
|  | ||||
| 		System.out.println("Database is ready.") | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	fun backupDatabase(currentVersion: Int) { | ||||
| 		val filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion) | ||||
| @@ -550,7 +552,9 @@ class Database private constructor(var client: TelegramClient) { | ||||
| 	fun fetchSetting(key: String): String? { | ||||
| 		val rs = stmt!!.executeQuery("SELECT value FROM settings WHERE key='${key}'") | ||||
| 		rs.next() | ||||
| 		return rs.getString(1) | ||||
| 		val result = rs.getString(1) | ||||
| 		rs.close() | ||||
| 		return result | ||||
| 	} | ||||
| 	 | ||||
| 	fun saveSetting(key: String, value: String?) { | ||||
| @@ -562,6 +566,7 @@ class Database private constructor(var client: TelegramClient) { | ||||
| 			ps.setString(2, value) | ||||
| 		} | ||||
| 		ps.execute() | ||||
| 		ps.close() | ||||
| 	} | ||||
| 	 | ||||
| 	fun getIdsFromQuery(query: String): LinkedList<Int> { | ||||
| @@ -787,18 +792,6 @@ class Database private constructor(var client: TelegramClient) { | ||||
| 	} | ||||
|  | ||||
| 	companion object { | ||||
| 		private val logger = LoggerFactory.getLogger(Database::class.java) | ||||
| 		internal var instance: Database? = null | ||||
|  | ||||
| 		fun init(c: TelegramClient) { | ||||
| 			instance = Database(c) | ||||
| 		} | ||||
|  | ||||
| 		fun getInstance(): Database { | ||||
| 			if (instance == null) throw RuntimeException("Database is not initialized but getInstance() was called.") | ||||
| 			return instance!! | ||||
| 		} | ||||
|  | ||||
| 		fun bytesToTLMessage(b: ByteArray?): TLMessage? { | ||||
| 			try { | ||||
| 				if (b == null) return null | ||||
|   | ||||
| @@ -34,9 +34,23 @@ object Utils { | ||||
|  | ||||
| 	private val logger = LoggerFactory.getLogger(Utils::class.java) as Logger | ||||
|  | ||||
| 	fun getAccounts(): Vector<String> { | ||||
| 	fun print_accounts(file_base: String) { | ||||
| 		println("List of available accounts:") | ||||
| 		val accounts = getAccounts(file_base) | ||||
| 		if (accounts.size > 0) { | ||||
| 			for (str in accounts) { | ||||
| 				println(" " + str.anonymize()) | ||||
| 			} | ||||
| 			println("Use '--account <x>' to use one of those accounts.") | ||||
| 		} else { | ||||
| 			println("NO ACCOUNTS FOUND") | ||||
| 			println("Use '--login' to login to a telegram account.") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	fun getAccounts(file_base: String): Vector<String> { | ||||
| 		val accounts = Vector<String>() | ||||
| 		val folder = File(Config.FILE_BASE) | ||||
| 		val folder = File(file_base) | ||||
| 		val files = folder.listFiles() | ||||
| 		if (files != null) | ||||
| 			for (f in files) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user