2016-04-08 17:40:00 +02:00
|
|
|
'use strict';
|
|
|
|
|
2016-04-09 00:21:58 +02:00
|
|
|
var Format = require('../../util/format');
|
|
|
|
|
2016-04-08 17:40:00 +02:00
|
|
|
var AutoTypeRunner = function(ops) {
|
|
|
|
this.ops = ops;
|
|
|
|
this.pendingResolves = 0;
|
2016-04-09 00:21:58 +02:00
|
|
|
this.entry = null;
|
|
|
|
this.now = new Date();
|
2016-04-08 17:40:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.PendingResolve = { pending: true };
|
|
|
|
|
|
|
|
AutoTypeRunner.Keys = {
|
|
|
|
tab: 'tab', enter: 'enter', space: 'space',
|
|
|
|
up: 'up', down: 'down', left: 'left', right: 'right', home: 'home', end: 'end', pgup: 'pgup', pgdn: 'pgdn',
|
|
|
|
insert: 'ins', ins: 'ins', delete: 'del', del: 'del', backspace: 'bs', bs: 'bs', bksp: 'bs', esc: 'esc',
|
|
|
|
win: 'win', lwin: 'win', rwin: 'rwin', f1: 'f1', f2: 'f2', f3: 'f3', f4: 'f4', f5: 'f5', f6: 'f6',
|
|
|
|
f7: 'f7', f8: 'f8', f9: 'f9', f10: 'f10', f11: 'f11', f12: 'f12', f13: 'f13', f14: 'f14', f15: 'f15', f16: 'f16',
|
|
|
|
add: 'add', subtract: 'subtract', multiply: 'multiply', divide: 'divide',
|
|
|
|
numpad0: 'n0', numpad1: 'n1', numpad2: 'n2', numpad3: 'n3', numpad4: 'n4',
|
|
|
|
numpad5: 'n5', numpad6: 'n6', numpad7: 'n7', numpad8: 'n8', numpad9: 'n9'
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.Substitutions = {
|
2016-04-09 00:21:58 +02:00
|
|
|
title: function(runner, op) { return runner.getEntryFieldKeys('Title', op); },
|
|
|
|
username: function(runner, op) { return runner.getEntryFieldKeys('UserName', op); },
|
|
|
|
url: function(runner, op) { return runner.getEntryFieldKeys('URL', op); },
|
|
|
|
password: function(runner, op) { return runner.getEntryFieldKeys('Password', op); },
|
|
|
|
notes: function(runner, op) { return runner.getEntryFieldKeys('Notes', op); },
|
|
|
|
group: function(runner) { return runner.getEntryGroupName(); },
|
|
|
|
totp: function() { /*TODO*/ },
|
|
|
|
s: function(runner, op) { return runner.getEntryFieldKeys(op.arg, op); },
|
|
|
|
'dt_simple': function(runner) { return runner.dt('simple'); },
|
|
|
|
'dt_year': function(runner) { return runner.dt('Y'); },
|
|
|
|
'dt_month': function(runner) { return runner.dt('M'); },
|
|
|
|
'dt_day': function(runner) { return runner.dt('D'); },
|
|
|
|
'dt_hour': function(runner) { return runner.dt('h'); },
|
|
|
|
'dt_minute': function(runner) { return runner.dt('m'); },
|
|
|
|
'dt_second': function(runner) { return runner.dt('s'); },
|
|
|
|
'dt_utc_simple': function(runner) { return runner.udt('simple'); },
|
|
|
|
'dt_utc_year': function(runner) { return runner.udt('Y'); },
|
|
|
|
'dt_utc_month': function(runner) { return runner.udt('M'); },
|
|
|
|
'dt_utc_day': function(runner) { return runner.udt('D'); },
|
|
|
|
'dt_utc_hour': function(runner) { return runner.udt('h'); },
|
|
|
|
'dt_utc_minute': function(runner) { return runner.udt('m'); },
|
|
|
|
'dt_utc_second': function(runner) { return runner.udt('s'); }
|
2016-04-08 17:40:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.Commands = {
|
|
|
|
wait: function() {},
|
|
|
|
setDelay: function() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.resolve = function(entry, callback) {
|
|
|
|
this.entry = entry;
|
|
|
|
try {
|
|
|
|
this.resolveOps(this.ops);
|
|
|
|
if (!this.pendingResolves) {
|
|
|
|
callback();
|
2016-04-08 23:12:56 +02:00
|
|
|
} else {
|
|
|
|
this.resolveCallback = callback;
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
return callback(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.resolveOps = function(ops) {
|
|
|
|
for (var i = 0, len = ops.length; i < len; i++) {
|
2016-04-08 23:12:56 +02:00
|
|
|
this.resolveOp(ops[i]);
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.resolveOp = function(op) {
|
2016-04-08 23:12:56 +02:00
|
|
|
if (op.type === 'group') {
|
|
|
|
this.resolveOps(op.value);
|
|
|
|
return;
|
|
|
|
}
|
2016-04-08 17:40:00 +02:00
|
|
|
if (op.value.length === 1 && !op.sep) {
|
|
|
|
// {x}
|
|
|
|
op.type = 'text';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (op.value.length === 1 && op.sep === ' ') {
|
|
|
|
// {x 3}
|
|
|
|
op.type = 'text';
|
|
|
|
var ch = op.value, text = ch, len = +op.arg;
|
|
|
|
while (text.length < len) {
|
|
|
|
text += ch;
|
|
|
|
}
|
|
|
|
op.value = text;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var lowerValue = op.value.toLowerCase();
|
|
|
|
var key = AutoTypeRunner.Keys[lowerValue];
|
|
|
|
if (key) {
|
|
|
|
if (op.sep === ' ' && +op.arg > 0) {
|
|
|
|
// {TAB 3}
|
|
|
|
op.type = 'group';
|
|
|
|
op.value = [];
|
2016-04-08 23:12:56 +02:00
|
|
|
var count = +op.arg;
|
2016-04-08 17:40:00 +02:00
|
|
|
for (var i = 0; i < count; i++) {
|
2016-04-08 23:12:56 +02:00
|
|
|
op.value.push({type: 'key', value: key});
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// {TAB}
|
|
|
|
op.type = 'key';
|
|
|
|
op.value = key;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var substitution = AutoTypeRunner.Substitutions[lowerValue];
|
|
|
|
if (substitution) {
|
2016-04-08 23:12:56 +02:00
|
|
|
// {title}
|
2016-04-08 17:40:00 +02:00
|
|
|
op.type = 'text';
|
|
|
|
op.value = substitution(this, op);
|
|
|
|
if (op.value === AutoTypeRunner.PendingResolve) {
|
|
|
|
this.pendingResolves++;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-04-08 23:12:56 +02:00
|
|
|
if (!this.tryParseCommand(op)) {
|
|
|
|
throw 'Bad op: ' + op.value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.tryParseCommand = function(op) {
|
|
|
|
switch (op.value.toLowerCase()) {
|
2016-04-08 17:40:00 +02:00
|
|
|
case 'clearfield':
|
2016-04-08 23:12:56 +02:00
|
|
|
// {CLEARFIELD}
|
2016-04-08 17:40:00 +02:00
|
|
|
op.type = 'group';
|
|
|
|
op.value = [{ type: 'key', value: 'a', mod: { '^': true } }, { type: 'key', value: 'bs' }];
|
2016-04-08 23:12:56 +02:00
|
|
|
return true;
|
2016-04-08 17:40:00 +02:00
|
|
|
case 'vkey':
|
2016-04-08 23:12:56 +02:00
|
|
|
// {VKEY 10} {VKEY 0x1F}
|
2016-04-08 17:40:00 +02:00
|
|
|
op.type = 'key';
|
2016-04-08 23:12:56 +02:00
|
|
|
op.value = parseInt(op.arg);
|
|
|
|
if (isNaN(op.value) || op.value <= 0) {
|
|
|
|
throw 'Bad vkey: ' + op.arg;
|
|
|
|
}
|
|
|
|
return true;
|
2016-04-08 17:40:00 +02:00
|
|
|
case 'delay':
|
2016-04-08 23:12:56 +02:00
|
|
|
// {DELAY 5} {DELAY=5}
|
2016-04-08 17:40:00 +02:00
|
|
|
op.type = 'cmd';
|
|
|
|
op.value = op.sep === '=' ? 'setDelay' : 'wait';
|
2016-04-08 23:12:56 +02:00
|
|
|
if (!op.arg) {
|
|
|
|
throw 'Delay requires seconds count';
|
|
|
|
}
|
|
|
|
if (isNaN(+op.arg)) {
|
|
|
|
throw 'Bad delay: ' + op.arg;
|
|
|
|
}
|
|
|
|
if (op.arg <= 0) {
|
|
|
|
throw 'Delay requires positive interval';
|
|
|
|
}
|
2016-04-08 17:40:00 +02:00
|
|
|
op.arg = +op.arg;
|
2016-04-08 23:12:56 +02:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
2016-04-08 23:12:56 +02:00
|
|
|
|
2016-04-08 17:40:00 +02:00
|
|
|
};
|
|
|
|
|
2016-04-09 00:21:58 +02:00
|
|
|
AutoTypeRunner.prototype.getEntryFieldKeys = function(field, op) {
|
|
|
|
if (!field) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
field = field.toLowerCase();
|
|
|
|
var value = null;
|
|
|
|
_.findKey(this.entry.entry.fields, function(val, f) {
|
|
|
|
if (f.toLowerCase() === field) {
|
|
|
|
value = val;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!value) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
if (value.isProtected) {
|
|
|
|
op.type = 'group';
|
|
|
|
var ops = [];
|
|
|
|
value.forEachChar(function(ch) {
|
|
|
|
ops.push({ type: 'text', value: String.fromCharCode(ch) });
|
|
|
|
});
|
|
|
|
return ops.length ? ops : '';
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.getEntryGroupName = function() {
|
|
|
|
return this.entry.group.get('title');
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.dt = function(part) {
|
|
|
|
switch (part) {
|
|
|
|
case 'simple':
|
|
|
|
return this.dt('Y') + this.dt('M') + this.dt('D') + this.dt('h') + this.dt('m') + this.dt('s');
|
|
|
|
case 'Y':
|
|
|
|
return this.now.getFullYear().toString();
|
|
|
|
case 'M':
|
|
|
|
return Format.pad(this.now.getMonth() + 1, 2);
|
|
|
|
case 'D':
|
|
|
|
return Format.pad(this.now.getDate(), 2);
|
|
|
|
case 'h':
|
|
|
|
return Format.pad(this.now.getHours(), 2);
|
|
|
|
case 'm':
|
|
|
|
return Format.pad(this.now.getMinutes(), 2);
|
|
|
|
case 's':
|
|
|
|
return Format.pad(this.now.getSeconds(), 2);
|
|
|
|
default:
|
|
|
|
throw 'Bad part: ' + part;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTypeRunner.prototype.udt = function(part) {
|
|
|
|
switch (part) {
|
|
|
|
case 'simple':
|
|
|
|
return this.udt('Y') + this.udt('M') + this.udt('D') + this.udt('h') + this.udt('m') + this.udt('s');
|
|
|
|
case 'Y':
|
|
|
|
return this.now.getUTCFullYear().toString();
|
|
|
|
case 'M':
|
|
|
|
return Format.pad(this.now.getUTCMonth() + 1, 2);
|
|
|
|
case 'D':
|
|
|
|
return Format.pad(this.now.getUTCDate(), 2);
|
|
|
|
case 'h':
|
|
|
|
return Format.pad(this.now.getUTCHours(), 2);
|
|
|
|
case 'm':
|
|
|
|
return Format.pad(this.now.getUTCMinutes(), 2);
|
|
|
|
case 's':
|
|
|
|
return Format.pad(this.now.getUTCSeconds(), 2);
|
|
|
|
default:
|
|
|
|
throw 'Bad part: ' + part;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2016-04-08 17:40:00 +02:00
|
|
|
AutoTypeRunner.prototype.run = function() {
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = AutoTypeRunner;
|