Wrapper.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. import Foundation
  2. import UIKit
  3. import AVFoundation
  4. public class DcContext {
  5. /// TODO: THIS global instance should be replaced in the future, for example for a multi-account scenario,
  6. /// where we want to have more than one DcContext.
  7. static let dcContext: DcContext = DcContext()
  8. public var logger: Logger?
  9. var contextPointer: OpaquePointer?
  10. public var lastErrorString: String?
  11. public var lastWarningString: String = "" // temporary thing to get a grip on some weird errors
  12. public var maxConfigureProgress: Int = 0 // temporary thing to get a grip on some weird errors
  13. private init() {
  14. }
  15. deinit {
  16. if contextPointer == nil { return } // avoid a warning about a "careless call"
  17. dc_context_unref(contextPointer)
  18. contextPointer = nil
  19. }
  20. /// Injection of DcContext is preferred over the usage of the shared variable
  21. public static var shared: DcContext {
  22. return .dcContext
  23. }
  24. public func getMessageIds(chatId: Int, count: Int? = nil, from: Int? = nil) -> [Int] {
  25. let cMessageIds = getChatMessages(chatId: chatId)
  26. let ids: [Int]
  27. if let from = from {
  28. // skip last part
  29. ids = DcUtils.copyAndFreeArrayWithOffset(inputArray: cMessageIds, len: count, skipEnd: from)
  30. } else if let count = count {
  31. // skip first part
  32. ids = DcUtils.copyAndFreeArrayWithLen(inputArray: cMessageIds, len: count)
  33. } else {
  34. ids = DcUtils.copyAndFreeArrayWithLen(inputArray: cMessageIds)
  35. }
  36. return ids
  37. }
  38. public func createContact(name: String, email: String) -> Int {
  39. return Int(dc_create_contact(contextPointer, name, email))
  40. }
  41. public func deleteContact(contactId: Int) -> Bool {
  42. return dc_delete_contact(self.contextPointer, UInt32(contactId)) == 1
  43. }
  44. public func getContacts(flags: Int32, queryString: String? = nil) -> [Int] {
  45. let cContacts = dc_get_contacts(contextPointer, UInt32(flags), queryString)
  46. return DcUtils.copyAndFreeArray(inputArray: cContacts)
  47. }
  48. public func getBlockedContacts() -> [Int] {
  49. let cBlockedContacts = dc_get_blocked_contacts(contextPointer)
  50. return DcUtils.copyAndFreeArray(inputArray: cBlockedContacts)
  51. }
  52. public func addContacts(contactString: String) {
  53. dc_add_address_book(contextPointer, contactString)
  54. }
  55. public func getChat(chatId: Int) -> DcChat {
  56. let chatPointer = dc_get_chat(contextPointer, UInt32(chatId))
  57. return DcChat(chatPointer: chatPointer)
  58. }
  59. public func getChatIdByContactId(contactId: Int) -> Int {
  60. return Int(dc_get_chat_id_by_contact_id(contextPointer, UInt32(contactId)))
  61. }
  62. public func getChatIdByContactIdOld(_ contactId: Int) -> Int? {
  63. // deprecated function, use getChatIdByContactId() and check for != 0 as for all other places IDs are used
  64. let chatId = dc_get_chat_id_by_contact_id(contextPointer, UInt32(contactId))
  65. if chatId == 0 {
  66. return nil
  67. } else {
  68. return Int(chatId)
  69. }
  70. }
  71. public func createChatByMessageId(_ messageId: Int) -> DcChat {
  72. let chatId = dc_create_chat_by_msg_id(contextPointer, UInt32(messageId))
  73. return getChat(chatId: Int(chatId))
  74. }
  75. public func getChatlist(flags: Int32, queryString: String?, queryId: Int) -> DcChatlist {
  76. let chatlistPointer = dc_get_chatlist(contextPointer, flags, queryString, UInt32(queryId))
  77. let chatlist = DcChatlist(chatListPointer: chatlistPointer)
  78. return chatlist
  79. }
  80. public func sendMsgSync(chatId: Int, msg: DcMsg) {
  81. dc_send_msg_sync(contextPointer, UInt32(chatId), msg.messagePointer)
  82. }
  83. public func getChatMedia(chatId: Int, messageType: Int32, messageType2: Int32, messageType3: Int32) -> [Int] {
  84. guard let messagesPointer = dc_get_chat_media(contextPointer, UInt32(chatId), messageType, messageType2, messageType3) else {
  85. return []
  86. }
  87. let messageIds: [Int] = DcUtils.copyAndFreeArray(inputArray: messagesPointer)
  88. return messageIds
  89. }
  90. @discardableResult
  91. public func createChatByContactId(contactId: Int) -> Int {
  92. return Int(dc_create_chat_by_contact_id(contextPointer, UInt32(contactId)))
  93. }
  94. public func createGroupChat(verified: Bool, name: String) -> Int {
  95. return Int(dc_create_group_chat(contextPointer, verified ? 1 : 0, name))
  96. }
  97. public func addContactToChat(chatId: Int, contactId: Int) -> Bool {
  98. return dc_add_contact_to_chat(contextPointer, UInt32(chatId), UInt32(contactId)) == 1
  99. }
  100. public func removeContactFromChat(chatId: Int, contactId: Int) -> Bool {
  101. return dc_remove_contact_from_chat(contextPointer, UInt32(chatId), UInt32(contactId)) == 1
  102. }
  103. public func setChatName(chatId: Int, name: String) -> Bool {
  104. return dc_set_chat_name(contextPointer, UInt32(chatId), name) == 1
  105. }
  106. public func deleteChat(chatId: Int) {
  107. dc_delete_chat(contextPointer, UInt32(chatId))
  108. }
  109. public func archiveChat(chatId: Int, archive: Bool) {
  110. dc_set_chat_visibility(contextPointer, UInt32(chatId), Int32(archive ? DC_CHAT_VISIBILITY_ARCHIVED : DC_CHAT_VISIBILITY_NORMAL))
  111. }
  112. public func setChatVisibility(chatId: Int, visibility: Int32) {
  113. dc_set_chat_visibility(contextPointer, UInt32(chatId), visibility)
  114. }
  115. public func marknoticedChat(chatId: Int) {
  116. dc_marknoticed_chat(self.contextPointer, UInt32(chatId))
  117. }
  118. public func getSecurejoinQr (chatId: Int) -> String? {
  119. if let cString = dc_get_securejoin_qr(self.contextPointer, UInt32(chatId)) {
  120. let swiftString = String(cString: cString)
  121. dc_str_unref(cString)
  122. return swiftString
  123. }
  124. return nil
  125. }
  126. public func joinSecurejoin (qrCode: String) -> Int {
  127. return Int(dc_join_securejoin(contextPointer, qrCode))
  128. }
  129. public func checkQR(qrCode: String) -> DcLot {
  130. return DcLot(dc_check_qr(contextPointer, qrCode))
  131. }
  132. public func setConfigFromQR(qrCode: String) -> Bool {
  133. return dc_set_config_from_qr(contextPointer, qrCode) != 0
  134. }
  135. public func stopOngoingProcess() {
  136. dc_stop_ongoing_process(contextPointer)
  137. }
  138. public func getInfo() -> [[String]] {
  139. if let cString = dc_get_info(contextPointer) {
  140. let info = String(cString: cString)
  141. dc_str_unref(cString)
  142. logger?.info(info)
  143. return info.components(separatedBy: "\n").map { val in
  144. val.components(separatedBy: "=")
  145. }
  146. }
  147. return []
  148. }
  149. public func getContactEncrInfo(contactId: Int) -> String {
  150. if let cString = dc_get_contact_encrinfo(contextPointer, UInt32(contactId)) {
  151. let switftString = String(cString: cString)
  152. dc_str_unref(cString)
  153. return switftString
  154. }
  155. return "ErrGetContactEncrInfo"
  156. }
  157. public func interruptIdle() {
  158. }
  159. public func getEventEmitter() -> DcEventEmitter {
  160. let eventEmitterPointer = dc_get_event_emitter(contextPointer)
  161. return DcEventEmitter(eventEmitterPointer: eventEmitterPointer)
  162. }
  163. public func openDatabase(dbFile: String) {
  164. var version = ""
  165. if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
  166. version += " " + appVersion
  167. }
  168. contextPointer = dc_context_new("iOS" + version, dbFile, nil)
  169. }
  170. public func closeDatabase() {
  171. dc_context_unref(contextPointer)
  172. contextPointer = nil
  173. }
  174. public func maybeStartIo() {
  175. if isConfigured() {
  176. dc_start_io(contextPointer)
  177. }
  178. }
  179. public func stopIo() {
  180. dc_stop_io(contextPointer)
  181. }
  182. public func isIoRunning() -> Bool {
  183. return dc_is_io_running(contextPointer) != 0
  184. }
  185. public func setStockTranslation(id: Int32, localizationKey: String) {
  186. dc_set_stock_translation(contextPointer, UInt32(id), String.localized(localizationKey))
  187. }
  188. public func getDraft(chatId: Int) -> String? {
  189. if let draft = dc_get_draft(contextPointer, UInt32(chatId)) {
  190. if let cString = dc_msg_get_text(draft) {
  191. let swiftString = String(cString: cString)
  192. dc_str_unref(cString)
  193. dc_msg_unref(draft)
  194. return swiftString
  195. }
  196. dc_msg_unref(draft)
  197. return nil
  198. }
  199. return nil
  200. }
  201. public func setDraft(chatId: Int, draftText: String) {
  202. let draft = dc_msg_new(contextPointer, DC_MSG_TEXT)
  203. dc_msg_set_text(draft, draftText.cString(using: .utf8))
  204. dc_set_draft(contextPointer, UInt32(chatId), draft)
  205. // cleanup
  206. dc_msg_unref(draft)
  207. }
  208. public func getFreshMessages() -> DcArray {
  209. return DcArray(arrayPointer: dc_get_fresh_msgs(contextPointer))
  210. }
  211. public func markSeenMessages(messageIds: [UInt32], count: Int = 1) {
  212. let ptr = UnsafePointer(messageIds)
  213. dc_markseen_msgs(contextPointer, ptr, Int32(count))
  214. }
  215. public func getChatMessages(chatId: Int) -> OpaquePointer {
  216. return dc_get_chat_msgs(contextPointer, UInt32(chatId), 0, 0)
  217. }
  218. public func getMsgInfo(msgId: Int) -> String {
  219. if let cString = dc_get_msg_info(self.contextPointer, UInt32(msgId)) {
  220. let swiftString = String(cString: cString)
  221. dc_str_unref(cString)
  222. return swiftString
  223. }
  224. return "ErrGetMsgInfo"
  225. }
  226. public func deleteMessage(msgId: Int) {
  227. dc_delete_msgs(contextPointer, [UInt32(msgId)], 1)
  228. }
  229. public func forwardMessage(with msgId: Int, to chat: Int) {
  230. dc_forward_msgs(contextPointer, [UInt32(msgId)], 1, UInt32(chat))
  231. }
  232. public func sendTextInChat(id: Int, message: String) {
  233. dc_send_text_msg(contextPointer, UInt32(id), message)
  234. }
  235. public func initiateKeyTransfer() -> String? {
  236. if let cString = dc_initiate_key_transfer(self.contextPointer) {
  237. let swiftString = String(cString: cString)
  238. dc_str_unref(cString)
  239. return swiftString
  240. }
  241. return nil
  242. }
  243. public func continueKeyTransfer(msgId: Int, setupCode: String) -> Bool {
  244. return dc_continue_key_transfer(self.contextPointer, UInt32(msgId), setupCode) != 0
  245. }
  246. public func configure() {
  247. maxConfigureProgress = 0
  248. lastWarningString = ""
  249. dc_configure(contextPointer)
  250. }
  251. public func setChatMuteDuration(chatId: Int, duration: Int) {
  252. dc_set_chat_mute_duration(self.contextPointer, UInt32(chatId), Int64(duration))
  253. }
  254. public func setChatEphemeralTimer(chatId: Int, duration: Int) {
  255. dc_set_chat_ephemeral_timer(self.contextPointer, UInt32(chatId), UInt32(duration))
  256. }
  257. public func getChatEphemeralTimer(chatId: Int) -> Int {
  258. return Int(dc_get_chat_ephemeral_timer(self.contextPointer, UInt32(chatId)))
  259. }
  260. public func getConfig(_ key: String) -> String? {
  261. guard let cString = dc_get_config(self.contextPointer, key) else { return nil }
  262. let value = String(cString: cString)
  263. dc_str_unref(cString)
  264. if value.isEmpty {
  265. return nil
  266. }
  267. return value
  268. }
  269. public func setConfig(_ key: String, _ value: String?) {
  270. if let v = value {
  271. dc_set_config(self.contextPointer, key, v)
  272. } else {
  273. dc_set_config(self.contextPointer, key, nil)
  274. }
  275. }
  276. public func getConfigBool(_ key: String) -> Bool {
  277. return strToBool(getConfig(key))
  278. }
  279. public func setConfigBool(_ key: String, _ value: Bool) {
  280. let vStr = value ? "1" : "0"
  281. setConfig(key, vStr)
  282. }
  283. public func getConfigInt(_ key: String) -> Int {
  284. if let str = getConfig(key) {
  285. return Int(str) ?? 0
  286. }
  287. return 0
  288. }
  289. public func setConfigInt(_ key: String, _ value: Int) {
  290. setConfig(key, String(value))
  291. }
  292. public func getUnreadMessages(chatId: Int) -> Int {
  293. return Int(dc_get_fresh_msg_cnt(contextPointer, UInt32(chatId)))
  294. }
  295. public func estimateDeletionCnt(fromServer: Bool, timeout: Int) -> Int {
  296. return Int(dc_estimate_deletion_cnt(contextPointer, fromServer ? 1 : 0, Int64(timeout)))
  297. }
  298. public func isConfigured() -> Bool {
  299. return dc_is_configured(contextPointer) != 0
  300. }
  301. public func getSelfAvatarImage() -> UIImage? {
  302. guard let fileName = selfavatar else { return nil }
  303. let path: URL = URL(fileURLWithPath: fileName, isDirectory: false)
  304. if path.isFileURL {
  305. do {
  306. let data = try Data(contentsOf: path)
  307. return UIImage(data: data)
  308. } catch {
  309. logger?.warning("failed to load image: \(fileName), \(error)")
  310. return nil
  311. }
  312. }
  313. return nil
  314. }
  315. public func setChatProfileImage(chatId: Int, path: String?) {
  316. dc_set_chat_profile_image(contextPointer, UInt32(chatId), path)
  317. }
  318. @discardableResult
  319. public func addDeviceMessage(label: String, msg: DcMsg) -> Int {
  320. return Int(dc_add_device_msg(contextPointer, label.cString(using: .utf8), msg.cptr))
  321. }
  322. public func updateDeviceChats() {
  323. dc_update_device_chats(contextPointer)
  324. }
  325. public func getProviderFromEmail(addr: String) -> DcProvider? {
  326. guard let dcProviderPointer = dc_provider_new_from_email(contextPointer, addr) else { return nil }
  327. return DcProvider(dcProviderPointer)
  328. }
  329. public func imex(what: Int32, directory: String) {
  330. dc_imex(contextPointer, what, directory, nil)
  331. }
  332. public func imexHasBackup(filePath: String) -> String? {
  333. var file: String?
  334. if let cString = dc_imex_has_backup(contextPointer, filePath) {
  335. file = String(cString: cString)
  336. dc_str_unref(cString)
  337. }
  338. return file
  339. }
  340. public func isSendingLocationsToChat(chatId: Int) -> Bool {
  341. return dc_is_sending_locations_to_chat(contextPointer, UInt32(chatId)) == 1
  342. }
  343. public func sendLocationsToChat(chatId: Int, seconds: Int) {
  344. dc_send_locations_to_chat(contextPointer, UInt32(chatId), Int32(seconds))
  345. }
  346. public func setLocation(latitude: Double, longitude: Double, accuracy: Double) {
  347. dc_set_location(contextPointer, latitude, longitude, accuracy)
  348. }
  349. public func searchMessages(chatId: Int = 0, searchText: String) -> [Int] {
  350. guard let arrayPointer = dc_search_msgs(contextPointer, UInt32(chatId), searchText) else {
  351. return []
  352. }
  353. let messageIds = DcUtils.copyAndFreeArray(inputArray: arrayPointer)
  354. return messageIds
  355. }
  356. // call dc_maybe_network() from a worker thread.
  357. public func maybeNetwork() {
  358. dc_maybe_network(contextPointer)
  359. }
  360. // also, there is no much worth in adding a separate function or so
  361. // for each config option - esp. if they are just forwarded to the core
  362. // and set/get only at one line of code each.
  363. // this adds a complexity that can be avoided -
  364. // and makes grep harder as these names are typically named following different guidelines.
  365. public var displayname: String? {
  366. set { setConfig("displayname", newValue) }
  367. get { return getConfig("displayname") }
  368. }
  369. public var selfstatus: String? {
  370. set { setConfig("selfstatus", newValue) }
  371. get { return getConfig("selfstatus") }
  372. }
  373. public var selfavatar: String? {
  374. set { setConfig("selfavatar", newValue) }
  375. get { return getConfig("selfavatar") }
  376. }
  377. public var addr: String? {
  378. set { setConfig("addr", newValue) }
  379. get { return getConfig("addr") }
  380. }
  381. public var mailServer: String? {
  382. set { setConfig("mail_server", newValue) }
  383. get { return getConfig("mail_server") }
  384. }
  385. public var mailUser: String? {
  386. set { setConfig("mail_user", newValue) }
  387. get { return getConfig("mail_user") }
  388. }
  389. public var mailPw: String? {
  390. set { setConfig("mail_pw", newValue) }
  391. get { return getConfig("mail_pw") }
  392. }
  393. public var mailPort: String? {
  394. set { setConfig("mail_port", newValue) }
  395. get { return getConfig("mail_port") }
  396. }
  397. public var sendServer: String? {
  398. set { setConfig("send_server", newValue) }
  399. get { return getConfig("send_server") }
  400. }
  401. public var sendUser: String? {
  402. set { setConfig("send_user", newValue) }
  403. get { return getConfig("send_user") }
  404. }
  405. public var sendPw: String? {
  406. set { setConfig("send_pw", newValue) }
  407. get { return getConfig("send_pw") }
  408. }
  409. public var sendPort: String? {
  410. set { setConfig("send_port", newValue) }
  411. get { return getConfig("send_port") }
  412. }
  413. public var certificateChecks: Int {
  414. set {
  415. setConfig("smtp_certificate_checks", "\(newValue)")
  416. setConfig("imap_certificate_checks", "\(newValue)")
  417. }
  418. get {
  419. if let str = getConfig("imap_certificate_checks") {
  420. return Int(str) ?? 0
  421. } else {
  422. return 0
  423. }
  424. }
  425. }
  426. private var serverFlags: Int {
  427. // IMAP-/SMTP-flags as a combination of DC_LP flags
  428. set {
  429. setConfig("server_flags", "\(newValue)")
  430. }
  431. get {
  432. if let str = getConfig("server_flags") {
  433. return Int(str) ?? 0
  434. } else {
  435. return 0
  436. }
  437. }
  438. }
  439. public func setAuthFlags(flags: Int) {
  440. var sf = serverFlags
  441. sf = sf & ~0x6 // DC_LP_AUTH_FLAGS
  442. sf = sf | flags
  443. serverFlags = sf
  444. }
  445. public func getAuthFlags() -> Int {
  446. var sf = serverFlags
  447. sf = sf & 0x6 // DC_LP_AUTH_FLAGS
  448. return sf
  449. }
  450. public var e2eeEnabled: Bool {
  451. set { setConfigBool("e2ee_enabled", newValue) }
  452. get { return getConfigBool("e2ee_enabled") }
  453. }
  454. public var mdnsEnabled: Bool {
  455. set { setConfigBool("mdns_enabled", newValue) }
  456. get { return getConfigBool("mdns_enabled") }
  457. }
  458. public var showEmails: Int {
  459. // one of DC_SHOW_EMAILS_*
  460. set { setConfigInt("show_emails", newValue) }
  461. get { return getConfigInt("show_emails") }
  462. }
  463. // do not use. use DcContext::isConfigured() instead
  464. public var configured: Bool {
  465. return getConfigBool("configured")
  466. }
  467. }
  468. public class DcEventEmitter {
  469. private var eventEmitterPointer: OpaquePointer?
  470. // takes ownership of specified pointer
  471. public init(eventEmitterPointer: OpaquePointer?) {
  472. self.eventEmitterPointer = eventEmitterPointer
  473. }
  474. public func getNextEvent() -> DcEvent? {
  475. guard let eventPointer = dc_get_next_event(eventEmitterPointer) else { return nil }
  476. return DcEvent(eventPointer: eventPointer)
  477. }
  478. deinit {
  479. dc_event_emitter_unref(eventEmitterPointer)
  480. }
  481. }
  482. public class DcEvent {
  483. private var eventPointer: OpaquePointer?
  484. // takes ownership of specified pointer
  485. public init(eventPointer: OpaquePointer?) {
  486. self.eventPointer = eventPointer
  487. }
  488. deinit {
  489. dc_event_unref(eventPointer)
  490. }
  491. public var id: Int32 {
  492. return Int32(dc_event_get_id(eventPointer))
  493. }
  494. public var data1Int: Int {
  495. return Int(dc_event_get_data1_int(eventPointer))
  496. }
  497. public var data2Int: Int {
  498. return Int(dc_event_get_data2_int(eventPointer))
  499. }
  500. public var data2String: String {
  501. guard let cString = dc_event_get_data2_str(eventPointer) else { return "" }
  502. let swiftString = String(cString: cString)
  503. dc_str_unref(cString)
  504. return swiftString
  505. }
  506. }
  507. public class DcChatlist {
  508. private var chatListPointer: OpaquePointer?
  509. // takes ownership of specified pointer
  510. public init(chatListPointer: OpaquePointer?) {
  511. self.chatListPointer = chatListPointer
  512. }
  513. deinit {
  514. dc_chatlist_unref(chatListPointer)
  515. }
  516. public var length: Int {
  517. return dc_chatlist_get_cnt(chatListPointer)
  518. }
  519. public func getChatId(index: Int) -> Int {
  520. return Int(dc_chatlist_get_chat_id(chatListPointer, index))
  521. }
  522. public func getMsgId(index: Int) -> Int {
  523. return Int(dc_chatlist_get_msg_id(chatListPointer, index))
  524. }
  525. public func getSummary(index: Int) -> DcLot {
  526. guard let lotPointer = dc_chatlist_get_summary(self.chatListPointer, index, nil) else {
  527. fatalError("lot-pointer was nil")
  528. }
  529. return DcLot(lotPointer)
  530. }
  531. }
  532. public class DcChat {
  533. var chatPointer: OpaquePointer?
  534. // use DcContext.getChat() instead of calling the constructor directly
  535. public init(chatPointer: OpaquePointer?) {
  536. self.chatPointer = chatPointer
  537. }
  538. deinit {
  539. dc_chat_unref(chatPointer)
  540. }
  541. public var id: Int {
  542. return Int(dc_chat_get_id(chatPointer))
  543. }
  544. public var name: String {
  545. guard let cString = dc_chat_get_name(chatPointer) else { return "" }
  546. let swiftString = String(cString: cString)
  547. dc_str_unref(cString)
  548. return swiftString
  549. }
  550. public var type: Int {
  551. return Int(dc_chat_get_type(chatPointer))
  552. }
  553. public var chatType: ChatType {
  554. return ChatType(rawValue: type) ?? ChatType.GROUP // group as fallback - shouldn't get here
  555. }
  556. public var color: UIColor {
  557. return UIColor(netHex: Int(dc_chat_get_color(chatPointer)))
  558. }
  559. public var isArchived: Bool {
  560. return Int(dc_chat_get_visibility(chatPointer)) == DC_CHAT_VISIBILITY_ARCHIVED
  561. }
  562. public var visibility: Int32 {
  563. return dc_chat_get_visibility(chatPointer)
  564. }
  565. public var isUnpromoted: Bool {
  566. return Int(dc_chat_is_unpromoted(chatPointer)) != 0
  567. }
  568. public var isGroup: Bool {
  569. let type = Int(dc_chat_get_type(chatPointer))
  570. return type == DC_CHAT_TYPE_GROUP || type == DC_CHAT_TYPE_VERIFIED_GROUP
  571. }
  572. public var isSelfTalk: Bool {
  573. return Int(dc_chat_is_self_talk(chatPointer)) != 0
  574. }
  575. public var isDeviceTalk: Bool {
  576. return Int(dc_chat_is_device_talk(chatPointer)) != 0
  577. }
  578. public var canSend: Bool {
  579. return Int(dc_chat_can_send(chatPointer)) != 0
  580. }
  581. public var isVerified: Bool {
  582. return dc_chat_is_verified(chatPointer) > 0
  583. }
  584. public var isMuted: Bool {
  585. return dc_chat_is_muted(chatPointer) != 0
  586. }
  587. public var contactIds: [Int] {
  588. return DcUtils.copyAndFreeArray(inputArray: dc_get_chat_contacts(DcContext.shared.contextPointer, UInt32(id)))
  589. }
  590. public lazy var profileImage: UIImage? = { [weak self] in
  591. guard let cString = dc_chat_get_profile_image(chatPointer) else { return nil }
  592. let filename = String(cString: cString)
  593. dc_str_unref(cString)
  594. let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
  595. if path.isFileURL {
  596. do {
  597. let data = try Data(contentsOf: path)
  598. let image = UIImage(data: data)
  599. return image
  600. } catch {
  601. DcContext.shared.logger?.warning("failed to load image: \(filename), \(error)")
  602. return nil
  603. }
  604. }
  605. return nil
  606. }()
  607. public var profileImageURL: URL? {
  608. guard let cString = dc_chat_get_profile_image(chatPointer) else { return nil }
  609. let filename = String(cString: cString)
  610. dc_str_unref(cString)
  611. let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
  612. return path
  613. }
  614. public var isSendingLocations: Bool {
  615. return dc_chat_is_sending_locations(chatPointer) == 1
  616. }
  617. }
  618. public class DcArray {
  619. private var dcArrayPointer: OpaquePointer?
  620. public init(arrayPointer: OpaquePointer) {
  621. dcArrayPointer = arrayPointer
  622. }
  623. deinit {
  624. dc_array_unref(dcArrayPointer)
  625. }
  626. public var count: Int {
  627. return Int(dc_array_get_cnt(dcArrayPointer))
  628. }
  629. }
  630. public class DcMsg {
  631. var messagePointer: OpaquePointer?
  632. /**
  633. viewType: one of
  634. DC_MSG_TEXT,
  635. DC_MSG_IMAGE,
  636. DC_MSG_GIF,
  637. DC_MSG_STICKER,
  638. DC_MSG_AUDIO,
  639. DC_MSG_VOICE,
  640. DC_MSG_VIDEO,
  641. DC_MSG_FILE
  642. */
  643. public init(viewType: Int32) {
  644. messagePointer = dc_msg_new(DcContext.shared.contextPointer, viewType)
  645. }
  646. public init(id: Int) {
  647. messagePointer = dc_get_msg(DcContext.shared.contextPointer, UInt32(id))
  648. }
  649. public init(type: Int32) {
  650. messagePointer = dc_msg_new(DcContext.shared.contextPointer, type)
  651. }
  652. deinit {
  653. dc_msg_unref(messagePointer)
  654. }
  655. public var cptr: OpaquePointer? {
  656. return messagePointer
  657. }
  658. public lazy var sentDate: Date = {
  659. Date(timeIntervalSince1970: Double(timestamp))
  660. }()
  661. public func formattedSentDate() -> String {
  662. return DateUtils.getExtendedRelativeTimeSpanString(timeStamp: Double(timestamp))
  663. }
  664. public var isForwarded: Bool {
  665. return dc_msg_is_forwarded(messagePointer) != 0
  666. }
  667. public var messageId: String {
  668. return "\(id)"
  669. }
  670. public var id: Int {
  671. return Int(dc_msg_get_id(messagePointer))
  672. }
  673. public var fromContactId: Int {
  674. return Int(dc_msg_get_from_id(messagePointer))
  675. }
  676. public lazy var fromContact: DcContact = {
  677. DcContact(id: fromContactId)
  678. }()
  679. public var isFromCurrentSender: Bool {
  680. return fromContact.id == DcContact(id: Int(DC_CONTACT_ID_SELF)).id
  681. }
  682. public var chatId: Int {
  683. return Int(dc_msg_get_chat_id(messagePointer))
  684. }
  685. public var text: String? {
  686. set {
  687. if let newValue = newValue {
  688. dc_msg_set_text(messagePointer, newValue.cString(using: .utf8))
  689. } else {
  690. dc_msg_set_text(messagePointer, nil)
  691. }
  692. }
  693. get {
  694. guard let cString = dc_msg_get_text(messagePointer) else { return nil }
  695. let swiftString = String(cString: cString)
  696. dc_str_unref(cString)
  697. return swiftString
  698. }
  699. }
  700. public var viewtype: MessageViewType? {
  701. switch dc_msg_get_viewtype(messagePointer) {
  702. case 0:
  703. return nil
  704. case DC_MSG_AUDIO:
  705. return .audio
  706. case DC_MSG_FILE:
  707. return .file
  708. case DC_MSG_GIF:
  709. return .gif
  710. case DC_MSG_TEXT:
  711. return .text
  712. case DC_MSG_IMAGE:
  713. return .image
  714. case DC_MSG_STICKER:
  715. return .image
  716. case DC_MSG_VIDEO:
  717. return .video
  718. case DC_MSG_VOICE:
  719. return .voice
  720. default:
  721. return nil
  722. }
  723. }
  724. public var fileURL: URL? {
  725. if let file = self.file {
  726. return URL(fileURLWithPath: file, isDirectory: false)
  727. }
  728. return nil
  729. }
  730. public lazy var image: UIImage? = { [weak self] in
  731. let filetype = dc_msg_get_viewtype(messagePointer)
  732. if let path = fileURL, filetype == DC_MSG_IMAGE || filetype == DC_MSG_GIF {
  733. if path.isFileURL {
  734. do {
  735. let data = try Data(contentsOf: path)
  736. let image = UIImage(data: data)
  737. return image
  738. } catch {
  739. DcContext.shared.logger?.warning("failed to load image: \(path), \(error)")
  740. return nil
  741. }
  742. }
  743. return nil
  744. } else {
  745. return nil
  746. }
  747. }()
  748. public var file: String? {
  749. if let cString = dc_msg_get_file(messagePointer) {
  750. let str = String(cString: cString)
  751. dc_str_unref(cString)
  752. return str.isEmpty ? nil : str
  753. }
  754. return nil
  755. }
  756. public var filemime: String? {
  757. if let cString = dc_msg_get_filemime(messagePointer) {
  758. let str = String(cString: cString)
  759. dc_str_unref(cString)
  760. return str.isEmpty ? nil : str
  761. }
  762. return nil
  763. }
  764. public var filename: String? {
  765. if let cString = dc_msg_get_filename(messagePointer) {
  766. let str = String(cString: cString)
  767. dc_str_unref(cString)
  768. return str.isEmpty ? nil : str
  769. }
  770. return nil
  771. }
  772. public func setFile(filepath: String?, mimeType: String? = nil) {
  773. dc_msg_set_file(messagePointer, filepath, mimeType)
  774. }
  775. public func setDimension(width: CGFloat, height: CGFloat) {
  776. dc_msg_set_dimension(messagePointer, Int32(width), Int32(height))
  777. }
  778. public var filesize: Int {
  779. return Int(dc_msg_get_filebytes(messagePointer))
  780. }
  781. // DC_MSG_*
  782. public var type: Int {
  783. return Int(dc_msg_get_viewtype(messagePointer))
  784. }
  785. // DC_STATE_*
  786. public var state: Int {
  787. return Int(dc_msg_get_state(messagePointer))
  788. }
  789. public var timestamp: Int64 {
  790. return Int64(dc_msg_get_timestamp(messagePointer))
  791. }
  792. public var isInfo: Bool {
  793. return dc_msg_is_info(messagePointer) == 1
  794. }
  795. public var isSetupMessage: Bool {
  796. return dc_msg_is_setupmessage(messagePointer) == 1
  797. }
  798. public var setupCodeBegin: String {
  799. guard let cString = dc_msg_get_setupcodebegin(messagePointer) else { return "" }
  800. let swiftString = String(cString: cString)
  801. dc_str_unref(cString)
  802. return swiftString
  803. }
  804. public func summary(chars: Int) -> String? {
  805. guard let cString = dc_msg_get_summarytext(messagePointer, Int32(chars)) else { return nil }
  806. let swiftString = String(cString: cString)
  807. dc_str_unref(cString)
  808. return swiftString
  809. }
  810. public func summary(chat: DcChat) -> DcLot {
  811. guard let chatPointer = chat.chatPointer else {
  812. fatalError()
  813. }
  814. guard let dcLotPointer = dc_msg_get_summary(messagePointer, chatPointer) else {
  815. fatalError()
  816. }
  817. return DcLot(dcLotPointer)
  818. }
  819. public func showPadlock() -> Bool {
  820. return dc_msg_get_showpadlock(messagePointer) == 1
  821. }
  822. public func sendInChat(id: Int) {
  823. dc_send_msg(DcContext.shared.contextPointer, UInt32(id), messagePointer)
  824. }
  825. public func previousMediaURLs() -> [URL] {
  826. var urls: [URL] = []
  827. var prev: Int = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(id), -1, Int32(type), 0, 0))
  828. while prev != 0 {
  829. let prevMessage = DcMsg(id: prev)
  830. if let url = prevMessage.fileURL {
  831. urls.insert(url, at: 0)
  832. }
  833. prev = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(prevMessage.id), -1, Int32(prevMessage.type), 0, 0))
  834. }
  835. return urls
  836. }
  837. public func nextMediaURLs() -> [URL] {
  838. var urls: [URL] = []
  839. var next: Int = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(id), 1, Int32(type), 0, 0))
  840. while next != 0 {
  841. let nextMessage = DcMsg(id: next)
  842. if let url = nextMessage.fileURL {
  843. urls.append(url)
  844. }
  845. next = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(nextMessage.id), 1, Int32(nextMessage.type), 0, 0))
  846. }
  847. return urls
  848. }
  849. }
  850. public class DcContact {
  851. private var contactPointer: OpaquePointer?
  852. public init(id: Int) {
  853. contactPointer = dc_get_contact(DcContext.shared.contextPointer, UInt32(id))
  854. }
  855. deinit {
  856. dc_contact_unref(contactPointer)
  857. }
  858. public var displayName: String {
  859. guard let cString = dc_contact_get_display_name(contactPointer) else { return "" }
  860. let swiftString = String(cString: cString)
  861. dc_str_unref(cString)
  862. return swiftString
  863. }
  864. public var nameNAddr: String {
  865. guard let cString = dc_contact_get_name_n_addr(contactPointer) else { return "" }
  866. let swiftString = String(cString: cString)
  867. dc_str_unref(cString)
  868. return swiftString
  869. }
  870. public var name: String {
  871. guard let cString = dc_contact_get_name(contactPointer) else { return "" }
  872. let swiftString = String(cString: cString)
  873. dc_str_unref(cString)
  874. return swiftString
  875. }
  876. public var email: String {
  877. guard let cString = dc_contact_get_addr(contactPointer) else { return "" }
  878. let swiftString = String(cString: cString)
  879. dc_str_unref(cString)
  880. return swiftString
  881. }
  882. public var isVerified: Bool {
  883. return dc_contact_is_verified(contactPointer) > 0
  884. }
  885. public var isBlocked: Bool {
  886. return dc_contact_is_blocked(contactPointer) == 1
  887. }
  888. public lazy var profileImage: UIImage? = { [weak self] in
  889. guard let cString = dc_contact_get_profile_image(contactPointer) else { return nil }
  890. let filename = String(cString: cString)
  891. dc_str_unref(cString)
  892. let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
  893. if path.isFileURL {
  894. do {
  895. let data = try Data(contentsOf: path)
  896. return UIImage(data: data)
  897. } catch {
  898. DcContext.shared.logger?.warning("failed to load image: \(filename), \(error)")
  899. return nil
  900. }
  901. }
  902. return nil
  903. }()
  904. public var profileImageURL: URL? {
  905. guard let cString = dc_contact_get_profile_image(contactPointer) else { return nil }
  906. let filename = String(cString: cString)
  907. dc_str_unref(cString)
  908. return URL(fileURLWithPath: filename, isDirectory: false)
  909. }
  910. public var color: UIColor {
  911. return UIColor(netHex: Int(dc_contact_get_color(contactPointer)))
  912. }
  913. public var id: Int {
  914. return Int(dc_contact_get_id(contactPointer))
  915. }
  916. public func block() {
  917. dc_block_contact(DcContext.shared.contextPointer, UInt32(id), 1)
  918. }
  919. public func unblock() {
  920. dc_block_contact(DcContext.shared.contextPointer, UInt32(id), 0)
  921. }
  922. public func marknoticed() {
  923. dc_marknoticed_contact(DcContext.shared.contextPointer, UInt32(id))
  924. }
  925. }
  926. public class DcLot {
  927. private var dcLotPointer: OpaquePointer?
  928. // takes ownership of specified pointer
  929. public init(_ dcLotPointer: OpaquePointer) {
  930. self.dcLotPointer = dcLotPointer
  931. }
  932. deinit {
  933. dc_lot_unref(dcLotPointer)
  934. }
  935. public var text1: String? {
  936. guard let cString = dc_lot_get_text1(dcLotPointer) else { return nil }
  937. let swiftString = String(cString: cString)
  938. dc_str_unref(cString)
  939. return swiftString
  940. }
  941. public var text1Meaning: Int {
  942. return Int(dc_lot_get_text1_meaning(dcLotPointer))
  943. }
  944. public var text2: String? {
  945. guard let cString = dc_lot_get_text2(dcLotPointer) else { return nil }
  946. let swiftString = String(cString: cString)
  947. dc_str_unref(cString)
  948. return swiftString
  949. }
  950. public var timestamp: Int64 {
  951. return Int64(dc_lot_get_timestamp(dcLotPointer))
  952. }
  953. public var state: Int {
  954. return Int(dc_lot_get_state(dcLotPointer))
  955. }
  956. public var id: Int {
  957. return Int(dc_lot_get_id(dcLotPointer))
  958. }
  959. }
  960. public class DcProvider {
  961. private var dcProviderPointer: OpaquePointer?
  962. // takes ownership of specified pointer
  963. public init(_ dcProviderPointer: OpaquePointer) {
  964. self.dcProviderPointer = dcProviderPointer
  965. }
  966. deinit {
  967. dc_provider_unref(dcProviderPointer)
  968. }
  969. public var status: Int {
  970. return Int(dc_provider_get_status(dcProviderPointer))
  971. }
  972. public var beforeLoginHint: String {
  973. guard let cString = dc_provider_get_before_login_hint(dcProviderPointer) else { return "" }
  974. let swiftString = String(cString: cString)
  975. dc_str_unref(cString)
  976. return swiftString
  977. }
  978. public var getOverviewPage: String {
  979. guard let cString = dc_provider_get_overview_page(dcProviderPointer) else { return "" }
  980. let swiftString = String(cString: cString)
  981. dc_str_unref(cString)
  982. return swiftString
  983. }
  984. }
  985. public enum ChatType: Int {
  986. case SINGLE = 100
  987. case GROUP = 120
  988. case VERIFIEDGROUP = 130
  989. }
  990. public enum MessageViewType: CustomStringConvertible {
  991. case audio
  992. case file
  993. case gif
  994. case image
  995. case text
  996. case video
  997. case voice
  998. public var description: String {
  999. switch self {
  1000. // Use Internationalization, as appropriate.
  1001. case .audio: return "Audio"
  1002. case .file: return "File"
  1003. case .gif: return "GIF"
  1004. case .image: return "Image"
  1005. case .text: return "Text"
  1006. case .video: return "Video"
  1007. case .voice: return "Voice"
  1008. }
  1009. }
  1010. }
  1011. func strToBool(_ value: String?) -> Bool {
  1012. if let vStr = value {
  1013. if let vInt = Int(vStr) {
  1014. return vInt == 1
  1015. }
  1016. return false
  1017. }
  1018. return false
  1019. }