2018-06-23 15:27:30 +02:00
|
|
|
import * as papa from 'papaparse';
|
|
|
|
|
2018-07-05 23:29:35 +02:00
|
|
|
import { ImportResult } from '../models/domain/importResult';
|
|
|
|
|
2018-07-10 23:51:47 +02:00
|
|
|
import { CipherView } from '../models/view/cipherView';
|
2018-07-05 23:29:35 +02:00
|
|
|
import { CollectionView } from '../models/view/collectionView';
|
2018-06-23 15:27:30 +02:00
|
|
|
import { LoginUriView } from '../models/view/loginUriView';
|
|
|
|
|
2018-06-25 21:19:51 +02:00
|
|
|
import { Utils } from '../misc/utils';
|
2018-07-10 23:51:47 +02:00
|
|
|
import { FieldView } from '../models/view/fieldView';
|
|
|
|
import { LoginView } from '../models/view/loginView';
|
|
|
|
|
|
|
|
import { CipherType } from '../enums/cipherType';
|
|
|
|
import { FieldType } from '../enums/fieldType';
|
2018-06-25 21:19:51 +02:00
|
|
|
|
2018-06-23 15:27:30 +02:00
|
|
|
export abstract class BaseImporter {
|
2018-07-06 01:00:00 +02:00
|
|
|
organization = false;
|
|
|
|
|
2018-07-10 22:38:43 +02:00
|
|
|
protected newLineRegex = /(?:\r\n|\r|\n)/;
|
|
|
|
|
2018-06-23 15:27:30 +02:00
|
|
|
protected passwordFieldNames = [
|
|
|
|
'password', 'pass word', 'passphrase', 'pass phrase',
|
|
|
|
'pass', 'code', 'code word', 'codeword',
|
|
|
|
'secret', 'secret word', 'personpwd',
|
|
|
|
'key', 'keyword', 'key word', 'keyphrase', 'key phrase',
|
|
|
|
'form_pw', 'wppassword', 'pin', 'pwd', 'pw', 'pword', 'passwd',
|
|
|
|
'p', 'serial', 'serial#', 'license key', 'reg #',
|
|
|
|
|
|
|
|
// Non-English names
|
2018-06-28 04:09:39 +02:00
|
|
|
'passwort',
|
2018-06-23 15:27:30 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
protected usernameFieldNames = [
|
|
|
|
'user', 'name', 'user name', 'username', 'login name',
|
|
|
|
'email', 'e-mail', 'id', 'userid', 'user id',
|
|
|
|
'login', 'form_loginname', 'wpname', 'mail',
|
|
|
|
'loginid', 'login id', 'log', 'personlogin',
|
|
|
|
'first name', 'last name', 'card#', 'account #',
|
|
|
|
'member', 'member #',
|
|
|
|
|
|
|
|
// Non-English names
|
2018-06-28 04:09:39 +02:00
|
|
|
'nom', 'benutzername',
|
2018-06-23 15:27:30 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
protected notesFieldNames = [
|
2018-06-28 04:09:39 +02:00
|
|
|
'note', 'notes', 'comment', 'comments', 'memo',
|
|
|
|
'description', 'free form', 'freeform',
|
|
|
|
'free text', 'freetext', 'free',
|
2018-06-23 15:27:30 +02:00
|
|
|
|
|
|
|
// Non-English names
|
2018-06-28 04:09:39 +02:00
|
|
|
'kommentar',
|
2018-06-23 15:27:30 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
protected uriFieldNames: string[] = [
|
|
|
|
'url', 'hyper link', 'hyperlink', 'link',
|
|
|
|
'host', 'hostname', 'host name', 'server', 'address',
|
|
|
|
'hyper ref', 'href', 'web', 'website', 'web site', 'site',
|
|
|
|
'web-site', 'uri',
|
|
|
|
|
|
|
|
// Non-English names
|
2018-06-28 04:09:39 +02:00
|
|
|
'ort', 'adresse',
|
2018-06-23 15:27:30 +02:00
|
|
|
];
|
|
|
|
|
2018-07-08 05:02:53 +02:00
|
|
|
protected parseXml(data: string): Document {
|
|
|
|
const parser = new DOMParser();
|
|
|
|
const doc = parser.parseFromString(data, 'application/xml');
|
|
|
|
return doc != null && doc.querySelector('parsererror') == null ? doc : null;
|
|
|
|
}
|
|
|
|
|
2018-06-23 15:27:30 +02:00
|
|
|
protected parseCsv(data: string, header: boolean): any[] {
|
|
|
|
const result = papa.parse(data, {
|
|
|
|
header: header,
|
|
|
|
encoding: 'UTF-8',
|
2018-06-23 20:46:23 +02:00
|
|
|
skipEmptyLines: false,
|
2018-06-23 15:27:30 +02:00
|
|
|
});
|
|
|
|
if (result.errors != null && result.errors.length > 0) {
|
|
|
|
result.errors.forEach((e) => {
|
|
|
|
// tslint:disable-next-line
|
|
|
|
console.warn('Error parsing row ' + e.row + ': ' + e.message);
|
|
|
|
});
|
|
|
|
}
|
2018-06-23 20:46:23 +02:00
|
|
|
return result.data && result.data.length > 0 ? result.data : null;
|
2018-06-23 15:27:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected parseSingleRowCsv(rowData: string) {
|
|
|
|
if (this.isNullOrWhitespace(rowData)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const parsedRow = this.parseCsv(rowData, false);
|
|
|
|
if (parsedRow != null && parsedRow.length > 0 && parsedRow[0].length > 0) {
|
|
|
|
return parsedRow[0];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected makeUriArray(uri: string | string[]): LoginUriView[] {
|
|
|
|
if (uri == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof uri === 'string') {
|
|
|
|
const loginUri = new LoginUriView();
|
|
|
|
loginUri.uri = this.fixUri(uri);
|
|
|
|
loginUri.match = null;
|
|
|
|
return [loginUri];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uri.length > 0) {
|
|
|
|
const returnArr: LoginUriView[] = [];
|
|
|
|
uri.forEach((u) => {
|
|
|
|
const loginUri = new LoginUriView();
|
|
|
|
loginUri.uri = this.fixUri(u);
|
|
|
|
loginUri.match = null;
|
|
|
|
returnArr.push(loginUri);
|
|
|
|
});
|
|
|
|
return returnArr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected fixUri(uri: string) {
|
|
|
|
if (uri == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
uri = uri.toLowerCase().trim();
|
|
|
|
if (uri.indexOf('://') === -1 && uri.indexOf('.') >= 0) {
|
|
|
|
uri = 'http://' + uri;
|
|
|
|
}
|
|
|
|
if (uri.length > 1000) {
|
|
|
|
return uri.substring(0, 1000);
|
|
|
|
}
|
|
|
|
return uri;
|
|
|
|
}
|
|
|
|
|
2018-06-25 21:19:51 +02:00
|
|
|
protected nameFromUrl(url: string) {
|
|
|
|
const hostname = Utils.getHostname(url);
|
|
|
|
if (this.isNullOrWhitespace(hostname)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return hostname.startsWith('www.') ? hostname.replace('www.', '') : hostname;
|
|
|
|
}
|
|
|
|
|
2018-06-23 15:27:30 +02:00
|
|
|
protected isNullOrWhitespace(str: string): boolean {
|
|
|
|
return str == null || str.trim() === '';
|
|
|
|
}
|
|
|
|
|
|
|
|
protected getValueOrDefault(str: string, defaultValue: string = null): string {
|
|
|
|
if (this.isNullOrWhitespace(str)) {
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected splitNewLine(str: string): string[] {
|
2018-07-10 22:38:43 +02:00
|
|
|
return str.split(this.newLineRegex);
|
2018-06-23 15:27:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ref https://stackoverflow.com/a/5911300
|
|
|
|
protected getCardBrand(cardNum: string) {
|
|
|
|
if (this.isNullOrWhitespace(cardNum)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Visa
|
|
|
|
let re = new RegExp('^4');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'Visa';
|
|
|
|
}
|
|
|
|
|
2018-06-28 04:09:39 +02:00
|
|
|
// Mastercard
|
2018-06-23 15:27:30 +02:00
|
|
|
// Updated for Mastercard 2017 BINs expansion
|
|
|
|
if (/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/
|
|
|
|
.test(cardNum)) {
|
|
|
|
return 'Mastercard';
|
|
|
|
}
|
|
|
|
|
|
|
|
// AMEX
|
|
|
|
re = new RegExp('^3[47]');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'Amex';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Discover
|
|
|
|
re = new RegExp('^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'Discover';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Diners
|
|
|
|
re = new RegExp('^36');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'Diners Club';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Diners - Carte Blanche
|
|
|
|
re = new RegExp('^30[0-5]');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'Diners Club';
|
|
|
|
}
|
|
|
|
|
|
|
|
// JCB
|
|
|
|
re = new RegExp('^35(2[89]|[3-8][0-9])');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'JCB';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Visa Electron
|
|
|
|
re = new RegExp('^(4026|417500|4508|4844|491(3|7))');
|
|
|
|
if (cardNum.match(re) != null) {
|
|
|
|
return 'Visa';
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2018-07-05 23:29:35 +02:00
|
|
|
|
|
|
|
protected moveFoldersToCollections(result: ImportResult) {
|
2018-07-10 22:38:43 +02:00
|
|
|
result.folderRelationships.forEach((r) => result.collectionRelationships.push(r));
|
2018-07-05 23:29:35 +02:00
|
|
|
result.collections = result.folders.map((f) => {
|
|
|
|
const collection = new CollectionView();
|
|
|
|
collection.name = f.name;
|
|
|
|
return collection;
|
|
|
|
});
|
2018-07-10 22:38:43 +02:00
|
|
|
result.folderRelationships = [];
|
2018-07-05 23:29:35 +02:00
|
|
|
result.folders = [];
|
|
|
|
}
|
2018-07-08 05:02:53 +02:00
|
|
|
|
|
|
|
protected querySelectorDirectChild(parentEl: Element, query: string) {
|
|
|
|
const els = this.querySelectorAllDirectChild(parentEl, query);
|
|
|
|
return els.length === 0 ? null : els[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
protected querySelectorAllDirectChild(parentEl: Element, query: string) {
|
|
|
|
return Array.from(parentEl.querySelectorAll(query)).filter((el) => el.parentNode === parentEl);
|
|
|
|
}
|
2018-07-10 23:51:47 +02:00
|
|
|
|
|
|
|
protected initLoginCipher() {
|
|
|
|
const cipher = new CipherView();
|
|
|
|
cipher.favorite = false;
|
|
|
|
cipher.notes = '';
|
|
|
|
cipher.fields = [];
|
|
|
|
cipher.login = new LoginView();
|
|
|
|
cipher.type = CipherType.Login;
|
|
|
|
return cipher;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected cleanupCipher(cipher: CipherView) {
|
|
|
|
if (cipher == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cipher.type !== CipherType.Login) {
|
|
|
|
cipher.login = null;
|
|
|
|
}
|
|
|
|
if (this.isNullOrWhitespace(cipher.name)) {
|
|
|
|
cipher.name = '--';
|
|
|
|
}
|
|
|
|
if (this.isNullOrWhitespace(cipher.notes)) {
|
|
|
|
cipher.notes = null;
|
|
|
|
} else {
|
|
|
|
cipher.notes = cipher.notes.trim();
|
|
|
|
}
|
|
|
|
if (cipher.fields != null && cipher.fields.length === 0) {
|
|
|
|
cipher.fields = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected processKvp(cipher: CipherView, key: string, value: string) {
|
|
|
|
if (this.isNullOrWhitespace(value)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this.isNullOrWhitespace(key)) {
|
|
|
|
key = '';
|
|
|
|
}
|
|
|
|
if (value.length > 200 || value.search(this.newLineRegex) > -1) {
|
|
|
|
if (cipher.notes == null) {
|
|
|
|
cipher.notes = '';
|
|
|
|
}
|
|
|
|
cipher.notes += (key + ': ' + value + '\n');
|
|
|
|
} else {
|
|
|
|
if (cipher.fields == null) {
|
|
|
|
cipher.fields = [];
|
|
|
|
}
|
|
|
|
const field = new FieldView();
|
|
|
|
field.type = FieldType.Text;
|
|
|
|
field.name = key;
|
|
|
|
field.value = value;
|
|
|
|
cipher.fields.push(field);
|
|
|
|
}
|
|
|
|
}
|
2018-06-23 15:27:30 +02:00
|
|
|
}
|