2014-10-22 16:38:44 +00:00
package eu.siacs.conversations.parser ;
2015-07-05 20:53:34 +00:00
import android.support.annotation.NonNull ;
2015-06-25 14:58:24 +00:00
import android.util.Base64 ;
2014-11-17 19:45:00 +00:00
import android.util.Log ;
2015-10-16 21:48:42 +00:00
import android.util.Pair ;
2014-11-17 19:45:00 +00:00
2015-06-25 14:58:24 +00:00
import org.whispersystems.libaxolotl.IdentityKey ;
import org.whispersystems.libaxolotl.ecc.Curve ;
import org.whispersystems.libaxolotl.ecc.ECPublicKey ;
import org.whispersystems.libaxolotl.state.PreKeyBundle ;
2015-10-16 21:48:42 +00:00
import java.io.ByteArrayInputStream ;
import java.security.cert.CertificateException ;
import java.security.cert.CertificateFactory ;
import java.security.cert.X509Certificate ;
2014-12-21 20:43:58 +00:00
import java.util.ArrayList ;
import java.util.Collection ;
2015-06-25 14:58:24 +00:00
import java.util.HashMap ;
2015-06-29 12:18:11 +00:00
import java.util.HashSet ;
2015-06-25 14:58:24 +00:00
import java.util.List ;
import java.util.Map ;
2015-06-29 12:18:11 +00:00
import java.util.Set ;
2014-12-21 20:43:58 +00:00
2014-11-17 19:45:00 +00:00
import eu.siacs.conversations.Config ;
2015-07-08 15:44:24 +00:00
import eu.siacs.conversations.crypto.axolotl.AxolotlService ;
2014-10-22 16:38:44 +00:00
import eu.siacs.conversations.entities.Account ;
import eu.siacs.conversations.entities.Contact ;
import eu.siacs.conversations.services.XmppConnectionService ;
2014-12-21 20:43:58 +00:00
import eu.siacs.conversations.utils.Xmlns ;
2014-10-22 16:38:44 +00:00
import eu.siacs.conversations.xml.Element ;
import eu.siacs.conversations.xmpp.OnIqPacketReceived ;
2014-12-21 20:43:58 +00:00
import eu.siacs.conversations.xmpp.OnUpdateBlocklist ;
2014-11-05 20:55:47 +00:00
import eu.siacs.conversations.xmpp.jid.Jid ;
2014-10-22 16:38:44 +00:00
import eu.siacs.conversations.xmpp.stanzas.IqPacket ;
public class IqParser extends AbstractParser implements OnIqPacketReceived {
2014-12-21 20:43:58 +00:00
public IqParser ( final XmppConnectionService service ) {
2014-10-22 16:38:44 +00:00
super ( service ) ;
}
2014-12-23 17:09:29 +00:00
private void rosterItems ( final Account account , final Element query ) {
2014-12-21 20:43:58 +00:00
final String version = query . getAttribute ( " ver " ) ;
2014-10-22 16:38:44 +00:00
if ( version ! = null ) {
account . getRoster ( ) . setVersion ( version ) ;
}
2014-12-21 20:43:58 +00:00
for ( final Element item : query . getChildren ( ) ) {
2014-10-22 16:38:44 +00:00
if ( item . getName ( ) . equals ( " item " ) ) {
2014-12-01 09:25:36 +00:00
final Jid jid = item . getAttributeAsJid ( " jid " ) ;
if ( jid = = null ) {
2014-12-19 12:40:16 +00:00
continue ;
2014-12-01 09:25:36 +00:00
}
2014-12-21 20:43:58 +00:00
final String name = item . getAttribute ( " name " ) ;
final String subscription = item . getAttribute ( " subscription " ) ;
final Contact contact = account . getRoster ( ) . getContact ( jid ) ;
2016-05-19 08:47:27 +00:00
boolean bothPre = contact . getOption ( Contact . Options . TO ) & & contact . getOption ( Contact . Options . FROM ) ;
2014-10-22 16:38:44 +00:00
if ( ! contact . getOption ( Contact . Options . DIRTY_PUSH ) ) {
contact . setServerName ( name ) ;
2014-11-16 16:21:21 +00:00
contact . parseGroupsFromElement ( item ) ;
2014-10-22 16:38:44 +00:00
}
if ( subscription ! = null ) {
if ( subscription . equals ( " remove " ) ) {
contact . resetOption ( Contact . Options . IN_ROSTER ) ;
contact . resetOption ( Contact . Options . DIRTY_DELETE ) ;
contact . resetOption ( Contact . Options . PREEMPTIVE_GRANT ) ;
} else {
contact . setOption ( Contact . Options . IN_ROSTER ) ;
contact . resetOption ( Contact . Options . DIRTY_PUSH ) ;
contact . parseSubscriptionFromElement ( item ) ;
}
}
2016-05-19 08:47:27 +00:00
boolean both = contact . getOption ( Contact . Options . TO ) & & contact . getOption ( Contact . Options . FROM ) ;
if ( ( both ! = bothPre ) & & both ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : gained mutual presence subscription with " + contact . getJid ( ) ) ;
AxolotlService axolotlService = account . getAxolotlService ( ) ;
if ( axolotlService ! = null ) {
axolotlService . clearErrorsInFetchStatusMap ( contact . getJid ( ) ) ;
}
}
2014-11-17 20:28:16 +00:00
mXmppConnectionService . getAvatarService ( ) . clear ( contact ) ;
2014-10-22 16:38:44 +00:00
}
}
2014-11-17 20:28:16 +00:00
mXmppConnectionService . updateConversationUi ( ) ;
2014-10-22 16:38:44 +00:00
mXmppConnectionService . updateRosterUi ( ) ;
}
2014-12-21 20:43:58 +00:00
public String avatarData ( final IqPacket packet ) {
final Element pubsub = packet . findChild ( " pubsub " ,
2015-06-26 13:41:02 +00:00
" http://jabber.org/protocol/pubsub " ) ;
2014-10-22 16:38:44 +00:00
if ( pubsub = = null ) {
return null ;
}
2014-12-21 20:43:58 +00:00
final Element items = pubsub . findChild ( " items " ) ;
2014-10-22 16:38:44 +00:00
if ( items = = null ) {
return null ;
}
return super . avatarData ( items ) ;
}
2015-06-25 14:58:24 +00:00
public Element getItem ( final IqPacket packet ) {
final Element pubsub = packet . findChild ( " pubsub " ,
" http://jabber.org/protocol/pubsub " ) ;
if ( pubsub = = null ) {
return null ;
}
final Element items = pubsub . findChild ( " items " ) ;
if ( items = = null ) {
return null ;
}
return items . findChild ( " item " ) ;
}
2015-07-05 20:53:34 +00:00
@NonNull
2015-06-29 12:18:11 +00:00
public Set < Integer > deviceIds ( final Element item ) {
Set < Integer > deviceIds = new HashSet < > ( ) ;
2015-07-05 20:53:34 +00:00
if ( item ! = null ) {
final Element list = item . findChild ( " list " ) ;
if ( list ! = null ) {
for ( Element device : list . getChildren ( ) ) {
if ( ! device . getName ( ) . equals ( " device " ) ) {
continue ;
}
try {
Integer id = Integer . valueOf ( device . getAttribute ( " id " ) ) ;
deviceIds . add ( id ) ;
} catch ( NumberFormatException e ) {
2015-12-12 14:58:22 +00:00
Log . e ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Encountered invalid <device> node in PEP ( " + e . getMessage ( ) + " ): " + device . toString ( ) + " , skipping... " ) ;
2015-07-05 20:53:34 +00:00
continue ;
}
}
2015-06-25 14:58:24 +00:00
}
}
return deviceIds ;
}
public Integer signedPreKeyId ( final Element bundle ) {
final Element signedPreKeyPublic = bundle . findChild ( " signedPreKeyPublic " ) ;
if ( signedPreKeyPublic = = null ) {
return null ;
}
return Integer . valueOf ( signedPreKeyPublic . getAttribute ( " signedPreKeyId " ) ) ;
}
public ECPublicKey signedPreKeyPublic ( final Element bundle ) {
ECPublicKey publicKey = null ;
final Element signedPreKeyPublic = bundle . findChild ( " signedPreKeyPublic " ) ;
if ( signedPreKeyPublic = = null ) {
return null ;
}
try {
publicKey = Curve . decodePoint ( Base64 . decode ( signedPreKeyPublic . getContent ( ) , Base64 . DEFAULT ) , 0 ) ;
2016-02-11 11:26:43 +00:00
} catch ( Throwable e ) {
2015-07-08 15:44:24 +00:00
Log . e ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Invalid signedPreKeyPublic in PEP: " + e . getMessage ( ) ) ;
2015-06-25 14:58:24 +00:00
}
return publicKey ;
}
public byte [ ] signedPreKeySignature ( final Element bundle ) {
final Element signedPreKeySignature = bundle . findChild ( " signedPreKeySignature " ) ;
if ( signedPreKeySignature = = null ) {
return null ;
}
2015-08-30 09:11:54 +00:00
try {
return Base64 . decode ( signedPreKeySignature . getContent ( ) , Base64 . DEFAULT ) ;
2016-02-11 11:26:43 +00:00
} catch ( Throwable e ) {
2015-08-30 09:11:54 +00:00
Log . e ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : Invalid base64 in signedPreKeySignature " ) ;
return null ;
}
2015-06-25 14:58:24 +00:00
}
public IdentityKey identityKey ( final Element bundle ) {
IdentityKey identityKey = null ;
final Element identityKeyElement = bundle . findChild ( " identityKey " ) ;
if ( identityKeyElement = = null ) {
return null ;
}
try {
identityKey = new IdentityKey ( Base64 . decode ( identityKeyElement . getContent ( ) , Base64 . DEFAULT ) , 0 ) ;
2016-02-11 11:26:43 +00:00
} catch ( Throwable e ) {
2015-07-08 15:44:24 +00:00
Log . e ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Invalid identityKey in PEP: " + e . getMessage ( ) ) ;
2015-06-25 14:58:24 +00:00
}
return identityKey ;
}
public Map < Integer , ECPublicKey > preKeyPublics ( final IqPacket packet ) {
Map < Integer , ECPublicKey > preKeyRecords = new HashMap < > ( ) ;
2015-06-29 12:18:11 +00:00
Element item = getItem ( packet ) ;
if ( item = = null ) {
2015-07-08 15:44:24 +00:00
Log . d ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Couldn't find <item> in bundle IQ packet: " + packet ) ;
2015-06-29 12:18:11 +00:00
return null ;
}
final Element bundleElement = item . findChild ( " bundle " ) ;
if ( bundleElement = = null ) {
2015-06-26 13:41:02 +00:00
return null ;
}
2015-06-29 12:18:11 +00:00
final Element prekeysElement = bundleElement . findChild ( " prekeys " ) ;
2015-06-26 13:41:02 +00:00
if ( prekeysElement = = null ) {
2015-07-08 15:44:24 +00:00
Log . d ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Couldn't find <prekeys> in bundle IQ packet: " + packet ) ;
2015-06-26 13:41:02 +00:00
return null ;
}
2015-06-25 14:58:24 +00:00
for ( Element preKeyPublicElement : prekeysElement . getChildren ( ) ) {
if ( ! preKeyPublicElement . getName ( ) . equals ( " preKeyPublic " ) ) {
2015-07-08 15:44:24 +00:00
Log . d ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Encountered unexpected tag in prekeys list: " + preKeyPublicElement ) ;
2015-06-25 14:58:24 +00:00
continue ;
}
Integer preKeyId = Integer . valueOf ( preKeyPublicElement . getAttribute ( " preKeyId " ) ) ;
try {
ECPublicKey preKeyPublic = Curve . decodePoint ( Base64 . decode ( preKeyPublicElement . getContent ( ) , Base64 . DEFAULT ) , 0 ) ;
preKeyRecords . put ( preKeyId , preKeyPublic ) ;
2016-02-11 11:26:43 +00:00
} catch ( Throwable e ) {
2015-07-08 15:44:24 +00:00
Log . e ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " Invalid preKeyPublic (ID= " + preKeyId + " ) in PEP: " + e . getMessage ( ) + " , skipping... " ) ;
2015-06-25 14:58:24 +00:00
continue ;
}
}
return preKeyRecords ;
}
2015-10-16 21:48:42 +00:00
public Pair < X509Certificate [ ] , byte [ ] > verification ( final IqPacket packet ) {
Element item = getItem ( packet ) ;
Element verification = item ! = null ? item . findChild ( " verification " , AxolotlService . PEP_PREFIX ) : null ;
Element chain = verification ! = null ? verification . findChild ( " chain " ) : null ;
Element signature = verification ! = null ? verification . findChild ( " signature " ) : null ;
if ( chain ! = null & & signature ! = null ) {
List < Element > certElements = chain . getChildren ( ) ;
X509Certificate [ ] certificates = new X509Certificate [ certElements . size ( ) ] ;
try {
CertificateFactory certificateFactory = CertificateFactory . getInstance ( " X.509 " ) ;
int i = 0 ;
for ( Element cert : certElements ) {
certificates [ i ] = ( X509Certificate ) certificateFactory . generateCertificate ( new ByteArrayInputStream ( Base64 . decode ( cert . getContent ( ) , Base64 . DEFAULT ) ) ) ;
+ + i ;
}
return new Pair < > ( certificates , Base64 . decode ( signature . getContent ( ) , Base64 . DEFAULT ) ) ;
} catch ( CertificateException e ) {
return null ;
}
} else {
return null ;
}
}
2015-06-26 13:41:02 +00:00
public PreKeyBundle bundle ( final IqPacket bundle ) {
Element bundleItem = getItem ( bundle ) ;
if ( bundleItem = = null ) {
return null ;
}
final Element bundleElement = bundleItem . findChild ( " bundle " ) ;
if ( bundleElement = = null ) {
return null ;
}
ECPublicKey signedPreKeyPublic = signedPreKeyPublic ( bundleElement ) ;
Integer signedPreKeyId = signedPreKeyId ( bundleElement ) ;
byte [ ] signedPreKeySignature = signedPreKeySignature ( bundleElement ) ;
IdentityKey identityKey = identityKey ( bundleElement ) ;
if ( signedPreKeyPublic = = null | | identityKey = = null ) {
return null ;
}
return new PreKeyBundle ( 0 , 0 , 0 , null ,
signedPreKeyId , signedPreKeyPublic , signedPreKeySignature , identityKey ) ;
}
2015-06-25 14:58:24 +00:00
public List < PreKeyBundle > preKeys ( final IqPacket preKeys ) {
List < PreKeyBundle > bundles = new ArrayList < > ( ) ;
Map < Integer , ECPublicKey > preKeyPublics = preKeyPublics ( preKeys ) ;
2015-06-26 13:41:02 +00:00
if ( preKeyPublics ! = null ) {
for ( Integer preKeyId : preKeyPublics . keySet ( ) ) {
ECPublicKey preKeyPublic = preKeyPublics . get ( preKeyId ) ;
bundles . add ( new PreKeyBundle ( 0 , 0 , preKeyId , preKeyPublic ,
0 , null , null , null ) ) ;
}
}
2015-06-25 14:58:24 +00:00
return bundles ;
}
2014-10-22 16:38:44 +00:00
@Override
2014-12-21 20:43:58 +00:00
public void onIqPacketReceived ( final Account account , final IqPacket packet ) {
2015-08-23 15:53:23 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . ERROR | | packet . getType ( ) = = IqPacket . TYPE . TIMEOUT ) {
2015-08-23 06:01:47 +00:00
return ;
} else if ( packet . hasChild ( " query " , Xmlns . ROSTER ) & & packet . fromServer ( account ) ) {
2014-12-21 01:13:13 +00:00
final Element query = packet . findChild ( " query " ) ;
// If this is in response to a query for the whole roster:
2014-12-30 13:16:25 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-12-21 01:13:13 +00:00
account . getRoster ( ) . markAllAsNotInRoster ( ) ;
2014-10-22 16:38:44 +00:00
}
2014-12-21 01:13:13 +00:00
this . rosterItems ( account , query ) ;
} else if ( ( packet . hasChild ( " block " , Xmlns . BLOCKING ) | | packet . hasChild ( " blocklist " , Xmlns . BLOCKING ) ) & &
2014-12-30 13:50:51 +00:00
packet . fromServer ( account ) ) {
2014-12-21 01:13:13 +00:00
// Block list or block push.
Log . d ( Config . LOGTAG , " Received blocklist update from server " ) ;
final Element blocklist = packet . findChild ( " blocklist " , Xmlns . BLOCKING ) ;
final Element block = packet . findChild ( " block " , Xmlns . BLOCKING ) ;
final Collection < Element > items = blocklist ! = null ? blocklist . getChildren ( ) :
( block ! = null ? block . getChildren ( ) : null ) ;
// If this is a response to a blocklist query, clear the block list and replace with the new one.
// Otherwise, just update the existing blocklist.
2014-12-30 13:16:25 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-12-21 01:13:13 +00:00
account . clearBlocklist ( ) ;
2015-01-05 15:17:05 +00:00
account . getXmppConnection ( ) . getFeatures ( ) . setBlockListRequested ( true ) ;
2014-12-21 01:13:13 +00:00
}
if ( items ! = null ) {
final Collection < Jid > jids = new ArrayList < > ( items . size ( ) ) ;
// Create a collection of Jids from the packet
for ( final Element item : items ) {
if ( item . getName ( ) . equals ( " item " ) ) {
final Jid jid = item . getAttributeAsJid ( " jid " ) ;
if ( jid ! = null ) {
jids . add ( jid ) ;
2014-12-21 20:43:58 +00:00
}
}
}
2014-12-21 01:13:13 +00:00
account . getBlocklist ( ) . addAll ( jids ) ;
2014-12-21 20:43:58 +00:00
}
2014-12-21 01:13:13 +00:00
// Update the UI
mXmppConnectionService . updateBlocklistUi ( OnUpdateBlocklist . Status . BLOCKED ) ;
2016-06-22 10:22:36 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . SET ) {
final IqPacket response = packet . generateResponse ( IqPacket . TYPE . RESULT ) ;
mXmppConnectionService . sendIqPacket ( account , response , null ) ;
}
2014-12-21 01:13:13 +00:00
} else if ( packet . hasChild ( " unblock " , Xmlns . BLOCKING ) & &
2014-12-30 13:50:51 +00:00
packet . fromServer ( account ) & & packet . getType ( ) = = IqPacket . TYPE . SET ) {
2014-12-21 01:13:13 +00:00
Log . d ( Config . LOGTAG , " Received unblock update from server " ) ;
final Collection < Element > items = packet . findChild ( " unblock " , Xmlns . BLOCKING ) . getChildren ( ) ;
if ( items . size ( ) = = 0 ) {
// No children to unblock == unblock all
account . getBlocklist ( ) . clear ( ) ;
} else {
final Collection < Jid > jids = new ArrayList < > ( items . size ( ) ) ;
for ( final Element item : items ) {
if ( item . getName ( ) . equals ( " item " ) ) {
final Jid jid = item . getAttributeAsJid ( " jid " ) ;
if ( jid ! = null ) {
jids . add ( jid ) ;
2014-12-21 20:43:58 +00:00
}
}
}
2014-12-21 01:13:13 +00:00
account . getBlocklist ( ) . removeAll ( jids ) ;
2014-12-21 20:43:58 +00:00
}
2014-12-21 01:13:13 +00:00
mXmppConnectionService . updateBlocklistUi ( OnUpdateBlocklist . Status . UNBLOCKED ) ;
2016-06-22 10:22:36 +00:00
final IqPacket response = packet . generateResponse ( IqPacket . TYPE . RESULT ) ;
mXmppConnectionService . sendIqPacket ( account , response , null ) ;
2014-12-21 01:13:13 +00:00
} else if ( packet . hasChild ( " open " , " http://jabber.org/protocol/ibb " )
| | packet . hasChild ( " data " , " http://jabber.org/protocol/ibb " ) ) {
mXmppConnectionService . getJingleConnectionManager ( )
. deliverIbbPacket ( account , packet ) ;
} else if ( packet . hasChild ( " query " , " http://jabber.org/protocol/disco#info " ) ) {
2015-02-16 09:06:09 +00:00
final IqPacket response = mXmppConnectionService . getIqGenerator ( ) . discoResponse ( packet ) ;
mXmppConnectionService . sendIqPacket ( account , response , null ) ;
} else if ( packet . hasChild ( " query " , " jabber:iq:version " ) ) {
final IqPacket response = mXmppConnectionService . getIqGenerator ( ) . versionResponse ( packet ) ;
mXmppConnectionService . sendIqPacket ( account , response , null ) ;
2014-12-21 01:13:13 +00:00
} else if ( packet . hasChild ( " ping " , " urn:xmpp:ping " ) ) {
2014-12-30 13:16:25 +00:00
final IqPacket response = packet . generateResponse ( IqPacket . TYPE . RESULT ) ;
2014-12-21 01:13:13 +00:00
mXmppConnectionService . sendIqPacket ( account , response , null ) ;
2014-10-22 16:38:44 +00:00
} else {
2015-08-23 06:01:47 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . GET | | packet . getType ( ) = = IqPacket . TYPE . SET ) {
2014-12-30 13:16:25 +00:00
final IqPacket response = packet . generateResponse ( IqPacket . TYPE . ERROR ) ;
2014-12-21 01:13:13 +00:00
final Element error = response . addChild ( " error " ) ;
error . setAttribute ( " type " , " cancel " ) ;
2015-08-23 06:01:47 +00:00
error . addChild ( " feature-not-implemented " , " urn:ietf:params:xml:ns:xmpp-stanzas " ) ;
2014-10-22 16:38:44 +00:00
account . getXmppConnection ( ) . sendIqPacket ( response , null ) ;
2015-08-23 06:01:47 +00:00
}
2014-10-22 16:38:44 +00:00
}
}
}