Wrapper.swift 34 KB

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