2017-08-11 13:47:54 +02:00
package de.vanitasvitae.sync_client ;
import java.io.IOException ;
2017-08-15 17:44:02 +02:00
import java.security.InvalidAlgorithmParameterException ;
import java.security.InvalidKeyException ;
import java.security.NoSuchAlgorithmException ;
import java.security.NoSuchProviderException ;
import java.util.HashMap ;
2017-08-11 13:47:54 +02:00
import java.util.List ;
import java.util.Scanner ;
2017-08-15 17:44:02 +02:00
import javax.crypto.BadPaddingException ;
import javax.crypto.IllegalBlockSizeException ;
import javax.crypto.NoSuchPaddingException ;
2017-08-11 13:47:54 +02:00
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 ;
2017-08-15 17:44:02 +02:00
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 ;
2017-08-11 13:47:54 +02:00
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 ( ) ;
2017-08-17 22:48:51 +02:00
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 ( " Print debug messages to the console. " ) . build ( ) ;
Option directory = Option . builder ( " D " ) . longOpt ( " directory " ) . hasArg ( ) . required ( ) . desc ( " Directory which will be synced. " ) . build ( ) ;
Option master = Option . builder ( " m " ) . longOpt ( " master " ) . desc ( " Start in master mode. When this flag is missing, the client will be started in slave mode. " ) . build ( ) ;
Option help = Option . builder ( " h " ) . longOpt ( " help " ) . desc ( " Display this help text. " ) . build ( ) ;
2017-08-11 13:47:54 +02:00
options . addOption ( username ) ;
options . addOption ( password ) ;
options . addOption ( debug ) ;
options . addOption ( directory ) ;
options . addOption ( master ) ;
2017-08-15 17:53:50 +02:00
options . addOption ( help ) ;
2017-08-11 13:47:54 +02:00
return options ;
}
2017-08-15 17:44:02 +02:00
public static Slave slave ( String username , String password , String dir ) throws IOException , InterruptedException ,
XMPPException , SmackException , NoSuchPaddingException , CorruptedOmemoKeyException , InvalidKeyException ,
NoSuchAlgorithmException , IllegalBlockSizeException , BadPaddingException , NoSuchProviderException ,
InvalidAlgorithmParameterException {
2017-08-11 13:47:54 +02:00
return new Slave ( username , password , dir ) ;
}
2017-08-15 17:44:02 +02:00
public static Master master ( String username , String password , String dir ) throws IOException , InterruptedException ,
XMPPException , SmackException , CorruptedOmemoKeyException , InvalidKeyException , NoSuchAlgorithmException ,
NoSuchPaddingException , BadPaddingException , IllegalBlockSizeException , NoSuchProviderException ,
InvalidAlgorithmParameterException {
2017-08-11 13:47:54 +02:00
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 ;
}
2017-08-15 17:53:50 +02:00
if ( cmd . hasOption ( 'h' ) ) {
formatter . printHelp ( " client " , options , true ) ;
return ;
}
2017-08-11 13:47:54 +02:00
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 ) ;
2017-08-15 17:44:02 +02:00
OmemoManager omemoManager = OmemoManager . getInstanceFor ( client . connection ) ;
2017-08-11 13:47:54 +02:00
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 ;
2017-08-15 17:44:02 +02:00
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 ;
2017-08-15 17:53:50 +02:00
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 ;
2017-08-11 13:47:54 +02:00
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 ) ;
}
}