Cette classe implémente l'interface Runnable et utilise la bibliothèque "picocli" pour fournir une interface de ligne de commande pour AdeptActivate.java. AdeptActivateCli analyse valide et applique les options de ligne de commande, y compris la définition des valeurs par défaut. Il appelle ensuite les méthodes de AdeptActivate.java pour effectuer

l'activation réelle.
This commit is contained in:
2025-10-24 17:05:50 -07:00
parent 7605ed8fc3
commit 72bbebf9b2

View File

@@ -0,0 +1,266 @@
import common.*;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import picocli.CommandLine;
import picocli.CommandLine.*;
import javax.xml.parsers.ParserConfigurationException;
import java.io.Console;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import static common.Activation.HOBBES_DEFAULT_VERSION;
/**
* A command-line interface that collects necessary data to pass to the {@link AdeptActivate} class.
* If the "--win" flag is set and this software is running on a Windows operating system an ADEActivation
* object and a WinDevice object are created which read activation data from the Windows registry,
* assuming that the computer has already been activated by the Adobe Digital Edition software. Most
* command line options, are ignored in this scenario. <br/><br/>
* On non-Windows OSes the command line options are validated and used to create a new Activation object
* and a new Device object that are passed to a new {@link AdeptActivate} instance.
* The {@link AdeptActivate#signIn( Document )} and
* {@link AdeptActivate#activateDevice( Document )} methods are called to activate this device with the
* Adobe Content Server activation service, or optionally to some other content server.<br/><br/>
* In either case the activation info is written as libgourou-compatible XML files to the output folder.<br/><br/>
* This class is separate from the {@link AdeptActivate} class to permit automated testing of the application logic.
*/
@Command(name = "adept_activate", mixinStandardHelpOptions = true,
version = "Adept Activate (Java) 0.1.0",
description = "Creates new device files used by Adobe Adept processing")
public class AdeptActivateCli implements Runnable
{
// Command-line options are defined using picocli annotations.
@Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
private boolean helpRequested;
@Option(names = {"-A", "--anonymous"}, description = "Anonymous account, no username/password needed.\n"
+ "If present, username and password will be ignored.")
private boolean anonymous;
@Option(names = {"-u", "--username"}, description = "AdobeID username (e.g., adobe.com email account).")
private String username;
@Option(names = {"-p", "--password"}, description = "AdobeID password (will be prompted if not provided).")
private String password;
@Option(names = {"-D", "--adept-directory"}, description = "adept directory that will contains device.xml, " +
"activation.xml and devicesalt (default: {user.home}/.config/jadept)")
String outputDir;
@Option(names = {"-a", "--activation-file"}, description = "activation file name (optional)")
private String activationFileName = "activation.xml";
@Option(names = {"-H", "--hobbes-version"}, description = "Force RMSDK version (default: ${DEFAULT-VALUE}).",
defaultValue = HOBBES_DEFAULT_VERSION)
private String hobbesVersion;
@Option(names = {"-r", "--random-serial"}, description = "Generate a random device serial.")
private boolean randomSerial;
@Option( names = {"-f", "--force" }, description = "overwrite existing activation file (device files will not be overwritten")
private boolean force = false;
@Option(names = {"-v", "--verbose"}, description = "increase logging verbosity")
private boolean verbose;
@Option(names = {"-V", "--version"}, description = "Display libgourou version.")
private boolean versionRequested;
@Option( names={ "--mac" }, description = "use Macintosh default file locations" )
private boolean isMac;
@Option( names = {"--win" }, description = "attempt to read adept settings from the Windows " +
"registry and create corresponding libgourou files")
private boolean isWin;
@Option( names = {"--acs"}, description = "URL of the Adobe Content Server activation service (optional)")
private String ACSServer = null;
@Option(names = {"-e", "--export-private-key"}, description = "Export the user's private key")
private boolean exportPrivateKey = false;
@Option(names = {"-d", "--debug"}, description = "Log debug messages; output network messages to the output directory")
private boolean debug = false;
private final Logger LOGGER = Logger.getLogger( AdeptActivate.class.getName() );
/**
* The main logic of the application, executed by picocli.
*/
@Override
public void run()
{
Level logLevel = verbose ? Level.FINEST : Level.INFO;
LOGGER.setLevel( logLevel );
if (versionRequested)
{
System.out.println("JGourou ver. 0.1.0 based on libgourou ver. 0.8.7" );
}
Activation.OS os = Activation.getOS();
// don't allow the windows flag if we're not on Windows OS.
if (!os.equals( Activation.OS.WINDOWS ))
isWin = false;
if (!isWin) // the following settings only matter if we're trying to create a new activation
{
if (null == username && null == password)
{
// No username or password specified, use "anonymous" by default
anonymous = true;
}
if (username != null && anonymous)
{
System.err.println( "Error: Must use either --username OR --anonymous, but not both." );
CommandLine.usage( this, System.err );
return;
}
if (anonymous)
{
username = "anonymous";
password = "";
}
// username was provided; get password if not provided
else if (password == null)
{
Console console = System.console();
if (console == null)
{
System.err.println( "Error: Cannot get password. No console available." );
return;
}
char[] passwordArray = console.readPassword( "Enter password for <%s>: ", username );
password = new String( passwordArray );
}
}
// --- Determine and validate output directory ---
String finalOutputDir;
if (outputDir == null || outputDir.isEmpty())
{
finalOutputDir = Activation.getDefaultAdeptDir();
}
else
{
finalOutputDir = Paths.get( outputDir ).toAbsolutePath().toString();
}
Path outPath = Paths.get( finalOutputDir, activationFileName );
if (Files.exists( outPath ) && !force && !exportPrivateKey)
{
System.out.println( "!! Warning !! : " + finalOutputDir + " already exists." );
System.out.print( "Activation data will be overwritten. Would you like to continue? [y/N] " );
try (Scanner scanner = new Scanner( System.in ))
{
String response = scanner.nextLine();
if (!response.equalsIgnoreCase( "y" ))
{
System.out.println( "Operation cancelled." );
return;
}
}
}
// --- Run the activation logic ---
LOGGER.log( Level.INFO, "Starting ADEPT activation process..." );
try
{
Activation activation;
Device device;
Path dirPath = Paths.get( finalOutputDir );
if (!Files.exists( dirPath ))
{
Files.createDirectories( dirPath );
}
AdeptActivate activator = null;
if (isWin)
{
activation = new WinActivation( outPath,null, logLevel ); // We're reading activation info
// from the Windows registry, so no network access should be required.
device = new WinDevice( (WinActivation) activation, logLevel ); // Updates the privateLicenseKey in activation
((WinActivation) activation).updateFromWindows( dirPath, (WinActivation) activation,
(WinDevice) device );
}
else if (!exportPrivateKey || force) // If we're exporting the public key don't
// create new activation files, UNLESS we're also forcing output
{
if (ACSServer == null || ACSServer.isEmpty())
{ // use default value
ACSServer = "https://adeactivate.adobe.com/adept"; // ACS_SERVER
}
device = // Device.createDevice( finalOutputDir, hobbesVersion, randomSerial );
new Device( dirPath.resolve( "device.xml" ), dirPath.resolve( "devicesalt" ),
hobbesVersion, randomSerial, logLevel );
activation = Activation.createActivation( outPath, ACSServer, new HttpUtils(), logLevel );
activator = new AdeptActivate( activation, device, finalOutputDir );
activator.setLogLevel( LOGGER.getLevel() );
// These two methods are split up for testing purposes.
activator.signIn( activator.buildSignInRequest( username, password,
activation.getAuthenticationCertificate() ));
activator.activateDevice( activator.buildActivateReq());
}
else // exporting the private key based on existing libgourou files.
{
activation = new Activation(outPath, new HttpUtils(), logLevel );
device = new Device( Paths.get( finalOutputDir, "device.xml"),
Paths.get(finalOutputDir, "devicesalt"), hobbesVersion, randomSerial, logLevel );
activator = new AdeptActivate( activation, device, finalOutputDir );
}
String message;
if (exportPrivateKey)
{
if (null == activator)
activator = new AdeptActivate( activation, device, finalOutputDir );
activator.exportPrivateLicenseKey();
message = "Private license key exported to " + finalOutputDir;
LOGGER.info( message );
System.out.println( message );
}
message = "User " + username + " fully signed and device activated in " + finalOutputDir;
LOGGER.info( message );
System.out.println( message );
}
catch (GourouException e)
{
System.out.println( "An error occurred during activation: " + e.getMessage() );
if (LOGGER.getLevel().intValue() < Level.FINER.intValue())
{
//noinspection CallToPrintStackTrace
e.printStackTrace();
}
}
catch (IOException e)
{
System.err.println( "Unable to write to the file system: " + e.getMessage());
}
catch (ParserConfigurationException | SAXException e)
{
System.err.println( "Unable to parse one or more XML Documents: " + e.getMessage());
}
catch (GeneralSecurityException e)
{
System.err.println( "Failure in the cryptographic process: " + e.getMessage());
}
catch (InterruptedException ignore) {}
}
/**
* Main entry point of the application.
*/
public static void main( String[] args )
{
int exitCode = new CommandLine( new AdeptActivateCli( ) ).execute( args );
System.exit( exitCode );
}
}