diff --git a/src/importers/kasperskyTxtImporter.ts b/src/importers/kasperskyTxtImporter.ts new file mode 100644 index 0000000000..895c693950 --- /dev/null +++ b/src/importers/kasperskyTxtImporter.ts @@ -0,0 +1,124 @@ +import { BaseImporter } from './baseImporter'; +import { Importer } from './importer'; + +import { ImportResult } from '../models/domain/importResult'; + +const NotesHeader = 'Notes\n\n'; +const ApplicationsHeader = 'Applications\n\n'; +const WebsitesHeader = 'Websites\n\n'; +const Delimiter = '\n---\n'; + +export class KasperskyTxtImporter extends BaseImporter implements Importer { + parse(data: string): ImportResult { + const result = new ImportResult(); + + let notesData: string; + let applicationsData: string; + let websitesData: string; + let workingData = this.splitNewLine(data).join('\n'); + + if (workingData.indexOf(NotesHeader) !== -1) { + const parts = workingData.split(NotesHeader); + if (parts.length > 1) { + workingData = parts[0]; + notesData = parts[1]; + } + } + if (workingData.indexOf(ApplicationsHeader) !== -1) { + const parts = workingData.split(ApplicationsHeader); + if (parts.length > 1) { + workingData = parts[0]; + applicationsData = parts[1]; + } + } + if (workingData.indexOf(WebsitesHeader) === 0) { + const parts = workingData.split(WebsitesHeader); + if (parts.length > 1) { + workingData = parts[0]; + websitesData = parts[1]; + } + } + + const notes = this.parseDataCategory(notesData); + const applications = this.parseDataCategory(applicationsData); + const websites = this.parseDataCategory(websitesData); + + notes.forEach((n) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(n.get('Name')); + cipher.notes = this.getValueOrDefault(n.get('Text')); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + websites.concat(applications).forEach((w) => { + const cipher = this.initLoginCipher(); + const nameKey = w.has('Website name') ? 'Website name' : 'Application'; + cipher.name = this.getValueOrDefault(w.get(nameKey), ''); + if (!this.isNullOrWhitespace(w.get('Login name'))) { + if (!this.isNullOrWhitespace(cipher.name)) { + cipher.name += ': '; + } + cipher.name += w.get('Login name'); + } + cipher.notes = this.getValueOrDefault(w.get('Comment')); + if (w.has('Website URL')) { + cipher.login.uris = this.makeUriArray(w.get('Website URL')); + } + cipher.login.username = this.getValueOrDefault(w.get('Login')); + cipher.login.password = this.getValueOrDefault(w.get('Password')); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return result; + } + + private parseDataCategory(data: string): Array> { + if (this.isNullOrWhitespace(data) || data.indexOf(Delimiter) === -1) { + return []; + } + const items: Array> = []; + data.split(Delimiter).forEach((p) => { + if (p.indexOf('\n') === -1) { + return; + } + const item = new Map(); + let itemComment: string; + let itemCommentKey: string; + p.split('\n').forEach((l) => { + if (itemComment != null) { + itemComment += ('\n' + l); + return; + } + const colonIndex = l.indexOf(':'); + let key: string; + let val: string; + if (colonIndex === -1) { + return; + } else { + key = l.substring(0, colonIndex); + if (l.length > colonIndex + 1) { + val = l.substring(colonIndex + 2); + } + } + if (key != null) { + item.set(key, val); + } + if (key === 'Comment' || key === 'Text') { + itemComment = val; + itemCommentKey = key; + } + }); + if (itemComment != null && itemCommentKey != null) { + item.set(itemCommentKey, itemComment); + } + if (item.size === 0) { + return; + } + items.push(item); + }); + return items; + } +} diff --git a/src/services/import.service.ts b/src/services/import.service.ts index 72e12181a5..6f39bf52c6 100644 --- a/src/services/import.service.ts +++ b/src/services/import.service.ts @@ -38,6 +38,7 @@ import { FirefoxCsvImporter } from '../importers/firefoxCsvImporter'; import { FSecureFskImporter } from '../importers/fsecureFskImporter'; import { GnomeJsonImporter } from '../importers/gnomeJsonImporter'; import { Importer } from '../importers/importer'; +import { KasperskyTxtImporter } from '../importers/kasperskyTxtImporter'; import { KeePass2XmlImporter } from '../importers/keepass2XmlImporter'; import { KeePassXCsvImporter } from '../importers/keepassxCsvImporter'; import { KeeperCsvImporter } from '../importers/keeperCsvImporter'; @@ -109,6 +110,7 @@ export class ImportService implements ImportServiceAbstraction { { id: 'passmanjson', name: 'Passman (json)' }, { id: 'avastcsv', name: 'Avast Passwords (csv)' }, { id: 'fsecurefsk', name: 'F-Secure KEY (fsk)' }, + { id: 'kasperskytxt', name: 'Kaspersky Password Manager (txt)' }, ]; constructor(private cipherService: CipherService, private folderService: FolderService, @@ -235,6 +237,8 @@ export class ImportService implements ImportServiceAbstraction { return new AvastCsvImporter(); case 'fsecurefsk': return new FSecureFskImporter(); + case 'kasperskytxt': + return new KasperskyTxtImporter(); default: return null; }