Управљање рачунима

In this section, you will learn how to manage a Jami account: creating an account, modifying its basic settings, and deleting an account. This section does not cover the meaning of each individual setting or using the account to perform actions such as adding contacts.

What is an account?

In Jami, an account is represented by an X.509 certificate chain composed of three certificates:

  1. CA: A Self-Signed certificate if generated locally, or issued by a company.

  2. Account: Contains the public key, its fingerprint serves as the Jami ID.

  3. Device: Associated with the account.

Белешка

This structure allows a company to revoke an entire account if necessary, and enables an account owner to revoke individual devices (e.g., if a device is stolen).

Creating a New Account

Daemon-side API

The ConfigurationManager API (cx.ring.Ring.ConfigurationManager) provides the addAccount method:

<method name="addAccount" tp:name-for-bindings="addAccount">
    <tp:docstring>
        Add a new account. When created, the signal <tp:member-ref>accountsChanged</tp:member-ref> is emitted. The clients must then call <tp:member-ref>getAccountList</tp:member-ref> to update their internal data structure.
        <tp:rationale>If no details are specified, the default parameters are used.</tp:rationale>
        <tp:rationale>The core tries to register the account as soon it is created.</tp:rationale>
    </tp:docstring>
    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="MapStringString"/>
    <arg type="a{ss}" name="details" direction="in"  tp:type="String_String_Map">
        <tp:docstring>
             The new account settings
        </tp:docstring>
    </arg>
    <arg type="s" name="createdAccountId" direction="out">
        <tp:docstring>
             A new account ID
        </tp:docstring>
    </arg>
</method>

By default, you can retrieve template parameters using getAccountTemplate(type) with type="JAMI" or type="SIP".

Core Implementation

The core logic for account creation resides in src/account_factory.cpp, within the AccountFactory::createAccount method.

std::shared_ptr<Account>
AccountFactory::createAccount(std::string_view accountType, const std::string& id)
{
    if (hasAccount(id)) {
        JAMI_ERROR("Existing account {}", id);
        return nullptr;
    }

    const auto& it = generators_.find(accountType);
    if (it == generators_.cend())
        return {};

    std::shared_ptr<Account> account = it->second(id);
    {
        std::lock_guard lock(mutex_);
        auto m = accountMaps_.find(accountType);
        if (m == accountMaps_.end())
            m = accountMaps_.emplace(std::string(accountType), AccountMap<Account>{}).first;
        m->second.emplace(id, account);
    }
    return account;
}

After calling addAccount (or createAccount via the AccountFactory), the accountsChanged signal is emitted. Clients should handle this signal and invoke getAccountList to update their local state.

Internal Archive Format

When an account is created, its data is stored in a gzip archive. If a password is provided, the archive is encrypted with AES: dht::crypto::aesEncrypt(archive, password) (dht::crypto::aesEncrypt is defined in OpenDHT and use nettle/{aes,gcm}). Inside the archive, you will find a JSON file containing:

  1. The private key ringAccountKey and certificate chain ringAccountCert, base64 encoded.

  2. The generated CA key ringCAKey.

  3. The certificate revocation list for devices ringAccountCRL.

  4. The contact list.

  5. Постављања рачуна

Deleting an Account

