Wrapper.swift 32 KB

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