Hello,
for some time now, I have been using the pwgen utility to generate
random passwords for all the different web accounts I register.
While this works it would be much nicer to have this integrated into
the browser. So this past Saturday I sat down and implemented a
proof of concept integration for rekonq (see attached patch).
My implementation adds a "Generate random password" context menu action when
the menu is shown above a password input field. This action
1. generates a random password of length 20 chars
2. fills in the password input field with the password
3. iterates over all password fields in the same form
and fills them with the password (since registration
forms often have a confirm password field)
4. copies the password to the clipboard
The generator uses qrand to generate the passwords, which are alphanumeric. It
tries to use /dev/urandom or, if not available, the number of milliseconds
since 1/1/1970 to seed the random number generator on first use.
What do you think?
Best,
Jonathan Vernerdiff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6df5a64..924ef06 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -17,6 +17,7 @@ set(rekonq_KDEINIT_SRCS
sessionwidget.cpp
urlresolver.cpp
websnap.cpp
+ passwordgenerator.cpp
#----------------------------------------
adblock/adblockelementhiding.cpp
adblock/adblockhostmatcher.cpp
diff --git a/src/passwordgenerator.cpp b/src/passwordgenerator.cpp
new file mode 100644
index 0000000..02c8477
--- /dev/null
+++ b/src/passwordgenerator.cpp
@@ -0,0 +1,40 @@
+#include "passwordgenerator.h"
+
+#include <QDateTime>
+#include <QFile>
+#include <QDataStream>
+
+
+bool PasswordGenerator::initialized = false;
+
+void PasswordGenerator::initPasswordGenerator()
+{
+ if (! initialized) {
+ // Inspired by KPassGen
+ // http://kde-apps.org/content/show.php/KPassGen?content=108673
+ QFile urandom("/dev/urandom");
+ qint64 seed = QDateTime::currentMSecsSinceEpoch();
+ if (urandom.open(QIODevice::ReadOnly)){
+ QDataStream stream(&urandom);
+ stream >> seed;
+ }
+ qsrand(seed);
+ initialized = false;
+ }
+}
+
+QString PasswordGenerator::generateRandomPassword(int length)
+{
+ static const char alphanum[] =
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ QString ret = "";
+
+ if (! initialized ) initPasswordGenerator();
+
+ for (int i = 0; i < length; ++i)
+ ret += alphanum[qrand() % (sizeof(alphanum) - 1)];
+
+ return ret;
+}
diff --git a/src/passwordgenerator.h b/src/passwordgenerator.h
new file mode 100644
index 0000000..3bb7c87
--- /dev/null
+++ b/src/passwordgenerator.h
@@ -0,0 +1,17 @@
+#ifndef PASSWORDGENERATOR_H
+#define PASSWORDGENERATOR_H
+
+#include <QString>
+
+void initPasswordGenerator();
+void generateRandomPassword();
+
+class PasswordGenerator {
+private:
+ static bool initialized;
+public:
+ static QString generateRandomPassword(int length = 20);
+ static void initPasswordGenerator();
+};
+
+#endif // PWGEN_H
diff --git a/src/webtab/webview.cpp b/src/webtab/webview.cpp
index 6b8416f..ef94f7b 100644
--- a/src/webtab/webview.cpp
+++ b/src/webtab/webview.cpp
@@ -46,6 +46,8 @@
#include "webtab.h"
#include "webwindow.h"
+#include "passwordgenerator.h"
+
// KDE Includes
#include <KAction>
#include <KActionMenu>
@@ -88,6 +90,11 @@ static QVariant execJScript(QWebHitTestResult result, const QString& script)
return element.evaluateJavaScript(script);
}
+static bool is_password_input(QWebHitTestResult result)
+{
+ QWebElement element(result.element());
+ return (element.tagName().toUpper() == QL1S("INPUT") && element.attribute(QL1S("type")).toUpper()==QL1S("PASSWORD"));
+}
// --------------------------------------------------------------------------------------------------
@@ -320,6 +327,9 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
if (m_contextMenuHitResult.isContentSelected())
resultHit = WebView::TextSelection;
+ if (is_password_input(m_contextMenuHitResult))
+ resultHit = WebView::PasswordSelection;
+
// --------------------------------------------------------------------------------
// Ok, let's start filling up the menu...
@@ -332,6 +342,22 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
QAction *a;
+ // PASSWORD FORM FIELD ACTIONS ----------------------------------------------------
+ if (resultHit == WebView::PasswordSelection)
+ {
+ QWebElement element = m_contextMenuHitResult.element();
+ QString elementID = element.attribute("id");
+ if ( ! element.hasAttribute("id") )
+ {
+ elementID = "fill_password_id"+PasswordGenerator::generateRandomPassword(5);
+ element.setAttribute("id",elementID);
+ }
+ a = new KAction(KIcon("lock"), i18n("&Generate a random password"), &menu);
+ a->setData(elementID);
+ connect(a, SIGNAL(triggered(bool)), this, SLOT(generatePassword()));
+ menu.addAction(a);
+ }
+
// EMPTY PAGE ACTIONS -------------------------------------------------------------
if (resultHit == WebView::EmptySelection)
{
@@ -561,32 +587,34 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
// DEFAULT ACTIONs (on the bottom) ------------------------------------------------
menu.addSeparator();
- if (resultHit & WebView::LinkSelection)
- {
- menu.addAction(pageAction(KWebPage::CopyLinkToClipboard));
-
- a = new KAction(KIcon("bookmark-new"), i18n("&Bookmark link"), &menu);
- a->setData(m_contextMenuHitResult.linkUrl());
- connect(a, SIGNAL(triggered(bool)), this, SLOT(bookmarkLink()));
- menu.addAction(a);
- }
- else
+ if ( ! resultHit & WebView::PasswordSelection )
{
- if (!m_parentTab->isWebApp() && webwin)
+ if (resultHit & WebView::LinkSelection)
{
- a = webwin->actionByName(KStandardAction::name(KStandardAction::AddBookmark));
+ menu.addAction(pageAction(KWebPage::CopyLinkToClipboard));
+
+ a = new KAction(KIcon("bookmark-new"), i18n("&Bookmark link"), &menu);
+ a->setData(m_contextMenuHitResult.linkUrl());
+ connect(a, SIGNAL(triggered(bool)), this, SLOT(bookmarkLink()));
menu.addAction(a);
}
+ else
+ {
+ if (!m_parentTab->isWebApp() && webwin )
+ {
+ a = webwin->actionByName(KStandardAction::name(KStandardAction::AddBookmark));
+ menu.addAction(a);
+ }
+ }
+ menu.addAction(sendByMailAction);
+ menu.addSeparator();
}
- menu.addAction(sendByMailAction);
- menu.addSeparator();
-
if (webwin)
menu.addAction(webwin->actionByName("web_inspector"));
// SPELL CHECK Actions
- if (m_contextMenuHitResult.isContentEditable())
+ if (m_contextMenuHitResult.isContentEditable() && (! resultHit & WebView::PasswordSelection) )
{
menu.addSeparator();
a = KStandardAction::spelling(this, SLOT(spellCheck()), &menu);
@@ -844,6 +872,29 @@ void WebView::openLinkInPrivateWindow()
emit loadUrl(url, Rekonq::NewPrivateWindow);
}
+void WebView::generatePassword()
+{
+
+ QString pwd = PasswordGenerator::generateRandomPassword(20);
+
+ // Find the password fields and fill them in
+ KAction *a = qobject_cast<KAction*>(sender());
+ QString elementID = a->data().toString();
+ QWebElement element = page()->mainFrame()->findFirstElement("#"+elementID);
+ QWebElement parent = element.parent();
+ while( ! parent.isNull() && parent.tagName().toUpper() != QL1S("FORM") )
+ parent = parent.parent();
+ if ( ! parent.isNull() ) {
+ QWebElement el;
+ foreach(el, parent.findAll("input[type='password']")) {
+ el.setAttribute("value",pwd);
+ }
+ } else element.setAttribute("value",pwd);
+
+ // Copy the password to the clipboard
+ QApplication::clipboard()->setText(pwd);
+
+}
void WebView::bookmarkLink()
{
diff --git a/src/webtab/webview.h b/src/webtab/webview.h
index bbcd10f..3438c73 100644
--- a/src/webtab/webview.h
+++ b/src/webtab/webview.h
@@ -57,10 +57,11 @@ public:
enum ContextType
{
- EmptySelection = 0x00000000,
- LinkSelection = 0x00000001,
- ImageSelection = 0x00000010,
- TextSelection = 0x00000100
+ EmptySelection = 0x00000000,
+ LinkSelection = 0x00000001,
+ ImageSelection = 0x00000010,
+ TextSelection = 0x00000100,
+ PasswordSelection = 0x00001000,
};
explicit WebView(QWidget *parent, bool isPrivateBrowsing);
@@ -113,6 +114,8 @@ private Q_SLOTS:
void slotSpellCheckDone(const QString&);
void sendByMail();
+ void generatePassword();
+
void saveImage();
void viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
void slotCopyImageLocation();
_______________________________________________
rekonq mailing list
[email protected]
https://mail.kde.org/mailman/listinfo/rekonq