Wrapper.swift 28 KB

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