/*
*
* Panbox - encryption for cloud storage
* Copyright (C) 2014-2015 by Fraunhofer SIT and Sirrix AG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additonally, third party code may be provided with notices and open source
* licenses from communities and third parties that govern the use of those
* portions, and any licenses granted hereunder do not alter any rights and
* obligations you may have under such open source licenses, however, the
* disclaimer of warranty and limitation of liability provisions of the GPLv3
* will apply to all the product.
*
*/
package org.panbox.desktop.common;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.SplashScreen;
import java.awt.TrayIcon.MessageType;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.ServiceRecord;
import javax.crypto.Cipher;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.panbox.OS;
import org.panbox.Settings;
import org.panbox.core.Utils;
import org.panbox.core.crypto.CryptCore;
import org.panbox.core.crypto.KeyConstants;
import org.panbox.core.csp.CSPAdapterFactory;
import org.panbox.core.csp.StorageBackendType;
import org.panbox.core.devicemgmt.DeviceType;
import org.panbox.core.exception.ShareMetaDataException;
import org.panbox.core.identitymgmt.AbstractAddressbookManager;
import org.panbox.core.identitymgmt.AbstractIdentity;
import org.panbox.core.identitymgmt.AbstractIdentityManager;
import org.panbox.core.identitymgmt.CloudProviderInfo;
import org.panbox.core.identitymgmt.Identity;
import org.panbox.core.identitymgmt.PanboxContact;
import org.panbox.core.identitymgmt.SimpleAddressbook;
import org.panbox.core.identitymgmt.VCardProtector;
import org.panbox.core.identitymgmt.exceptions.ContactExistsException;
import org.panbox.core.pairing.PAKCorePairingHandler;
import org.panbox.core.pairing.PAKCorePairingHandler.PairingType;
import org.panbox.core.pairing.PAKCorePairingRequester;
import org.panbox.core.pairing.PairingInformation;
import org.panbox.core.pairing.PairingNotificationReceiver;
import org.panbox.core.pairing.PairingNotificationReceiver.PairingResult;
import org.panbox.core.pairing.bluetooth.BluetoothPairingInformation;
import org.panbox.core.pairing.bluetooth.PAKBluetoothPairingRequester;
import org.panbox.core.pairing.bluetooth.RemoteDeviceDiscovery;
import org.panbox.core.pairing.file.PanboxFilePairingUtils;
import org.panbox.core.pairing.file.PanboxFilePairingUtils.PanboxFilePairingLoadReturnContainer;
import org.panbox.core.pairing.file.PanboxFilePairingUtils.PanboxFilePairingWriteReturnContainer;
import org.panbox.core.pairing.network.NetworkPairingInformation;
import org.panbox.core.pairing.network.PAKNetworkPairingRequester;
import org.panbox.desktop.common.csp.gui.dropbox.DropboxWizardDialog;
import org.panbox.desktop.common.devicemgmt.DeviceManagerException;
import org.panbox.desktop.common.devicemgmt.DeviceManagerImpl;
import org.panbox.desktop.common.gui.OperationAbortedException;
import org.panbox.desktop.common.gui.PairOrCreateDialog;
import org.panbox.desktop.common.gui.PairThisDeviceDialog;
import org.panbox.desktop.common.gui.PanboxClientGUI;
import org.panbox.desktop.common.gui.PasswordEnterDialog;
import org.panbox.desktop.common.gui.PasswordEnterDialog.PermissionType;
import org.panbox.desktop.common.gui.PleaseWaitDialog;
import org.panbox.desktop.common.gui.SetupWizardDialog;
import org.panbox.desktop.common.gui.TextContextMenu;
import org.panbox.desktop.common.gui.addressbook.ContactListModel;
import org.panbox.desktop.common.gui.addressbook.ContactShareParticipant;
import org.panbox.desktop.common.gui.addressbook.PanboxGUIContact;
import org.panbox.desktop.common.gui.addressbook.PanboxMyContact;
import org.panbox.desktop.common.gui.devices.DeviceListModel;
import org.panbox.desktop.common.gui.devices.DeviceShareParticipant;
import org.panbox.desktop.common.gui.devices.PanboxDevice;
import org.panbox.desktop.common.gui.shares.PanboxShare;
import org.panbox.desktop.common.gui.shares.PanboxSharePermission;
import org.panbox.desktop.common.gui.shares.ShareListModel;
import org.panbox.desktop.common.gui.shares.ShareParticipantListModel;
import org.panbox.desktop.common.identitymgmt.sqlightimpl.AddressbookManager;
import org.panbox.desktop.common.identitymgmt.sqlightimpl.IdentityManager;
import org.panbox.desktop.common.pairing.callables.BluetoothPairingCallable;
import org.panbox.desktop.common.pairing.callables.NetworkPairingCallable;
import org.panbox.desktop.common.sharemgmt.CreateShareNotAllowedException;
import org.panbox.desktop.common.sharemgmt.IPanboxService;
import org.panbox.desktop.common.sharemgmt.IShareManager;
import org.panbox.desktop.common.sharemgmt.ShareDoesNotExistException;
import org.panbox.desktop.common.sharemgmt.ShareInaccessibleException;
import org.panbox.desktop.common.sharemgmt.ShareManagerException;
import org.panbox.desktop.common.sharemgmt.ShareManagerImpl;
import org.panbox.desktop.common.sharemgmt.ShareNameAlreadyExistsException;
import org.panbox.desktop.common.sharemgmt.SharePathAlreadyExistsException;
import org.panbox.desktop.common.sharemgmt.ShareWatchService;
import org.panbox.desktop.common.sharemgmt.UnknownOwnerException;
import org.panbox.desktop.common.utils.DesktopApi;
import org.panbox.desktop.common.utils.FileUtils;
import org.panbox.desktop.common.utils.SingleInstanceLock;
import org.panbox.desktop.common.utils.SingleInstanceLockWMIC;
import org.panbox.desktop.common.utils.VersionUtils;
import org.panbox.desktop.common.vfs.backend.dropbox.DropboxAdapterFactory;
import org.panbox.desktop.common.vfs.backend.dropbox.DropboxClientIntegration;
import ezvcard.VCard;
public abstract class PanboxClient {
protected static final Logger logger = Logger.getLogger("org.panbox");
protected static final ResourceBundle bundle = ResourceBundle.getBundle("org.panbox.desktop.common.gui.Messages",
Settings.getInstance().getLocale());
protected final ShareListModel shareList = new ShareListModel();
protected final ContactListModel contactList = new ContactListModel();
protected final DeviceListModel deviceList = new DeviceListModel();
public final IShareManager shareManager;
public final AbstractIdentityManager identityManager;
public final AddressbookManager addressbookManager;
public final DeviceManagerImpl deviceManager;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final ScheduledExecutorService canceller = Executors.newSingleThreadScheduledExecutor();
private Future<?> task = null;
private ScheduledFuture<Void> scheduledTask = null;
private AbstractIdentity myId;
protected SplashScreen splash;
protected Graphics2D splashGraphics;
enum SplashScreenState {
CHECK_ALREADY_RUNNING, LOADING_IDENTITY, LOADING_SHARES, LOADING_DEVICES, LOADING_CONTACTS, ABOUT
};
protected void renderSplashFrame(SplashScreenState state) {
if (splash != null && splashGraphics != null) {
try {
String splashLoading = null;
if (state == SplashScreenState.CHECK_ALREADY_RUNNING) {
splashLoading = bundle.getString("splashscreen.checkingInstance");
} else if (state == SplashScreenState.LOADING_IDENTITY) {
splashLoading = bundle.getString("splashscreen.loadingIdentity");
} else if (state == SplashScreenState.LOADING_SHARES) {
splashLoading = bundle.getString("splashscreen.loadingShares");
} else if (state == SplashScreenState.LOADING_DEVICES) {
splashLoading = bundle.getString("splashscreen.loadingDevices");
} else if (state == SplashScreenState.LOADING_CONTACTS) {
splashLoading = bundle.getString("splashscreen.loadingContacts");
} else if (state == SplashScreenState.ABOUT) {
splashLoading = "Version: "
+ new String(PanboxDesktopConstants.PANBOX_VERSION, StandardCharsets.UTF_8);
} else {
splashLoading = "Loading...";
}
splashGraphics.setComposite(AlphaComposite.Clear);
splashGraphics.fillRect(0, 0, 500, 335);
splashGraphics.setPaintMode();
splashGraphics.setColor(Color.BLACK);
splashGraphics.drawString(splashLoading, 120, 250);
splash.update();
} catch (IllegalStateException e) {
// Splash screen has been closed already by showing dialog!
}
}
}
/**
* initializes global CCP context menu. See {@link TextContextMenu}.
*/
private void initGlobalContextMenu() {
UIManager.addAuxiliaryLookAndFeel(new LookAndFeel() {
private final UIDefaults defaults = new UIDefaults() {
private static final long serialVersionUID = 4521137314455023162L;
@Override
public javax.swing.plaf.ComponentUI getUI(JComponent c) {
if (c instanceof javax.swing.text.JTextComponent) {
if (c.getClientProperty(this) == null) {
c.setComponentPopupMenu(TextContextMenu.INSTANCE);
c.putClientProperty(this, Boolean.TRUE);
}
}
return null;
}
};
@Override
public UIDefaults getDefaults() {
return defaults;
};
@Override
public String getID() {
return "TextContextMenu";
}
@Override
public String getName() {
return getID();
}
@Override
public String getDescription() {
return getID();
}
@Override
public boolean isNativeLookAndFeel() {
return false;
}
@Override
public boolean isSupportedLookAndFeel() {
return true;
}
});
}
public PanboxClient(IPanboxService service) throws Exception {
// Set language to de_DE or en_US since only those two are supported for
// now!
Settings settings = Settings.getInstance();
if (settings.getLocale().getLanguage().equals(new Locale("de").getLanguage())) {
settings.setLanguage("de_DE");
} else {
settings.setLanguage("en_US");
}
splash = SplashScreen.getSplashScreen();
if (splash == null) {
splashGraphics = null;
System.out.println("SplashScreen.getSplashScreen() returned null");
} else {
splashGraphics = splash.createGraphics();
if (splashGraphics == null) {
System.out.println("g is null");
}
}
renderSplashFrame(SplashScreenState.CHECK_ALREADY_RUNNING);
// init context menu early, otherwise it may not be available for setup
// dialogs
initGlobalContextMenu();
// some health tests
if (!checkSupportedKeySize()) {
logger.error("Symmetric key of " + KeyConstants.SYMMETRIC_KEY_SIZE
+ " not supported by this JVM. Application will exit...");
JOptionPane.showMessageDialog(null, bundle.getString("client.startup.invalidkeysize"),
bundle.getString("client.startup.error.title"), JOptionPane.ERROR_MESSAGE);
System.exit(DesktopApi.EXIT_INVALID_KEY_LENGTH);
}
if (OS.getOperatingSystem().isLinux()) {
try {
if (!SingleInstanceLock.lock()) {
if (checkPanboxProcessesRunning()) {
// panbox processes running in background
JOptionPane.showMessageDialog(null, bundle.getString("PanboxClient.alreadyRunning"));
System.exit(DesktopApi.EXIT_ERR_ALREADY_RUNNING);
} else {
// file-lock detected and no panbox processes running in
// background -> system crash
if (panboxMounted()) {
int ret = JOptionPane.showConfirmDialog(null,
bundle.getString("PanboxClient.crashDetected"),
bundle.getString("PanboxClient.unmountPanbox"), JOptionPane.YES_NO_OPTION);
if (ret == JOptionPane.YES_OPTION) {
boolean umount = mountPointHandler();
if (!umount) {
JOptionPane.showMessageDialog(null,
MessageFormat.format(bundle.getString("PanboxClient.unmountNotPossible"),
Settings.getInstance().getMountDir(),
Settings.getInstance().getMountDir()));
System.exit(DesktopApi.EXIT_CRASH_DETECTED);
}
} else if (ret == JOptionPane.NO_OPTION || ret == JOptionPane.CLOSED_OPTION) {
JOptionPane.showMessageDialog(null,
MessageFormat.format(bundle.getString("PanboxClient.unmountDirectory"),
Settings.getInstance().getMountDir()));
System.exit(DesktopApi.EXIT_CRASH_DETECTED);
}
}
// SingleInstanceLock.forceUnlock();
}
}
} catch (IOException e) {
logger.error("PanboxClient : Exception thrown while determining if program is already running: ", e);
System.exit(DesktopApi.EXIT_ERR_UNKNOWN);
}
} else {
// Windows uses WMIC implementation
if (!SingleInstanceLockWMIC.lock()) {
JOptionPane.showMessageDialog(null, bundle.getString("PanboxClient.alreadyRunning"));
System.exit(DesktopApi.EXIT_ERR_ALREADY_RUNNING);
}
}
renderSplashFrame(SplashScreenState.LOADING_IDENTITY);
this.addressbookManager = new AddressbookManager();
this.identityManager = IdentityManager.getInstance();
this.identityManager.init(addressbookManager);
this.myId = identityManager.loadMyIdentity(new SimpleAddressbook());
this.deviceManager = DeviceManagerImpl.getInstance();
this.shareManager = ShareManagerImpl.getInstance(service);
// create identity if necessary
boolean justInited = false;
if (null == this.myId) {
initIdentity();
justInited = true;
}
registerShutdownHook();
// os-specific actions
this.deviceManager.setIdentity(myId);
this.shareManager.setIdentity(myId);
// init share watch service
this.shareWatchService = new ShareWatchService(this);
this.shareWatchService.start();
if (justInited) {
// reinit splash screen for another try
splash = SplashScreen.getSplashScreen();
if (splash == null) {
splashGraphics = null;
System.out.println("SplashScreen.getSplashScreen() returned null");
} else {
splashGraphics = splash.createGraphics();
if (splashGraphics == null) {
System.out.println("g is null");
}
}
}
renderSplashFrame(SplashScreenState.LOADING_SHARES);
// fill models, but catch exceptions to still allow for application
// startup (and the user to save contacts, data, ...)
try {
refreshShareListModel();
} catch (Exception e) {
logger.error("Unable to initialize share list!", e);
JOptionPane.showMessageDialog(getMainWindow(),
bundle.getString("PanboxClient.initialLoadingOfSharesFailed"), bundle.getString("client.error"),
JOptionPane.ERROR_MESSAGE);
}
renderSplashFrame(SplashScreenState.LOADING_DEVICES);
try {
refreshDeviceListModel();
} catch (Exception e) {
logger.error(bundle.getString("PanboxClient.unableInitDeviceList"), e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.initialDeviceSetupFailed"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
renderSplashFrame(SplashScreenState.LOADING_CONTACTS);
try {
refreshContactListModel();
} catch (Exception e) {
logger.error("Unable to initialize contact list!", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.initialContactSetupFailed"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
setup();
}
private ShareWatchService shareWatchService;
public ShareWatchService getShareWatchService() {
return shareWatchService;
}
public abstract void restartApplication() throws IOException;
// returns true if processes running
abstract protected boolean checkPanboxProcessesRunning();
// returns true if panbox-directory successful unmounted
abstract protected boolean mountPointHandler();
// returns true if panbox-directory mounted
abstract protected boolean panboxMounted();
public ContactListModel getContactList() {
return contactList;
}
public DeviceListModel getDeviceList() {
return deviceList;
}
public ShareListModel getShareList() {
return shareList;
}
public ShareParticipantListModel getShareParticipantListForShare(PanboxShare share) {
return share.generatePermissionsModel(myId);
}
public AbstractIdentity getIdentity() {
return myId;
}
// ==================== INIT FUNCTIONS ====================
public void refreshDeviceShareList() {
try {
refreshShareListModel();
mainWindow.refreshDeviceShareList();
} catch (Exception e) {
e.printStackTrace();
}
}
public void refreshAddressbookList() {
refreshContactListModel();
mainWindow.refreshAddressbookList();
}
/**
* checks this shares device lists.
*
* @param share
* @return
*/
public PanboxShare checkShareIntegrity(PanboxShare share) {
if (share.isOwner()) {
Collection<PanboxContact> coll = shareManager.checkShareDeviceListIntegrity(share);
char[] password = null;
PanboxShare retShare = null;
try {
for (Iterator<PanboxContact> it = coll.iterator(); it.hasNext();) {
PanboxContact contact = (PanboxContact) it.next();
int ret = JOptionPane.showConfirmDialog(getMainWindow(),
MessageFormat.format(bundle.getString("client.checkShareIntegrity.devicelistreset"),
contact.getEmail()),
bundle.getString("client.warn"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (ret == JOptionPane.YES_OPTION) {
if (password == null) {
password = PasswordEnterDialog.invoke(PermissionType.SHARE);
}
logger.info("Will try to reinitialize share device list for contact " + contact.getEmail()
+ " in share " + share.getName() + " ...");
try {
retShare = shareManager.resetShareInvitation(share, contact.getEmail(), password);
} catch (UnrecoverableKeyException e) {
logger.error("Re-initializing share device list failed due to unrecoverable key!", e);
JOptionPane.showMessageDialog(getMainWindow(),
bundle.getString("PanboxClient.unableToRecoverKeys"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (ShareManagerException | ShareMetaDataException e) {
logger.error("Re-initializing share device list failed!", e);
JOptionPane.showMessageDialog(getMainWindow(),
MessageFormat.format(
bundle.getString("client.checkShareIntegrity.devicelistresetfailed"),
contact.getEmail()),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(getMainWindow(),
MessageFormat.format(bundle.getString("client.checkShareIntegrity.devicelistnotreset"),
contact.getEmail()),
bundle.getString("client.warn"), JOptionPane.WARNING_MESSAGE);
}
}
} finally {
if (password != null) {
Utils.eraseChars(password);
}
}
return retShare;
}
return null;
}
public void refreshShareListModel() throws ShareManagerException {
// remove old elements
shareList.removeAllElements();
// insert new elements
List<String> shareNames = new ArrayList<String>();
try {
shareNames = shareManager.getInstalledShares();
} catch (ShareManagerException sme) {
logger.error("Could not obtain list of installed share names! No shares will be loaded ...", sme);
throw sme;
}
boolean succ = true;
for (String shareName : shareNames) {
try {
PanboxShare share = shareManager.getShareForName(shareName);
PanboxShare tmp;
if ((tmp = checkShareIntegrity(share)) != null) {
share = tmp;
}
shareWatchService.registerShare(share);
shareList.addElement(share);
} catch (ShareDoesNotExistException e) {
logger.error("Share does not exist in the local database! Sharename: " + shareName, e);
succ = false;
} catch (ShareInaccessibleException e) {
logger.error("Unable to access backend diretory for share " + shareName
+ ". Trying to remove share from local database", e);
try {
shareManager.removeShareFromDB(shareName);
logger.info("Share entry " + shareName + " has been removed from local database!");
} catch (ShareManagerException e2) {
logger.error("Unable to remove share entry " + shareName + " from local database!");
}
succ = false;
}
catch (UnrecoverableKeyException | ShareMetaDataException e) {
logger.error("Unable to load share " + shareName + ". Share metadata may be corrupt", e);
succ = false;
}
}
if (!succ) {
throw new ShareManagerException("Encountered error while loading one or more shares.");
}
}
public void refreshContactListModel() {
// remove old elements
contactList.removeAllElements();
// create & insert own identity into data model
contactList.addElement(new PanboxMyContact(myId));
// load & add remaining contacts
Collection<org.panbox.core.identitymgmt.PanboxContact> contacts = myId.getAddressbook().getContacts();
for (org.panbox.core.identitymgmt.PanboxContact c : contacts) {
contactList.addElement(new PanboxGUIContact(c));
}
}
public void refreshDeviceListModel() throws DeviceManagerException {
// remove old elements
deviceList.removeAllElements();
// insert new elements
List<PanboxDevice> devices = deviceManager.getDeviceList();
for (PanboxDevice d : devices) {
deviceList.addElement(d);
}
}
/**
* Method handles os-independent application initialization actions, then
* calls abstract os-specific startup handlers.
*
* @throws Exception
*/
@Deprecated
protected void initApplication() throws Exception {
// create identity if necessary
if (null == this.myId) {
initIdentity();
}
registerShutdownHook();
// os-specific actions
setup();
}
protected PanboxClientGUI mainWindow;
protected void registerMainwindow(PanboxClientGUI mainwindow) {
mainWindow = mainwindow;
}
/**
* @return the mainWindow
*/
public PanboxClientGUI getMainWindow() {
return mainWindow;
}
/**
* Method for handling all actions to be taken upon application startup. To
* be defined in os-specifc implementations
*
* @throws Exception
*/
abstract protected void setup() throws Exception;
/**
* Method for handling all actions to be taken upon application shutdown. To
* be defined in os-specifc implementations
*
* @throws Exception
*/
abstract protected void shutdown() throws Exception;
public void initIdentity() throws Exception {
if (this.myId != null) {
throw new IllegalStateException(
"You can not initialize the identityManager if it has been initialized before.");
}
while (this.myId == null) {
PairOrCreateDialog pocDialog = new PairOrCreateDialog(null, true);
pocDialog.setLocationRelativeTo(null);
pocDialog.setVisible(true);
if (pocDialog.isPairing() == null) {
JOptionPane.showMessageDialog(null, bundle.getString("client.setupwizard.aborted.msg"),
bundle.getString("client.warn"), JOptionPane.WARNING_MESSAGE);
logger.debug("Panbox setup wizard has been aborted.");
throw new OperationAbortedException("The setup wizard has been aborted.");
} else if (pocDialog.isPairing()) {
PairThisDeviceDialog pairDialog = new PairThisDeviceDialog(null);
pairDialog.setVisible(true);
File pFile = pairDialog.getPairingFile();
if (pFile != null) {
loadPairingFile(pFile);
} else {
try {
String pairingPassword = pairDialog.getPairingPassword();
runGeneralPairingRequester(pairingPassword);
} catch (OperationAbortedException ex) {
logger.debug("Panbox pairing wizard has been aborted.");
}
}
this.identityManager.init(addressbookManager);
this.myId = identityManager.loadMyIdentity(new SimpleAddressbook());
} else {
SetupWizardDialog dialog = new SetupWizardDialog(null, true);
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
if (dialog.wasCanceled()) {
logger.debug("Panbox setup wizard has been aborted.");
continue;
}
// show please wait dialog
PleaseWaitDialog loadDialog = new PleaseWaitDialog(null, bundle.getString("PanboxGeneratingIdentity"));
loadDialog.setLocationRelativeTo(null);
loadDialog.setVisible(true);
// create identity and initial keypairs
Identity id = new Identity(new SimpleAddressbook(), dialog.getEmail(), dialog.getFirstname(),
dialog.getLastname());
KeyPair ownerKeySign = CryptCore.generateKeypair();
KeyPair ownerKeyEnc = CryptCore.generateKeypair();
KeyPair deviceKey = CryptCore.generateKeypair();
char[] password = dialog.getPassword();
String deviceName = dialog.getDevicename();
boolean protectDeviceKey = dialog.isProtectDeviceKey();
id.setOwnerKeySign(ownerKeySign, password);
id.setOwnerKeyEnc(ownerKeyEnc, password);
Settings.getInstance().setPairingType(PairingType.MASTER);
id.addDeviceKey(deviceKey, dialog.getDevicename());
Settings.getInstance().setDeviceName(deviceName);
identityManager.init(addressbookManager);
identityManager.storeMyIdentity(id);
try {
deviceManager.setIdentity(id);
if (protectDeviceKey) {
Settings.getInstance().setProtectedDeviceKey(true);
// use identity password for protection
deviceManager.addThisDevice(deviceName, deviceKey, DeviceType.DESKTOP, password);
} else {
Settings.getInstance().setProtectedDeviceKey(false);
// use well known secret for protection
deviceManager.addThisDevice(deviceName, deviceKey, DeviceType.DESKTOP);
}
refreshDeviceListModel();
} catch (DeviceManagerException ex) {
// Simply ignore this for now!
}
this.myId = id;
// hide please wait dialog
loadDialog.dispose();
}
}
// update share manager identity to new identity!
shareManager.setIdentity(myId);
refreshContactListModel();
// Check if Dropbox is installed. If yes -> Show wizard dialog!
DropboxAdapterFactory dropboxAdapterFactory = (DropboxAdapterFactory) CSPAdapterFactory
.getInstance(StorageBackendType.DROPBOX);
DropboxClientIntegration dropboxClientIntegration = (DropboxClientIntegration) dropboxAdapterFactory
.getClientAdapter();
if (dropboxClientIntegration.isClientInstalled()) {
DropboxWizardDialog dialog = new DropboxWizardDialog(mainWindow, true);
dialog.setAccessToken(Settings.getInstance().getDropboxAccessToken());
if (!Settings.getInstance().getDropboxSynchronizationDir().equals("")) {
dialog.setDropboxSyncDirPath(Settings.getInstance().getDropboxSynchronizationDir());
}
dialog.setVisible(true);
if (dialog.getAccessToken() != null && dialog.getDropboxSyncDirPath() != null) {
Settings.getInstance().setDropboxAccessToken(dialog.getAccessToken());
Settings.getInstance().setDropboxSynchronizationDir(dialog.getDropboxSyncDirPath());
}
}
// Show message about the successful client setup!
JOptionPane.showMessageDialog(null, bundle.getObject("client.setupwizard.success.msg"),
bundle.getString("client.setupwizard.title"), JOptionPane.INFORMATION_MESSAGE);
}
// ==================== CLIENT SPECIFIC EVENTS ====================
// Share list events
public void addShare(PanboxShare share, char[] password) {
try {
share = ShareManagerImpl.getInstance().addNewShare(share, password);
PanboxShare tmp;
if ((tmp = checkShareIntegrity(share)) != null) {
share = tmp;
}
shareWatchService.registerShare(share);
shareList.addElement(share);
informAddShare(share); // inform OS specific implementation about
// this event!
} catch (CreateShareNotAllowedException e) {
logger.error("Slave tried to create a new share!", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("client.slaveCreateShareNotAllowed"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (UnrecoverableKeyException e) {
logger.error("Unable to recover key!", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.unableToRecoverKeys"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (ShareMetaDataException e) {
logger.error("Error in share metadata", e);
JOptionPane.showMessageDialog(getMainWindow(),
bundle.getString("PanboxClient.errorWhileAccessingShareMetadata"), bundle.getString("client.error"),
JOptionPane.ERROR_MESSAGE);
} catch (ShareNameAlreadyExistsException e) {
logger.error("Share name already exists", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.shareNameAlreadyExists"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (SharePathAlreadyExistsException e) {
logger.error("Share path has already been configured!", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.sharePathAlreadyConfigured"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (UnknownOwnerException ex) {
logger.error("PanboxClient : addShare : UnknownOwnerException occured: ", ex);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.shareOwnerUnknown"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (ShareManagerException ex) {
logger.error("PanboxClient : addShare : ShareManagerException occured: ", ex);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("client.error.addShare"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
logger.error("PanboxClient : addShare : Exception occured: ", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("client.error.addShare"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
}
public abstract void informAddShare(PanboxShare share) throws Exception;
public void removeShare(PanboxShare share) {
try {
ShareManagerImpl.getInstance().removeShare(share.getName(), share.getPath(), share.getType());
shareWatchService.removeShare(share);
shareList.removeElement(share);
informRemoveShare(share); // inform OS specific implementation
// about
// // this event!
} catch (ShareManagerException ex) {
logger.error("PanboxClient : removeShare : ShareManagerException occured: ", ex);
JOptionPane.showMessageDialog(null, bundle.getString("client.error.removeShare"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
logger.error("PanboxClient : removeShare : Exception occured: ", e);
}
}
public abstract void informRemoveShare(PanboxShare share) throws Exception;
// Share participants list events
public void removePermissionFromShare(PanboxShare share, PanboxSharePermission permission, char[] password) {
// first panbox release does not support permission removal. disabled
// for now
// if (permission instanceof DeviceShareParticipant) {
// DeviceShareParticipant dPermission = (DeviceShareParticipant)
// permission;
//
// PanboxDevice device = dPermission.getDevice();
//
// try {
// share = ShareManagerImpl.getInstance().removeDevicePermission(
// share, device.getDeviceName(), password);
// } catch (ShareDoesNotExistException e) {
// logger.error("Share not found!", e);
// JOptionPane.showMessageDialog(getMainWindow(),
// bundle.getString("PanboxClient.shareNotFound"),
// bundle.getString("client.error"),
// JOptionPane.ERROR_MESSAGE);
// } catch (ShareManagerException e) {
// logger.error("Could not remove device from share!", e);
// JOptionPane
// .showMessageDialog(
// getMainWindow(),
// bundle.getString("PanboxClient.errorWhileRemovingDeviceFromShare"),
// bundle.getString("client.error"),
// JOptionPane.ERROR_MESSAGE);
// } catch (UnrecoverableKeyException e) {
// logger.error("Unable to recover key!", e);
// JOptionPane.showMessageDialog(getMainWindow(),
// bundle.getString("PanboxClient.unableToRecoverKeys"),
// bundle.getString("client.error"),
// JOptionPane.ERROR_MESSAGE);
// } catch (ShareMetaDataException e) {
// logger.error("Error in share metadata", e);
// JOptionPane
// .showMessageDialog(
// getMainWindow(),
// bundle.getString("PanboxClient.errorWhileAccessingShareMetadata"),
// bundle.getString("client.error"),
// JOptionPane.ERROR_MESSAGE);
// }
// } else if (permission instanceof ContactShareParticipant) {
// // TODO Add implementation here, once user deletion is
// // supported
// }
}
public PanboxShare addPermissionToShare(PanboxShare share, PanboxSharePermission permission, char[] password)
throws UnrecoverableKeyException, ShareDoesNotExistException, ShareManagerException,
ShareMetaDataException {
if (permission instanceof DeviceShareParticipant) {
// New device has been added to share
DeviceShareParticipant dPermission = (DeviceShareParticipant) permission;
PanboxDevice device = dPermission.getDevice();
share = ShareManagerImpl.getInstance().addDevicePermission(share, device.getDeviceName(), password);
} else if (permission instanceof ContactShareParticipant) {
// New contact has been added to share
ContactShareParticipant cPermission = (ContactShareParticipant) permission;
PanboxGUIContact contact = cPermission.getContact();
share = ShareManagerImpl.getInstance().addContactPermission(share, contact.getEmail(), password);
}
// update sharelist contents
for (int i = 0; i < shareList.size(); i++) {
if (shareList.get(i).getUuid().equals(share.getUuid())) {
shareList.set(i, share);
break;
}
}
return share;
}
public void saveMyContact(ArrayList<CloudProviderInfo> removedCSPs, ArrayList<CloudProviderInfo> addedCSPs) {
for (CloudProviderInfo csp : removedCSPs) {
this.myId.delCloudProviderByProviderName(csp);
}
for (CloudProviderInfo csp : addedCSPs) {
this.myId.addCloudProvider(csp);
}
identityManager.storeMyIdentity(this.myId);
}
// Contact list events
// public void saveContact(String mail, String newFirstName, String newName,
public void saveContact(PanboxGUIContact contact, String newFirstName, String newName,
ArrayList<CloudProviderInfo> removedCSPs, ArrayList<CloudProviderInfo> addedCSPs, boolean verified) {
// org.panbox.core.identitymgmt.PanboxContact contact = this.myId
// .getAddressbook().contactExists(mail);
contact.setFirstName(newFirstName);
contact.setName(newName);
for (CloudProviderInfo csp : removedCSPs) {
contact.removeCloudProvider(csp.getProviderName());
}
for (CloudProviderInfo csp : addedCSPs) {
contact.addCloudProvider(csp);
}
if (verified) {
contact.setTrustLevel(PanboxContact.TRUSTED_CONTACT);
} else {
contact.setTrustLevel(PanboxContact.UNTRUSTED_CONTACT);
}
addressbookManager.persistContacts(this.myId.getAddressbook().getContacts(), this.myId.getID());
}
public void importContacts(VCard[] vcs, boolean authVerified) {
try {
addressbookManager.importContacts(this.myId, vcs, authVerified);
} catch (ContactExistsException e) {
StringBuilder b = new StringBuilder();
List<PanboxContact> existingContacts = e.getContacts();
for (PanboxContact c : existingContacts) {
b.append("- ");
b.append(c.getFirstName() + " " + c.getName() + " (" + c.getEmail() + ")");
b.append("\n");
}
JOptionPane.showMessageDialog(getMainWindow(),
MessageFormat.format(bundle.getString("PanboxClient.contactAlreadyExists"), b.toString()), "Info",
JOptionPane.INFORMATION_MESSAGE);
} finally {
identityManager.storeMyIdentity(this.myId);
refreshContactListModel();
}
}
public void removeContact(PanboxGUIContact contact) {
// First, check if contact that is to be removed, still is owner of any
// share we currently have in our share list
try {
List<String> shareNames = this.shareManager.getInstalledShares();
List<PanboxShare> knownShares = new ArrayList<PanboxShare>();
for (String shareName : shareNames) {
PanboxShare share = null;
share = shareManager.getShareForName(shareName);
if (share != null) {
knownShares.add(share);
}
}
List<PanboxShare> sharesToBeRemoved = new ArrayList<PanboxShare>();
StringBuffer slist = new StringBuffer();
for (PanboxShare panboxShare : knownShares) {
if (panboxShare.isOwner(contact.getPublicKeySign())) {
sharesToBeRemoved.add(panboxShare);
// prepare message
slist.append("\n- ");
slist.append(panboxShare.getName());
}
}
if (sharesToBeRemoved.size() > 0) {
// there still are share to be removed
JCheckBox checkbox = new JCheckBox(bundle.getString("client.shareList.removeShareDirectoryMessage"));
String message = MessageFormat.format(bundle.getString("PanboxClient.shareOwnerRemoveMessage"),
slist.toString());
Object[] params = { message, checkbox };
int reallyRemove = JOptionPane.showConfirmDialog(getMainWindow(), params,
bundle.getString("PanboxClient.reallyRemoveShares"), JOptionPane.YES_NO_OPTION);
if (reallyRemove == JOptionPane.YES_OPTION) {
// user chose to remove shares
for (PanboxShare share : sharesToBeRemoved) {
PleaseWaitDialog d = null;
if (checkbox.isSelected()) {
try {
d = new PleaseWaitDialog(getMainWindow(),
bundle.getString("PanboxClient.operationInProgress"));
d.setLocationRelativeTo(getMainWindow());
d.setVisible(true);
FileUtils.deleteDirectoryTree(new File(share.getPath()));
// FileUtils.deleteDirectory(new File(share
// .getPath()));
} catch (IOException e) {
logger.error("Failed to remove share source directory!", e);
JOptionPane.showMessageDialog(getMainWindow(),
bundle.getString("PanboxClient.deleteShareContentsFailed"),
bundle.getString("error"), JOptionPane.ERROR_MESSAGE);
} finally {
if (d != null) {
d.dispose();
}
}
}
removeShare(share);
}
// contact will be removed later on
} else if (reallyRemove == JOptionPane.NO_OPTION) {
// do nothing
return;
}
}
} catch (NullPointerException | ShareManagerException | UnrecoverableKeyException | ShareMetaDataException e) {
logger.error("Could not obtain list of installed shares!", e);
int ret = JOptionPane.showConfirmDialog(getMainWindow(),
bundle.getString("PanboxClient.unableToDetermineIfUserIsOwner"),
bundle.getString("PanboxClient.unableToReadShares"), JOptionPane.YES_NO_OPTION);
if (ret == JOptionPane.NO_OPTION) {
return;
}
}
// try to remove contact
if (!this.myId.deleteContact(contact.getEmail())) {
JOptionPane.showMessageDialog(getMainWindow(),
bundle.getString("PanboxClient.couldNotDeleteFromAddressbook"), bundle.getString("client.error"),
JOptionPane.ERROR_MESSAGE);
} else {
identityManager.storeMyIdentity(this.myId);
// operation was successful, refresh view
contactList.removeElement(contact);
}
}
public void exportContacts(List<PanboxGUIContact> contacts, File vcardFile, char[] exportPIN) {
Collection<VCard> vcards = new LinkedList<VCard>();
for (PanboxGUIContact c : contacts) {
VCard v;
if (c instanceof PanboxMyContact) {
v = AbstractAddressbookManager.contact2VCard(this.myId);
} else {
v = AbstractAddressbookManager.contact2VCard((PanboxContact) c.getModel());
}
vcards.add(v);
}
File tmpFileForVcard = new File(System.getProperty("java.io.tmpdir") + File.separator + "panboxTMPex.vcf");
if (!AbstractAddressbookManager.exportContacts(vcards, tmpFileForVcard)) {
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.couldNotExportContacts"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
try {
logger.info("Exporting contacts with export PIN " + String.valueOf(exportPIN));
VCardProtector.protectVCF(vcardFile, tmpFileForVcard, exportPIN);
} catch (Exception e) {
logger.warn("protectVCF Exception", e);
}
if (tmpFileForVcard.exists()) {
tmpFileForVcard.delete();
}
}
public void exportContacts(List<PanboxGUIContact> contacts, File vcardFile) {
Collection<VCard> vcards = new LinkedList<VCard>();
for (PanboxGUIContact c : contacts) {
VCard v;
if (c instanceof PanboxMyContact) {
v = AbstractAddressbookManager.contact2VCard(this.myId);
} else {
v = AbstractAddressbookManager.contact2VCard((PanboxContact) c.getModel());
}
vcards.add(v);
}
if (!AbstractAddressbookManager.exportContacts(vcards, vcardFile)) {
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("PanboxClient.couldNotExportContacts"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
}
public void addCSPtoContact(PanboxGUIContact contact, String csp, String account) {
contact.addCloudProvider(new CloudProviderInfo(csp, account));
}
public void removeCSPfromContact(PanboxGUIContact contact, String csp) {
contact.removeCloudProvider(csp);
}
// Device list events
public NetworkPairingInformation initDevicePairingLAN() throws SocketException {
Settings s = Settings.getInstance();
NetworkPairingInformation info = new NetworkPairingInformation(s.getPairingInterface(), s.getPairingAddress());
extendPairingInformation(info);
return info;
}
public BluetoothPairingInformation initDevicePairingBluetooth()
throws BluetoothStateException, InterruptedException {
// This is the first time we are going to touch the Bluetooth API so we
// should attach our logger to the com.intel.bluetooth logging
appendBluetoothLogging();
// Make bluetooth device discoverbale!
LocalDevice local = LocalDevice.getLocalDevice();
try {
local.setDiscoverable(DiscoveryAgent.GIAC);
} catch (BluetoothStateException e) {
logger.debug(
"PanboxClient : initDevicePairingBluetooth : First try to set discoverable failed. Will try again in 1sec.");
Thread.sleep(1000);
local.setDiscoverable(DiscoveryAgent.GIAC);
}
// setting LocalDevice is only need for Linux, since on Windows we
// bluecove only supports one device!
BluetoothPairingInformation info = new BluetoothPairingInformation(local);
extendPairingInformation(info);
return info;
}
private void appendBluetoothLogging() {
Logger bluecoveLog = Logger.getLogger("com.intel.bluetooth");
@SuppressWarnings("rawtypes")
Enumeration loggers = logger.getAllAppenders();
while (loggers.hasMoreElements()) {
Appender appender = (Appender) loggers.nextElement();
bluecoveLog.addAppender(appender);
}
}
private void extendPairingInformation(PairingInformation info) {
logger.debug("PanboxClient : extendPairingInformation : Will now extend given device pairing information...");
final JTextField deviceNameField = new JTextField(bundle.getString("PanboxClient.chooseDeviceName"));
deviceNameField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (deviceNameField.getText().equals(bundle.getString("PanboxClient.chooseDeviceName"))) {
deviceNameField.setText("");
}
}
});
JOptionPane.showMessageDialog(null, deviceNameField, bundle.getString("PanboxClient.enterDeviceName"),
JOptionPane.INFORMATION_MESSAGE);
info.setDeviceName(deviceNameField.getText());
logger.debug("PanboxClient : extendPairingInformation : Set device name to: " + info.getDeviceName());
}
public void runMasterPairingOnPairingHandle(final PairingInformation info, final String password,
final PairingNotificationReceiver receiver, final char[] keyPassword) {
logger.debug("PanboxClient : runMasterPairingOnPairingHandle : Master pairing started: " + info);
runGeneralPairingHandler(PairingType.MASTER, info, password, receiver, keyPassword);
}
public void runSlavePairingOnPairingHandle(final PairingInformation info, final String password,
final PairingNotificationReceiver receiver) {
logger.debug("PanboxClient : runSlavePairingOnPairingHandle : Slave pairing started: " + info);
runGeneralPairingHandler(PairingType.SLAVE, info, password, receiver, null);
}
private void runGeneralPairingHandler(final PairingType type, final PairingInformation info, final String password,
final PairingNotificationReceiver receiver, final char[] keyPassword) {
logger.debug("PanboxClient : runGeneralPairingHandler : Pairing is about to start: " + "Type: " + type + " for "
+ info);
final PanboxClient thisClient = this;
try {
if (info instanceof NetworkPairingInformation) {
task = executor.submit(new NetworkPairingCallable(type, info, password, receiver, keyPassword, this));
} else if (info instanceof BluetoothPairingInformation) {
task = executor.submit(new BluetoothPairingCallable(type, info, password, receiver, keyPassword, this));
}
} catch (Exception e) {
logger.error("PanboxClient : runGeneralPairingHandler : Setting up pairing failed: ", e);
JOptionPane.showMessageDialog(getMainWindow(), bundle.getString("client.pairing.couldnotsetup.message"),
bundle.getString("client.pairing.failed"), JOptionPane.ERROR_MESSAGE);
task = null;
return; // pairing setup failed. Return and don't start!
}
scheduledTask = canceller.schedule(new Callable<Void>() {
@Override
public Void call() throws Exception {
synchronized (thisClient) {
if (task != null && !task.isCancelled() && !task.isDone()) {
task = null;
logger.debug("PanboxClient : runGeneralPairingHandler : Timeout for pairing task!");
receiver.inform(PairingResult.TIMEOUT);
} else {
logger.debug(
"PanboxClient : runGeneralPairingHandler : Timeout for task that has been finished/canceled already!");
}
return null;
}
}
}, PAKCorePairingHandler.PAIRING_TIMEOUT, TimeUnit.MILLISECONDS);
}
public synchronized void stopDevicePairing() {
logger.debug("PanboxClient : stopDevicePairing : called");
if (task != null && scheduledTask != null) {
task.cancel(true);
logger.debug("PanboxClient : stopDevicePairing : Old pairing task has been canceled. Canceled?: "
+ task.isCancelled());
task = null;
scheduledTask.cancel(true);
scheduledTask = null;
} else {
logger.debug("PanboxClient : stopDevicePairing : No current running device pairing to stop!");
}
}
private void runGeneralPairingRequester(final String qrPassword) {
String[] splitPW = qrPassword.split(":");
String addr = splitPW[0];
String password = splitPW[1];
KeyPair deviceKey = CryptCore.generateKeypair();
PleaseWaitDialog dialog = new PleaseWaitDialog(null, bundle.getString("client.pairing.pleasewait.message"));
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
// try if addr is InetAddress
try {
InetAddress inetaddr = InetAddress.getByName(addr);
PAKNetworkPairingRequester requester = new PAKNetworkPairingRequester(inetaddr, password,
org.panbox.core.devicemgmt.DeviceType.DESKTOP, deviceKey);
requester.runProtocol();
finishGeneralPairing(deviceKey, requester);
dialog.dispose();
} catch (UnknownHostException e) {
// Try to connect via Bluetooth! If this also fails then the host
// could not be connected.
try {
// do the search for service discovery
RemoteDeviceDiscovery.discover();
List<ServiceRecord> records = RemoteDeviceDiscovery.getServiceRecordsByDeviceAddr(addr);
// check whether we found our device or not
if (records != null) {
try {
PAKBluetoothPairingRequester requester = new PAKBluetoothPairingRequester(password,
DeviceType.DESKTOP, deviceKey, records.get(0)); // Since
// we
// only
// search
// for
// a
// single UUID we will only
// find a single
// ServiceRecord!
finishGeneralPairing(deviceKey, requester);
dialog.dispose();
return;
} catch (IOException ex) {
logger.error(
"PanboxClient : runGeneralPairingRequester : Bluetooth pairing failed with IO exception!",
ex);
}
} else {
logger.error(
"PanboxClient : runGeneralPairingRequester : The specified device was not found on Device search!");
}
} catch (BluetoothStateException | InterruptedException ex) {
logger.error("PanboxClient : runGeneralPairingRequester : Bluetooth device lookup failed!", ex);
}
dialog.dispose();
// Neither LAN nor Bluetooth could find a host. Host could not be
// connected!
JOptionPane.showMessageDialog(null, bundle.getString("client.pairing.couldnotconnect.message"),
bundle.getString("client.pairing.failed"), JOptionPane.ERROR_MESSAGE);
logger.error("PanboxClient : runGeneralPairingRequester : Could not connect to host.");
} catch (Exception e) {
dialog.dispose();
JOptionPane.showMessageDialog(null, bundle.getString("client.pairing.failed.message"),
bundle.getString("client.pairing.failed"), JOptionPane.ERROR_MESSAGE);
logger.error("PanboxClient : runGeneralPairingRequester : Pairing failed with exception: ", e);
}
}
private void finishGeneralPairing(KeyPair deviceKey, PAKCorePairingRequester requester) {
switch (requester.getType()) {
case MASTER:
finishMasterPairing(requester.geteMail(), requester.getFirstName(), requester.getLastName(),
requester.getKeyPassword(), requester.getOwnerCertEnc(), requester.getOwnerKeyEnc(),
requester.getOwnerCertSign(), requester.getOwnerKeySign(), deviceKey, requester.getDeviceName(),
requester.getDevCert(), requester.getKnownDevices(), requester.getKnownContacts());
break;
case SLAVE:
finishSlavePairing(requester.geteMail(), requester.getFirstName(), requester.getLastName(),
requester.getOwnerCertEnc(), requester.getOwnerCertSign(), deviceKey, requester.getDeviceName(),
requester.getDevCert(), requester.getKnownDevices(), requester.getKnownContacts());
break;
default:
logger.error("PanboxClient : runGeneralPairingRequester : Unknown pairing type");
}
}
private void finishMasterPairing(String eMail, String firstName, String lastName, char[] keyPassword,
X509Certificate certEnc, KeyPair ownerKeyEnc, X509Certificate certSign, KeyPair ownerKeySign,
KeyPair deviceKey, String deviceName, X509Certificate deviceCert, Map<String, X509Certificate> devices,
Collection<VCard> contacts) {
logger.debug("PanboxClient : finishMasterPairing : Will set up master identity.");
// create identity and initial keypairs
Identity id = new Identity(new SimpleAddressbook(), eMail, firstName, lastName);
id.setOwnerKeySign(certSign);
id.setOwnerKeyEnc(certEnc);
id.setOwnerKeySign(ownerKeySign, keyPassword);
id.setOwnerKeyEnc(ownerKeyEnc, keyPassword);
Settings.getInstance().setPairingType(PairingType.MASTER);
id.addDeviceKey(deviceKey, deviceName);
Settings.getInstance().setDeviceName(deviceName);
identityManager.init(addressbookManager);
identityManager.storeMyIdentity(id);
// Erase keys and passwords from memory
Arrays.fill(keyPassword, '\u0000');
// This code can be inserted once Java 8 implements destroy-Method
// in order to remove key material securely from JVM memory
// try {
// ownerKeyEnc.getPrivate().destroy();
// } catch (DestroyFailedException e1) {
// logger.warn(
// "PanboxClient : finishMasterPairing : Could not destroy private enc
// key after pairing: ",
// e1);
// }
// try {
// ownerKeySign.getPrivate().destroy();
// } catch (DestroyFailedException e1) {
// logger.warn(
// "PanboxClient : finishMasterPairing : Could not destroy private sign
// key after pairing: ",
// e1);
// }
try {
File contactsFile = File.createTempFile("panbox-contacts", null);
AbstractAddressbookManager.exportContacts(contacts, contactsFile);
addressbookManager.importContacts(id, contactsFile, true);
contactsFile.delete(); // we can now remove this tempFile
identityManager.storeMyIdentity(id);
} catch (ContactExistsException | IOException e) {
logger.warn("PanboxClient : setUpIdentity : Could not import all contacts to addressbook.");
}
try {
deviceManager.setIdentity(id);
deviceManager.addThisDevice(deviceName, deviceKey, DeviceType.DESKTOP);
// add other known devices
for (Entry<String, X509Certificate> dev : devices.entrySet()) {
deviceManager.addDevice(dev.getKey(), dev.getValue(), DeviceType.DESKTOP);
}
refreshDeviceListModel();
} catch (DeviceManagerException ex) {
logger.error("PanboxClient : finishMasterPairing : Could not add device to device list.", ex);
}
}
private void finishSlavePairing(String eMail, String firstName, String lastName, X509Certificate ownerCertEnc,
X509Certificate ownerCertSign, KeyPair deviceKey, String deviceName, X509Certificate deviceCert,
Map<String, X509Certificate> devices, Collection<VCard> contacts) {
logger.debug("PanboxClient : finishSlavePairing : Will set up slave identity.");
// create identity and initial keypairs
Identity id = new Identity(new SimpleAddressbook(), eMail, firstName, lastName);
id.setOwnerKeyEnc(ownerCertEnc);
id.setOwnerKeySign(ownerCertSign);
Settings.getInstance().setPairingType(PairingType.SLAVE);
id.addDeviceKey(deviceKey, deviceName);
Settings.getInstance().setDeviceName(deviceName);
identityManager.init(addressbookManager);
identityManager.storeMyIdentity(id);
try {
File contactsFile = File.createTempFile("panbox-contacts", null);
AbstractAddressbookManager.exportContacts(contacts, contactsFile);
addressbookManager.importContacts(id, contactsFile, true);
contactsFile.delete(); // we can now remove this tempFile
identityManager.storeMyIdentity(id);
} catch (ContactExistsException | IOException e) {
logger.warn("PanboxClient : setUpIdentity : Could not import all contacts to addressbook.");
}
try {
deviceManager.setIdentity(id);
deviceManager.addThisDevice(deviceName, deviceKey, DeviceType.DESKTOP);
// add other known devices
for (Entry<String, X509Certificate> dev : devices.entrySet()) {
deviceManager.addDevice(dev.getKey(), dev.getValue(), DeviceType.DESKTOP);
}
refreshDeviceListModel();
} catch (DeviceManagerException ex) {
logger.error("PanboxClient : finishMasterPairing : Could not add device to device list.", ex);
}
}
/**
* Stores a pairing file at the specified path for the specified device and
* type
*
* @param outputFile
* Pairing file to be saved
* @param devicename
* Name of the device that should be paired
* @param password
* Password of the identity
*/
public void storePairingFile(File outputFile, String devicename, char[] password, PairingType type,
DeviceType devType) {
logger.debug("PanboxClient : storePairingFile : Storing pairing container to: " + outputFile.getAbsolutePath());
try {
PanboxFilePairingWriteReturnContainer retCon = null;
if (type == PairingType.MASTER) {
retCon = PanboxFilePairingUtils.storePairingFile(outputFile, devicename, password, type, devType,
myId.getEmail(), myId.getFirstName(), myId.getName(), myId.getPrivateKeyEnc(password),
myId.getCertEnc(), myId.getPrivateKeySign(password), myId.getCertSign(), getDevicePairingMap(),
getContactsPairingList());
} else {
retCon = PanboxFilePairingUtils.storePairingFile(outputFile, devicename, password, type, devType,
myId.getEmail(), myId.getFirstName(), myId.getName(), null, myId.getCertEnc(), null,
myId.getCertSign(), getDevicePairingMap(), getContactsPairingList());
}
logger.debug("PanboxClient : storePairingFile : Storing pairing container finished.");
deviceManager.addDevice(retCon.getDevicename(), retCon.getDevCert(), retCon.getDevType());
refreshDeviceListModel();
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException ex) {
logger.error("PanboxClient : storePairingFile : Exception caught: ", ex);
JOptionPane.showMessageDialog(null, bundle.getString("PanboxClient.errorWhileStoringPairingContainer"),
bundle.getString("PanboxClient.errorWhileStoringPairingContainer"), JOptionPane.ERROR_MESSAGE);
} catch (UnrecoverableKeyException ex) {
logger.error("PanboxClient : storePairingFile : Wrong password: ", ex);
JOptionPane.showMessageDialog(null, bundle.getString("PanboxClient.wrongPassword"),
bundle.getString("PanboxClient.errorWhileStoringPairingContainer"), JOptionPane.ERROR_MESSAGE);
} catch (DeviceManagerException ex) {
logger.warn("PanboxClient : storePairingFile : Exception caught after pairing file: ", ex);
JOptionPane.showMessageDialog(null, bundle.getString("PanboxClient.errorCouldNotAddDevicePairing"),
bundle.getString("PanboxClient.errorCouldNotAddDevicePairingTitle"), JOptionPane.ERROR_MESSAGE);
}
}
public Map<String, X509Certificate> getDevicePairingMap() {
Map<String, X509Certificate> devices = new HashMap<>();
try {
for (PanboxDevice dev : deviceManager.getDeviceList()) {
X509Certificate cert = (X509Certificate) myId.getDeviceCert(dev.getDeviceName());
if (cert != null) {
devices.put(dev.getDeviceName(), cert);
} else {
logger.error("PanboxClient : storePairingFile : Could not get device key for device "
+ dev.getDeviceName() + ". Will not add it to device list.");
}
}
} catch (DeviceManagerException ex) {
logger.error(
"PanboxClient : storePairingFile : Could not get device list from DeviceManager. Will not add any device to list.");
}
return devices;
}
public Collection<VCard> getContactsPairingList() {
Collection<VCard> vcards = new ArrayList<VCard>();
for (PanboxContact contact : myId.getAddressbook().getContacts()) {
vcards.add(AbstractAddressbookManager.contact2VCard(contact));
}
return vcards;
}
public void loadPairingFile(File inputFile) throws IOException {
logger.debug("PanboxClient : loadPairingFile : Started importing pairing file: " + inputFile);
char[] password = PasswordEnterDialog.invoke(PermissionType.DEVICE);
try {
PanboxFilePairingLoadReturnContainer retCon = PanboxFilePairingUtils.loadPairingFile(inputFile, password);
setUpIdentity(retCon.geteMail(), retCon.getFirstName(), retCon.getLastName(), retCon.getPassword(),
retCon.getDeviceName(), retCon.getDevicePrivKey(), retCon.getDeviceCert(), retCon.getSignPrivKey(),
retCon.getSignCert(), retCon.getEncPrivKey(), retCon.getEncCert(), retCon.getDevices(),
retCon.getContactsFile());
} catch (IllegalArgumentException e) {
logger.error("PanboxClient : loadPairingFile : Could not read pairing file!");
JOptionPane.showMessageDialog(null,
"The provided Panbox pairing file was corrupt. Please create a new one.", "Panbox Pairing failed",
JOptionPane.ERROR_MESSAGE);
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | InvalidKeySpecException e) {
logger.error("PanboxClient : loadPairingFile : Exception: ", e);
JOptionPane.showMessageDialog(null, "An error occurred while pairing. Please try again",
"Panbox Pairing failed", JOptionPane.ERROR_MESSAGE);
} catch (UnrecoverableKeyException e) {
logger.error("PanboxClient : loadPairingFile : Wrong password while pairing...");
JOptionPane.showMessageDialog(null, "The password you entered was wrong. Please try again.",
"Panbox Pairing failed", JOptionPane.ERROR_MESSAGE);
} finally {
if (password != null) {
Arrays.fill(password, (char) 0);
}
}
}
private void setUpIdentity(String email, String firstName, String lastName, char[] password, String devicename,
PrivateKey deviceKey, Certificate deviceCert, PrivateKey ownerKeySign, Certificate ownerCertSign,
PrivateKey ownerKeyEnc, Certificate ownerCertEnc, Map<String, X509Certificate> devices, File contactsFile)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// create identity and initial keypairs
Identity id = new Identity(new SimpleAddressbook(), email, firstName, lastName);
id.setOwnerKeyEnc(ownerCertEnc);
id.setOwnerKeySign(ownerCertSign);
if (ownerKeySign != null && ownerKeyEnc != null) {
id.setOwnerKeySign(CryptCore.privateKeyToKeyPair(ownerKeySign), password);
id.setOwnerKeyEnc(CryptCore.privateKeyToKeyPair(ownerKeyEnc), password);
Settings.getInstance().setPairingType(PairingType.MASTER);
} else {
Settings.getInstance().setPairingType(PairingType.SLAVE);
}
id.addDeviceKey(CryptCore.privateKeyToKeyPair(deviceKey), deviceCert, devicename);
Settings.getInstance().setDeviceName(devicename);
identityManager.init(addressbookManager);
identityManager.storeMyIdentity(id);
try {
// we assume the pairingfile to be trustworthy
// w.r.t. the contact trust level
addressbookManager.importContacts(id, contactsFile, true);
contactsFile.delete(); // we can now remove this tempFile
identityManager.storeMyIdentity(id);
} catch (ContactExistsException e) {
logger.warn("PanboxClient : setUpIdentity : Could not import all contacts to addressbook.");
}
this.myId = id;
deviceManager.setIdentity(id);
shareManager.setIdentity(id);
try {
// add this device
deviceManager.addThisDevice(devicename, CryptCore.privateKeyToKeyPair(deviceKey), DeviceType.DESKTOP);
// add other known devices
for (Entry<String, X509Certificate> dev : devices.entrySet()) {
deviceManager.addDevice(dev.getKey(), dev.getValue(), DeviceType.DESKTOP);
}
refreshDeviceListModel();
} catch (DeviceManagerException ex) {
// Simply ignore this for now!
}
}
// Settings events
public void languageChanged(Locale locale) {
// currently no implementation planned
}
public void settingsFolderChanged(File file) {
// currently no implementation planned
}
public abstract void panboxFolderChanged(String path);
/**
* adds JVM shutdown hook to safeguard execution of clean up operations like
* unmounting the vfs
*/
protected void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
shutdown();
} catch (Exception e) {
logger.error("Encountered error during application shutdown: " + e.getMessage());
}
}
});
}
public abstract void restartTrayIcon();
public ShareListModel getDeviceShares(PanboxDevice device) {
ShareListModel result = new ShareListModel();
int max = shareList.getSize();
for (int i = 0; i < max; i++) {
PanboxShare share = shareList.get(i);
if (share.getDevices().contains(device)) {
result.addElement(share);
}
}
return result;
}
public String getOnlineFilename(String shareName, String path) {
String res = null;
try {
PanboxShare share = shareManager.getShareForName(shareName);
res = shareManager.getOnlineFilename(share, path);
} catch (Exception ex) {
logger.error("PanboxClient : getOnlineFilename : Exception occured: ", ex);
JOptionPane.showMessageDialog(null, bundle.getString("client.error.getOnlineFilename"),
bundle.getString("client.error"), JOptionPane.ERROR_MESSAGE);
}
return res;
}
public abstract void showTrayMessage(String title, String message, MessageType type);
public void conflictNotification(PanboxShare share, String chk) {
JOptionPane.showMessageDialog(null,
MessageFormat.format(bundle.getString("client.warning.conflictNotification"), share.getName(), chk),
bundle.getString("client.warn"), JOptionPane.WARNING_MESSAGE);
}
public PanboxShare reloadShare(PanboxShare share) {
showTrayMessage(bundle.getString("client.warn"),
MessageFormat.format(bundle.getString("client.shareReloadNotification"), share.getName()),
MessageType.WARNING);
try {
PanboxShare nshare = shareManager.reloadShareMetadata(share);
PanboxShare tmp;
if ((tmp = checkShareIntegrity(share)) != null) {
nshare = tmp;
}
int index = shareList.indexOf(share);
if (index != -1) {
shareList.setElementAt(nshare, index);
} else {
logger.error("Could not find share instance " + share + " in shareList");
}
getMainWindow().refreshShare();
return nshare;
} catch (ShareManagerException e) {
JOptionPane.showMessageDialog(null, bundle.getString("client.error.shareReloadFailed"),
bundle.getString("error"), JOptionPane.ERROR_MESSAGE);
}
return null;
}
public abstract void openShareFolder(String name);
/**
* checks if symmetric key size defined in {@link KeyConstants} is supported
* by JVM
*
* @return <code>true</code> if key size is valid, <code>false</code>
* otherwise
*/
public static boolean checkSupportedKeySize() {
try {
int ksize = Cipher.getMaxAllowedKeyLength(KeyConstants.SYMMETRIC_ALGORITHM);
logger.info("Maximum supported key size: " + ksize);
if (ksize < KeyConstants.SYMMETRIC_KEY_SIZE) {
return false;
}
} catch (NoSuchAlgorithmException e) {
logger.error("Unable to check supported key size!", e);
return false;
}
return true;
}
public void executeVersionCheck() {
if (VersionUtils.isNewerVersionAvailable()) {
MessageFormat formatter = new MessageFormat("", Settings.getInstance().getLocale());
formatter.applyPattern(bundle.getString("PanboxClient.newversion.message"));
JOptionPane pane = new JOptionPane(
formatter.format(new Object[] { VersionUtils.getRemoteVersion(OS.getOperatingSystem()) }),
JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION);
JDialog dialog = pane.createDialog(null, bundle.getString("PanboxClient.newversion"));
dialog.setModal(false);
dialog.setVisible(true);
pane.getValue();
}
}
}