|
| 1 | +/* Copyright Torbjorn Tyridal 2015 |
| 2 | +
|
| 3 | + This file is part of Masterpassword for Firefox (herby known as "the software"). |
| 4 | +
|
| 5 | + The software is free software: you can redistribute it and/or modify |
| 6 | + it under the terms of the GNU General Public License as published by |
| 7 | + the Free Software Foundation, either version 3 of the License, or |
| 8 | + (at your option) any later version. |
| 9 | +
|
| 10 | + The software is distributed in the hope that it will be useful, |
| 11 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + GNU General Public License for more details. |
| 14 | +
|
| 15 | + You should have received a copy of the GNU General Public License |
| 16 | + along with the software. If not, see <http://www.gnu.org/licenses/>. |
| 17 | +*/ |
| 18 | + |
| 19 | +/* |
| 20 | + * |
| 21 | + * Minimal dbus api for accessing Secret Services API (gnome keyring / ksecretservice) |
| 22 | + * and Kwallet. |
| 23 | + * |
| 24 | + **/ |
| 25 | + |
| 26 | +exports.dbus = (function(){ |
| 27 | + |
| 28 | +try { |
| 29 | + var { setTimeout } = require("sdk/timers"); |
| 30 | + var {Cu} = require('chrome'); |
| 31 | + Cu.import("resource://gre/modules/ctypes.jsm"); |
| 32 | +} catch(e) { |
| 33 | + console.error("Failed to load js-ctypes"); |
| 34 | + return null; |
| 35 | +} |
| 36 | + |
| 37 | + |
| 38 | +function dbus() { |
| 39 | + var lib |
| 40 | + try { lib = ctypes.open("libdbus-1.so.3"); } |
| 41 | + catch(e) { |
| 42 | + console.error("Failed to load libdbus-1.so.3"); |
| 43 | + return null; |
| 44 | + } |
| 45 | + |
| 46 | + const DBusError = ctypes.StructType('DBusError', [ |
| 47 | + {'name': ctypes.char.ptr}, |
| 48 | + {'message': ctypes.char.ptr}, |
| 49 | + {'dummy1': ctypes.int}, |
| 50 | + {'dummy2': ctypes.int}, |
| 51 | + {'dummy3': ctypes.int}, |
| 52 | + {'dummy4': ctypes.int}, |
| 53 | + {'dummy5': ctypes.int}, |
| 54 | + {'padding': ctypes.voidptr_t}, |
| 55 | + ]), |
| 56 | + DBusConnection = ctypes.StructType('DBusConnection'), |
| 57 | + DBusMessage = ctypes.StructType('DBusMessage'), |
| 58 | + DBusMessageIter = ctypes.StructType('DBusMessageIter', [ |
| 59 | + {'dummy1': ctypes.voidptr_t}, |
| 60 | + {'dummy2': ctypes.voidptr_t}, |
| 61 | + {'dummy3': ctypes.uint32_t}, |
| 62 | + {'dummy4': ctypes.int}, |
| 63 | + {'dummy5': ctypes.int}, |
| 64 | + {'dummy6': ctypes.int}, |
| 65 | + {'dummy7': ctypes.int}, |
| 66 | + {'dummy8': ctypes.int}, |
| 67 | + {'dummy9': ctypes.int}, |
| 68 | + {'dummy10': ctypes.int}, |
| 69 | + {'dummy11': ctypes.int}, |
| 70 | + {'pad1': ctypes.int}, |
| 71 | + {'pad2': ctypes.int}, |
| 72 | + {'pad3': ctypes.voidptr_t} |
| 73 | + |
| 74 | + ]), |
| 75 | + DBusPendingCall = ctypes.StructType('DBusPendingCall'); |
| 76 | + |
| 77 | + const bus_get = lib.declare('dbus_bus_get', ctypes.default_abi, DBusConnection.ptr, |
| 78 | + ctypes.int, DBusError.ptr), |
| 79 | + message_iter_init = lib.declare('dbus_message_iter_init', ctypes.default_abi, ctypes.bool, |
| 80 | + DBusMessage.ptr, DBusMessageIter.ptr), |
| 81 | + message_iter_get_arg_type = lib.declare('dbus_message_iter_get_arg_type', ctypes.default_abi, ctypes.int, |
| 82 | + DBusMessageIter.ptr), |
| 83 | + message_iter_get_basic = lib.declare('dbus_message_iter_get_basic', ctypes.default_abi, ctypes.void_t, |
| 84 | + DBusMessageIter.ptr, ctypes.voidptr_t), |
| 85 | + message_iter_recurse = lib.declare('dbus_message_iter_recurse', ctypes.default_abi, ctypes.void_t, |
| 86 | + DBusMessageIter.ptr, DBusMessageIter.ptr), |
| 87 | + message_iter_init_append = lib.declare('dbus_message_iter_init_append', ctypes.default_abi, ctypes.void_t, |
| 88 | + DBusMessage.ptr, DBusMessageIter.ptr), |
| 89 | + message_iter_append_basic = lib.declare('dbus_message_iter_append_basic', ctypes.default_abi, ctypes.bool, |
| 90 | + DBusMessageIter.ptr, ctypes.int, ctypes.voidptr_t), |
| 91 | + message_iter_next = lib.declare('dbus_message_iter_next', ctypes.default_abi, ctypes.bool, |
| 92 | + DBusMessageIter.ptr), |
| 93 | + message_iter_open_container = lib.declare('dbus_message_iter_open_container', ctypes.default_abi, ctypes.bool, |
| 94 | + DBusMessageIter.ptr, ctypes.int, ctypes.char.ptr, DBusMessageIter.ptr), |
| 95 | + message_iter_close_container = lib.declare('dbus_message_iter_close_container', ctypes.default_abi, ctypes.bool, |
| 96 | + DBusMessageIter.ptr, DBusMessageIter.ptr), |
| 97 | + message_new_method_call = lib.declare('dbus_message_new_method_call', ctypes.default_abi, DBusMessage.ptr, |
| 98 | + ctypes.char.ptr, ctypes.char.ptr, ctypes.char.ptr, ctypes.char.ptr ), |
| 99 | + message_unref = lib.declare('dbus_message_unref', ctypes.default_abi, ctypes.void_t, |
| 100 | + DBusMessage.ptr ), |
| 101 | + connection_send_with_reply_and_block = lib.declare('dbus_connection_send_with_reply_and_block', ctypes.default_abi, DBusMessage.ptr, |
| 102 | + DBusConnection.ptr, DBusMessage.ptr, ctypes.int, DBusError.ptr); |
| 103 | + connection_send_with_reply = lib.declare('dbus_connection_send_with_reply', ctypes.default_abi, ctypes.bool, |
| 104 | + DBusConnection.ptr, DBusMessage.ptr, DBusPendingCall.ptr.ptr, ctypes.int); |
| 105 | + pending_call_block = lib.declare('dbus_pending_call_block', ctypes.default_abi, ctypes.void_t, |
| 106 | + DBusPendingCall.ptr); |
| 107 | + pending_call_steal_reply = lib.declare('dbus_pending_call_steal_reply', ctypes.default_abi, DBusMessage.ptr, |
| 108 | + DBusPendingCall.ptr); |
| 109 | + pending_call_unref = lib.declare('dbus_pending_call_unref', ctypes.default_abi, ctypes.void_t, |
| 110 | + DBusPendingCall.ptr); |
| 111 | + pending_call_get_completed = lib.declare('dbus_pending_call_get_completed', ctypes.default_abi, ctypes.bool, |
| 112 | + DBusPendingCall.ptr); |
| 113 | + set_error_from_message = lib.declare('dbus_set_error_from_message', ctypes.default_abi, ctypes.bool, |
| 114 | + DBusError.ptr, DBusMessage.ptr); |
| 115 | + |
| 116 | + const dbus_type = {'string': 's'.charCodeAt(0), |
| 117 | + 'objectpath': 'o'.charCodeAt(0), |
| 118 | + 'integer': 'i'.charCodeAt(0), |
| 119 | + 'bool': 'b'.charCodeAt(0), |
| 120 | + 'int64': 'x'.charCodeAt(0), |
| 121 | + 'variant': 'v'.charCodeAt(0), |
| 122 | + 'array': 'a'.charCodeAt(0), |
| 123 | + 'byte': 'y'.charCodeAt(0), |
| 124 | + 'struct': 'r'.charCodeAt(0), |
| 125 | + 'dict_entry': 'e'.charCodeAt(0), |
| 126 | + } |
| 127 | + |
| 128 | + function send_with_reply(con, msg, timeout) { |
| 129 | + var toreturn = [], |
| 130 | + rep, |
| 131 | + pendingCall = DBusPendingCall.ptr(); |
| 132 | + |
| 133 | + if (timeout == undefined) timeout = -1; |
| 134 | + else timeout = Math.round(timeout * 1000); |
| 135 | + |
| 136 | + connection_send_with_reply(con, msg, pendingCall.address(), timeout); |
| 137 | + //pending_call_block(pendingCall); |
| 138 | + |
| 139 | + return new Promise(function(resolve, fail){ |
| 140 | + |
| 141 | + (function loop(){ |
| 142 | + if (!pending_call_get_completed(pendingCall)) { |
| 143 | + setTimeout(loop, 200); |
| 144 | + return; |
| 145 | + } |
| 146 | + |
| 147 | + rep = pending_call_steal_reply(pendingCall); |
| 148 | + if (rep.isNull()) { |
| 149 | + throw new Error("steal_reply returned null"); |
| 150 | + } |
| 151 | + |
| 152 | + var err = new DBusError(); |
| 153 | + if (set_error_from_message(err.address(), rep)) { |
| 154 | + throw new Error("method execute, Failed to send message:"+err.name.readString()+"\n"+err.message.readString()); |
| 155 | + } else { |
| 156 | + it = new DBusMessageIter(); |
| 157 | + if (message_iter_init(rep, it.address())) |
| 158 | + toreturn = iter_result(it.address()); |
| 159 | + } |
| 160 | + message_unref(rep); |
| 161 | + pending_call_unref(pendingCall); |
| 162 | + |
| 163 | + resolve(toreturn); |
| 164 | + })(); |
| 165 | + }); |
| 166 | + } |
| 167 | + |
| 168 | + function iter_result(it) { |
| 169 | + var typ, data, ret = []; |
| 170 | + for (typ = message_iter_get_arg_type(it); typ != 0; message_iter_next(it), typ = message_iter_get_arg_type(it)) { |
| 171 | + switch(typ) { |
| 172 | + case dbus_type.objectpath: |
| 173 | + case dbus_type.string: |
| 174 | + data = ctypes.char.ptr(); |
| 175 | + message_iter_get_basic(it, data.address()); |
| 176 | + ret.push(data.readString()); |
| 177 | + break; |
| 178 | + case dbus_type.integer: |
| 179 | + data = ctypes.int(); |
| 180 | + message_iter_get_basic(it, data.address()); |
| 181 | + ret.push(data.value); |
| 182 | + break; |
| 183 | + case dbus_type.int64: |
| 184 | + data = ctypes.int64_t(); |
| 185 | + message_iter_get_basic(it, data.address()); |
| 186 | + ret.push(data.value); |
| 187 | + break; |
| 188 | + case dbus_type.byte: |
| 189 | + data = ctypes.uint8_t(); |
| 190 | + message_iter_get_basic(it, data.address()); |
| 191 | + ret.push(data.value); |
| 192 | + break; |
| 193 | + case dbus_type.struct: |
| 194 | + case dbus_type.variant: |
| 195 | + case dbus_type.array: |
| 196 | + data = new DBusMessageIter(); |
| 197 | + message_iter_recurse(it, data.address()); |
| 198 | + if (typ==dbus_type.variant) |
| 199 | + ret.push( iter_result(data.address())[0] ); |
| 200 | + else |
| 201 | + ret.push( iter_result(data.address()) ); |
| 202 | + break; |
| 203 | + default: |
| 204 | + console.warn("iter_result, Unknown data type",typ); |
| 205 | + } |
| 206 | + } |
| 207 | + return ret; |
| 208 | + } |
| 209 | + |
| 210 | + var err = new DBusError(); |
| 211 | + var dbus_con = bus_get(0, err.address()); |
| 212 | + if (dbus_con.isNull()) { |
| 213 | + console.error('Failed to get bus', err.name, err.message); |
| 214 | + return null; |
| 215 | + } |
| 216 | + |
| 217 | + /// helpers /// |
| 218 | + function iter_each(ar, cb) { |
| 219 | + for (var key in ar) |
| 220 | + if (ar.hasOwnProperty(key)) |
| 221 | + cb(key, ar[key]); |
| 222 | + } |
| 223 | + function msg_arg_str(it, s) { |
| 224 | + var ss = ctypes.char.array()(s); |
| 225 | + var x = ctypes.cast(ss.address(), ctypes.char.ptr); |
| 226 | + message_iter_append_basic(it, dbus_type.string, x.address()); |
| 227 | + } |
| 228 | + function msg_arg_objectpath(it, s) { |
| 229 | + var ss = ctypes.char.array()(s); |
| 230 | + var x = ctypes.cast(ss.address(), ctypes.char.ptr); |
| 231 | + message_iter_append_basic(it, dbus_type.objectpath, x.address()); |
| 232 | + } |
| 233 | + function msg_arg_dict(it, typ, then) { |
| 234 | + var dic = new DBusMessageIter(); |
| 235 | + message_iter_open_container(it, dbus_type.array, typ, dic.address()); |
| 236 | + then(dic.address()); |
| 237 | + message_iter_close_container(it, dic.address()); |
| 238 | + } |
| 239 | + function msg_arg_dict_entry(dic, key, then) { |
| 240 | + var entry = new DBusMessageIter(); |
| 241 | + message_iter_open_container(dic, dbus_type.dict_entry, null, entry.address()); |
| 242 | + msg_arg_str(entry.address(), key); |
| 243 | + then(entry.address()); |
| 244 | + message_iter_close_container(dic, entry.address()); |
| 245 | + } |
| 246 | + function msg_arg_variant(it, typ, then) { |
| 247 | + var va = new DBusMessageIter(); |
| 248 | + message_iter_open_container(it, dbus_type.variant, typ, va.address()); |
| 249 | + then(va.address()); |
| 250 | + message_iter_close_container(it, va.address()); |
| 251 | + } |
| 252 | + function msg_arg_struct(it, then) { |
| 253 | + var va = new DBusMessageIter(); |
| 254 | + message_iter_open_container(it, dbus_type.struct, null, va.address()); |
| 255 | + then(va.address()); |
| 256 | + message_iter_close_container(it, va.address()); |
| 257 | + } |
| 258 | + function msg_arg_bytearray(it, ar) { |
| 259 | + var va = new DBusMessageIter(); |
| 260 | + message_iter_open_container(it, dbus_type.array, 'y', va.address()); |
| 261 | + for (var x of ar) { |
| 262 | + if (x.charCodeAt(0) > 255) throw new Error("Illegal byte array thing.. did you forget to convert to utf-8?"); |
| 263 | + var x = ctypes.uint8_t(x.charCodeAt(0)); |
| 264 | + message_iter_append_basic(va.address(), dbus_type.byte, x.address()); |
| 265 | + } |
| 266 | + message_iter_close_container(it, va.address()); |
| 267 | + } |
| 268 | + //compounds |
| 269 | + function msg_arg_var_str(it, s) { |
| 270 | + msg_arg_variant(it, 's', function(v){ msg_arg_str(v, s);}); |
| 271 | + } |
| 272 | + function msg_arg_dict_strs(it, d) { |
| 273 | + msg_arg_dict(it, '{ss}', function(dic) { |
| 274 | + iter_each(d, function(key, val) { |
| 275 | + msg_arg_dict_entry(dic, key, function(c){ msg_arg_str(c, d[key]); }); |
| 276 | + }); |
| 277 | + }); |
| 278 | + } |
| 279 | + |
| 280 | + const new_method_call = function(dest, path, iface, method) { |
| 281 | + var m = message_new_method_call(dest, path, iface, method); |
| 282 | + var it = new DBusMessageIter(); |
| 283 | + message_iter_init_append(m, it.address()); |
| 284 | + return { |
| 285 | + 'secret_services_attributes_arg': function(label, attrs) { |
| 286 | + msg_arg_dict(it.address(), '{sv}', function(dic) { |
| 287 | + msg_arg_dict_entry(dic, 'org.freedesktop.Secret.Item.Label', function(e){ msg_arg_var_str(e, label); }); |
| 288 | + msg_arg_dict_entry(dic, 'org.freedesktop.Secret.Item.Attributes', function(e) { |
| 289 | + msg_arg_variant(e, 'a{ss}', function(v) { |
| 290 | + msg_arg_dict_strs(v, attrs); |
| 291 | + }); |
| 292 | + }); |
| 293 | + }); |
| 294 | + }, |
| 295 | + 'secret_services_passwd_arg': function(session, session_key, pass, pass_fmt) { |
| 296 | + msg_arg_struct(it.address(), function(s) { |
| 297 | + msg_arg_objectpath(s, session); |
| 298 | + msg_arg_bytearray(s, session_key); |
| 299 | + msg_arg_bytearray(s, pass); |
| 300 | + msg_arg_str(s, pass_fmt); |
| 301 | + }); |
| 302 | + }, |
| 303 | + 'dict_str_arg': function(s) { msg_arg_dict_strs(it.address(), s); }, |
| 304 | + 'var_string_arg': function(s) { msg_arg_var_str(it.address(), s); }, |
| 305 | + 'string_arg': function(s) { msg_arg_str(it.address(), s); }, |
| 306 | + 'objpath_arg': function(s) { msg_arg_objectpath(it.address(), s); }, |
| 307 | + 'int_arg': function(s) { |
| 308 | + var x = ctypes.int(s); |
| 309 | + message_iter_append_basic(it.address(), dbus_type.integer, x.address()); |
| 310 | + }, |
| 311 | + 'int64_arg': function(s) { |
| 312 | + var x = ctypes.int64_t(s); |
| 313 | + message_iter_append_basic(it.address(), dbus_type.int64, x.address()); |
| 314 | + }, |
| 315 | + 'bool_arg': function(s) { |
| 316 | + var x = ctypes.int({true:1, false:0}[s]); |
| 317 | + message_iter_append_basic(it.address(), dbus_type.bool, x.address()); |
| 318 | + }, |
| 319 | + 'execute': function(timeout) { |
| 320 | + console.log('execute',method); |
| 321 | + var p = send_with_reply(dbus_con, m, timeout); |
| 322 | + message_unref(m); |
| 323 | + return p; |
| 324 | + } |
| 325 | + }; |
| 326 | + } |
| 327 | + |
| 328 | + const get_property = function(dest, path, iface, prop) { |
| 329 | + var m = new_method_call(dest, path, 'org.freedesktop.DBus.Properties', 'Get'); |
| 330 | + m.string_arg(iface); |
| 331 | + m.string_arg(prop); |
| 332 | + return m.execute().then(function(dta){ return dta[0]; }); |
| 333 | + } |
| 334 | + |
| 335 | + return { |
| 336 | + 'get': function (dest) { return { |
| 337 | + 'load': function(path) { return { |
| 338 | + 'bind': function(iface) { return { |
| 339 | + 'method_call': function(m) { return new_method_call(dest, path, iface, m); }, |
| 340 | + 'get_property': function(p, cb) { return get_property(dest, path, iface, p, cb); } |
| 341 | + };} |
| 342 | + };} |
| 343 | + };} |
| 344 | + }; |
| 345 | +} |
| 346 | +return dbus |
| 347 | +})(); |
0 commit comments