244 lines
11 KiB
Java
244 lines
11 KiB
Java
package de.vanitasvitae.sync_client;
|
|
|
|
import java.io.IOException;
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.NoSuchProviderException;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Scanner;
|
|
|
|
import javax.crypto.BadPaddingException;
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
import javax.crypto.NoSuchPaddingException;
|
|
|
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
|
import org.jivesoftware.smack.SmackConfiguration;
|
|
import org.jivesoftware.smack.SmackException;
|
|
import org.jivesoftware.smack.XMPPException;
|
|
import org.jivesoftware.smack.packet.Presence;
|
|
import org.jivesoftware.smack.roster.Roster;
|
|
import org.jivesoftware.smack.roster.RosterEntry;
|
|
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
|
import org.jivesoftware.smackx.omemo.OmemoManager;
|
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
|
|
|
import org.apache.commons.cli.CommandLine;
|
|
import org.apache.commons.cli.CommandLineParser;
|
|
import org.apache.commons.cli.DefaultParser;
|
|
import org.apache.commons.cli.HelpFormatter;
|
|
import org.apache.commons.cli.Option;
|
|
import org.apache.commons.cli.Options;
|
|
import org.apache.commons.cli.ParseException;
|
|
import org.jxmpp.jid.BareJid;
|
|
import org.jxmpp.jid.FullJid;
|
|
import org.jxmpp.jid.impl.JidCreate;
|
|
import org.jxmpp.stringprep.XmppStringprepException;
|
|
|
|
public class Main {
|
|
|
|
private static Options prepareOptions() {
|
|
Options options = new Options();
|
|
Option username = Option.builder("u").longOpt("username").required().argName("user@server.tld").hasArg().desc("Barejid of the XMPP account").build();
|
|
Option password = Option.builder("p").longOpt("password").required().argName("swordfish").hasArg().desc("Password of the XMPP account").build();
|
|
Option debug = Option.builder("d").longOpt("debug").desc("Start in debug mode").build();
|
|
Option directory = Option.builder("D").longOpt("directory").hasArg().required().desc("Directory to sync").build();
|
|
Option master = Option.builder("m").longOpt("master").desc("Start in master mode").build();
|
|
Option help = Option.builder("h").longOpt("help").desc("Display this help text").build();
|
|
options.addOption(username);
|
|
options.addOption(password);
|
|
options.addOption(debug);
|
|
options.addOption(directory);
|
|
options.addOption(master);
|
|
options.addOption(help);
|
|
return options;
|
|
}
|
|
|
|
public static Slave slave(String username, String password, String dir) throws IOException, InterruptedException,
|
|
XMPPException, SmackException, NoSuchPaddingException, CorruptedOmemoKeyException, InvalidKeyException,
|
|
NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException,
|
|
InvalidAlgorithmParameterException {
|
|
return new Slave(username, password, dir);
|
|
}
|
|
|
|
public static Master master(String username, String password, String dir) throws IOException, InterruptedException,
|
|
XMPPException, SmackException, CorruptedOmemoKeyException, InvalidKeyException, NoSuchAlgorithmException,
|
|
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchProviderException,
|
|
InvalidAlgorithmParameterException {
|
|
return new Master(username, password, dir);
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
Options options = prepareOptions();
|
|
HelpFormatter formatter = new HelpFormatter();
|
|
CommandLineParser parser = new DefaultParser();
|
|
|
|
CommandLine cmd = null;
|
|
try {
|
|
cmd = parser.parse(options, args);
|
|
} catch (ParseException e) {
|
|
formatter.printHelp("client", options, true);
|
|
return;
|
|
}
|
|
|
|
if (cmd.hasOption('h')) {
|
|
formatter.printHelp("client", options, true);
|
|
return;
|
|
}
|
|
|
|
String username = cmd.getOptionValue('u');
|
|
String password = cmd.getOptionValue('p');
|
|
String dir = cmd.getOptionValue('D');
|
|
|
|
if (cmd.hasOption('d')) {
|
|
SmackConfiguration.DEBUG = true;
|
|
}
|
|
|
|
Client client;
|
|
if (cmd.hasOption('m')) {
|
|
client = master(username, password, dir);
|
|
} else {
|
|
client = slave(username, password, dir);
|
|
}
|
|
|
|
client.login();
|
|
System.out.println("Ready!");
|
|
|
|
Scanner scanner = new Scanner(System.in);
|
|
|
|
outerloop: while (true) {
|
|
String line = scanner.nextLine();
|
|
if (line == null || line.isEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
String[] split = line.split(" ");
|
|
if (split.length == 0) {
|
|
continue;
|
|
}
|
|
|
|
Roster roster = Roster.getInstanceFor(client.connection);
|
|
OmemoManager omemoManager = OmemoManager.getInstanceFor(client.connection);
|
|
|
|
switch (split[0]) {
|
|
case "/add":
|
|
if (split.length != 2) {
|
|
System.out.println("Usage: /add user@server.tld");
|
|
for (RosterEntry r : roster.getEntries()) {
|
|
System.out.println(r.getJid());
|
|
}
|
|
continue outerloop;
|
|
}
|
|
try {
|
|
BareJid bareJid = getJid(split[1]);
|
|
roster.createEntry(getJid(split[1]), bareJid.getLocalpartOrNull().toString(), null);
|
|
} catch (XmppStringprepException e) {
|
|
System.out.println("Illegal BareJid.");
|
|
}
|
|
break;
|
|
|
|
case "/sync":
|
|
if (split.length != 2) {
|
|
System.out.println("Usage: /sync user@server.tld");
|
|
for (FullJid fullJid : client.remotes) {
|
|
System.out.println(fullJid.toString());
|
|
}
|
|
continue outerloop;
|
|
}
|
|
|
|
List<Presence> presences = roster.getPresences(getJid(split[1]));
|
|
|
|
if (presences.size() == 0) {
|
|
System.out.println("No device online!");
|
|
continue outerloop;
|
|
} else if (presences.size() == 1) {
|
|
client.addRemote(presences.get(0).getFrom().asFullJidOrThrow());
|
|
} else {
|
|
while (true) {
|
|
System.out.println("Please select a resource:");
|
|
for (int i = 0; i < presences.size(); i++) {
|
|
System.out.println(i + " " + presences.get(i).getFrom().asFullJidOrThrow().toString());
|
|
}
|
|
int selection = Integer.parseInt(scanner.nextLine());
|
|
if (selection < 0 || selection >= presences.size()) {
|
|
System.out.println("Invalid selection.");
|
|
continue;
|
|
}
|
|
client.addRemote(presences.get(selection).getFrom().asFullJidOrThrow());
|
|
continue outerloop;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "/trust":
|
|
if (split.length != 2) {
|
|
System.out.println("Usage: /trust user@server.tld");
|
|
continue outerloop;
|
|
}
|
|
|
|
BareJid jid = getJid(split[1]);
|
|
omemoManager.requestDeviceListUpdateFor(jid);
|
|
omemoManager.requestDeviceListUpdateFor(client.connection.getUser().asBareJid());
|
|
HashMap<OmemoDevice, OmemoFingerprint> theirFPs = omemoManager.getActiveFingerprints(jid);
|
|
HashMap<OmemoDevice, OmemoFingerprint> ourFPs = omemoManager.getActiveFingerprints(client.connection.getUser().asBareJid());
|
|
System.out.println("Their fingerprints: <deviceId>: <fingerprint>: <trusted>");
|
|
for (OmemoDevice d : theirFPs.keySet()) {
|
|
OmemoFingerprint f = theirFPs.get(d);
|
|
System.out.println(d.getDeviceId() + ": " + f + ": " + omemoManager.isTrustedOmemoIdentity(d, f));
|
|
System.out.println("Trust? (y/n)");
|
|
String a = scanner.nextLine();
|
|
if (a.isEmpty()) {
|
|
continue;
|
|
} else if (a.toLowerCase().equals("y")) {
|
|
omemoManager.trustOmemoIdentity(d, f);
|
|
System.out.println("Trusted.");
|
|
} else {
|
|
omemoManager.distrustOmemoIdentity(d, f);
|
|
System.out.println("Distrusted.");
|
|
}
|
|
}
|
|
|
|
System.out.println("Our fingerprints: <deviceId>: <fingerprint>: <trusted>");
|
|
for (OmemoDevice d : ourFPs.keySet()) {
|
|
OmemoFingerprint f = ourFPs.get(d);
|
|
System.out.println(d.getDeviceId() + ": " + f + ": " + omemoManager.isTrustedOmemoIdentity(d, f));
|
|
System.out.println("Trust? (y/n)");
|
|
String a = scanner.nextLine();
|
|
if (a.isEmpty()) {
|
|
continue;
|
|
} else if (a.toLowerCase().equals("y")) {
|
|
omemoManager.trustOmemoIdentity(d, f);
|
|
System.out.println("Trusted.");
|
|
} else {
|
|
omemoManager.distrustOmemoIdentity(d, f);
|
|
System.out.println("Distrusted.");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "/help":
|
|
System.out.println("Available commands: \n" +
|
|
"/add <user@server.tld>: Add user to roster, or display current roster.\n" +
|
|
"/sync <user@server.tld>: Add a users device to synced devices or display currently synced devices.\n" +
|
|
"/trust user@sever.tld: Trust a users OMEMO identities.\n" +
|
|
"/help: Show this help text.\n" +
|
|
"/quit: Quit the application.");
|
|
break;
|
|
|
|
case "/quit":
|
|
((AbstractXMPPConnection) client.connection).disconnect(new Presence(Presence.Type.unavailable, "Shutdown", 100, Presence.Mode.away));
|
|
break outerloop;
|
|
}
|
|
}
|
|
scanner.close();
|
|
System.exit(0);
|
|
}
|
|
|
|
public static BareJid getJid(String string) throws XmppStringprepException {
|
|
return JidCreate.bareFrom(string);
|
|
}
|
|
}
|