Wrapper.swift 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  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.copyAndFreeArray(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 setStockTranslation(id: Int32, localizationKey: String) {
  183. dc_set_stock_translation(contextPointer, UInt32(id), String.localized(localizationKey))
  184. }
  185. public func getDraft(chatId: Int) -> DcMsg? {
  186. if let draft = dc_get_draft(contextPointer, UInt32(chatId)) {
  187. return DcMsg(pointer: draft)
  188. }
  189. return nil
  190. }
  191. public func setDraft(chatId: Int, message: DcMsg?) {
  192. dc_set_draft(contextPointer, UInt32(chatId), message?.messagePointer)
  193. }
  194. public func getFreshMessages() -> DcArray {
  195. return DcArray(arrayPointer: dc_get_fresh_msgs(contextPointer))
  196. }
  197. public func markSeenMessages(messageIds: [UInt32]) {
  198. messageIds.withUnsafeBufferPointer { ptr in
  199. dc_markseen_msgs(contextPointer, ptr.baseAddress, Int32(ptr.count))
  200. }
  201. }
  202. public func getChatMessages(chatId: Int) -> OpaquePointer {
  203. return dc_get_chat_msgs(contextPointer, UInt32(chatId), 0, 0)
  204. }
  205. public func getMsgInfo(msgId: Int) -> String {
  206. if let cString = dc_get_msg_info(self.contextPointer, UInt32(msgId)) {
  207. let swiftString = String(cString: cString)
  208. dc_str_unref(cString)
  209. return swiftString
  210. }
  211. return "ErrGetMsgInfo"
  212. }
  213. public func deleteMessage(msgId: Int) {
  214. dc_delete_msgs(contextPointer, [UInt32(msgId)], 1)
  215. }
  216. public func deleteMessages(msgIds: [Int]) {
  217. dc_delete_msgs(contextPointer, msgIds.compactMap{ UInt32($0) }, Int32(msgIds.count))
  218. }
  219. public func forwardMessage(with msgId: Int, to chat: Int) {
  220. dc_forward_msgs(contextPointer, [UInt32(msgId)], 1, UInt32(chat))
  221. }
  222. public func sendTextInChat(id: Int, message: String) {
  223. dc_send_text_msg(contextPointer, UInt32(id), message)
  224. }
  225. public func initiateKeyTransfer() -> String? {
  226. if let cString = dc_initiate_key_transfer(self.contextPointer) {
  227. let swiftString = String(cString: cString)
  228. dc_str_unref(cString)
  229. return swiftString
  230. }
  231. return nil
  232. }
  233. public func continueKeyTransfer(msgId: Int, setupCode: String) -> Bool {
  234. return dc_continue_key_transfer(self.contextPointer, UInt32(msgId), setupCode) != 0
  235. }
  236. public func configure() {
  237. maxConfigureProgress = 0
  238. lastWarningString = ""
  239. dc_configure(contextPointer)
  240. }
  241. public func setChatMuteDuration(chatId: Int, duration: Int) {
  242. dc_set_chat_mute_duration(self.contextPointer, UInt32(chatId), Int64(duration))
  243. }
  244. public func setChatEphemeralTimer(chatId: Int, duration: Int) {
  245. dc_set_chat_ephemeral_timer(self.contextPointer, UInt32(chatId), UInt32(duration))
  246. }
  247. public func getChatEphemeralTimer(chatId: Int) -> Int {
  248. return Int(dc_get_chat_ephemeral_timer(self.contextPointer, UInt32(chatId)))
  249. }
  250. public func getConfig(_ key: String) -> String? {
  251. guard let cString = dc_get_config(self.contextPointer, key) else { return nil }
  252. let value = String(cString: cString)
  253. dc_str_unref(cString)
  254. if value.isEmpty {
  255. return nil
  256. }
  257. return value
  258. }
  259. public func setConfig(_ key: String, _ value: String?) {
  260. if let v = value {
  261. dc_set_config(self.contextPointer, key, v)
  262. } else {
  263. dc_set_config(self.contextPointer, key, nil)
  264. }
  265. }
  266. public func getConfigBool(_ key: String) -> Bool {
  267. return strToBool(getConfig(key))
  268. }
  269. public func setConfigBool(_ key: String, _ value: Bool) {
  270. let vStr = value ? "1" : "0"
  271. setConfig(key, vStr)
  272. }
  273. public func getConfigInt(_ key: String) -> Int {
  274. if let str = getConfig(key) {
  275. return Int(str) ?? 0
  276. }
  277. return 0
  278. }
  279. public func setConfigInt(_ key: String, _ value: Int) {
  280. setConfig(key, String(value))
  281. }
  282. public func getUnreadMessages(chatId: Int) -> Int {
  283. return Int(dc_get_fresh_msg_cnt(contextPointer, UInt32(chatId)))
  284. }
  285. public func estimateDeletionCnt(fromServer: Bool, timeout: Int) -> Int {
  286. return Int(dc_estimate_deletion_cnt(contextPointer, fromServer ? 1 : 0, Int64(timeout)))
  287. }
  288. public func isConfigured() -> Bool {
  289. return dc_is_configured(contextPointer) != 0
  290. }
  291. public func getSelfAvatarImage() -> UIImage? {
  292. guard let fileName = selfavatar else { return nil }
  293. let path: URL = URL(fileURLWithPath: fileName, isDirectory: false)
  294. if path.isFileURL {
  295. do {
  296. let data = try Data(contentsOf: path)
  297. return UIImage(data: data)
  298. } catch {
  299. logger?.warning("failed to load image: \(fileName), \(error)")
  300. return nil
  301. }
  302. }
  303. return nil
  304. }
  305. public func setChatProfileImage(chatId: Int, path: String?) {
  306. dc_set_chat_profile_image(contextPointer, UInt32(chatId), path)
  307. }
  308. @discardableResult
  309. public func addDeviceMessage(label: String, msg: DcMsg) -> Int {
  310. return Int(dc_add_device_msg(contextPointer, label.cString(using: .utf8), msg.cptr))
  311. }
  312. public func getProviderFromEmail(addr: String) -> DcProvider? {
  313. guard let dcProviderPointer = dc_provider_new_from_email(contextPointer, addr) else { return nil }
  314. return DcProvider(dcProviderPointer)
  315. }
  316. public func imex(what: Int32, directory: String) {
  317. dc_imex(contextPointer, what, directory, nil)
  318. }
  319. public func imexHasBackup(filePath: String) -> String? {
  320. var file: String?
  321. if let cString = dc_imex_has_backup(contextPointer, filePath) {
  322. file = String(cString: cString)
  323. dc_str_unref(cString)
  324. }
  325. return file
  326. }
  327. public func isSendingLocationsToChat(chatId: Int) -> Bool {
  328. return dc_is_sending_locations_to_chat(contextPointer, UInt32(chatId)) == 1
  329. }
  330. public func sendLocationsToChat(chatId: Int, seconds: Int) {
  331. dc_send_locations_to_chat(contextPointer, UInt32(chatId), Int32(seconds))
  332. }
  333. public func setLocation(latitude: Double, longitude: Double, accuracy: Double) {
  334. dc_set_location(contextPointer, latitude, longitude, accuracy)
  335. }
  336. public func searchMessages(chatId: Int = 0, searchText: String) -> [Int] {
  337. guard let arrayPointer = dc_search_msgs(contextPointer, UInt32(chatId), searchText) else {
  338. return []
  339. }
  340. let messageIds = DcUtils.copyAndFreeArray(inputArray: arrayPointer)
  341. return messageIds
  342. }
  343. // call dc_maybe_network() from a worker thread.
  344. public func maybeNetwork() {
  345. dc_maybe_network(contextPointer)
  346. }
  347. // also, there is no much worth in adding a separate function or so
  348. // for each config option - esp. if they are just forwarded to the core
  349. // and set/get only at one line of code each.
  350. // this adds a complexity that can be avoided -
  351. // and makes grep harder as these names are typically named following different guidelines.
  352. public var displayname: String? {
  353. set { setConfig("displayname", newValue) }
  354. get { return getConfig("displayname") }
  355. }
  356. public var selfstatus: String? {
  357. set { setConfig("selfstatus", newValue) }
  358. get { return getConfig("selfstatus") }
  359. }
  360. public var selfavatar: String? {
  361. set { setConfig("selfavatar", newValue) }
  362. get { return getConfig("selfavatar") }
  363. }
  364. public var addr: String? {
  365. set { setConfig("addr", newValue) }
  366. get { return getConfig("addr") }
  367. }
  368. public var mailServer: String? {
  369. set { setConfig("mail_server", newValue) }
  370. get { return getConfig("mail_server") }
  371. }
  372. public var mailUser: String? {
  373. set { setConfig("mail_user", newValue) }
  374. get { return getConfig("mail_user") }
  375. }
  376. public var mailPw: String? {
  377. set { setConfig("mail_pw", newValue) }
  378. get { return getConfig("mail_pw") }
  379. }
  380. public var mailPort: String? {
  381. set { setConfig("mail_port", newValue) }
  382. get { return getConfig("mail_port") }
  383. }
  384. public var sendServer: String? {
  385. set { setConfig("send_server", newValue) }
  386. get { return getConfig("send_server") }
  387. }
  388. public var sendUser: String? {
  389. set { setConfig("send_user", newValue) }
  390. get { return getConfig("send_user") }
  391. }
  392. public var sendPw: String? {
  393. set { setConfig("send_pw", newValue) }
  394. get { return getConfig("send_pw") }
  395. }
  396. public var sendPort: String? {
  397. set { setConfig("send_port", newValue) }
  398. get { return getConfig("send_port") }
  399. }
  400. public var certificateChecks: Int {
  401. set {
  402. setConfig("smtp_certificate_checks", "\(newValue)")
  403. setConfig("imap_certificate_checks", "\(newValue)")
  404. }
  405. get {
  406. if let str = getConfig("imap_certificate_checks") {
  407. return Int(str) ?? 0
  408. } else {
  409. return 0
  410. }
  411. }
  412. }
  413. private var serverFlags: Int {
  414. // IMAP-/SMTP-flags as a combination of DC_LP flags
  415. set {
  416. setConfig("server_flags", "\(newValue)")
  417. }
  418. get {
  419. if let str = getConfig("server_flags") {
  420. return Int(str) ?? 0
  421. } else {
  422. return 0
  423. }
  424. }
  425. }
  426. public func setAuthFlags(flags: Int) {
  427. var sf = serverFlags
  428. sf = sf & ~0x6 // DC_LP_AUTH_FLAGS
  429. sf = sf | flags
  430. serverFlags = sf
  431. }
  432. public func getAuthFlags() -> Int {
  433. var sf = serverFlags
  434. sf = sf & 0x6 // DC_LP_AUTH_FLAGS
  435. return sf
  436. }
  437. public var e2eeEnabled: Bool {
  438. set { setConfigBool("e2ee_enabled", newValue) }
  439. get { return getConfigBool("e2ee_enabled") }
  440. }
  441. public var mdnsEnabled: Bool {
  442. set { setConfigBool("mdns_enabled", newValue) }
  443. get { return getConfigBool("mdns_enabled") }
  444. }
  445. public var showEmails: Int {
  446. // one of DC_SHOW_EMAILS_*
  447. set { setConfigInt("show_emails", newValue) }
  448. get { return getConfigInt("show_emails") }
  449. }
  450. // do not use. use DcContext::isConfigured() instead
  451. public var configured: Bool {
  452. return getConfigBool("configured")
  453. }
  454. }
  455. public class DcEventEmitter {
  456. private var eventEmitterPointer: OpaquePointer?
  457. // takes ownership of specified pointer
  458. public init(eventEmitterPointer: OpaquePointer?) {
  459. self.eventEmitterPointer = eventEmitterPointer
  460. }
  461. public func getNextEvent() -> DcEvent? {
  462. guard let eventPointer = dc_get_next_event(eventEmitterPointer) else { return nil }
  463. return DcEvent(eventPointer: eventPointer)
  464. }
  465. deinit {
  466. dc_event_emitter_unref(eventEmitterPointer)
  467. }
  468. }
  469. public class DcEvent {
  470. private var eventPointer: OpaquePointer?
  471. // takes ownership of specified pointer
  472. public init(eventPointer: OpaquePointer?) {
  473. self.eventPointer = eventPointer
  474. }
  475. deinit {
  476. dc_event_unref(eventPointer)
  477. }
  478. public var id: Int32 {
  479. return Int32(dc_event_get_id(eventPointer))
  480. }
  481. public var data1Int: Int {
  482. return Int(dc_event_get_data1_int(eventPointer))
  483. }
  484. public var data2Int: Int {
  485. return Int(dc_event_get_data2_int(eventPointer))
  486. }
  487. public var data2String: String {
  488. guard let cString = dc_event_get_data2_str(eventPointer) else { return "" }
  489. let swiftString = String(cString: cString)
  490. dc_str_unref(cString)
  491. return swiftString
  492. }
  493. }
  494. public class DcChatlist {
  495. private var chatListPointer: OpaquePointer?
  496. // takes ownership of specified pointer
  497. public init(chatListPointer: OpaquePointer?) {
  498. self.chatListPointer = chatListPointer
  499. }
  500. deinit {
  501. dc_chatlist_unref(chatListPointer)
  502. }
  503. public var length: Int {
  504. return dc_chatlist_get_cnt(chatListPointer)
  505. }
  506. public func getChatId(index: Int) -> Int {
  507. return Int(dc_chatlist_get_chat_id(chatListPointer, index))
  508. }
  509. public func getMsgId(index: Int) -> Int {
  510. return Int(dc_chatlist_get_msg_id(chatListPointer, index))
  511. }
  512. public func getSummary(index: Int) -> DcLot {
  513. guard let lotPointer = dc_chatlist_get_summary(self.chatListPointer, index, nil) else {
  514. fatalError("lot-pointer was nil")
  515. }
  516. return DcLot(lotPointer)
  517. }
  518. }
  519. public class DcChat {
  520. var chatPointer: OpaquePointer?
  521. // use DcContext.getChat() instead of calling the constructor directly
  522. public init(chatPointer: OpaquePointer?) {
  523. self.chatPointer = chatPointer
  524. }
  525. deinit {
  526. dc_chat_unref(chatPointer)
  527. }
  528. public var id: Int {
  529. return Int(dc_chat_get_id(chatPointer))
  530. }
  531. public var name: String {
  532. guard let cString = dc_chat_get_name(chatPointer) else { return "" }
  533. let swiftString = String(cString: cString)
  534. dc_str_unref(cString)
  535. return swiftString
  536. }
  537. public var type: Int {
  538. return Int(dc_chat_get_type(chatPointer))
  539. }
  540. public var chatType: ChatType {
  541. return ChatType(rawValue: type) ?? ChatType.GROUP // group as fallback - shouldn't get here
  542. }
  543. public var color: UIColor {
  544. return UIColor(netHex: Int(dc_chat_get_color(chatPointer)))
  545. }
  546. public var isArchived: Bool {
  547. return Int(dc_chat_get_visibility(chatPointer)) == DC_CHAT_VISIBILITY_ARCHIVED
  548. }
  549. public var visibility: Int32 {
  550. return dc_chat_get_visibility(chatPointer)
  551. }
  552. public var isUnpromoted: Bool {
  553. return Int(dc_chat_is_unpromoted(chatPointer)) != 0
  554. }
  555. public var isGroup: Bool {
  556. return Int(dc_chat_get_type(chatPointer)) == DC_CHAT_TYPE_GROUP
  557. }
  558. public var isSelfTalk: Bool {
  559. return Int(dc_chat_is_self_talk(chatPointer)) != 0
  560. }
  561. public var isDeviceTalk: Bool {
  562. return Int(dc_chat_is_device_talk(chatPointer)) != 0
  563. }
  564. public var canSend: Bool {
  565. return Int(dc_chat_can_send(chatPointer)) != 0
  566. }
  567. public var isProtected: Bool {
  568. return dc_chat_is_protected(chatPointer) != 0
  569. }
  570. public var isMuted: Bool {
  571. return dc_chat_is_muted(chatPointer) != 0
  572. }
  573. public var contactIds: [Int] {
  574. return DcUtils.copyAndFreeArray(inputArray: dc_get_chat_contacts(DcContext.shared.contextPointer, UInt32(id)))
  575. }
  576. public lazy var profileImage: UIImage? = { [weak self] in
  577. guard let cString = dc_chat_get_profile_image(chatPointer) else { return nil }
  578. let filename = String(cString: cString)
  579. dc_str_unref(cString)
  580. let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
  581. if path.isFileURL {
  582. do {
  583. let data = try Data(contentsOf: path)
  584. let image = UIImage(data: data)
  585. return image
  586. } catch {
  587. DcContext.shared.logger?.warning("failed to load image: \(filename), \(error)")
  588. return nil
  589. }
  590. }
  591. return nil
  592. }()
  593. public var profileImageURL: URL? {
  594. guard let cString = dc_chat_get_profile_image(chatPointer) else { return nil }
  595. let filename = String(cString: cString)
  596. dc_str_unref(cString)
  597. let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
  598. return path
  599. }
  600. public var isSendingLocations: Bool {
  601. return dc_chat_is_sending_locations(chatPointer) == 1
  602. }
  603. }
  604. public class DcArray {
  605. private var dcArrayPointer: OpaquePointer?
  606. public init(arrayPointer: OpaquePointer) {
  607. dcArrayPointer = arrayPointer
  608. }
  609. deinit {
  610. dc_array_unref(dcArrayPointer)
  611. }
  612. public var count: Int {
  613. return Int(dc_array_get_cnt(dcArrayPointer))
  614. }
  615. }
  616. public class DcMsg {
  617. var messagePointer: OpaquePointer?
  618. /**
  619. viewType: one of
  620. DC_MSG_TEXT,
  621. DC_MSG_IMAGE,
  622. DC_MSG_GIF,
  623. DC_MSG_STICKER,
  624. DC_MSG_AUDIO,
  625. DC_MSG_VOICE,
  626. DC_MSG_VIDEO,
  627. DC_MSG_FILE
  628. */
  629. public init(viewType: Int32) {
  630. messagePointer = dc_msg_new(DcContext.shared.contextPointer, viewType)
  631. }
  632. public init(id: Int) {
  633. messagePointer = dc_get_msg(DcContext.shared.contextPointer, UInt32(id))
  634. }
  635. init(pointer: OpaquePointer) {
  636. messagePointer = pointer
  637. }
  638. deinit {
  639. dc_msg_unref(messagePointer)
  640. }
  641. public var cptr: OpaquePointer? {
  642. return messagePointer
  643. }
  644. public lazy var sentDate: Date = {
  645. Date(timeIntervalSince1970: Double(timestamp))
  646. }()
  647. public func formattedSentDate() -> String {
  648. return DateUtils.getExtendedRelativeTimeSpanString(timeStamp: Double(timestamp))
  649. }
  650. public var isForwarded: Bool {
  651. return dc_msg_is_forwarded(messagePointer) != 0
  652. }
  653. public var messageId: String {
  654. return "\(id)"
  655. }
  656. public var id: Int {
  657. return Int(dc_msg_get_id(messagePointer))
  658. }
  659. public var fromContactId: Int {
  660. return Int(dc_msg_get_from_id(messagePointer))
  661. }
  662. public lazy var fromContact: DcContact = {
  663. DcContact(id: fromContactId)
  664. }()
  665. public var isFromCurrentSender: Bool {
  666. return fromContact.id == DcContact(id: Int(DC_CONTACT_ID_SELF)).id
  667. }
  668. public var chatId: Int {
  669. return Int(dc_msg_get_chat_id(messagePointer))
  670. }
  671. public var text: String? {
  672. set {
  673. if let newValue = newValue {
  674. dc_msg_set_text(messagePointer, newValue.cString(using: .utf8))
  675. } else {
  676. dc_msg_set_text(messagePointer, nil)
  677. }
  678. }
  679. get {
  680. guard let cString = dc_msg_get_text(messagePointer) else { return nil }
  681. let swiftString = String(cString: cString)
  682. dc_str_unref(cString)
  683. return swiftString
  684. }
  685. }
  686. public var quoteText: String? {
  687. guard let cString = dc_msg_get_quoted_text(messagePointer) else { return nil }
  688. let swiftString = String(cString: cString)
  689. dc_str_unref(cString)
  690. return swiftString
  691. }
  692. public var quoteMessage: DcMsg? {
  693. get {
  694. guard let msgpointer = dc_msg_get_quoted_msg(messagePointer) else { return nil }
  695. return DcMsg(pointer: msgpointer)
  696. }
  697. set {
  698. if newValue == nil {
  699. fatalError("Quote message cannot be set to null!")
  700. }
  701. dc_msg_set_quote(messagePointer, newValue?.messagePointer)
  702. }
  703. }
  704. public var viewtype: MessageViewType? {
  705. switch dc_msg_get_viewtype(messagePointer) {
  706. case 0:
  707. return nil
  708. case DC_MSG_AUDIO:
  709. return .audio
  710. case DC_MSG_FILE:
  711. return .file
  712. case DC_MSG_GIF:
  713. return .gif
  714. case DC_MSG_TEXT:
  715. return .text
  716. case DC_MSG_IMAGE:
  717. return .image
  718. case DC_MSG_STICKER:
  719. return .image
  720. case DC_MSG_VIDEO:
  721. return .video
  722. case DC_MSG_VOICE:
  723. return .voice
  724. default:
  725. return nil
  726. }
  727. }
  728. public var fileURL: URL? {
  729. if let file = self.file {
  730. return URL(fileURLWithPath: file, isDirectory: false)
  731. }
  732. return nil
  733. }
  734. public lazy var image: UIImage? = { [weak self] in
  735. let filetype = dc_msg_get_viewtype(messagePointer)
  736. if let path = fileURL, filetype == DC_MSG_IMAGE || filetype == DC_MSG_GIF {
  737. if path.isFileURL {
  738. do {
  739. let data = try Data(contentsOf: path)
  740. let image = UIImage(data: data)
  741. return image
  742. } catch {
  743. DcContext.shared.logger?.warning("failed to load image: \(path), \(error)")
  744. return nil
  745. }
  746. }
  747. return nil
  748. } else {
  749. return nil
  750. }
  751. }()
  752. public var messageHeight: CGFloat {
  753. return CGFloat(dc_msg_get_height(messagePointer))
  754. }
  755. public var messageWidth: CGFloat {
  756. return CGFloat(dc_msg_get_width(messagePointer))
  757. }
  758. public func setLateFilingMediaSize(width: CGFloat, height: CGFloat, duration: Int) {
  759. dc_msg_latefiling_mediasize(messagePointer, Int32(width), Int32(height), Int32(duration))
  760. }
  761. public var file: String? {
  762. if let cString = dc_msg_get_file(messagePointer) {
  763. let str = String(cString: cString)
  764. dc_str_unref(cString)
  765. return str.isEmpty ? nil : str
  766. }
  767. return nil
  768. }
  769. public var filemime: String? {
  770. if let cString = dc_msg_get_filemime(messagePointer) {
  771. let str = String(cString: cString)
  772. dc_str_unref(cString)
  773. return str.isEmpty ? nil : str
  774. }
  775. return nil
  776. }
  777. public var filename: String? {
  778. if let cString = dc_msg_get_filename(messagePointer) {
  779. let str = String(cString: cString)
  780. dc_str_unref(cString)
  781. return str.isEmpty ? nil : str
  782. }
  783. return nil
  784. }
  785. public func setFile(filepath: String?, mimeType: String? = nil) {
  786. dc_msg_set_file(messagePointer, filepath, mimeType)
  787. }
  788. public func setDimension(width: CGFloat, height: CGFloat) {
  789. dc_msg_set_dimension(messagePointer, Int32(width), Int32(height))
  790. }
  791. public var filesize: Int {
  792. return Int(dc_msg_get_filebytes(messagePointer))
  793. }
  794. // DC_MSG_*
  795. public var type: Int {
  796. return Int(dc_msg_get_viewtype(messagePointer))
  797. }
  798. // DC_STATE_*
  799. public var state: Int {
  800. return Int(dc_msg_get_state(messagePointer))
  801. }
  802. public var timestamp: Int64 {
  803. return Int64(dc_msg_get_timestamp(messagePointer))
  804. }
  805. public var isInfo: Bool {
  806. return dc_msg_is_info(messagePointer) == 1
  807. }
  808. public var isSetupMessage: Bool {
  809. return dc_msg_is_setupmessage(messagePointer) == 1
  810. }
  811. public var setupCodeBegin: String {
  812. guard let cString = dc_msg_get_setupcodebegin(messagePointer) else { return "" }
  813. let swiftString = String(cString: cString)
  814. dc_str_unref(cString)
  815. return swiftString
  816. }
  817. public func summary(chars: Int) -> String? {
  818. guard let cString = dc_msg_get_summarytext(messagePointer, Int32(chars)) else { return nil }
  819. let swiftString = String(cString: cString)
  820. dc_str_unref(cString)
  821. return swiftString
  822. }
  823. public func summary(chat: DcChat) -> DcLot {
  824. guard let chatPointer = chat.chatPointer else {
  825. fatalError()
  826. }
  827. guard let dcLotPointer = dc_msg_get_summary(messagePointer, chatPointer) else {
  828. fatalError()
  829. }
  830. return DcLot(dcLotPointer)
  831. }
  832. public func showPadlock() -> Bool {
  833. return dc_msg_get_showpadlock(messagePointer) == 1
  834. }
  835. public func sendInChat(id: Int) {
  836. dc_send_msg(DcContext.shared.contextPointer, UInt32(id), messagePointer)
  837. }
  838. public func previousMediaURLs() -> [URL] {
  839. var urls: [URL] = []
  840. var prev: Int = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(id), -1, Int32(type), 0, 0))
  841. while prev != 0 {
  842. let prevMessage = DcMsg(id: prev)
  843. if let url = prevMessage.fileURL {
  844. urls.insert(url, at: 0)
  845. }
  846. prev = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(prevMessage.id), -1, Int32(prevMessage.type), 0, 0))
  847. }
  848. return urls
  849. }
  850. public func nextMediaURLs() -> [URL] {
  851. var urls: [URL] = []
  852. var next: Int = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(id), 1, Int32(type), 0, 0))
  853. while next != 0 {
  854. let nextMessage = DcMsg(id: next)
  855. if let url = nextMessage.fileURL {
  856. urls.append(url)
  857. }
  858. next = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(nextMessage.id), 1, Int32(nextMessage.type), 0, 0))
  859. }
  860. return urls
  861. }
  862. }
  863. public class DcContact {
  864. private var contactPointer: OpaquePointer?
  865. public init(id: Int) {
  866. contactPointer = dc_get_contact(DcContext.shared.contextPointer, UInt32(id))
  867. }
  868. deinit {
  869. dc_contact_unref(contactPointer)
  870. }
  871. public var displayName: String {
  872. guard let cString = dc_contact_get_display_name(contactPointer) else { return "" }
  873. let swiftString = String(cString: cString)
  874. dc_str_unref(cString)
  875. return swiftString
  876. }
  877. public var nameNAddr: String {
  878. guard let cString = dc_contact_get_name_n_addr(contactPointer) else { return "" }
  879. let swiftString = String(cString: cString)
  880. dc_str_unref(cString)
  881. return swiftString
  882. }
  883. public var name: String {
  884. guard let cString = dc_contact_get_name(contactPointer) else { return "" }
  885. let swiftString = String(cString: cString)
  886. dc_str_unref(cString)
  887. return swiftString
  888. }
  889. public var email: String {
  890. guard let cString = dc_contact_get_addr(contactPointer) else { return "" }
  891. let swiftString = String(cString: cString)
  892. dc_str_unref(cString)
  893. return swiftString
  894. }
  895. public var isVerified: Bool {
  896. return dc_contact_is_verified(contactPointer) > 0
  897. }
  898. public var isBlocked: Bool {
  899. return dc_contact_is_blocked(contactPointer) == 1
  900. }
  901. public lazy var profileImage: UIImage? = { [weak self] in
  902. guard let cString = dc_contact_get_profile_image(contactPointer) else { return nil }
  903. let filename = String(cString: cString)
  904. dc_str_unref(cString)
  905. let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
  906. if path.isFileURL {
  907. do {
  908. let data = try Data(contentsOf: path)
  909. return UIImage(data: data)
  910. } catch {
  911. DcContext.shared.logger?.warning("failed to load image: \(filename), \(error)")
  912. return nil
  913. }
  914. }
  915. return nil
  916. }()
  917. public var profileImageURL: URL? {
  918. guard let cString = dc_contact_get_profile_image(contactPointer) else { return nil }
  919. let filename = String(cString: cString)
  920. dc_str_unref(cString)
  921. return URL(fileURLWithPath: filename, isDirectory: false)
  922. }
  923. public var color: UIColor {
  924. return UIColor(netHex: Int(dc_contact_get_color(contactPointer)))
  925. }
  926. public var id: Int {
  927. return Int(dc_contact_get_id(contactPointer))
  928. }
  929. public func block() {
  930. dc_block_contact(DcContext.shared.contextPointer, UInt32(id), 1)
  931. }
  932. public func unblock() {
  933. dc_block_contact(DcContext.shared.contextPointer, UInt32(id), 0)
  934. }
  935. public func marknoticed() {
  936. dc_marknoticed_contact(DcContext.shared.contextPointer, UInt32(id))
  937. }
  938. }
  939. public class DcLot {
  940. private var dcLotPointer: OpaquePointer?
  941. // takes ownership of specified pointer
  942. public init(_ dcLotPointer: OpaquePointer) {
  943. self.dcLotPointer = dcLotPointer
  944. }
  945. deinit {
  946. dc_lot_unref(dcLotPointer)
  947. }
  948. public var text1: String? {
  949. guard let cString = dc_lot_get_text1(dcLotPointer) else { return nil }
  950. let swiftString = String(cString: cString)
  951. dc_str_unref(cString)
  952. return swiftString
  953. }
  954. public var text1Meaning: Int {
  955. return Int(dc_lot_get_text1_meaning(dcLotPointer))
  956. }
  957. public var text2: String? {
  958. guard let cString = dc_lot_get_text2(dcLotPointer) else { return nil }
  959. let swiftString = String(cString: cString)
  960. dc_str_unref(cString)
  961. return swiftString
  962. }
  963. public var timestamp: Int64 {
  964. return Int64(dc_lot_get_timestamp(dcLotPointer))
  965. }
  966. public var state: Int {
  967. return Int(dc_lot_get_state(dcLotPointer))
  968. }
  969. public var id: Int {
  970. return Int(dc_lot_get_id(dcLotPointer))
  971. }
  972. }
  973. public class DcProvider {
  974. private var dcProviderPointer: OpaquePointer?
  975. // takes ownership of specified pointer
  976. public init(_ dcProviderPointer: OpaquePointer) {
  977. self.dcProviderPointer = dcProviderPointer
  978. }
  979. deinit {
  980. dc_provider_unref(dcProviderPointer)
  981. }
  982. public var status: Int {
  983. return Int(dc_provider_get_status(dcProviderPointer))
  984. }
  985. public var beforeLoginHint: String {
  986. guard let cString = dc_provider_get_before_login_hint(dcProviderPointer) else { return "" }
  987. let swiftString = String(cString: cString)
  988. dc_str_unref(cString)
  989. return swiftString
  990. }
  991. public var getOverviewPage: String {
  992. guard let cString = dc_provider_get_overview_page(dcProviderPointer) else { return "" }
  993. let swiftString = String(cString: cString)
  994. dc_str_unref(cString)
  995. return swiftString
  996. }
  997. }
  998. public enum ChatType: Int {
  999. case SINGLE = 100
  1000. case GROUP = 120
  1001. case VERIFIEDGROUP = 130
  1002. }
  1003. public enum MessageViewType: CustomStringConvertible {
  1004. case audio
  1005. case file
  1006. case gif
  1007. case image
  1008. case text
  1009. case video
  1010. case voice
  1011. public var description: String {
  1012. switch self {
  1013. // Use Internationalization, as appropriate.
  1014. case .audio: return "Audio"
  1015. case .file: return "File"
  1016. case .gif: return "GIF"
  1017. case .image: return "Image"
  1018. case .text: return "Text"
  1019. case .video: return "Video"
  1020. case .voice: return "Voice"
  1021. }
  1022. }
  1023. }
  1024. func strToBool(_ value: String?) -> Bool {
  1025. if let vStr = value {
  1026. if let vInt = Int(vStr) {
  1027. return vInt == 1
  1028. }
  1029. return false
  1030. }
  1031. return false
  1032. }