<commit_before>/*
    ***** BEGIN LICENSE BLOCK *****
    
    Copyright © 2014 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org
    
    This file is part of Zotero.
    
    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
    
    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
    
    ***** END LICENSE BLOCK *****
*/

if (!Zotero.Sync.Data) {
	Zotero.Sync.Data = {};
}

Zotero.Sync.Data.Local = {
	_loginManagerHost: 'chrome://zotero',
	_loginManagerRealm: 'Zotero Web API',
	_lastSyncTime: null,
	_lastClassicSyncTime: null,
	
	init: Zotero.Promise.coroutine(function* () {
		yield this._loadLastSyncTime();
		if (!_lastSyncTime) {
			yield this._loadLastClassicSyncTime();
		}
	}),
	
	
	getAPIKey: function () {
		Zotero.debug("Getting API key");
		var login = this._getAPIKeyLoginInfo();
		return login ? login.password : "";
	},
	
	
	setAPIKey: function (apiKey) {
		var loginManager = Components.classes["@mozilla.org/login-manager;1"]
			.getService(Components.interfaces.nsILoginManager);
		
		var oldLoginInfo = this._getAPIKeyLoginInfo();
		
		// Clear old login
		if ((!apiKey || apiKey === "")) {
			if (oldLoginInfo) {
				Zotero.debug("Clearing old API key");
				loginManager.removeLogin(oldLoginInfo);
			}
			return;
		}
		
		var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
				Components.interfaces.nsILoginInfo, "init");
		var loginInfo = new nsLoginInfo(
			this._loginManagerHost,
			null,
			this._loginManagerRealm,
			'API Key',
			apiKey,
			'',
			''
		);
		if (!oldLoginInfo) {
			Zotero.debug("Setting API key");
			loginManager.addLogin(loginInfo);
		}
		else {
			Zotero.debug("Replacing API key");
			loginManager.modifyLogin(oldLoginInfo, loginInfo);
		}
	},
	
	
	/**
	 * @return {nsILoginInfo|false}
	 */
	_getAPIKeyLoginInfo: function () {
		try {
			var loginManager = Components.classes["@mozilla.org/login-manager;1"]
				.getService(Components.interfaces.nsILoginManager);
			var logins = loginManager.findLogins(
				{},
				this._loginManagerHost,
				null,
				this._loginManagerRealm
			);
		}
		catch (e) {
			Zotero.logError(e);
			if (Zotero.isStandalone) {
				var msg = Zotero.getString('sync.error.loginManagerCorrupted1', Zotero.appName) + "\n\n"
					+ Zotero.getString('sync.error.loginManagerCorrupted2', [Zotero.appName, Zotero.appName]);
			}
			else {
				var msg = Zotero.getString('sync.error.loginManagerInaccessible') + "\n\n"
					+ Zotero.getString('sync.error.checkMasterPassword', Zotero.appName) + "\n\n"
					+ Zotero.getString('sync.error.corruptedLoginManager', Zotero.appName);
			}
			var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
				.getService(Components.interfaces.nsIPromptService);
			ps.alert(null, Zotero.getString('general.error'), msg);
			return false;
		}
		
		// Get API from returned array of nsILoginInfo objects
		return logins.length ? logins[0] : false;
	},
	
	
	getLegacyPassword: function (username) {
		var loginManagerHost = 'chrome://zotero';
		var loginManagerRealm = 'Zotero Sync Server';
		
		Zotero.debug('Getting Zotero sync password');
		
		var loginManager = Components.classes["@mozilla.org/login-manager;1"]
			.getService(Components.interfaces.nsILoginManager);
		try {
			var logins = loginManager.findLogins({}, loginManagerHost, null, loginManagerRealm);
		}
		catch (e) {
			Zotero.logError(e);
			return '';
		}
		
		// Find user from returned array of nsILoginInfo objects
		for (let i = 0; i < logins.length; i++) {
			if (logins[i].username == username) {
				return logins[i].password;
			}
		}
		
		// Pre-4.0.28.5 format, broken for findLogins and removeLogin in Fx41,
		var logins = loginManager.findLogins({}, loginManagerHost, "", null);
		for (let i = 0; i < logins.length; i++) {
			if (logins[i].username == username
					&& logins[i].formSubmitURL == "Zotero Sync Server") {
				return logins[i].password;
			}
		}
		return '';
	},
	
	
	removeLegacyLogins: function () {
		var loginManagerHost = 'chrome://zotero';
		var loginManagerRealm = 'Zotero Sync Server';
		
		Zotero.debug('Removing legacy Zotero sync credentials (api key acquired)');
		
		var loginManager = Components.classes["@mozilla.org/login-manager;1"]
			.getService(Components.interfaces.nsILoginManager);
		try {
			var logins = loginManager.findLogins({}, loginManagerHost, null, loginManagerRealm);
		}
		catch (e) {
			Zotero.logError(e);
			return '';
		}
		
		// Remove all legacy users
		for (let login of logins) {
			loginManager.removeLogin(login);
		}
		// Remove the legacy pref
		Zotero.Pref.clear('sync.server.username');
	},
	
	
	getLastSyncTime: function () {
		if (_lastSyncTime === null) {
			throw new Error("Last sync time not yet loaded");
		}
		return _lastSyncTime;
	},
	
	
	/**
	 * @return {Promise}
	 */
	updateLastSyncTime: function () {
		_lastSyncTime = new Date();
		return Zotero.DB.queryAsync(
			"REPLACE INTO version (schema, version) VALUES ('lastsync', ?)",
			Math.round(_lastSyncTime.getTime() / 1000)
		);
	},
	
	
	_loadLastSyncTime: Zotero.Promise.coroutine(function* () {
		var sql = "SELECT version FROM version WHERE schema='lastsync'";
		var lastsync = yield Zotero.DB.valueQueryAsync(sql);
		_lastSyncTime = (lastsync ? new Date(lastsync * 1000) : false);
	}),
	
	
	/**<commit_msg>Fix typo<commit_after>/*
    ***** BEGIN LICENSE BLOCK *****
    
    Copyright © 2014 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org
    
    This file is part of Zotero.
    
    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
    
    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
    
    ***** END LICENSE BLOCK *****
*/

