/*
* Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License, Version 1.0,
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package net.rim.ejde.internal.signing;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.UIManager;
import net.rim.ejde.internal.core.ContextManager;
import net.rim.ejde.internal.core.IConstants;
import net.rim.ejde.internal.core.IRIMMarker;
import net.rim.ejde.internal.model.BlackBerryProject;
import net.rim.ejde.internal.model.preferences.SignatureToolPreferences;
import net.rim.ejde.internal.ui.consoles.PackagingConsole;
import net.rim.ejde.internal.ui.preferences.SignatureToolPrefsPage;
import net.rim.ejde.internal.util.FileUtils;
import net.rim.ejde.internal.util.Messages;
import net.rim.ejde.internal.util.PackagingUtils;
import net.rim.ejde.internal.util.PlatformSpecificMessages;
import net.rim.ejde.internal.util.ResourceBuilderUtils;
import net.rim.ejde.internal.util.VMToolsUtils;
import net.rim.ejde.internal.util.VMUtils;
import net.rim.ejde.internal.util.WindowsRegistryReader;
import net.rim.ide.core.Util;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.internal.ui.preferences.PropertyAndPreferencePage;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.dialogs.PreferencesUtil;
public class SignatureToolLaunchAction {
private static Logger log = Logger.getLogger( SignatureToolLaunchAction.class );
Pattern _pattern = Pattern.compile( "Signing of files is complete.:(\\d+)/(\\d+)" );
/**
* Pointer to process if the signature tool is running
*/
private static Process _process;
private String _sigToolPath, _password;
private boolean _runSilent;
private IStatus _status;
private MessageConsoleStream _consoleOutputStream;
public SignatureToolLaunchAction( String sigToolPath ) {
_sigToolPath = sigToolPath;
PackagingConsole packagingConsole = PackagingConsole.getInstance();
ConsolePlugin.getDefault().getConsoleManager().addConsoles( new IConsole[] { packagingConsole } );
}
/**
* Signs the cod file of the BlackBerry projects in the given <code>projects</code>.
*
* @param action
* @param projects
*/
public static boolean signCodFiles( final Set< BlackBerryProject > projects, final IProgressMonitor monitor ) {
try {
ResourceBuilderUtils.cleanProblemMarkers( ResourcesPlugin.getWorkspace().getRoot(),
new String[] { IRIMMarker.SIGNATURE_TOOL_PROBLEM_MARKER }, IResource.DEPTH_ZERO );
} catch( CoreException e ) {
log.error( e.getMessage() );
}
CodeSigningRunnable runnable = new CodeSigningRunnable( projects );
new Thread( runnable ).start();
return runnable.waitForSigningToFinish( monitor );
}
/**
* Displays a warning dialog indicating that the signature tool is already running.
*/
private static void warnSignatureToolRunning() {
Display.getDefault().syncExec( new Runnable() {
public void run() {
MessageDialog dialog = new MessageDialog( ContextManager.getActiveWorkbenchShell(),
Messages.SignCommandHandler_SigToolRunningDialogTitleMsg, null,
Messages.SignCommandHandler_SigToolRunningDialogMsg, MessageDialog.WARNING,
new String[] { IDialogConstants.OK_LABEL }, 0 );
dialog.open();
}
} );
}
/**
* Run the action.
*/
public void run( final List< String > codFileList ) {
if( SignatureToolPreferences.getRunSignatureToolSilently() ) {
// Run Signature Tool Silently
_runSilent = true;
if( !SignatureToolPreferences.isPasswordCached() ) {
// Password has not been cached
Display.getDefault().syncExec( new Runnable() {
public void run() {
SignatureToolPasswordPromptDialog dialog = new SignatureToolPasswordPromptDialog( ContextManager
.getActiveWorkbenchShell(), SignatureToolPreferences.getCachedPassword() );
dialog.open();
if( dialog.getReturnCode() == 0 ) {
// User Pressed OK
_status = Status.OK_STATUS;
_password = dialog.getResponse();
} else {
_status = Status.CANCEL_STATUS;
}
}
} );
// user canceled the operation
if( !_status.isOK() ) {
return;
}
} else {
// Use the previously cached password
_password = SignatureToolPreferences.getCachedPassword();
}
} else {
// Don't Run Silently
_runSilent = false;
}
launchSignatureTool( codFileList );
}
static class CodeSigningRunnable implements Runnable {
private Set< BlackBerryProject > _targetProjects;
private boolean _running = true;
private boolean _succesful;
public CodeSigningRunnable( Set< BlackBerryProject > projects ) {
_targetProjects = projects;
}
@Override
public void run() {
try {
_succesful = true;
List< String > codFileList = PackagingUtils.getCodFilePathsFromProjects( _targetProjects );
IPath sigPath;
String sigPathString = IConstants.EMPTY_STRING;
sigPath = VMToolsUtils.getSignatureToolPath();
// check signature tool again
if( !VMToolsUtils.isVMToolValid() ) {
ResourceBuilderUtils.createProblemMarker( ResourcesPlugin.getWorkspace().getRoot(),
IRIMMarker.SIGNATURE_TOOL_PROBLEM_MARKER, Messages.SignatureTool_Not_Found_Msg, -1,
IMarker.SEVERITY_ERROR );
log.error( Messages.SignatureTool_Not_Found_Msg );
_succesful = false;
_running = false;
return;
}
sigPathString = sigPath.toOSString();
final SignatureToolLaunchAction action = new SignatureToolLaunchAction( sigPathString );
File cskFile = new File( VMToolsUtils.getVMToolsFolderPath() + File.separator + IConstants.CSK_FILE_NAME );
File dbFile = new File( VMToolsUtils.getVMToolsFolderPath() + File.separator + IConstants.DB_FILE_NAME );
while( ( ( !cskFile.exists() ) || ( !dbFile.exists() ) ) && _succesful ) {
// prompt user to install/import key files
Display.getDefault().syncExec( new Runnable() {
public void run() {
Shell shell = ContextManager.getActiveWorkbenchShell();
MessageDialog dialog = new MessageDialog( shell,
Messages.SignCommandHandler_MissingFilesDialogTitleMsg, null,
PlatformSpecificMessages.SignCommandHandler_MissingFilesDialogMsg, MessageDialog.ERROR,
new String[] { IDialogConstants.PROCEED_LABEL, IDialogConstants.CLOSE_LABEL }, 0 );
dialog.open();
if( dialog.getReturnCode() == 0 ) {
Map< String, Boolean > data = new HashMap< String, Boolean >();
data.put( PropertyAndPreferencePage.DATA_NO_LINK, Boolean.TRUE );
PreferencesUtil.createPreferenceDialogOn( shell, SignatureToolPrefsPage.ID, null, data ).open();
} else {
// user choose not to install key, stop signing
_succesful = false;
try {
ResourceBuilderUtils.createProblemMarker( ResourcesPlugin.getWorkspace().getRoot(),
IRIMMarker.SIGNATURE_TOOL_PROBLEM_MARKER, Messages.SignCommandHandler_MissingFileMsg,
-1, IMarker.SEVERITY_ERROR );
} catch( CoreException e ) {
log.error( e.getMessage() );
}
}
}
} );
}
if( _succesful ) {
// If the signature tool is already running, don't start a new one
if( action.getProcess() != null ) {
warnSignatureToolRunning();
_succesful = false;
} else {
action.run( codFileList );
}
}
} catch( Exception ex ) {
log.error( ex );
_succesful = false;
}
_running = false;
}
public boolean isSucceed() {
return _succesful;
}
public synchronized boolean waitForSigningToFinish( IProgressMonitor monitor ) {
while( _running ) {
Util.sleep( 1000 );
if( monitor.isCanceled() ) {
// close simulator tool
if( _process != null ) {
_process.destroy();
}
return true;
}
}
return _succesful;
}
}
/**
* Returns the process of launched signature tool or <code>null</code> if it has not been launched.
*
* @return The <code>Process</code>
*/
public Process getProcess() {
return _process;
}
/**
* Launches the Signature tool. It gets all selected projects and passes their output file to the signature tool.
*/
void launchSignatureTool( List< String > codFileList ) {
searchAndCopySignatureKeys();
log.debug( "Entering SignatureToolAction launchSignatureTool()" ); //$NON-NLS-1$
// This is a list of the commands to run. The first position is the
// actual command; subsequent entries are arguments.
List< String > commands = new LinkedList< String >();
// Find the path to java.exe
String javaHome = System.getProperty( IConstants.JAVA_HOME_PROPERTY );
IPath javaBinPath = new Path( javaHome ).append( IConstants.BIN_FOLD_NAME + File.separator + IConstants.JAVA_CMD );
commands.add( javaBinPath.toOSString() );
// Use the system look and feel
String lookAndFeelClass = UIManager.getSystemLookAndFeelClassName();
commands.add( IConstants.LOOK_AND_FEEL_CMD + lookAndFeelClass ); //$NON-NLS-1$
// Load from a jar
commands.add( IConstants.JAR_CMD );
commands.add( _sigToolPath );
// Add parameters for silent running
if( _runSilent ) {
commands.addAll( generateParameters() );
}
// Add cod files
commands.addAll( codFileList );
// Run the command
ProcessBuilder processBuilder = new ProcessBuilder( commands );
String signedFiles = "0";
try {
_consoleOutputStream = PackagingConsole.getInstance().newMessageStream();
_consoleOutputStream.println( "Signing files: " + codFileList );
_process = processBuilder.start();
BufferedReader is = new BufferedReader( new InputStreamReader( _process.getInputStream() ) );
String buffer;
while( ( buffer = is.readLine() ) != null ) {
// Print out console output for debugging purposes...
log.debug( buffer );
Matcher matcher = _pattern.matcher( buffer );
if( matcher.matches() ) {
signedFiles = matcher.group( 1 );
try {
if( Integer.parseInt( signedFiles ) > 0 ) {
// at least one file has been signed successful, cache the password
SignatureToolPreferences.setCachedPassword( _password );
}
} catch( NumberFormatException e ) {
// do nothing
}
}
}
_process.waitFor();
} catch( IOException e ) {
log.error( "Cannot run Signature Tool", e );
} catch( InterruptedException e ) {
// do nothing
} finally {
//Fix for MKS 1998217 - Current COD signing tool can only output messages
//in console or popup wizard not both.
if(SignatureToolPreferences.getRunSignatureToolSilently()){
_consoleOutputStream.println( "Signing completed:" + signedFiles + " files signed." );
}
_process = null;
try {
_consoleOutputStream.close();
} catch( IOException e ) {
// do nothing
}
log.debug( "Leaving SignatureToolAction launchSignatureTool()" ); //$NON-NLS-1$
}
}
protected List< String > generateParameters() {
List< String > parameters = new ArrayList< String >();
// Password
parameters.add( IConstants.SIGTOOL_PASSWORD );
parameters.add( _password );
// Automatic Signing
parameters.add( IConstants.SIGTOLL_AUTOMATIC );
// AutoClose
parameters.add( IConstants.SIGTOLL_AUTO_CLOSE );
// Print statistics
parameters.add( "-s" );
return parameters;
}
/**
* If signature files exist simply return, otherwise search for key files from the following order, copy the key files to
* sigTool directory if found. PLATFORM Windows
*
* Search order: 1. Installed component packs within Eclipse (from highest version to lowest version) 2. Legacy JDE Components
* (from highest version to lowest version) 3. Legacy JDEs (from highest version to lowest version)
*
* @return <code> if key files are found</code> otherwise returns <code>false</code>
*/
private boolean searchAndCopySignatureKeys() {
log.debug( "Searching keys files..." );
// check if key files exist in current SigTool directory
String sigToolDir = IConstants.EMPTY_STRING;
try {
sigToolDir = VMToolsUtils.getVMToolsFolderPath().toOSString();
} catch( IOException e ) {
log.error( e.getMessage(), e );
}
File cskFile = new File( sigToolDir + File.separator + IConstants.CSK_FILE_NAME );
File dbFile = new File( sigToolDir + File.separator + IConstants.DB_FILE_NAME );
if( cskFile.exists() && dbFile.exists() ) {
return true;
}
// search installed VMs in Eclipse
String location;
for( IVMInstall vm : VMUtils.getInstalledBBVMs() ) {
location = vm.getInstallLocation().getAbsolutePath() + File.separator + IConstants.BIN_FOLD_NAME;
if( copySignatureKeyFiles( location, sigToolDir ) ) {
return true;
}
}
// search legacy JDE Components registered in Windows registry from
// highest version to lowest version
List< String > legacyJDEComponentsPaths = WindowsRegistryReader.getInstalledJDEComponentsPaths();
int count = legacyJDEComponentsPaths.size();
for( int i = count - 1; i >= 0; i-- ) {
location = legacyJDEComponentsPaths.get( i );
if( copySignatureKeyFiles( location, sigToolDir ) ) {
return true;
}
}
// search legacy JDE registered in Windows registry from highest version
// to lowest version
List< String > legacyJDEPaths = WindowsRegistryReader.getInstalledJDEPaths();
count = legacyJDEPaths.size();
for( int i = count - 1; i >= 0; i-- ) {
location = legacyJDEPaths.get( i );
if( copySignatureKeyFiles( location, sigToolDir ) ) {
return true;
}
}
// key files are not found
return false;
}
private static boolean copySignatureKeyFiles( String source, String dest ) {
File srcCskFile = new File( source + File.separator + IConstants.CSK_FILE_NAME ); //$NON-NLS-1$ //$NON-NLS-2$
File srcDbFile = new File( source + File.separator + IConstants.DB_FILE_NAME ); //$NON-NLS-1$ //$NON-NLS-2$
if( srcCskFile.exists() && srcDbFile.exists() ) {
File dstCskFile = new File( dest + File.separator + IConstants.CSK_FILE_NAME ); //$NON-NLS-1$ //$NON-NLS-2$
File dstDbFile = new File( dest + File.separator + IConstants.DB_FILE_NAME ); //$NON-NLS-1$ //$NON-NLS-2$
log.info( "Copy " + srcCskFile.getPath() + " to " + dstCskFile.getPath() ); //$NON-NLS-1$ //$NON-NLS-2$
log.info( "Copy " + srcDbFile.getPath() + " to " + dstDbFile.getPath() ); //$NON-NLS-1$ //$NON-NLS-2$
// copy the key files to current component pack
try {
FileUtils.copyOverwrite( srcCskFile, dstCskFile );
FileUtils.copyOverwrite( srcDbFile, dstDbFile );
return true;
} catch( IOException e ) {
log.error( "failed to copy signature key files", e ); //$NON-NLS-1$
}
}
return false;
}
}