Major changes. Completely revamped notifications system.
This commit is contained in:
Родитель
3050511021
Коммит
fa6eb1009b
|
@ -0,0 +1,13 @@
|
|||
window.addEventListener('login-success', function() {
|
||||
alert('login-success');
|
||||
});//
|
||||
// window.__authSuccess = function(data) {
|
||||
// alert('login-success');
|
||||
// chrome.extension.sendMessage({
|
||||
// type: 'login_success',
|
||||
// message: {
|
||||
// email: data.email
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
//
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,3 @@
|
|||
#save-login-buttons {
|
||||
display:none;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="css/bootstrap.css" rel="stylesheet">
|
||||
<link href="css/notifications.css" rel="stylesheet">
|
||||
<script src="notification.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="save-login-buttons">
|
||||
<button id="btn-save" class="btn btn-primary">Save</button>
|
||||
<button id="btn-not-now" class="btn">Not now</button>
|
||||
<button id="btn-never-for-site" class="btn btn-warning">Never for this site</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,84 @@
|
|||
var notifID = null;
|
||||
|
||||
function init() {
|
||||
// Notification ID is stored after the hash ('#') in the URL
|
||||
notifID = parseInt(window.location.hash.substr(1));
|
||||
|
||||
// Get the notification object from the background script
|
||||
var notifObj = chrome.extension.getBackgroundPage().getNotificationForID(notifID);
|
||||
|
||||
displayAlert(notifObj);
|
||||
}
|
||||
|
||||
function getAlertBody(alertObj) {
|
||||
var bodyEl = document.createElement('div');
|
||||
if (alertObj.type == 'password_saved') {
|
||||
bodyEl.appendChild(document.createTextNode("SkyCrane can save login " + alertObj.username + ":"));
|
||||
var passwordImg = document.createElement('img');
|
||||
passwordImg.src = getDataURLForHash(alertObj.hash,50,13);
|
||||
bodyEl.appendChild(passwordImg);
|
||||
bodyEl.appendChild(document.createTextNode(" for site " + alertObj.hostname + "!"));
|
||||
|
||||
// Show save buttons, and wire to events
|
||||
document.getElementById('save-login-buttons').style.display = 'block';
|
||||
|
||||
function wire(func,id) {
|
||||
return function() { func(id); };
|
||||
}
|
||||
|
||||
var bPage = chrome.extension.getBackgroundPage();
|
||||
|
||||
document.getElementById('btn-save').addEventListener('click', wire(bPage.saveLogin,notifID));
|
||||
document.getElementById('btn-not-now').addEventListener('click', wire(bPage.notNow,notifID));
|
||||
document.getElementById('btn-never-for-site').addEventListener('click', wire(bPage.neverForSite,notifID));
|
||||
}
|
||||
return bodyEl;
|
||||
}
|
||||
|
||||
function displayAlert(alertObj) {
|
||||
// document.body.appendChild(createAlert(alertObj));
|
||||
document.body.insertBefore(getAlertBody(alertObj),document.body.children[0]);
|
||||
}
|
||||
|
||||
function getDataURLForHash(passwordHash,inputWidth,inputHeight,numColorBars) {
|
||||
function randomizeHash(passwordHash) {
|
||||
// Add a little bit of randomness to each byte
|
||||
for (var byteIdx = 0; byteIdx < passwordHash.length/2; byteIdx++) {
|
||||
var byte = parseInt(passwordHash.substr(byteIdx*2,2),16);
|
||||
// +/- 3, within 0-255
|
||||
byte = Math.min(Math.max(byte + parseInt(Math.random()*6)-3,0),255);
|
||||
var hexStr = byte.toString(16).length == 2 ? byte.toString(16) : '0' + byte.toString(16);
|
||||
passwordHash = passwordHash.substr(0,byteIdx*2) + hexStr + passwordHash.substr(byteIdx*2+2);
|
||||
}
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
if (!(numColorBars = Number(numColorBars)))
|
||||
numColorBars = 4;
|
||||
|
||||
// Make sure there's enough data for the number of desired colorBars
|
||||
numColorBars = Math.min(numColorBars,passwordHash.length/6);
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.height = inputHeight;
|
||||
canvas.width = inputWidth;
|
||||
var context = canvas.getContext('2d');
|
||||
|
||||
passwordHash = randomizeHash(passwordHash);
|
||||
|
||||
for (var hashBandX = 0; hashBandX < numColorBars; hashBandX++) {
|
||||
context.fillStyle='#' + passwordHash.substr(hashBandX*6,6);
|
||||
context.fillRect(hashBandX/numColorBars*inputWidth,0,inputWidth/numColorBars,inputHeight);
|
||||
|
||||
context.fillStyle='#000000';
|
||||
context.fillRect(((hashBandX+1)/numColorBars*inputWidth)-1,0,2,inputHeight);
|
||||
}
|
||||
|
||||
context.strokeStyle='#000000';
|
||||
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
|
||||
|
||||
|
||||
window.onload = init;
|
146
data/observer.js
146
data/observer.js
|
@ -1,59 +1,101 @@
|
|||
const VALID_USERNAME_INPUT_TYPES = ['text','email','url','tel','number'];
|
||||
|
||||
var forms = document.getElementsByTagName('form');
|
||||
for (var formIdx = 0; formIdx < forms.length; formIdx++) {
|
||||
console.log(forms[formIdx]);
|
||||
forms[formIdx].addEventListener('submit', function() {
|
||||
var newLoginObj = {
|
||||
hostname: window.location.host,
|
||||
formSubmitURL: this.action,
|
||||
// Always null for logins extracted from HTML forms
|
||||
httpRealm: null
|
||||
};
|
||||
var inputs = this.getElementsByTagName('input');
|
||||
var inputsList = Array.prototype.slice.call(inputs);
|
||||
var pwInputs = inputsList.filter(function(x) {
|
||||
return x.type == 'password';
|
||||
});
|
||||
var pwInput = null;
|
||||
(function() {
|
||||
var forms = document.getElementsByTagName('form');
|
||||
for (var formIdx = 0; formIdx < forms.length; formIdx++) {
|
||||
console.log(forms[formIdx]);
|
||||
forms[formIdx].addEventListener('submit', function() {
|
||||
var newLoginObj = {
|
||||
hostname: window.location.host,
|
||||
formSubmitURL: this.action,
|
||||
// Always null for logins extracted from HTML forms
|
||||
httpRealm: null
|
||||
};
|
||||
var inputs = this.getElementsByTagName('input');
|
||||
var inputsList = Array.prototype.slice.call(inputs);
|
||||
|
||||
if (pwInputs.length == 1) {
|
||||
// Find the username input - the input before the password field
|
||||
// of a valid type.
|
||||
var pwInputIdx = inputsList.indexOf(pwInputs[0]);
|
||||
var usernameInput = null;
|
||||
for (var inputIdx = pwInputIdx-1; inputIdx >= 0; inputIdx--) {
|
||||
if (VALID_USERNAME_INPUT_TYPES.indexOf(inputs[inputIdx].type) != -1) {
|
||||
usernameInput = inputs[inputIdx];
|
||||
break;
|
||||
var pwFields = [];
|
||||
for (var inputIdx = 0; inputIdx < inputsList.length; inputIdx++) {
|
||||
if (inputsList[inputIdx].type == 'password') {
|
||||
pwFields.push({
|
||||
idx: inputIdx,
|
||||
val: inputsList[inputIdx].value
|
||||
});
|
||||
}
|
||||
}
|
||||
// Couldn't find a valid username input field, so bail.
|
||||
if (!usernameInput) {
|
||||
return;
|
||||
var usernameInput = null;
|
||||
var pwInput = null;
|
||||
|
||||
if (pwFields.length == 1) {
|
||||
// Find the username input - the input before the password field
|
||||
// of a valid type.
|
||||
usernameInput = getUsernameFieldForPasswordField(inputsList,pwFields[0].idx);
|
||||
|
||||
pwInput = inputsList[pwFields[0].idx];
|
||||
}
|
||||
else if (pwFields.length == 2) {
|
||||
console.log('two pwinput fields!');
|
||||
// Assume an account creation form. As long as the two passwords
|
||||
// are equal, we should be good.
|
||||
if (pwFields[0].val != pwFields[1].val) {
|
||||
// Password inputs aren't equal, so bail.
|
||||
return;
|
||||
}
|
||||
usernameInput = getUsernameFieldForPasswordField(inputsList,pwFields[0].idx);
|
||||
pwInput = inputsList[pwFields[0].idx];
|
||||
}
|
||||
else if (pwFields.length == 3) {
|
||||
// Assume a password change form. Look for two passwords that are equal.
|
||||
var pwInputIdx = null;
|
||||
for (var pwFieldIdx in pwFields) {
|
||||
if (pwFields[0].val == pwFields[1].val) {
|
||||
pwInputIdx = pwFields[0].idx;
|
||||
}
|
||||
else if (pwFields[1].val == pwFields[2].val) {
|
||||
pwInputIdx = pwFields[1].idx;
|
||||
}
|
||||
else if (pwFields[0].val == pwFields[2].val) {
|
||||
pwInputIdx = pwFields[0].idx;
|
||||
}
|
||||
}
|
||||
// All password fields differ. No idea what's going on, so bail.
|
||||
if (!pwInputIdx) return;
|
||||
pwInput = inputsList[pwInputIdx];
|
||||
usernameInput = getUsernameFieldForPasswordField(inputsList,pwInputIdx);
|
||||
}
|
||||
newLoginObj['username'] = usernameInput.value;
|
||||
newLoginObj['password'] = pwInput.value;
|
||||
newLoginObj['usernameField'] = getFieldDescriptor(usernameInput);
|
||||
newLoginObj['passwordField'] = getFieldDescriptor(pwInput);
|
||||
storeLogin(newLoginObj);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the username field from inputsList that is
|
||||
// closest before the field at pwFieldIdx. Or null, if
|
||||
// no valid field exists.
|
||||
function getUsernameFieldForPasswordField(inputsList,pwFieldIdx) {
|
||||
const VALID_USERNAME_INPUT_TYPES = ['text','email','url','tel','number'];
|
||||
for (var inputIdx = pwFieldIdx-1; inputIdx >= 0; inputIdx--) {
|
||||
if (VALID_USERNAME_INPUT_TYPES.indexOf(inputsList[inputIdx].type) != -1) {
|
||||
return inputsList[inputIdx];
|
||||
}
|
||||
pwInput = pwInputs[0];
|
||||
}
|
||||
newLoginObj['username'] = usernameInput.value;
|
||||
newLoginObj['password'] = pwInput.value;
|
||||
newLoginObj['usernameField'] = getFieldDescriptor(usernameInput);
|
||||
newLoginObj['passwordField'] = getFieldDescriptor(pwInput);
|
||||
storeLogin(newLoginObj);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
// Couldn't find a valid username input field.
|
||||
return null;
|
||||
}
|
||||
|
||||
function getFieldDescriptor(elem) {
|
||||
return {
|
||||
'name': elem.name,
|
||||
'id': elem.id
|
||||
};
|
||||
}
|
||||
function getFieldDescriptor(elem) {
|
||||
return {
|
||||
'name': elem.name,
|
||||
'id': elem.id
|
||||
};
|
||||
}
|
||||
|
||||
function storeLogin(loginObj) {
|
||||
//username,password,site,usernameElement,passwordElement
|
||||
chrome.extension.sendMessage({
|
||||
type: 'add_login',
|
||||
message: loginObj
|
||||
});
|
||||
}
|
||||
function storeLogin(loginObj) {
|
||||
//username,password,site,usernameElement,passwordElement
|
||||
chrome.extension.sendMessage({
|
||||
type: 'add_login',
|
||||
message: loginObj
|
||||
});
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,38 @@
|
|||
.pwmgr-alert {
|
||||
left:0px;
|
||||
top:0px;
|
||||
position:fixed;
|
||||
width:100%;
|
||||
min-height:50px;
|
||||
|
||||
background-color:#E4EDF7;
|
||||
border:2px #e0e0ff solid;
|
||||
text-align:center;
|
||||
|
||||
z-index:1000000100;
|
||||
opacity:0.7;
|
||||
|
||||
font-family:helvetica,sans-serif;
|
||||
}
|
||||
|
||||
.pwmgr-alert:hover {
|
||||
opacity:1.0;
|
||||
}
|
||||
|
||||
.pwmgr-alert .pwmgr-close {
|
||||
color:red;
|
||||
float:right;
|
||||
|
||||
text-decoration:none;
|
||||
|
||||
margin-right:30px;
|
||||
}
|
||||
|
||||
.pwmgr-close:hover {
|
||||
background-color:red;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.pwmgr-alert .pwmgr-alert-text {
|
||||
padding:20px;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
(function() {
|
||||
function createAlert(alertObj) {
|
||||
var alertDiv = document.createElement('div');
|
||||
alertDiv.className = "pwmgr-alert";
|
||||
|
||||
var alertMessage = document.createElement('div');
|
||||
alertMessage.className = "pwmgr-alert-text";
|
||||
alertMessage.appendChild(getAlertBody(alertObj));
|
||||
|
||||
var closeAlert = document.createElement('a');
|
||||
closeAlert.href = "#";
|
||||
closeAlert.className = 'pwmgr-close';
|
||||
closeAlert.innerHTML = 'X';
|
||||
|
||||
closeAlert.addEventListener('click', function() {
|
||||
document.body.removeChild(alertDiv);
|
||||
});
|
||||
|
||||
alertDiv.appendChild(closeAlert);
|
||||
alertDiv.appendChild(alertMessage);
|
||||
|
||||
return alertDiv;
|
||||
}
|
||||
|
||||
function getAlertBody(alertObj) {
|
||||
var bodyEl = document.createElement('div');
|
||||
if (alertObj.type == 'password_saved') {
|
||||
bodyEl.appendChild(document.createTextNode("SkyCrane saved login " + alertObj.username + ":"));
|
||||
var passwordImg = document.createElement('img');
|
||||
passwordImg.src = getDataURLForHash(alertObj.hash,50,13);
|
||||
bodyEl.appendChild(passwordImg);
|
||||
bodyEl.appendChild(document.createTextNode(" for site " + alertObj.hostname + "!"));
|
||||
}
|
||||
return bodyEl;
|
||||
}
|
||||
|
||||
function displayAlert(alertObj) {
|
||||
// document.body.appendChild(createAlert(alertObj));
|
||||
document.body.insertBefore(createAlert(alertObj),document.body.children[0]);
|
||||
}
|
||||
|
||||
chrome.extension.onMessage.addListener(function(msg) {
|
||||
console.log('message recvd! ' + JSON.stringify(msg));
|
||||
if (msg.type == 'notify' && msg.notification !== undefined) {
|
||||
displayAlert(msg.notification);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function getDataURLForHash(passwordHash,inputWidth,inputHeight,numColorBars) {
|
||||
function randomizeHash(passwordHash) {
|
||||
// Add a little bit of randomness to each byte
|
||||
for (var byteIdx = 0; byteIdx < passwordHash.length/2; byteIdx++) {
|
||||
var byte = parseInt(passwordHash.substr(byteIdx*2,2),16);
|
||||
// +/- 3, within 0-255
|
||||
byte = Math.min(Math.max(byte + parseInt(Math.random()*6)-3,0),255);
|
||||
var hexStr = byte.toString(16).length == 2 ? byte.toString(16) : '0' + byte.toString(16);
|
||||
passwordHash = passwordHash.substr(0,byteIdx*2) + hexStr + passwordHash.substr(byteIdx*2+2);
|
||||
}
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
if (!(numColorBars = Number(numColorBars)))
|
||||
numColorBars = 4;
|
||||
|
||||
// Make sure there's enough data for the number of desired colorBars
|
||||
numColorBars = Math.min(numColorBars,passwordHash.length/6);
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.height = inputHeight;
|
||||
canvas.width = inputWidth;
|
||||
var context = canvas.getContext('2d');
|
||||
|
||||
passwordHash = randomizeHash(passwordHash);
|
||||
|
||||
for (var hashBandX = 0; hashBandX < numColorBars; hashBandX++) {
|
||||
context.fillStyle='#' + passwordHash.substr(hashBandX*6,6);
|
||||
context.fillRect(hashBandX/numColorBars*inputWidth,0,inputWidth/numColorBars,inputHeight);
|
||||
|
||||
context.fillStyle='#000000';
|
||||
context.fillRect(((hashBandX+1)/numColorBars*inputWidth)-1,0,2,inputHeight);
|
||||
}
|
||||
|
||||
context.strokeStyle='#000000';
|
||||
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,473 @@
|
|||
/*!
|
||||
* jsUri v1.1.1
|
||||
* https://github.com/derek-watson/jsUri
|
||||
*
|
||||
* Copyright 2011, Derek Watson
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Includes parseUri regular expressions
|
||||
* http://blog.stevenlevithan.com/archives/parseuri
|
||||
* Copyright 2007, Steven Levithan
|
||||
* Released under the MIT license.
|
||||
*
|
||||
* Date: Mon Nov 14 20:06:34 2011 -0800
|
||||
*/
|
||||
|
||||
|
||||
var Query = function (queryString) {
|
||||
|
||||
// query string parsing, parameter manipulation and stringification
|
||||
|
||||
'use strict';
|
||||
|
||||
var // parseQuery(q) parses the uri query string and returns a multi-dimensional array of the components
|
||||
parseQuery = function (q) {
|
||||
var arr = [], i, ps, p, keyval;
|
||||
|
||||
if (typeof (q) === 'undefined' || q === null || q === '') {
|
||||
return arr;
|
||||
}
|
||||
|
||||
if (q.indexOf('?') === 0) {
|
||||
q = q.substring(1);
|
||||
}
|
||||
|
||||
ps = q.toString().split(/[&;]/);
|
||||
|
||||
for (i = 0; i < ps.length; i++) {
|
||||
p = ps[i];
|
||||
keyval = p.split('=');
|
||||
arr.push([keyval[0], keyval[1]]);
|
||||
}
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
params = parseQuery(queryString),
|
||||
|
||||
// toString() returns a string representation of the internal state of the object
|
||||
toString = function () {
|
||||
var s = '', i, param;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
if (s.length > 0) {
|
||||
s += '&';
|
||||
}
|
||||
s += param.join('=');
|
||||
}
|
||||
return s.length > 0 ? '?' + s : s;
|
||||
},
|
||||
|
||||
decode = function (s) {
|
||||
s = decodeURIComponent(s);
|
||||
s = s.replace('+', ' ');
|
||||
return s;
|
||||
},
|
||||
|
||||
// getParamValues(key) returns the first query param value found for the key 'key'
|
||||
getParamValue = function (key) {
|
||||
var param, i;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
if (decode(key) === decode(param[0])) {
|
||||
return param[1];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// getParamValues(key) returns an array of query param values for the key 'key'
|
||||
getParamValues = function (key) {
|
||||
var arr = [], i, param;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
if (decode(key) === decode(param[0])) {
|
||||
arr.push(param[1]);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
|
||||
// deleteParam(key) removes all instances of parameters named (key)
|
||||
// deleteParam(key, val) removes all instances where the value matches (val)
|
||||
deleteParam = function (key, val) {
|
||||
|
||||
var arr = [], i, param, keyMatchesFilter, valMatchesFilter;
|
||||
|
||||
for (i = 0; i < params.length; i++) {
|
||||
|
||||
param = params[i];
|
||||
keyMatchesFilter = decode(param[0]) === decode(key);
|
||||
valMatchesFilter = decode(param[1]) === decode(val);
|
||||
|
||||
if ((arguments.length === 1 && !keyMatchesFilter) || (arguments.length === 2 && !keyMatchesFilter && !valMatchesFilter)) {
|
||||
arr.push(param);
|
||||
}
|
||||
}
|
||||
|
||||
params = arr;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// addParam(key, val) Adds an element to the end of the list of query parameters
|
||||
// addParam(key, val, index) adds the param at the specified position (index)
|
||||
addParam = function (key, val, index) {
|
||||
|
||||
if (arguments.length === 3 && index !== -1) {
|
||||
index = Math.min(index, params.length);
|
||||
params.splice(index, 0, [key, val]);
|
||||
} else if (arguments.length > 0) {
|
||||
params.push([key, val]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// replaceParam(key, newVal) deletes all instances of params named (key) and replaces them with the new single value
|
||||
// replaceParam(key, newVal, oldVal) deletes only instances of params named (key) with the value (val) and replaces them with the new single value
|
||||
// this function attempts to preserve query param ordering
|
||||
replaceParam = function (key, newVal, oldVal) {
|
||||
|
||||
var index = -1, i, param;
|
||||
|
||||
if (arguments.length === 3) {
|
||||
for (i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
if (decode(param[0]) === decode(key) && decodeURIComponent(param[1]) === decode(oldVal)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
deleteParam(key, oldVal).addParam(key, newVal, index);
|
||||
} else {
|
||||
for (i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
if (decode(param[0]) === decode(key)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
deleteParam(key);
|
||||
addParam(key, newVal, index);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// public api
|
||||
return {
|
||||
getParamValue: getParamValue,
|
||||
getParamValues: getParamValues,
|
||||
deleteParam: deleteParam,
|
||||
addParam: addParam,
|
||||
replaceParam: replaceParam,
|
||||
|
||||
toString: toString
|
||||
};
|
||||
};
|
||||
|
||||
var Uri = function (uriString) {
|
||||
|
||||
// uri string parsing, attribute manipulation and stringification
|
||||
|
||||
'use strict';
|
||||
|
||||
/*global Query: true */
|
||||
/*jslint regexp: false, plusplus: false */
|
||||
|
||||
var strictMode = false,
|
||||
|
||||
// parseUri(str) parses the supplied uri and returns an object containing its components
|
||||
parseUri = function (str) {
|
||||
|
||||
/*jslint unparam: true */
|
||||
var parsers = {
|
||||
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
||||
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
||||
},
|
||||
keys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"],
|
||||
q = {
|
||||
name: "queryKey",
|
||||
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
||||
},
|
||||
m = parsers[strictMode ? "strict" : "loose"].exec(str),
|
||||
uri = {},
|
||||
i = 14;
|
||||
|
||||
while (i--) {
|
||||
uri[keys[i]] = m[i] || "";
|
||||
}
|
||||
|
||||
uri[q.name] = {};
|
||||
uri[keys[12]].replace(q.parser, function ($0, $1, $2) {
|
||||
if ($1) {
|
||||
uri[q.name][$1] = $2;
|
||||
}
|
||||
});
|
||||
|
||||
return uri;
|
||||
},
|
||||
|
||||
uriParts = parseUri(uriString || ''),
|
||||
|
||||
queryObj = new Query(uriParts.query),
|
||||
|
||||
|
||||
/*
|
||||
Basic get/set functions for all properties
|
||||
*/
|
||||
|
||||
protocol = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
uriParts.protocol = val;
|
||||
}
|
||||
return uriParts.protocol;
|
||||
},
|
||||
|
||||
hasAuthorityPrefixUserPref = null,
|
||||
|
||||
// hasAuthorityPrefix: if there is no protocol, the leading // can be enabled or disabled
|
||||
hasAuthorityPrefix = function (val) {
|
||||
|
||||
if (typeof val !== 'undefined') {
|
||||
hasAuthorityPrefixUserPref = val;
|
||||
}
|
||||
|
||||
if (hasAuthorityPrefixUserPref === null) {
|
||||
return (uriParts.source.indexOf('//') !== -1);
|
||||
} else {
|
||||
return hasAuthorityPrefixUserPref;
|
||||
}
|
||||
},
|
||||
|
||||
userInfo = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
uriParts.userInfo = val;
|
||||
}
|
||||
return uriParts.userInfo;
|
||||
},
|
||||
|
||||
host = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
uriParts.host = val;
|
||||
}
|
||||
return uriParts.host;
|
||||
},
|
||||
|
||||
port = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
uriParts.port = val;
|
||||
}
|
||||
return uriParts.port;
|
||||
},
|
||||
|
||||
path = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
uriParts.path = val;
|
||||
}
|
||||
return uriParts.path;
|
||||
},
|
||||
|
||||
query = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
queryObj = new Query(val);
|
||||
}
|
||||
return queryObj;
|
||||
},
|
||||
|
||||
anchor = function (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
uriParts.anchor = val;
|
||||
}
|
||||
return uriParts.anchor;
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
Fluent setters for Uri uri properties
|
||||
*/
|
||||
|
||||
setProtocol = function (val) {
|
||||
protocol(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setHasAuthorityPrefix = function (val) {
|
||||
hasAuthorityPrefix(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setUserInfo = function (val) {
|
||||
userInfo(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setHost = function (val) {
|
||||
host(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setPort = function (val) {
|
||||
port(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setPath = function (val) {
|
||||
path(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setQuery = function (val) {
|
||||
query(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
setAnchor = function (val) {
|
||||
anchor(val);
|
||||
return this;
|
||||
},
|
||||
|
||||
/*
|
||||
Query method wrappers
|
||||
*/
|
||||
getQueryParamValue = function (key) {
|
||||
return query().getParamValue(key);
|
||||
},
|
||||
|
||||
getQueryParamValues = function (key) {
|
||||
return query().getParamValues(key);
|
||||
},
|
||||
|
||||
deleteQueryParam = function (key, val) {
|
||||
if (arguments.length === 2) {
|
||||
query().deleteParam(key, val);
|
||||
} else {
|
||||
query().deleteParam(key);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
addQueryParam = function (key, val, index) {
|
||||
if (arguments.length === 3) {
|
||||
query().addParam(key, val, index);
|
||||
} else {
|
||||
query().addParam(key, val);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
replaceQueryParam = function (key, newVal, oldVal) {
|
||||
if (arguments.length === 3) {
|
||||
query().replaceParam(key, newVal, oldVal);
|
||||
} else {
|
||||
query().replaceParam(key, newVal);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/*
|
||||
Serialization
|
||||
*/
|
||||
|
||||
// toString() stringifies the current state of the uri
|
||||
toString = function () {
|
||||
|
||||
var s = '',
|
||||
is = function (s) {
|
||||
return (s !== null && s !== '');
|
||||
};
|
||||
|
||||
if (is(protocol())) {
|
||||
s += protocol();
|
||||
if (protocol().indexOf(':') !== protocol().length - 1) {
|
||||
s += ':';
|
||||
}
|
||||
s += '//';
|
||||
} else {
|
||||
if (hasAuthorityPrefix() && is(host())) {
|
||||
s += '//';
|
||||
}
|
||||
}
|
||||
|
||||
if (is(userInfo()) && is(host())) {
|
||||
s += userInfo();
|
||||
if (userInfo().indexOf('@') !== userInfo().length - 1) {
|
||||
s += '@';
|
||||
}
|
||||
}
|
||||
|
||||
if (is(host())) {
|
||||
s += host();
|
||||
if (is(port())) {
|
||||
s += ':' + port();
|
||||
}
|
||||
}
|
||||
|
||||
if (is(path())) {
|
||||
s += path();
|
||||
} else {
|
||||
if (is(host()) && (is(query().toString()) || is(anchor()))) {
|
||||
s += '/';
|
||||
}
|
||||
}
|
||||
if (is(query().toString())) {
|
||||
if (query().toString().indexOf('?') !== 0) {
|
||||
s += '?';
|
||||
}
|
||||
s += query().toString();
|
||||
}
|
||||
|
||||
if (is(anchor())) {
|
||||
if (anchor().indexOf('#') !== 0) {
|
||||
s += '#';
|
||||
}
|
||||
s += anchor();
|
||||
}
|
||||
|
||||
return s;
|
||||
},
|
||||
|
||||
/*
|
||||
Cloning
|
||||
*/
|
||||
|
||||
// clone() returns a new, identical Uri instance
|
||||
clone = function () {
|
||||
return new Uri(toString());
|
||||
};
|
||||
|
||||
// public api
|
||||
return {
|
||||
|
||||
protocol: protocol,
|
||||
hasAuthorityPrefix: hasAuthorityPrefix,
|
||||
userInfo: userInfo,
|
||||
host: host,
|
||||
port: port,
|
||||
path: path,
|
||||
query: query,
|
||||
anchor: anchor,
|
||||
|
||||
setProtocol: setProtocol,
|
||||
setHasAuthorityPrefix: setHasAuthorityPrefix,
|
||||
setUserInfo: setUserInfo,
|
||||
setHost: setHost,
|
||||
setPort: setPort,
|
||||
setPath: setPath,
|
||||
setQuery: setQuery,
|
||||
setAnchor: setAnchor,
|
||||
|
||||
getQueryParamValue: getQueryParamValue,
|
||||
getQueryParamValues: getQueryParamValues,
|
||||
deleteQueryParam: deleteQueryParam,
|
||||
addQueryParam: addQueryParam,
|
||||
replaceQueryParam: replaceQueryParam,
|
||||
|
||||
toString: toString,
|
||||
clone: clone
|
||||
};
|
||||
};
|
||||
|
||||
/* add compatibility for users of jsUri <= 1.1.1 */
|
||||
var jsUri = Uri;
|
10
login.html
10
login.html
|
@ -1,10 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="https://login.persona.org/include.js"></script>
|
||||
<script src="lib/jquery.js"></script>
|
||||
<script src="login.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#" id="login-link">login</a>
|
||||
</body>
|
||||
</html>
|
5
login.js
5
login.js
|
@ -1,5 +0,0 @@
|
|||
$(document).ready(function() {
|
||||
$('#login-link').click(function() {
|
||||
navigator.id.request();
|
||||
});
|
||||
});
|
78
main.js
78
main.js
|
@ -4,17 +4,89 @@ var socket = io.connect(SKYCRANE_SERVER);
|
|||
|
||||
console.log('In chrome script');
|
||||
|
||||
// Maps a notificationID to the data extracted from it by an observer.
|
||||
var loginData = {};
|
||||
|
||||
var lastNotificationID = 0;
|
||||
|
||||
chrome.browserAction.onClicked.addListener(function(tab) {
|
||||
chrome.tabs.create({
|
||||
url: /*SKYCRANE_SERVER + "*/"login.html"
|
||||
url: SKYCRANE_SERVER + "/persona_auth"
|
||||
}, function(tab) {
|
||||
chrome.tabs.executeScript(tab.id, {file: "data/auth_content_script.js"});
|
||||
});
|
||||
});
|
||||
|
||||
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
||||
if (request.type == 'add_login') {
|
||||
socket.emit('add_login',request.message);
|
||||
// Prompt the user to save the login
|
||||
displayNotification({
|
||||
notify: true,
|
||||
tabId: sender.tab.id,
|
||||
notification: {
|
||||
type: 'password_saved',
|
||||
hash: SHA1(request.message.password),
|
||||
hostname: request.message.hostname,
|
||||
username: request.message.username
|
||||
}
|
||||
});
|
||||
|
||||
// socket.emit('add_login',request.message);
|
||||
// chrome.tabs.get(sender.tab.id,function(tab) {
|
||||
// });
|
||||
|
||||
console.log("add_login for tab: " + sender.tab.id);
|
||||
|
||||
}
|
||||
else if (request.type == 'login_success') {
|
||||
console.log('Successfully logged in with ',JSON.stringify(request.message));
|
||||
}
|
||||
console.log(sender.tab ?
|
||||
"from a content script:" + sender.tab.url :
|
||||
"from the extension");
|
||||
});
|
||||
});
|
||||
|
||||
function displayNotification(notificationObj) {
|
||||
loginData[lastNotificationID] = notificationObj;
|
||||
var notif = webkitNotifications.createHTMLNotification('data/notification.html#' + lastNotificationID);
|
||||
notif.show();
|
||||
|
||||
loginData[lastNotificationID].popupNotifs = [notif];
|
||||
lastNotificationID++;
|
||||
}
|
||||
|
||||
function getNotificationForID(notifID) {
|
||||
return loginData[notifID].notification;
|
||||
}
|
||||
|
||||
function saveToStorage(newLogin) {
|
||||
// TODO: Encrypt data, and send over chrome sync?
|
||||
chrome.storage.local.get('logins', function(storageLogins) {
|
||||
var siteName = request.message.hostname;
|
||||
if (storageLogins.sites === undefined) storageLogins.sites = {};
|
||||
if (storageLogins.sites[siteName] === undefined) storageLogins.sites[siteName] = [];
|
||||
storageLogins.sites[siteName].push(request.message);
|
||||
chrome.storage.local.set({'logins': storageLogins});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function saveLogin(notifID) {
|
||||
console.log('saveLogin for notifID: ', notifID);
|
||||
closeNotif(notifID);
|
||||
}
|
||||
|
||||
function notNow(notifID) {
|
||||
console.log('notNow for notifID: ', notifID);
|
||||
closeNotif(notifID);
|
||||
}
|
||||
|
||||
function neverForSite(notifID) {
|
||||
console.log('neverForSite for notifID: ', notifID);
|
||||
closeNotif(notifID);
|
||||
}
|
||||
|
||||
function closeNotif(notifID) {
|
||||
loginData[notifID].popupNotifs[0].close();
|
||||
delete loginData[notifID];
|
||||
}
|
|
@ -5,15 +5,14 @@
|
|||
"description": "Cross-platform password sync and management.",
|
||||
"permissions": [
|
||||
"tabs",
|
||||
"notifications"
|
||||
"notifications",
|
||||
"storage",
|
||||
"http://*/",
|
||||
"https://*/"
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["lib/jquery.js","lib/socket.io.js","main.js"]
|
||||
"scripts": ["lib/jquery.js","lib/jsuri.js","lib/socket.io.js","util.js","main.js"]
|
||||
},
|
||||
"permissions": [
|
||||
"http://*/",
|
||||
"https://*/"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_icon": "icons/small.png",
|
||||
"default_title": "SkyCrane"
|
||||
|
@ -21,9 +20,12 @@
|
|||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"css": [],
|
||||
"js": ["data/observer.js"]
|
||||
"css": ["data/pwmgr.css"],
|
||||
"js": ["data/pwmgr_inject.js","data/observer.js"]
|
||||
}
|
||||
],
|
||||
"content_security_policy": "script-src 'self' https://login.persona.org https://persona.org; object-src 'self'"
|
||||
"content_security_policy": "script-src 'self' https://login.persona.org https://persona.org; object-src 'self'; style-src 'self';",
|
||||
"web_accessible_resources": [
|
||||
"icons/small.png"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
//javascript:(function(){if(window.myBookmarklet!==undefined){myBookmarklet();}else{document.body.appendChild(document.createElement('script')).src='http://localhost:8000/public/bookmarklet.js';}})();
|
||||
function initSkyCrane() {
|
||||
$.ajax({
|
||||
url: "http://localhost:8000/get_login/" + window.location.host,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
var usernameFieldName = data.data.usernameField.name;
|
||||
var passwordFieldName = data.data.passwordField.name;
|
||||
|
||||
$('input[name="' + usernameFieldName + '"]').val(data.data.username);
|
||||
$('input[name="' + passwordFieldName + '"]').val(data.data.password);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Thanks, http://coding.smashingmagazine.com/2010/05/23/make-your-own-bookmarklets-with-jquery/
|
||||
(function(){
|
||||
|
||||
// the minimum version of jQuery we want
|
||||
var v = "1.3.2";
|
||||
|
||||
// check prior inclusion and version
|
||||
if (window.jQuery === undefined || window.jQuery.fn.jquery < v) {
|
||||
var done = false;
|
||||
var script = document.createElement("script");
|
||||
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/" + v + "/jquery.min.js";
|
||||
script.onload = script.onreadystatechange = function(){
|
||||
if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
|
||||
done = true;
|
||||
initSkyCrane();
|
||||
}
|
||||
};
|
||||
document.getElementsByTagName("head")[0].appendChild(script);
|
||||
} else {
|
||||
initSkyCrane();
|
||||
}
|
||||
|
||||
|
||||
})();
|
Двоичный файл не отображается.
|
@ -0,0 +1,73 @@
|
|||
|
||||
// Include jquery and Persona
|
||||
// Thanks, http://coding.smashingmagazine.com/2010/05/23/make-your-own-bookmarklets-with-jquery/
|
||||
(function(){
|
||||
|
||||
var libsLoaded = {
|
||||
'jquery': false,
|
||||
'persona': false
|
||||
};
|
||||
|
||||
function initIfReady() {
|
||||
for (var lib in libsLoaded) {
|
||||
if (!libsLoaded[lib]) return;
|
||||
}
|
||||
initSkyCrane();
|
||||
// navigator.id.request();
|
||||
appendPersonaButton();
|
||||
}
|
||||
|
||||
function loadScript(libName,scriptURL) {
|
||||
var script = document.createElement("script");
|
||||
script.src = scriptURL;
|
||||
script.onload = script.onreadystatechange = function(){
|
||||
if (!this.readyState || this.readyState == "loaded" || this.readyState == "complete") {
|
||||
libsLoaded[libName] = true;
|
||||
initIfReady();
|
||||
}
|
||||
};
|
||||
document.getElementsByTagName("head")[0].appendChild(script);
|
||||
}
|
||||
|
||||
// the minimum version of jQuery we want
|
||||
var v = "1.3.2";
|
||||
|
||||
// check prior inclusion and version
|
||||
if (window.jQuery === undefined || window.jQuery.fn.jquery < v) {
|
||||
loadScript("jquery","http://ajax.googleapis.com/ajax/libs/jquery/" + v + "/jquery.min.js");
|
||||
} else {
|
||||
libsLoaded.jquery = true;
|
||||
initIfReady();
|
||||
}
|
||||
|
||||
if (navigator.id === undefined) {
|
||||
loadScript("persona","https://login.persona.org/include.js");
|
||||
} else {
|
||||
libsLoaded.persona = true;
|
||||
initIfReady();
|
||||
}
|
||||
|
||||
//javascript:(function(){if(window.myBookmarklet!==undefined){myBookmarklet();}else{document.body.appendChild(document.createElement('script')).src='http://localhost:8000/public/bookmarklet.js';}})();
|
||||
function initSkyCrane() {
|
||||
$.ajax({
|
||||
url: "http://localhost:8000/get_login/" + window.location.host,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
var usernameFieldName = data.data.usernameField.name;
|
||||
var passwordFieldName = data.data.passwordField.name;
|
||||
|
||||
$('input[name="' + usernameFieldName + '"]').val(data.data.username);
|
||||
$('input[name="' + passwordFieldName + '"]').val(data.data.password);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function appendPersonaButton() {
|
||||
var personaButton = $('<input type="submit" value="Login with Persona"/>');
|
||||
personaButton.click(function() {
|
||||
navigator.id.request();
|
||||
});
|
||||
$(document.body).append(personaButton);
|
||||
}
|
||||
|
||||
})();
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,11 @@
|
|||
// $(document).ready(function() {
|
||||
// navigator.id.watch({
|
||||
// onlogin: function(assertion) {
|
||||
//
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// $('#login-link').click(function() {
|
||||
// navigator.id.request();
|
||||
// });
|
||||
// });
|
|
@ -1,8 +1,13 @@
|
|||
var redis = require("redis"),
|
||||
express = require('express'),
|
||||
http = require('http');
|
||||
http = require('http'),
|
||||
https = require('https'),
|
||||
browserid = require('express-browserid');
|
||||
|
||||
app = express();
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
server = http.createServer(app).listen(8000);
|
||||
|
||||
io = require('socket.io').listen(server);
|
||||
|
@ -17,12 +22,40 @@ app.configure(function () {
|
|||
next();
|
||||
}
|
||||
|
||||
// Need to allow crossdomain access for bookmarklet to work
|
||||
app.use(allowCrossDomain);
|
||||
|
||||
// Serve static files
|
||||
app.use('/public',express.static('public'));
|
||||
|
||||
app.use(express.bodyParser());
|
||||
|
||||
var MemoryStore = require('connect').session.MemoryStore;
|
||||
// TODO: Substitute actual session secret
|
||||
app.use(express.cookieParser("seeekrit string"));
|
||||
app.use(express.session());
|
||||
|
||||
// Install express-browserid routes
|
||||
browserid.plugAll(app);
|
||||
});
|
||||
|
||||
app.listen();
|
||||
|
||||
|
||||
// Authenticate with Persona.
|
||||
app.get('/persona_auth', function(req, res) {
|
||||
res.render('auth');
|
||||
});
|
||||
app.get('/test_auth', function(req, res) {
|
||||
if (req.session.email) {
|
||||
res.send("Hello, " + req.session.email + "!");
|
||||
}
|
||||
else {
|
||||
res.send("I don't know who you are.");
|
||||
}
|
||||
});
|
||||
//////////////////////////////////////////////
|
||||
|
||||
app.get('/get_login/:host', function(req, res, next) {
|
||||
client.hget('me@paulsawaya.com', req.params.host, function(err, data) {
|
||||
res.send(JSON.stringify({
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="https://login.persona.org/include.js"></script>
|
||||
<script src="/public/lib/jquery.js"></script>
|
||||
<script src="/public/login.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// TODO: Get express-browserid to load on document ready
|
||||
|
||||
var newScript = document.createElement('script');
|
||||
newScript.src = '/browserid/js/browserid-helper.js';
|
||||
$(newScript).attr('data-auto','false');
|
||||
document.head.appendChild(newScript);
|
||||
|
||||
$(window).on('login-success', window.__authSuccess);
|
||||
alert(window.__authSuccess);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
a {
|
||||
font-size:48pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to SkyCrane</h1>
|
||||
<a href="#" id="browserid-login">login</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,174 @@
|
|||
/**
|
||||
*
|
||||
* Secure Hash Algorithm (SHA1)
|
||||
* http://www.webtoolkit.info/
|
||||
*
|
||||
*
|
||||
**/
|
||||
function SHA1 (msg) {
|
||||
|
||||
function rotate_left(n,s) {
|
||||
var t4 = ( n<<s ) | (n>>>(32-s));
|
||||
return t4;
|
||||
};
|
||||
|
||||
function lsb_hex(val) {
|
||||
var str="";
|
||||
var i;
|
||||
var vh;
|
||||
var vl;
|
||||
|
||||
for( i=0; i<=6; i+=2 ) {
|
||||
vh = (val>>>(i*4+4))&0x0f;
|
||||
vl = (val>>>(i*4))&0x0f;
|
||||
str += vh.toString(16) + vl.toString(16);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
function cvt_hex(val) {
|
||||
var str="";
|
||||
var i;
|
||||
var v;
|
||||
|
||||
for( i=7; i>=0; i-- ) {
|
||||
v = (val>>>(i*4))&0x0f;
|
||||
str += v.toString(16);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
function Utf8Encode(string) {
|
||||
string = string.replace(/\r\n/g,"\n");
|
||||
var utftext = "";
|
||||
|
||||
for (var n = 0; n < string.length; n++) {
|
||||
|
||||
var c = string.charCodeAt(n);
|
||||
|
||||
if (c < 128) {
|
||||
utftext += String.fromCharCode(c);
|
||||
}
|
||||
else if((c > 127) && (c < 2048)) {
|
||||
utftext += String.fromCharCode((c >> 6) | 192);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
else {
|
||||
utftext += String.fromCharCode((c >> 12) | 224);
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return utftext;
|
||||
};
|
||||
|
||||
var blockstart;
|
||||
var i, j;
|
||||
var W = new Array(80);
|
||||
var H0 = 0x67452301;
|
||||
var H1 = 0xEFCDAB89;
|
||||
var H2 = 0x98BADCFE;
|
||||
var H3 = 0x10325476;
|
||||
var H4 = 0xC3D2E1F0;
|
||||
var A, B, C, D, E;
|
||||
var temp;
|
||||
|
||||
msg = Utf8Encode(msg);
|
||||
|
||||
var msg_len = msg.length;
|
||||
|
||||
var word_array = new Array();
|
||||
for( i=0; i<msg_len-3; i+=4 ) {
|
||||
j = msg.charCodeAt(i)<<24 | msg.charCodeAt(i+1)<<16 |
|
||||
msg.charCodeAt(i+2)<<8 | msg.charCodeAt(i+3);
|
||||
word_array.push( j );
|
||||
}
|
||||
|
||||
switch( msg_len % 4 ) {
|
||||
case 0:
|
||||
i = 0x080000000;
|
||||
break;
|
||||
case 1:
|
||||
i = msg.charCodeAt(msg_len-1)<<24 | 0x0800000;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
i = msg.charCodeAt(msg_len-2)<<24 | msg.charCodeAt(msg_len-1)<<16 | 0x08000;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
i = msg.charCodeAt(msg_len-3)<<24 | msg.charCodeAt(msg_len-2)<<16 | msg.charCodeAt(msg_len-1)<<8 | 0x80;
|
||||
break;
|
||||
}
|
||||
|
||||
word_array.push( i );
|
||||
|
||||
while( (word_array.length % 16) != 14 ) word_array.push( 0 );
|
||||
|
||||
word_array.push( msg_len>>>29 );
|
||||
word_array.push( (msg_len<<3)&0x0ffffffff );
|
||||
|
||||
|
||||
for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) {
|
||||
|
||||
for( i=0; i<16; i++ ) W[i] = word_array[blockstart+i];
|
||||
for( i=16; i<=79; i++ ) W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
|
||||
|
||||
A = H0;
|
||||
B = H1;
|
||||
C = H2;
|
||||
D = H3;
|
||||
E = H4;
|
||||
|
||||
for( i= 0; i<=19; i++ ) {
|
||||
temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotate_left(B,30);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for( i=20; i<=39; i++ ) {
|
||||
temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotate_left(B,30);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for( i=40; i<=59; i++ ) {
|
||||
temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotate_left(B,30);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for( i=60; i<=79; i++ ) {
|
||||
temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotate_left(B,30);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
H0 = (H0 + A) & 0x0ffffffff;
|
||||
H1 = (H1 + B) & 0x0ffffffff;
|
||||
H2 = (H2 + C) & 0x0ffffffff;
|
||||
H3 = (H3 + D) & 0x0ffffffff;
|
||||
H4 = (H4 + E) & 0x0ffffffff;
|
||||
|
||||
}
|
||||
|
||||
var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
|
||||
|
||||
return temp.toLowerCase();
|
||||
|
||||
}
|
Загрузка…
Ссылка в новой задаче