Deleting a Jami account is straightforward: since all key material resides on the local device, removing those key files effectively deletes the account. The only external record is the account’s username on the nameserver, which may persist depending on the server’s policies (for example, https://ns.jami.net does not currently support remote name deletion).

Daemon-side API

The ConfigurationManager API provides the removeAccount method:

<method name="removeAccount" tp:name-for-bindings="removeAccount">
    <tp:docstring>
        Remove an existing account. When removed, the signal <tp:member-ref>accountsChanged</tp:member-ref> is emitted. The clients must then call <tp:member-ref>getAccountList</tp:member-ref> to update their internal data structure.
    </tp:docstring>
    <arg type="s" name="accoundID" direction="in">
        <tp:docstring>
             The account to remove, identified by its ID
        </tp:docstring>
    </arg>
</method>

Kada se nalog izbriše, biće emitovan signal accountsChanged. Klijent treba da ažurira svoju internu strukturu nakon ovog signala drugim metodama u ConfigurationManager-u.

Core Implementation

The core removal logic is in src/accout_factory.cpp (AccountFactory::removeAccount), which deletes the account files and updates the dring.yml configuration.

void
AccountFactory::removeAccount(Account& account)
{
    std::string_view account_type = account.getAccountType();
    std::lock_guard lock(mutex_);
    const auto& id = account.getAccountID();
    JAMI_DEBUG("Removing account {:s}", id);
    auto m = accountMaps_.find(account_type);
    if (m != accountMaps_.end()) {
        m->second.erase(id);
        JAMI_DEBUG("Remaining {:d} {:s} account(s)", m->second.size(), account_type);
    }
}

Updating the details of an account

Daemon-side API

The ConfigurationManager API provides the setAccountDetails method:

<method name="setAccountDetails" tp:name-for-bindings="setAccountDetails">
    <tp:docstring>
        Send new account parameters, or account parameters changes, to the core. The hash table is not required to be complete, only the updated parameters may be specified.
        <tp:rationale>Account settings are written to the configuration file when the app properly quits.</tp:rationale>
        <tp:rationale>After calling this method, the core will emit the signal <tp:member-ref>accountDetailsChanged</tp:member-ref> with the updated data. The client must subscribe to this signal and use it to update its internal data structure.</tp:rationale>
    </tp:docstring>
    <arg type="s" name="accountID" direction="in">
    </arg>
    <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="MapStringString"/>
    <arg type="a{ss}" name="details" direction="in" tp:type="String_String_Map">
    </arg>
</method>

The map can contains a partial update and accountDetailsChanged will be emitted on success. getAccountDetails

Manage Devices

Add a device

There are two methods to add a device to an existing account.

1. Restore from Backup Archive

Use exportToFile to extract the account archive to a local path:

<method name="exportToFile" tp:name-for-bindings="exportToFile">
    <tp:added version="5.1.0"/>
    <tp:docstring>
        Copy the account archive to the path provided in argument.
    </tp:docstring>
    <arg type="s" name="accountID" direction="in">
    </arg>
    <arg type="s" name="destinationPath" direction="in">
    </arg>
    <arg type="s" name="password" direction="in">
    </arg>
    <arg type="b" name="success" direction="out">
        <tp:docstring>
            True if the operation was initialized successfully.
        </tp:docstring>
    </arg>
</method>

2. Export on the DHT network

Use exportOnRing to publish the encrypted archive on the DHT and obtain a PIN:

<method name="exportOnRing" tp:name-for-bindings="exportOnRing">
    <tp:docstring>
        Export account on the DHT using the given password and generated PIN (returned through exportOnRingEnded signal).
    </tp:docstring>
    <arg type="s" name="accountID" direction="in">
    </arg>
    <arg type="s" name="password" direction="in">
    </arg>
    <arg type="b" name="success" direction="out">
        <tp:docstring>
            True if the operation was initialized successfully. exportOnRingEnded will be trigered on completion.
        </tp:docstring>
    </arg>
</method>

Then exportOnRingEnded is emitted. Clients must listen for the signal to retrieve the PIN.

Revoke a device

To revoke a device, call revokeDevice:

<method name="revokeDevice" tp:name-for-bindings="revokeDevice">
    <tp:docstring>
        Revoke device attached to the given Jami account, and publish the new revocation list.
    </tp:docstring>
    <arg type="s" name="accountID" direction="in">
    </arg>
    <arg type="s" name="password" direction="in">
    </arg>
    <arg type="s" name="deviceId" direction="in">
    </arg>
    <arg type="b" name="success" direction="out">
        <tp:docstring>
            True if the operation was performed successfully.
        </tp:docstring>
    </arg>
</method>
<signal name="deviceRevocationEnded" tp:name-for-bindings="deviceRevocationEnded">
    <tp:docstring>
        Notify clients when the revokeDevice operation ended.
    </tp:docstring>
    <arg type="s" name="accountID">
    </arg>
    <arg type="s" name="deviceId">
    </arg>
    <arg type="i" name="status">
        <tp:docstring>
            Status code: 0 for success
            <ul>
                <li>SUCCESS = 0         everything went fine. Device is now revoked.</li>
                <li>WRONG_PASSWORD = 1  revocation failed: wrong password.</li>
                <li>UNKNOWN_DEVICE = 2  revocation failed: unknown device.</li>
            </ul>
        </tp:docstring>
    </arg>
</signal>

Upon completion, the deviceRevocationEnded signal is emitted with a status code:

  • 0 (SUCCESS)

  • 1 (WRONG_PASSWORD)

  • 2 (UNKNOWN_DEVICE)

Clients should handle this signal to confirm the revocation status.