#!/usr/bin/env python3
"""Add an account to GNOME Online Accounts, the way Settings does.

Settings adds accounts by calling AddAccount on the Online Accounts service,
which saves the account and keeps its password in the GNOME keyring. This
calls the same method. The account comes on stdin rather than as arguments,
so its password doesn't show up in the process list.

Usage:
  goa-account exists <provider> <presentation-identity>
  goa-account add < account.json

account.json:
  {"provider": "owncloud", "identity": "...", "presentation_identity": "...",
   "credentials": {"password": "..."}, "details": {"Uri": "...", ...}}
"""

import json
import sys

import gi

gi.require_version("Gio", "2.0")
from gi.repository import Gio, GLib  # noqa: E402

BUS_NAME = "org.gnome.OnlineAccounts"


def call(bus, path, interface, method, parameters, reply_type):
    return bus.call_sync(BUS_NAME, path, interface, method, parameters,
                         GLib.VariantType(reply_type), Gio.DBusCallFlags.NONE,
                         -1, None).unpack()


def accounts(bus):
    (objects,) = call(bus, "/org/gnome/OnlineAccounts",
                      "org.freedesktop.DBus.ObjectManager", "GetManagedObjects",
                      None, "(a{oa{sa{sv}}})")
    for interfaces in objects.values():
        account = interfaces.get("org.gnome.OnlineAccounts.Account")
        if account:
            yield account


def exists(bus, provider, presentation_identity):
    return any(account.get("ProviderType") == provider
               and account.get("PresentationIdentity") == presentation_identity
               for account in accounts(bus))


def add(bus, account):
    credentials = {key: GLib.Variant("s", value)
                   for key, value in account["credentials"].items()}
    parameters = GLib.Variant("(sssa{sv}a{ss})", (
        account["provider"], account["identity"],
        account["presentation_identity"], credentials, account["details"]))
    call(bus, "/org/gnome/OnlineAccounts/Manager",
         "org.gnome.OnlineAccounts.Manager", "AddAccount", parameters, "(o)")


def main():
    bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
    if len(sys.argv) == 4 and sys.argv[1] == "exists":
        return 0 if exists(bus, sys.argv[2], sys.argv[3]) else 1
    if len(sys.argv) == 2 and sys.argv[1] == "add":
        account = json.load(sys.stdin)
        if exists(bus, account["provider"], account["presentation_identity"]):
            return 0
        add(bus, account)
        return 0
    print(__doc__.strip().split("\n\n")[2], file=sys.stderr)
    return 2


if __name__ == "__main__":
    try:
        sys.exit(main())
    except GLib.Error as error:
        print(f"GNOME Online Accounts: {error.message}", file=sys.stderr)
        sys.exit(1)
