Files
chatmail-core/node/lib/deltachat.ts
2024-01-19 18:09:19 +01:00

235 lines
5.9 KiB
TypeScript

/* eslint-disable camelcase */
import binding from './binding'
import { EventId2EventName } from './constants'
import { EventEmitter } from 'events'
import { existsSync } from 'fs'
import rawDebug from 'debug'
import { tmpdir } from 'os'
import { join } from 'path'
import { Context } from './context'
const debug = rawDebug('deltachat:node:index')
const noop = function () {}
interface NativeAccount {}
/**
* Wrapper around dcn_account_t*
*/
export class AccountManager extends EventEmitter {
dcn_accounts: NativeAccount
accountDir: string
jsonRpcStarted = false
constructor(cwd: string, writable = true) {
super()
debug('DeltaChat constructor')
this.accountDir = cwd
this.dcn_accounts = binding.dcn_accounts_new(
this.accountDir,
writable ? 1 : 0
)
}
getAllAccountIds() {
return binding.dcn_accounts_get_all(this.dcn_accounts)
}
selectAccount(account_id: number) {
return binding.dcn_accounts_select_account(this.dcn_accounts, account_id)
}
selectedAccount(): number {
return binding.dcn_accounts_get_selected_account(this.dcn_accounts)
}
addAccount(): number {
return binding.dcn_accounts_add_account(this.dcn_accounts)
}
addClosedAccount(): number {
return binding.dcn_accounts_add_closed_account(this.dcn_accounts)
}
removeAccount(account_id: number) {
return binding.dcn_accounts_remove_account(this.dcn_accounts, account_id)
}
accountContext(account_id: number) {
const native_context = binding.dcn_accounts_get_account(
this.dcn_accounts,
account_id
)
if (native_context === null) {
throw new Error(
`could not get context with id ${account_id}, does it even exist? please check your ids`
)
}
return new Context(this, native_context, account_id)
}
migrateAccount(dbfile: string): number {
return binding.dcn_accounts_migrate_account(this.dcn_accounts, dbfile)
}
close() {
this.stopIO()
debug('unrefing context')
binding.dcn_accounts_unref(this.dcn_accounts)
debug('Unref end')
}
emit(
event: string | symbol,
account_id: number,
data1: any,
data2: any
): boolean {
super.emit('ALL', event, account_id, data1, data2)
return super.emit(event, account_id, data1, data2)
}
handleCoreEvent(
eventId: number,
accountId: number,
data1: number,
data2: number | string
) {
const eventString = EventId2EventName[eventId]
debug('event', eventString, accountId, data1, data2)
debug(eventString, data1, data2)
if (!this.emit) {
console.log('Received an event but EventEmitter is already destroyed.')
console.log(eventString, data1, data2)
return
}
this.emit(eventString, accountId, data1, data2)
}
startEvents() {
if (this.dcn_accounts === null) {
throw new Error('dcn_account is null')
}
binding.dcn_accounts_start_event_handler(
this.dcn_accounts,
this.handleCoreEvent.bind(this)
)
debug('Started event handler')
}
startJsonRpcHandler(callback: ((response: string) => void) | null) {
if (this.dcn_accounts === null) {
throw new Error('dcn_account is null')
}
if (!callback) {
throw new Error('no callback set')
}
if (this.jsonRpcStarted) {
throw new Error('jsonrpc was started already')
}
binding.dcn_accounts_start_jsonrpc(this.dcn_accounts, callback.bind(this))
debug('Started JSON-RPC handler')
this.jsonRpcStarted = true
}
jsonRpcRequest(message: string) {
if (!this.jsonRpcStarted) {
throw new Error(
'jsonrpc is not active, start it with startJsonRpcHandler first'
)
}
binding.dcn_json_rpc_request(this.dcn_accounts, message)
}
startIO() {
binding.dcn_accounts_start_io(this.dcn_accounts)
}
stopIO() {
binding.dcn_accounts_stop_io(this.dcn_accounts)
}
static maybeValidAddr(addr: string) {
debug('DeltaChat.maybeValidAddr')
if (addr === null) return false
return Boolean(binding.dcn_maybe_valid_addr(addr))
}
static parseGetInfo(info: string) {
debug('static _getInfo')
const result: { [key: string]: string } = {}
const regex = /^(\w+)=(.*)$/i
info
.split('\n')
.filter(Boolean)
.forEach((line) => {
const match = regex.exec(line)
if (match) {
result[match[1]] = match[2]
}
})
return result
}
static newTemporary() {
let directory = null
while (true) {
const randomString = Math.random().toString(36).substring(2, 5)
directory = join(tmpdir(), 'deltachat-' + randomString)
if (!existsSync(directory)) break
}
const dc = new AccountManager(directory)
const accountId = dc.addAccount()
const context = dc.accountContext(accountId)
return { dc, context, accountId, directory }
}
static getSystemInfo() {
debug('DeltaChat.getSystemInfo')
const { dc, context } = AccountManager.newTemporary()
const info = AccountManager.parseGetInfo(
binding.dcn_get_info(context.dcn_context)
)
const {
deltachat_core_version,
sqlite_version,
sqlite_thread_safe,
libetpan_version,
openssl_version,
compile_date,
arch,
} = info
const result = {
deltachat_core_version,
sqlite_version,
sqlite_thread_safe,
libetpan_version,
openssl_version,
compile_date,
arch,
}
context.unref()
dc.close()
return result
}
/** get information about the provider
*
* This function creates a temporary context to be standalone,
* if possible use `Context.getProviderFromEmail` instead. (otherwise potential proxy settings are not used)
* @deprecated
*/
static getProviderFromEmail(email: string) {
debug('DeltaChat.getProviderFromEmail')
const { dc, context } = AccountManager.newTemporary()
const provider = context.getProviderFromEmail(email)
context.unref()
dc.close()
return provider
}
}