1pass.lua
branchgtkui
changeset 45 cf6a06f368e6
parent 44 2150bce729df
child 46 fe4f59680246
equal deleted inserted replaced
44:2150bce729df 45:cf6a06f368e6
     4 local basedir = "1Password/1Password.agilekeychain/data/default"  -- !!! FIXME
     4 local basedir = "1Password/1Password.agilekeychain/data/default"  -- !!! FIXME
     5 local password = argv[2]
     5 local password = argv[2]
     6 local items = nil
     6 local items = nil
     7 local faveitems = nil
     7 local faveitems = nil
     8 local keyhookRunning = false
     8 local keyhookRunning = false
       
     9 local keyhookGuiMenus = nil
       
    10 
       
    11 
       
    12 local function runGarbageCollector()
       
    13     --local memused = math.floor(collectgarbage("count") * 1024.0)
       
    14     --print("Collecting garbage (currently using " .. memused .. " bytes).")
       
    15     collectgarbage()
       
    16     --local newmemused = math.floor(collectgarbage("count") * 1024.0)
       
    17     --print("Now using " .. newmemused .. " bytes (" .. memused - newmemused .. " bytes savings).")
       
    18 end
     9 
    19 
    10 local passwordTypeNameMap = {
    20 local passwordTypeNameMap = {
    11     ["webforms.WebForm"] = "Logins",
    21     ["webforms.WebForm"] = "Logins",
    12     ["wallet.financial.CreditCard"] = "Credit cards",
    22     ["wallet.financial.CreditCard"] = "Credit cards",
    13     ["passwords.Password"] = "Passwords",
    23     ["passwords.Password"] = "Passwords",
   101 
   111 
   102 local function loadContents()
   112 local function loadContents()
   103     return load_json(basedir .. "/contents.js")
   113     return load_json(basedir .. "/contents.js")
   104 end
   114 end
   105 
   115 
       
   116 local function makeMenu()
       
   117     return {}
       
   118 end
       
   119 
       
   120 local function appendMenuItem(menu, text, callback)
       
   121     local item = {}
       
   122     item["text"] = text
       
   123     if callback ~= nil then
       
   124         item["callback"] = callback
       
   125     end
       
   126     menu[#menu+1] = item
       
   127     return item
       
   128 end
       
   129 
       
   130 local function setMenuItemSubmenu(menuitem, submenu)
       
   131     menuitem["submenu"] = submenu
       
   132 end
       
   133 
       
   134 
       
   135 
   106 local function build_secret_menuitem(menu, type, str, hidden)
   136 local function build_secret_menuitem(menu, type, str, hidden)
   107     if str == nil then
   137     if str == nil then
   108         return nil
   138         return nil
   109     end
   139     end
   110 
   140 
   115     local text = type .. " " .. valuestr
   145     local text = type .. " " .. valuestr
   116 
   146 
   117     local callback = function()
   147     local callback = function()
   118         copyToClipboard(str)
   148         copyToClipboard(str)
   119         --print("Copied data [" .. str .. "] to clipboard.")
   149         --print("Copied data [" .. str .. "] to clipboard.")
   120         keyhookRunning = false
   150         guiDestroyMenu(keyhookGuiMenus[1])
   121     end
   151     end
   122     return appendGuiMenuItem(menu, text, callback)
   152     return appendMenuItem(menu, text, callback)
   123 end
   153 end
   124 
   154 
   125 
   155 
   126 local secret_menuitem_builders = {}
   156 local secret_menuitem_builders = {}
   127 
   157 
   292     if secure == nil then
   322     if secure == nil then
   293         return
   323         return
   294     end
   324     end
   295     --dumptable("secure " .. info.name, secure)
   325     --dumptable("secure " .. info.name, secure)
   296 
   326 
   297     local menuitem = appendGuiMenuItem(menu, info.name)
   327     local menuitem = appendMenuItem(menu, info.name)
   298 
   328 
   299     if secret_menuitem_builders[info.type] == nil then
   329     if secret_menuitem_builders[info.type] == nil then
   300         print("WARNING: don't know how to handle items of type " .. info.type)
   330         print("WARNING: don't know how to handle items of type " .. info.type)
   301         dumptable("secure " .. info.type .. " (" .. info.name .. ")", secure)
   331         dumptable("secure " .. info.type .. " (" .. info.name .. ")", secure)
   302         return
   332         return
   305     if metadata.faveIndex ~= nil then
   335     if metadata.faveIndex ~= nil then
   306         --dumptable("fave metadata " .. info.name, metadata)
   336         --dumptable("fave metadata " .. info.name, metadata)
   307         faveitems[metadata.faveIndex] = { info=info, secure=secure }
   337         faveitems[metadata.faveIndex] = { info=info, secure=secure }
   308     end
   338     end
   309 
   339 
   310     local submenu = makeGuiMenu()
   340     local submenu = makeMenu()
   311     secret_menuitem_builders[info.type](submenu, info, secure)
   341     secret_menuitem_builders[info.type](submenu, info, secure)
   312     setGuiMenuItemSubmenu(menuitem, submenu)
   342     setMenuItemSubmenu(menuitem, submenu)
   313 end
   343 end
   314 
   344 
   315 local function prepItems()
   345 local function prepItems()
   316     items = {}
   346     items = {}
   317     local contents = loadContents()
   347     local contents = loadContents()
   334 local function lockKeychain()
   364 local function lockKeychain()
   335     -- lose the existing password and key, prompt user again.
   365     -- lose the existing password and key, prompt user again.
   336     password = argv[2]  -- might be nil, don't reset if on command line.
   366     password = argv[2]  -- might be nil, don't reset if on command line.
   337     keys["SL5"] = nil
   367     keys["SL5"] = nil
   338     passwordUnlockTime = nil
   368     passwordUnlockTime = nil
   339     keyhookRunning = false
       
   340     setPowermateLED(false)
   369     setPowermateLED(false)
   341     collectgarbage()
   370 
       
   371     -- kill the popup if it exists.
       
   372     if (keyhookGuiMenus ~= nil) and (keyhookGuiMenus[1] ~= nil) then
       
   373         guiDestroyMenu(keyhookGuiMenus[1])
       
   374     end
   342 end
   375 end
   343 
   376 
   344 function pumpLua()  -- not local! Called from C!
   377 function pumpLua()  -- not local! Called from C!
   345     -- !!! FIXME: this should lose the key in RAM and turn off the Powermate
   378     -- !!! FIXME: this should lose the key in RAM and turn off the Powermate
   346     -- !!! FIXME:  LED when the time expires instead of if the time has
   379     -- !!! FIXME:  LED when the time expires instead of if the time has
   352             lockKeychain()
   385             lockKeychain()
   353         end
   386         end
   354     end
   387     end
   355 end
   388 end
   356 
   389 
       
   390 function escapePressed()  -- not local! Called from C!
       
   391     if keyhookGuiMenus[1] then
       
   392         guiDestroyMenu(keyhookGuiMenus[1])
       
   393     end
       
   394 end
       
   395 
       
   396 
       
   397 local buildGuiMenuList
       
   398 
       
   399 local function spawnSubMenu(button, submenu, depth)
       
   400     local guimenu = guiCreateSubMenu(button)
       
   401 
       
   402     for i = #keyhookGuiMenus, depth, -1 do
       
   403         if keyhookGuiMenus[i] then
       
   404             --print("Destroying conflicting submenu at depth " .. i)
       
   405             guiDestroyMenu(keyhookGuiMenus[i])
       
   406             keyhookGuiMenus[i] = nil
       
   407         end
       
   408     end
       
   409 
       
   410     --print("New submenu at depth " .. depth)
       
   411     keyhookGuiMenus[depth] = guimenu
       
   412 
       
   413     buildGuiMenuList(guimenu, submenu)
       
   414     guiShowWindow(guimenu)
       
   415 end
       
   416 
       
   417 local function buildGuiMenuItem(guimenu, item)
       
   418     local cb = item["callback"]
       
   419     if cb == nil then
       
   420         local submenu = item["submenu"]
       
   421         local depth = #keyhookGuiMenus+1
       
   422         cb = function (button)
       
   423             return spawnSubMenu(button, submenu, depth)
       
   424         end
       
   425     end
       
   426     guiAddMenuItem(guimenu, item["text"], cb)
       
   427 end
       
   428 
       
   429 buildGuiMenuList = function(guimenu, list)
       
   430     for i,v in ipairs(list) do
       
   431         buildGuiMenuItem(guimenu, v)
       
   432     end
       
   433 end
       
   434 
       
   435 local function buildSearchResultsMenuCategory(guimenu, menu, str)
       
   436     local submenu = menu["submenu"]
       
   437     if not submenu then return end
       
   438 
       
   439     local name = menu["text"]
       
   440     -- !!! FIXME: hacky. We should really list favorites first anyhow.
       
   441     if name == "Favorites" then return end
       
   442 
       
   443     for i,v in ipairs(submenu) do
       
   444         if string.find(string.lower(v["text"]), str, 1, true) ~= nil then
       
   445             buildGuiMenuItem(guimenu, v)
       
   446         end
       
   447     end
       
   448 end
       
   449 
       
   450 local function buildSearchResultsMenuList(guimenu, topmenu, str)
       
   451     for i,v in ipairs(topmenu) do
       
   452         buildSearchResultsMenuCategory(guimenu, v, str)
       
   453     end
       
   454 end
       
   455 
       
   456 local function searchEntryChanged(guimenu, str, topmenu)
       
   457     --print("search changed to '" .. str .. "' ...")
       
   458     guiRemoveAllMenuItems(guimenu)
       
   459     if str == "" then
       
   460         buildGuiMenuList(guimenu, topmenu)
       
   461     else
       
   462         buildSearchResultsMenuList(guimenu, topmenu, string.lower(str))
       
   463     end
       
   464     guiShowWindow(guimenu)
       
   465 end
       
   466 
       
   467 local function handleMenuDestroyed()
       
   468     --print("Destroying main menu...")
       
   469     for i,v in ipairs(keyhookGuiMenus) do
       
   470         if i > 1 then
       
   471             guiDestroyMenu(v)
       
   472         end
       
   473     end
       
   474     keyhookGuiMenus = nil
       
   475     keyhookRunning = false
       
   476 
       
   477     runGarbageCollector()
       
   478 end
       
   479 
       
   480 local function launchGuiMenu(topmenu)
       
   481     local guimenu = guiCreateTopLevelMenu("1pass",
       
   482 
       
   483         function(guimenu, str) -- search text changed callback
       
   484             return searchEntryChanged(guimenu, str, topmenu)
       
   485         end,
       
   486 
       
   487         function()  -- window destroyed callback
       
   488             handleMenuDestroyed()
       
   489         end
       
   490     )
       
   491     keyhookGuiMenus = {}
       
   492     keyhookGuiMenus[#keyhookGuiMenus+1] = guimenu
       
   493     buildGuiMenuList(guimenu, topmenu)
       
   494     guiShowWindow(guimenu)
       
   495 end
   357 
   496 
   358 function keyhookPressed()  -- not local! Called from C!
   497 function keyhookPressed()  -- not local! Called from C!
   359 --print("keyhookPressed: running==" .. tostring(keyhookRunning))
   498     --print("keyhookPressed: running==" .. tostring(keyhookRunning))
   360 --    if keyhookRunning then
   499     if keyhookRunning then
   361 --        return
   500         return
   362 --    end
   501     end
   363 
   502 
   364     keyhookRunning = true
   503     keyhookRunning = true
   365 
   504 
   366     while password == nil do
   505     while password == nil do
   367         password = runGuiPasswordPrompt(getHint())
   506         password = runGuiPasswordPrompt(getHint())
   385     if not prepItems() then
   524     if not prepItems() then
   386         keyhookRunning = false
   525         keyhookRunning = false
   387         return
   526         return
   388     end
   527     end
   389 
   528 
   390     local topmenu = makeGuiMenu()
   529     local topmenu = makeMenu()
   391     local favesmenu = makeGuiMenu()
   530     local favesmenu = makeMenu()
   392     faveitems = {}
   531     faveitems = {}
   393 
   532 
   394     setGuiMenuItemSubmenu(appendGuiMenuItem(topmenu, "Favorites"), favesmenu)
   533     setMenuItemSubmenu(appendMenuItem(topmenu, "Favorites"), favesmenu)
   395 
   534 
   396     appendGuiMenuItem(topmenu, "Lock keychain", function() lockKeychain() end)
   535     appendMenuItem(topmenu, "Lock keychain", function() lockKeychain() end)
   397 
   536 
   398     for orderi,type in ipairs(passwordTypeOrdering) do
   537     for orderi,type in ipairs(passwordTypeOrdering) do
   399         local bucket = items[type]
   538         local bucket = items[type]
   400         if bucket ~= nil then
   539         if bucket ~= nil then
   401             local realname = passwordTypeNameMap[type]
   540             local realname = passwordTypeNameMap[type]
   402             if realname == nil then
   541             if realname == nil then
   403                 realname = type
   542                 realname = type
   404             end
   543             end
   405             local menuitem = appendGuiMenuItem(topmenu, realname)
   544             local menuitem = appendMenuItem(topmenu, realname)
   406             local submenu = makeGuiMenu()
   545             local submenu = makeMenu()
   407             table.sort(bucket, function(a, b) return a.name < b.name end)
   546             table.sort(bucket, function(a, b) return a.name < b.name end)
   408             for i,v in pairs(bucket) do
   547             for i,v in pairs(bucket) do
   409                 build_secret_menuitems(v, submenu)
   548                 build_secret_menuitems(v, submenu)
   410             end
   549             end
   411             setGuiMenuItemSubmenu(menuitem, submenu)
   550             setMenuItemSubmenu(menuitem, submenu)
   412         else
   551         else
   413             --print("no bucket found for item type '" .. type .. "'")
   552             --print("no bucket found for item type '" .. type .. "'")
   414         end
   553         end
   415     end
   554     end
   416     
   555     
   431         return iter
   570         return iter
   432     end
   571     end
   433 
   572 
   434     for i,v in favepairs(faveitems) do
   573     for i,v in favepairs(faveitems) do
   435         --dumptable("fave " .. i, v)
   574         --dumptable("fave " .. i, v)
   436         local menuitem = appendGuiMenuItem(favesmenu, v.info.name)
   575         local menuitem = appendMenuItem(favesmenu, v.info.name)
   437         local submenu = makeGuiMenu()
   576         local submenu = makeMenu()
   438         secret_menuitem_builders[v.info.type](submenu, v.info, v.secure)
   577         secret_menuitem_builders[v.info.type](submenu, v.info, v.secure)
   439         setGuiMenuItemSubmenu(menuitem, submenu)
   578         setMenuItemSubmenu(menuitem, submenu)
   440     end
   579     end
   441 
   580 
   442     favepairs = nil
   581     favepairs = nil
   443     faveitems = nil
   582     faveitems = nil
   444 
   583 
   445     popupGuiMenu(topmenu)
   584     launchGuiMenu(topmenu)
   446 end
   585 end
   447 
   586 
   448 
   587 
   449 -- Mainline!
   588 -- Mainline!
   450 
   589