if (!Zotero.Sync.Data) {
	Zotero.Sync.Data = {};
}

Zotero.Sync.Data.Local = {
	_loginManagerHost: 'chrome://zotero',
	_loginManagerRealm: 'Zotero Web API',
	_lastSyncTime: null,
	_lastClassicSyncTime: null,
	
	init: Zotero.Promise.coroutine(function* () {
		yield this._loadLastSyncTime();
		if (!_lastSyncTime) {
			yield this._loadLastClassicSyncTime();
		}
	}),
	
	
	getAPIKey: function () {
		Zotero.debug("Getting API key");
		var login = this._getAPIKeyLoginInfo();
		return login ? login.password : "";
	},
	
	
	setAPIKey: function (apiKey) {
		var loginManager = Components.classes["@mozilla.org/login-manager;1"]
			.getService(Components.interfaces.nsILoginManager);
		
		var oldLoginInfo = this._getAPIKeyLoginInfo();
		
		// Clear old login
		if ((!apiKey || apiKey === "")) {
			if (oldLoginInfo) {
				Zotero.debug("Clearing old API key");
				loginManager.removeLogin(oldLoginInfo);
			}
			return;
		}
		
		var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
				Components.interfaces.nsILoginInfo, "init");
		var loginInfo = new nsLoginInfo(
			this._loginManagerHost,
			null,
			this._loginManagerRealm,
			'API Key',
			apiKey,
			'',
			''
		);
		if (!oldLoginInfo) {
			Zotero.debug("Setting API key");
			loginManager.addLogin(loginInfo);
		}
		else {
			Zotero.debug("Replacing API key");
			loginManager.modifyLogin(oldLoginInfo, loginInfo);
		}
	},
	
	
	/**
	 * @return {nsILoginInfo|false}
	 */
	_getAPIKeyLoginInfo: function () {
		try {
			var loginManager = Components.classes["@mozilla.org/login-manager;1"]
				.getService(Components.interfaces.nsILoginManager);
			var logins = loginManager.findLogins(
				{},
				this._loginManagerHost,
				null,
				this._loginManagerRealm
			);
		}
		catch (e) {
			Zotero.logError(e);
			if (Zotero.isStandalone) {
				var msg = Zotero.getString('sync.error.loginManagerCorrupted1', Zotero.appName) + "\n\n"
					+ Zotero.getString('sync.error.loginManagerCorrupted2', [Zotero.appName, Zotero.appName]);
			}
			else {
				var msg = Zotero.getString('sync.error.loginManagerInaccessible') + "\n\n"
					+ Zotero.getString('sync.error.checkMasterPassword', Zotero.appName) + "\n\n"
					+ Zotero.getString('sync.error.corruptedLoginManager', Zotero.appName);
			}
			var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
				.getService(Components.interfaces.nsIPromptService);
			ps.alert(null, Zotero.getString('general.error'), msg);
			return false;
		}
		
		// Get API from returned array of nsILoginInfo objects
		return logins.length ? logins[0] : false;
	},
	
	
	getLegacyPassword: function (username) {
		var loginManagerHost = 'chrome://zotero';
		var loginManagerRealm = 'Zotero Sync Server';
		
		Zotero.debug('Getting Zotero sync password');
		
		var loginManager = Components.classes["@mozilla.org/login-manager;1"]
			.getService(Components.interfaces.nsILoginManager);
		try {
			var logins = loginManager.findLogins({}, loginManagerHost, null, loginManagerRealm);
		}
		catch (e) {
			Zotero.logError(e);
			return '';
		}
		
		// Find user from returned array of nsILoginInfo objects
		for (let i = 0; i < logins.length; i++) {
			if (logins[i].username == username) {
				return logins[i].password;
			}
		}
		
		// Pre-4.0.28.5 format, broken for findLogins and removeLogin in Fx41,
		var logins = loginManager.findLogins({}, loginManagerHost, "", null);
		for (let i = 0; i < logins.length; i++) {
			if (logins[i].username == username
					&& logins[i].formSubmitURL == "Zotero Sync Server") {
				return logins[i].password;
			}
		}
		return '';
	},
	
	
	removeLegacyLogins: function () {
		var loginManagerHost = 'chrome://zotero';
		var loginManagerRealm = 'Zotero Sync Server';
		
		Zotero.debug('Removing legacy Zotero sync credentials (api key acquired)');
		
		var loginManager = Components.classes["@mozilla.org/login-manager;1"]
			.getService(Components.interfaces.nsILoginManager);
		try {
			var logins = loginManager.findLogins({}, loginManagerHost, null, loginManagerRealm);
		}
		catch (e) {
			Zotero.logError(e);
			return '';
		}
		
		// Remove all legacy users
		for (let login of logins) {
			loginManager.removeLogin(login);
		}
		// Remove the legacy pref
		Zotero.Prefs.clear('sync.server.username');
	},
	
	
	getLastSyncTime: function () {
		if (_lastSyncTime === null) {
			throw new Error("Last sync time not yet loaded");
		}
		return _lastSyncTime;
	},
	
	
	/**
	 * @return {Promise}
	 */
	updateLastSyncTime: function () {
		_lastSyncTime = new Date();
		return Zotero.DB.queryAsync(
			"REPLACE INTO version (schema, version) VALUES ('lastsync', ?)",
			Math.round(_lastSyncTime.getTime() / 1000)
		);
	},
	
	
	_loadLastSyncTime: Zotero.Promise.coroutine(function* () {
		var sql = "SELECT version FROM version WHERE schema='lastsync'";
		var lastsync = yield Zotero.DB.valueQueryAsync(sql);
		_lastSyncTime = (lastsync ? new Date(lastsync * 1000) : false);
	}),
	
	
	/**
{'fraction_non_alphanumeric': 0.12858589267634155, 'fraction_numerical': 0.005231184610192373, 'mean_word_length': 3.429372197309417, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3008334', 'n_tokens_mistral': 4468, 'n_tokens_neox': 3859, 'n_words': 909}