Sergey
Sergey

Reputation: 1348

Lua LGI unpack GLib.Variant object

I'm trying to get password from keyring for awesome-wm session (using DBus via lgi library).

I'm able to find keyring entry path, open communication session and unlock entry. Then I call GetSecrets method and store result into secret variable. According to the documentation it is supposed to be a struct Secret. Seems like lgi cannot handle this type and passes it as userdata type (at least I wasn't able to make it give access to struct fields). Is there a way to get struct Secret value field contents without writing custom C handler?

Here is the code:

local bus = Gio.bus_get_sync(Gio.BusType.SESSION, nil)

local attr = {}
attr[1] = {attribute = "value"} -- attribute-value pair to search for

-- search for secret path
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "SearchItems"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(a{ss})", attr))

local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
                                                     -1, nil)
local location
for _, l in result:get_body():pairs() do
   if #l > 0 then location = l[1] end
end
print(location) -- returns "/org/freedesktop/secrets/collection/Default/1"

-- open session
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "OpenSession"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(sv)", {"plain", GLib.Variant("s", "")}))

local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
                                                     -1, nil)
local session = result:get_body()[2]
print(session) -- returns "/org/freedesktop/secrets/session/s4"

-- unlock key
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "Unlock"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(ao)", {{location}}))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
                                                     -1, nil)
-- at this point key property "Locked" if false. tested using d-feet

-- get secret
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "GetSecrets"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(aoo)", {{location},session}))

local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
                                                     -1, nil)
local secret = result:get_body()
print(#secret) -- returns "1"
print(secret)  -- returns table address
print(type(secret))  -- returns "userdata"

-- lock key
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "Lock"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(ao)", {{location}}))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
                                                     -1, nil)

-- close session
local name = "org.freedesktop.secrets"
local object = location
local interface = "org.freedesktop.Secret.Session"
local method = "Close"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
                                                     -1, nil)

edit

When I do print(secret), lgi.rec 0x7f57d0014960:GLib.Variant is returned. So, secret is an object. How can I retrieve value field from GLib.Variant object?

edit 2

secret:get_data_as_bytes():get_data() dumps struct in bytearray form; secret:print() returns formatted string of struct. I wonder if there's a better way.

edit 3

type of secret variable is (a{o(oayays)}) Code to recreate an object of that type:

local lgi       = require     'lgi'
local Gio       = lgi.require 'Gio'
local GLib      = lgi.require 'GLib'

local var = GLib.Variant("(a{o(oayays)})", {{["/path/to/object"]={"/path/to/session","parameters","value","content_type"}}})

Upvotes: 1

Views: 459

Answers (2)

Sergey
Sergey

Reputation: 1348

Finally I've found a solution. To unpack a value of a complex type, for example (a{o(oayays)}), one should use get_child_value function.

secret:get_child_value(0):get_child_value(0):get_child_value(1):get_child_value(2).value

Explanation: index tuple; index array; index dict; index tuple

Upvotes: 0

Emmanuel Lepage Vallee
Emmanuel Lepage Vallee

Reputation: 2762

(Note that I didn't install this password manager or try any of this at all)

Last time I asked the author of LGI about such issue, the answer was:

I have submitted fix to Variant which restores this functionality. So an example of use will be:

local function called_from_C(userdata)
local variant = GLib.Variant(userdata)
print(variant)
end

if you have to support older (well, released :) lgi versions, you can use undocumented way:

local core = require 'lgi.core'
local function called_from_C(userdata)
local variant = core.record.new(GLib.Variant, userdata)
print(variant)
end

Note that there is other ways. To work around another such bugs, I also once created a C Lua plugin and just wrote that code in C. That's actually rather trivial with Lua [2].

Another way, if you use LuaJIT, is to use the built-in FFI to just-in-time compile the struct definition into a Lua object [1].

Finally, if the question is more about how to unpack "working" GVariant values once they are consumed properly by LGI, look at my code for this here https://github.com/Elv13/wirefu/blob/master/proxy.lua

[1] http://luajit.org/ext_ffi_api.html

[2] https://github.com/Elv13/lua_async_binding/blob/master/src/luabridge.c#L407

Upvotes: 1

Related Questions