/*
* Created on Apr 10, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package com.aelitis.azureus.plugins.net.buddy;
import java.security.SecureRandom;
import java.util.*;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.Base32;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.RandomUtils;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.torrent.Torrent;
import org.gudy.azureus2.plugins.ui.UIManagerEvent;
import com.aelitis.azureus.core.util.CopyOnWriteList;
public class
BuddyPluginAZ2
{
public static final int RT_AZ2_REQUEST_MESSAGE = 1;
public static final int RT_AZ2_REPLY_MESSAGE = 2;
public static final int RT_AZ2_REQUEST_SEND_TORRENT = 3;
public static final int RT_AZ2_REPLY_SEND_TORRENT = 4;
public static final int RT_AZ2_REQUEST_CHAT = 5;
public static final int RT_AZ2_REPLY_CHAT = 6;
public static final int RT_AZ2_REQUEST_TRACK = 7;
public static final int RT_AZ2_REPLY_TRACK = 8;
public static final int RT_AZ2_REQUEST_RSS = 9;
public static final int RT_AZ2_REPLY_RSS = 10;
public static final int CHAT_MSG_TYPE_TEXT = 1;
public static final int CHAT_MSG_TYPE_PARTICIPANTS_ADDED = 2;
public static final int CHAT_MSG_TYPE_PARTICIPANTS_REMOVED = 3;
private static final int SEND_TIMEOUT = 2*60*1000;
private BuddyPlugin plugin;
private Map chats = new HashMap();
private CopyOnWriteList listeners = new CopyOnWriteList();
private CopyOnWriteList track_listeners = new CopyOnWriteList();
protected
BuddyPluginAZ2(
BuddyPlugin _plugin )
{
plugin = _plugin;
plugin.addRequestListener(
new BuddyPluginBuddyRequestListener()
{
public Map
requestReceived(
BuddyPluginBuddy from_buddy,
int subsystem,
Map request )
throws BuddyPluginException
{
if ( subsystem == BuddyPlugin.SUBSYSTEM_AZ2 ){
if ( !from_buddy.isAuthorised()){
throw( new BuddyPluginException( "Unauthorised" ));
}
return( processAZ2Request( from_buddy, request ));
}
return( null );
}
public void
pendingMessages(
BuddyPluginBuddy[] from_buddies )
{
}
});
}
protected Map
processAZ2Request(
final BuddyPluginBuddy from_buddy,
Map request )
throws BuddyPluginException
{
logMessage( "AZ2 request received: " + from_buddy.getString() + " -> " + request );
int type = ((Long)request.get( "type" )).intValue();
Map reply = new HashMap();
if ( type == RT_AZ2_REQUEST_MESSAGE ){
try{
String msg = new String( (byte[])request.get( "msg" ), "UTF8" );
from_buddy.setLastMessageReceived( msg );
}catch( Throwable e ){
}
reply.put( "type", new Long( RT_AZ2_REPLY_MESSAGE ));
}else if ( type == RT_AZ2_REQUEST_SEND_TORRENT ){
try{
final Torrent torrent = plugin.getPluginInterface().getTorrentManager().createFromBEncodedData((byte[])request.get( "torrent" ));
new AEThread2( "torrentAdder", true )
{
public void
run()
{
PluginInterface pi = plugin.getPluginInterface();
String msg = pi.getUtilities().getLocaleUtilities().getLocalisedMessageText(
"azbuddy.addtorrent.msg",
new String[]{ from_buddy.getName(), torrent.getName() });
long res = pi.getUIManager().showMessageBox(
"azbuddy.addtorrent.title",
"!" + msg + "!",
UIManagerEvent.MT_YES | UIManagerEvent.MT_NO );
if ( res == UIManagerEvent.MT_YES ){
pi.getUIManager().openTorrent( torrent );
}
}
}.start();
reply.put( "type", new Long( RT_AZ2_REPLY_SEND_TORRENT ));
}catch( Throwable e ){
throw( new BuddyPluginException( "Torrent receive failed " + type ));
}
}else if ( type == RT_AZ2_REQUEST_CHAT ){
Map msg = (Map)request.get( "msg" );
String id = new String((byte[])msg.get( "id" ));
chatInstance chat;
boolean new_chat = false;
synchronized( chats ){
chat = (chatInstance)chats.get( id );
if ( chat == null ){
if ( chats.size() > 32 ){
throw( new BuddyPluginException( "Too many chats" ));
}
chat = new chatInstance( id );
chats.put( id, chat );
new_chat = true;
}
}
if ( new_chat ){
informCreated( chat );
}
chat.addParticipant( from_buddy );
chat.process( from_buddy, msg );
reply.put( "type", new Long( RT_AZ2_REPLY_CHAT ));
}else if ( type == RT_AZ2_REQUEST_TRACK ){
Map msg = (Map)request.get( "msg" );
Iterator it = track_listeners.iterator();
boolean ok = false;
while( it.hasNext()){
try{
Map res = ((BuddyPluginAZ2TrackerListener)it.next()).messageReceived( from_buddy, msg );
if ( res != null ){
reply.put( "msg", res );
reply.put( "type", new Long( RT_AZ2_REPLY_TRACK ));
ok = true;
break;
}
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
if ( !ok ){
throw( new BuddyPluginException( "Unhandled request type " + type ));
}
}else if ( type == RT_AZ2_REQUEST_RSS ){
try{
Map<String,Object> res = new HashMap<String, Object>();
reply.put( "msg", res );
reply.put( "type", new Long( RT_AZ2_REPLY_RSS ));
Map msg = (Map)request.get( "msg" );
String category = new String((byte[])msg.get( "cat"), "UTF-8" );
byte[] hash = (byte[])msg.get( "hash" );
if ( hash == null ){
byte[] if_mod = (byte[])msg.get( "if_mod" );
BuddyPlugin.feedDetails feed = plugin.getRSS( from_buddy, category, if_mod==null?null:new String( if_mod, "UTF-8" ));
res.put( "rss", feed.getContent());
res.put( "last_mod", feed.getLastModified());
}else{
res.put( "torrent", plugin.getRSSTorrent( from_buddy, category, hash ));
}
}catch( BuddyPluginException e ){
throw( e );
}catch( Throwable e ){
throw( new BuddyPluginException( "Failed to handle rss", e ));
}
}else{
throw( new BuddyPluginException( "Unrecognised request type " + type ));
}
logMessage( "AZ2 reply sent: " + from_buddy.getString() + " <- " + reply );
return( reply );
}
public chatInstance
createChat(
BuddyPluginBuddy[] buddies )
{
byte[] id_bytes = new byte[20];
RandomUtils.SECURE_RANDOM.nextBytes( id_bytes );
String id = Base32.encode( id_bytes );
chatInstance chat;
synchronized( chats ){
chat = new chatInstance( id );
chats.put( id, chat );
}
logMessage( "Chat " + chat.getID() + " created" );
informCreated( chat );
chat.addParticipants( buddies, true );
return( chat );
}
protected void
destroyChat(
chatInstance chat )
{
synchronized( chats ){
chats.remove( chat.getID());
}
logMessage( "Chat " + chat.getID() + " destroyed" );
informDestroyed( chat );
}
protected void
informCreated(
chatInstance chat )
{
Iterator it = listeners.iterator();
while( it.hasNext()){
((BuddyPluginAZ2Listener)it.next()).chatCreated( chat );
}
}
protected void
informDestroyed(
chatInstance chat )
{
Iterator it = listeners.iterator();
while( it.hasNext()){
((BuddyPluginAZ2Listener)it.next()).chatDestroyed( chat );
}
}
public void
sendAZ2Message(
BuddyPluginBuddy buddy,
String msg )
{
try{
Map request = new HashMap();
request.put( "type", new Long( RT_AZ2_REQUEST_MESSAGE ));
request.put( "msg", msg.getBytes());
sendMessage( buddy, request );
}catch( Throwable e ){
logMessageAndPopup( "Send message failed", e );
}
}
protected void
sendAZ2Chat(
BuddyPluginBuddy buddy,
Map msg )
{
try{
Map request = new HashMap();
request.put( "type", new Long( RT_AZ2_REQUEST_CHAT ));
request.put( "msg", msg );
sendMessage( buddy, request );
}catch( Throwable e ){
logMessageAndPopup( "Send message failed", e );
}
}
public void
sendAZ2Torrent(
Torrent torrent,
BuddyPluginBuddy buddy )
{
try{
Map request = new HashMap();
request.put( "type", new Long( RT_AZ2_REQUEST_SEND_TORRENT ));
request.put( "torrent", torrent.writeToBEncodedData());
sendMessage( buddy, request );
}catch( Throwable e ){
logMessageAndPopup( "Send torrent failed", e );
}
}
public void
sendAZ2TrackerMessage(
BuddyPluginBuddy buddy,
Map msg,
final BuddyPluginAZ2TrackerListener listener )
{
logMessage( "AZ2 request sent: " + buddy.getString() + " <- " + msg );
try{
Map request = new HashMap();
request.put( "type", new Long( RT_AZ2_REQUEST_TRACK ));
request.put( "msg", msg );
buddy.sendMessage(
BuddyPlugin.SUBSYSTEM_AZ2,
request,
SEND_TIMEOUT,
new BuddyPluginBuddyReplyListener()
{
public void
replyReceived(
BuddyPluginBuddy from_buddy,
Map reply )
{
int type = ((Long)reply.get( "type")).intValue();
if ( type != RT_AZ2_REPLY_TRACK ){
sendFailed( from_buddy, new BuddyPluginException( "Mismatched reply type" ));
}
listener.messageReceived( from_buddy, (Map)reply.get( "msg" ));
}
public void
sendFailed(
BuddyPluginBuddy to_buddy,
BuddyPluginException cause )
{
listener.messageFailed( to_buddy, cause );
}
});
}catch( Throwable e ){
logMessageAndPopup( "Send message failed", e );
}
}
public void
sendAZ2RSSMessage(
BuddyPluginBuddy buddy,
Map msg,
final BuddyPluginAZ2TrackerListener listener )
{
logMessage( "AZ2 request sent: " + buddy.getString() + " <- " + msg );
try{
Map request = new HashMap();
request.put( "type", new Long( RT_AZ2_REQUEST_RSS ));
request.put( "msg", msg );
buddy.sendMessage(
BuddyPlugin.SUBSYSTEM_AZ2,
request,
SEND_TIMEOUT,
new BuddyPluginBuddyReplyListener()
{
public void
replyReceived(
BuddyPluginBuddy from_buddy,
Map reply )
{
int type = ((Long)reply.get( "type")).intValue();
if ( type != RT_AZ2_REPLY_RSS ){
sendFailed( from_buddy, new BuddyPluginException( "Mismatched reply type" ));
}
listener.messageReceived( from_buddy, (Map)reply.get( "msg" ));
}
public void
sendFailed(
BuddyPluginBuddy to_buddy,
BuddyPluginException cause )
{
listener.messageFailed( to_buddy, cause );
}
});
}catch( Throwable e ){
logMessage( "Send message failed", e );
}
}
protected void
sendMessage(
BuddyPluginBuddy buddy,
Map request )
throws BuddyPluginException
{
logMessage( "AZ2 request sent: " + buddy.getString() + " <- " + request );
buddy.getMessageHandler().queueMessage(
BuddyPlugin.SUBSYSTEM_AZ2,
request,
SEND_TIMEOUT );
}
public void
addListener(
BuddyPluginAZ2Listener listener )
{
listeners.add( listener );
}
public void
removeListener(
BuddyPluginAZ2Listener listener )
{
listeners.remove( listener );
}
public void
addTrackerListener(
BuddyPluginAZ2TrackerListener listener )
{
track_listeners.add( listener );
}
public void
removeTrackerListener(
BuddyPluginAZ2TrackerListener listener )
{
track_listeners.remove( listener );
}
protected void
logMessageAndPopup(
String str,
Throwable e )
{
logMessageAndPopup( str + ": " + Debug.getNestedExceptionMessage(e));
}
protected void
logMessageAndPopup(
String str )
{
logMessage( str );
plugin.getPluginInterface().getUIManager().showMessageBox(
"azbuddy.msglog.title", "!" + str + "!", UIManagerEvent.MT_OK );
}
protected void
logMessage(
String str )
{
plugin.logMessage( str );
}
protected void
logMessage(
String str,
Throwable e )
{
plugin.logMessage( str + ": " + Debug.getNestedExceptionMessage(e));
}
public class
chatInstance
extends BuddyPluginAdapter
{
private String id;
private Map participants = new HashMap();
private CopyOnWriteList listeners = new CopyOnWriteList();
private List history = new ArrayList();
protected
chatInstance(
String _id )
{
id = _id;
plugin.addListener( this );
}
public String
getID()
{
return( id );
}
public void
buddyAdded(
BuddyPluginBuddy buddy )
{
buddyChanged( buddy );
}
public void
buddyRemoved(
BuddyPluginBuddy buddy )
{
chatParticipant p = getParticipant( buddy );
if ( p != null ){
Iterator it = listeners.iterator();
while( it.hasNext()){
try{
((BuddyPluginAZ2ChatListener)it.next()).participantRemoved( p );
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}
}
public void
buddyChanged(
BuddyPluginBuddy buddy )
{
chatParticipant p = getParticipant( buddy );
if ( p != null ){
Iterator it = listeners.iterator();
while( it.hasNext()){
try{
((BuddyPluginAZ2ChatListener)it.next()).participantChanged( p );
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}
}
protected void
process(
BuddyPluginBuddy from_buddy,
Map msg )
{
chatParticipant p = getOrAddParticipant( from_buddy );
int type = ((Long)msg.get( "type")).intValue();
if ( type == CHAT_MSG_TYPE_TEXT ){
Iterator it = listeners.iterator();
synchronized( history ){
history.add( new chatMessage( p.getName(), msg ));
if ( history.size() > 128 ){
history.remove(0);
}
}
while( it.hasNext()){
try{
((BuddyPluginAZ2ChatListener)it.next()).messageReceived( p, msg );
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}else if ( type == CHAT_MSG_TYPE_PARTICIPANTS_ADDED ){
List added = (List)msg.get( "p" );
for (int i=0;i<added.size();i++){
Map participant = (Map)added.get(i);
String pk = new String((byte[])participant.get( "pk" ));
if ( !pk.equals( plugin.getPublicKey())){
addParticipant( pk );
}
}
}
}
public void
sendMessage(
Map msg )
{
msg.put( "type", new Long( CHAT_MSG_TYPE_TEXT ));
sendMessageBase( msg );
}
protected void
sendMessageBase(
Map msg )
{
Map ps;
synchronized( participants ){
ps = new HashMap( participants );
}
msg.put( "id", id );
Iterator it = ps.values().iterator();
while( it.hasNext()){
chatParticipant participant = (chatParticipant)it.next();
if ( participant.isAuthorised()){
sendAZ2Chat( participant.getBuddy(), msg );
}
}
}
public chatMessage[]
getHistory()
{
synchronized( history ){
chatMessage[] res = new chatMessage[history.size()];
history.toArray( res );
return( res );
}
}
protected chatParticipant
getOrAddParticipant(
BuddyPluginBuddy buddy )
{
return( addParticipant( buddy ));
}
public chatParticipant
addParticipant(
String pk )
{
chatParticipant p;
BuddyPluginBuddy buddy = plugin.getBuddyFromPublicKey( pk );
synchronized( participants ){
p = (chatParticipant)participants.get( pk );
if ( p != null ){
return( p );
}
if ( buddy == null ){
p = new chatParticipant( pk );
}else{
p = new chatParticipant( buddy );
}
participants.put( pk, p );
}
Iterator it = listeners.iterator();
while( it.hasNext()){
try{
((BuddyPluginAZ2ChatListener)it.next()).participantAdded( p );
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
return( p );
}
public chatParticipant
addParticipant(
BuddyPluginBuddy buddy )
{
return( addParticipant( buddy.getPublicKey()));
}
public void
addParticipants(
BuddyPluginBuddy[] buddies,
boolean inform_others )
{
for (int i=0;i<buddies.length;i++ ){
addParticipant( buddies[i] );
}
if ( inform_others ){
Map msg = new HashMap();
msg.put( "type", new Long( CHAT_MSG_TYPE_PARTICIPANTS_ADDED ));
List added = new ArrayList();
msg.put( "p", added );
for ( int i=0;i<buddies.length;i++){
Map map = new HashMap();
map.put( "pk", buddies[i].getPublicKey());
added.add( map );
}
sendMessageBase( msg );
}
}
protected chatParticipant
getParticipant(
BuddyPluginBuddy b )
{
String pk = b.getPublicKey();
synchronized( participants ){
chatParticipant p = (chatParticipant)participants.get( pk );
if ( p != null ){
return( p );
}
}
return( null );
}
public chatParticipant[]
getParticipants()
{
synchronized( participants ){
chatParticipant[] res = new chatParticipant[participants.size()];
participants.values().toArray( res );
return( res );
}
}
protected void
removeParticipant(
chatParticipant p )
{
boolean removed;
synchronized( participants ){
removed = participants.remove( p.getPublicKey()) != null;
}
if ( removed ){
Iterator it = listeners.iterator();
while( it.hasNext()){
try{
((BuddyPluginAZ2ChatListener)it.next()).participantRemoved( p );
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}
}
public void
destroy()
{
plugin.removeListener( this );
destroyChat( this );
}
public void
addListener(
BuddyPluginAZ2ChatListener listener )
{
listeners.add( listener );
}
public void
removeListener(
BuddyPluginAZ2ChatListener listener )
{
listeners.remove( listener );
}
}
public class
chatParticipant
{
private BuddyPluginBuddy buddy;
private String public_key;
protected
chatParticipant(
BuddyPluginBuddy _buddy )
{
buddy = _buddy;
}
protected
chatParticipant(
String pk )
{
public_key = pk;
}
public boolean
isAuthorised()
{
return( buddy != null );
}
public BuddyPluginBuddy
getBuddy()
{
return( buddy );
}
public String
getPublicKey()
{
if ( buddy != null ){
return( buddy.getPublicKey());
}
return( public_key );
}
public String
getName()
{
if ( buddy != null ){
return( buddy.getName());
}
return( public_key );
}
}
public class
chatMessage
{
private String nick;
private Map map;
protected
chatMessage(
String _nick,
Map _map )
{
nick = _nick;
map = _map;
}
public String
getNickName()
{
return( nick );
}
public Map
getMessage()
{
return( map );
}
}
}