2005-04-07 21:19:42 +02:00
// yacyPeerActions.java
// -------------------------------------
2008-07-20 19:14:51 +02:00
// (C) by Michael Peter Christen; mc@yacy.net
2008-11-06 11:07:53 +01:00
// first published on http://yacy.net
2005-04-07 21:19:42 +02:00
// Frankfurt, Germany, 2005
2005-10-17 17:46:12 +02:00
//
// $LastChangedDate$
// $LastChangedRevision$
// $LastChangedBy$
2005-04-07 21:19:42 +02:00
//
// 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; either version 2 of the License, or
// (at your option) any later version.
//
// 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 de.anomic.yacy ;
2010-06-01 15:02:11 +02:00
import java.util.Map ;
import java.util.concurrent.ConcurrentHashMap ;
2008-06-06 18:01:27 +02:00
2010-05-25 14:54:57 +02:00
import net.yacy.cora.document.RSSMessage ;
2009-10-10 01:13:30 +02:00
import net.yacy.kelondro.logging.Log ;
2009-10-11 02:24:42 +02:00
import net.yacy.kelondro.util.MapTools ;
2009-10-10 01:13:30 +02:00
2005-04-07 21:19:42 +02:00
public class yacyPeerActions {
2008-08-02 14:12:04 +02:00
private final yacySeedDB seedDB ;
2010-06-01 15:02:11 +02:00
private Map < String , String > userAgents ;
2005-04-07 21:19:42 +02:00
public long disconnects ;
2008-08-02 14:12:04 +02:00
private final yacyNewsPool newsPool ;
2005-04-07 21:19:42 +02:00
2008-08-02 14:12:04 +02:00
public yacyPeerActions ( final yacySeedDB seedDB , final yacyNewsPool newsPool ) {
2005-04-07 21:19:42 +02:00
this . seedDB = seedDB ;
2008-06-04 23:34:57 +02:00
this . newsPool = newsPool ;
2010-06-01 15:02:11 +02:00
this . userAgents = new ConcurrentHashMap < String , String > ( ) ;
2005-04-07 21:19:42 +02:00
this . disconnects = 0 ;
}
2008-06-04 23:34:57 +02:00
public void close ( ) {
// the seedDB and newsPool should be cleared elsewhere
if ( userAgents ! = null ) userAgents . clear ( ) ;
userAgents = null ;
2005-04-07 21:19:42 +02:00
}
2008-06-04 23:34:57 +02:00
2008-08-02 14:12:04 +02:00
public synchronized boolean connectPeer ( final yacySeed seed , final boolean direct ) {
2006-02-27 23:25:27 +01:00
// store a remote peer's seed
// returns true if the peer is new and previously unknown
2005-07-31 13:11:29 +02:00
if ( seed = = null ) {
2006-02-27 23:25:27 +01:00
yacyCore . log . logSevere ( " connect: WRONG seed (NULL) " ) ;
return false ;
2006-12-20 02:07:49 +01:00
}
2008-06-05 00:24:00 +02:00
final String error = seed . isProper ( false ) ;
2006-12-21 14:14:13 +01:00
if ( error ! = null ) {
2006-02-27 23:25:27 +01:00
yacyCore . log . logSevere ( " connect: WRONG seed ( " + seed . getName ( ) + " / " + seed . hash + " ): " + error ) ;
return false ;
2006-12-20 02:07:49 +01:00
}
2007-10-01 14:30:23 +02:00
if ( ( this . seedDB . mySeedIsDefined ( ) ) & & ( seed . hash . equals ( this . seedDB . mySeed ( ) . hash ) ) ) {
2007-04-30 00:05:34 +02:00
yacyCore . log . logInfo ( " connect: SELF reference " + seed . getPublicAddress ( ) ) ;
2006-02-27 23:25:27 +01:00
return false ;
2006-12-20 02:07:49 +01:00
}
2006-12-21 14:14:13 +01:00
final String peerType = seed . get ( yacySeed . PEERTYPE , yacySeed . PEERTYPE_VIRGIN ) ;
2006-12-20 02:07:49 +01:00
if ( ( peerType . equals ( yacySeed . PEERTYPE_VIRGIN ) ) | | ( peerType . equals ( yacySeed . PEERTYPE_JUNIOR ) ) ) {
2005-04-07 21:19:42 +02:00
// reject unqualified seeds
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting NOT QUALIFIED " + peerType + " seed " + seed . getName ( ) ) ;
2008-08-08 15:56:29 +02:00
return false ;
}
if ( ! ( peerType . equals ( yacySeed . PEERTYPE_SENIOR ) | | peerType . equals ( yacySeed . PEERTYPE_PRINCIPAL ) ) ) {
// reject unqualified seeds
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting NOT QUALIFIED " + peerType + " seed " + seed . getName ( ) ) ;
2006-12-20 02:07:49 +01:00
return false ;
}
2006-02-27 23:25:27 +01:00
2006-12-21 14:14:13 +01:00
final yacySeed doubleSeed = this . seedDB . lookupByIP ( seed . getInetAddress ( ) , true , false , false ) ;
2006-12-20 02:07:49 +01:00
if ( ( doubleSeed ! = null ) & & ( doubleSeed . getPort ( ) = = seed . getPort ( ) ) & & ( ! ( doubleSeed . hash . equals ( seed . hash ) ) ) ) {
// a user frauds with his peer different peer hashes
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting FRAUD (double hashes " + doubleSeed . hash + " / " + seed . hash + " on same port " + seed . getPort ( ) + " ) peer " + seed . getName ( ) ) ;
2006-12-20 02:07:49 +01:00
return false ;
}
2006-02-27 23:25:27 +01:00
2006-12-21 14:14:13 +01:00
if ( seed . get ( yacySeed . LASTSEEN , " " ) . length ( ) ! = 14 ) {
// hack for peers that do not have a LastSeen date
2007-02-03 00:54:27 +01:00
seed . setLastSeenUTC ( ) ;
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: reset wrong date ( " + seed . getName ( ) + " / " + seed . hash + " ) " ) ;
2006-12-21 14:14:13 +01:00
}
// connection time
2009-12-06 22:54:32 +01:00
final long nowUTC0Time = System . currentTimeMillis ( ) ; // is better to have this value in a variable for debugging
2007-02-03 00:54:27 +01:00
long ctimeUTC0 = seed . getLastSeenUTC ( ) ;
2005-09-27 18:28:55 +02:00
2007-02-03 00:54:27 +01:00
if ( ctimeUTC0 > nowUTC0Time ) {
// the peer is future-dated, correct it
seed . setLastSeenUTC ( ) ;
ctimeUTC0 = nowUTC0Time ;
assert ( seed . getLastSeenUTC ( ) - ctimeUTC0 < 100 ) ;
}
2009-05-12 00:26:21 +02:00
if ( Math . abs ( nowUTC0Time - ctimeUTC0 ) / 1000 / 60 > 60 * 6 ) {
2006-12-21 14:14:13 +01:00
// the new connection is out-of-age, we reject the connection
2011-01-03 21:52:54 +01:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting out-dated peer ' " + seed . getName ( ) + " ' from " + seed . getPublicAddress ( ) + " ; nowUTC0= " + nowUTC0Time + " , seedUTC0= " + ctimeUTC0 + " , TimeDiff= " + formatInterval ( Math . abs ( nowUTC0Time - ctimeUTC0 ) ) ) ;
2006-12-21 14:14:13 +01:00
return false ;
}
// disconnection time
long dtimeUTC0 ;
final yacySeed disconnectedSeed = seedDB . getDisconnected ( seed . hash ) ;
if ( disconnectedSeed = = null ) {
dtimeUTC0 = 0 ; // never disconnected: virtually disconnected maximum time ago
} else {
2007-11-09 10:40:42 +01:00
dtimeUTC0 = disconnectedSeed . getLong ( " dct " , 0 ) ;
2006-12-21 14:14:13 +01:00
}
if ( direct ) {
// remember the moment
// Date applies the local UTC offset, which is wrong
// we correct that by subtracting the local offset and adding
// the remote offset.
2007-02-03 00:54:27 +01:00
seed . setLastSeenUTC ( ) ;
2006-12-21 14:14:13 +01:00
seed . setFlagDirectConnect ( true ) ;
} else {
// set connection flag
if ( Math . abs ( nowUTC0Time - ctimeUTC0 ) > 120000 ) seed . setFlagDirectConnect ( false ) ; // 2 minutes
}
// update latest version number
2007-04-27 11:23:44 +02:00
if ( seed . getVersion ( ) > yacyVersion . latestRelease ) yacyVersion . latestRelease = seed . getVersion ( ) ;
2006-12-21 14:14:13 +01:00
// prepare to update
if ( disconnectedSeed ! = null ) {
// if the indirect connect aims to announce a peer that we know
// has been disconnected then we compare the dates:
// if the new peer has a LastSeen date, and that date is before
// the disconnection date, then we ignore the new peer
if ( ! direct ) {
if ( ctimeUTC0 < dtimeUTC0 ) {
// the disconnection was later, we reject the connection
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting disconnected peer ' " + seed . getName ( ) + " ' from " + seed . getPublicAddress ( ) ) ;
2006-12-21 14:14:13 +01:00
return false ;
2006-02-27 23:25:27 +01:00
}
2005-04-07 21:19:42 +02:00
}
2006-12-21 14:14:13 +01:00
// this is a return of a lost peer
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: returned KNOWN " + peerType + " peer ' " + seed . getName ( ) + " ' from " + seed . getPublicAddress ( ) ) ;
2006-12-21 14:14:13 +01:00
this . seedDB . addConnected ( seed ) ;
return true ;
2008-08-02 15:57:00 +02:00
}
final yacySeed connectedSeed = this . seedDB . getConnected ( seed . hash ) ;
if ( connectedSeed ! = null ) {
// the seed is known: this is an update
try {
// if the old LastSeen date is later then the other
// info, then we reject the info
if ( ( ctimeUTC0 < ( connectedSeed . getLastSeenUTC ( ) ) ) & & ( ! direct ) ) {
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting old info about peer ' " + seed . getName ( ) + " ' " ) ;
2006-12-21 14:14:13 +01:00
return false ;
}
2008-08-02 15:57:00 +02:00
/ * if ( connectedSeed . getName ( ) ! = seed . getName ( ) ) {
// TODO: update seed name lookup cache
} * /
} catch ( final NumberFormatException e ) {
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: rejecting wrong peer ' " + seed . getName ( ) + " ' from " + seed . getPublicAddress ( ) + " . Cause: " + e . getMessage ( ) ) ;
2008-08-02 15:57:00 +02:00
return false ;
2005-04-07 21:19:42 +02:00
}
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: updated KNOWN " + ( ( direct ) ? " direct " : " " ) + peerType + " peer ' " + seed . getName ( ) + " ' from " + seed . getPublicAddress ( ) ) ;
2008-08-02 15:57:00 +02:00
seedDB . addConnected ( seed ) ;
return true ;
}
// the seed is new
if ( ( seedDB . mySeedIsDefined ( ) ) & & ( seed . getIP ( ) . equals ( this . seedDB . mySeed ( ) . getIP ( ) ) ) ) {
// seed from the same IP as the calling client: can be
// the case if there runs another one over a NAT
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: saved NEW seed (myself IP) " + seed . getPublicAddress ( ) ) ;
2008-08-02 15:57:00 +02:00
} else {
// completely new seed
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: saved NEW " + peerType + " peer ' " + seed . getName ( ) + " ' from " + seed . getPublicAddress ( ) ) ;
2006-12-21 14:14:13 +01:00
}
2008-08-02 15:57:00 +02:00
this . seedDB . addConnected ( seed ) ;
return true ;
2005-04-07 21:19:42 +02:00
}
2008-08-02 14:12:04 +02:00
public boolean peerArrival ( final yacySeed peer , final boolean direct ) {
2006-03-20 21:31:51 +01:00
if ( peer = = null ) return false ;
2008-08-02 14:12:04 +02:00
final boolean res = connectPeer ( peer , direct ) ;
2005-04-07 21:19:42 +02:00
if ( res ) {
2008-05-07 01:05:48 +02:00
// perform all actions if peer is effective new
2009-06-30 11:27:46 +02:00
this . processPeerArrival ( peer ) ;
2010-06-29 21:20:45 +02:00
yacyChannel . channels ( yacyChannel . PEERNEWS ) . addMessage ( new RSSMessage ( peer . getName ( ) + " joined the network " , " " , " " ) ) ;
2005-04-07 21:19:42 +02:00
}
return res ;
}
2008-08-02 14:12:04 +02:00
public void peerDeparture ( final yacySeed peer , final String cause ) {
2006-03-20 21:31:51 +01:00
if ( peer = = null ) return ;
2008-05-07 01:05:48 +02:00
// we do this if we did not get contact with the other peer
2008-09-03 02:30:21 +02:00
if ( yacyCore . log . isFine ( ) ) yacyCore . log . logFine ( " connect: no contact to a " + peer . get ( yacySeed . PEERTYPE , yacySeed . PEERTYPE_VIRGIN ) + " peer ' " + peer . getName ( ) + " ' at " + peer . getPublicAddress ( ) + " . Cause: " + cause ) ;
2008-05-07 01:05:48 +02:00
synchronized ( seedDB ) {
2010-04-19 18:42:37 +02:00
if ( ! seedDB . hasDisconnected ( peer . hash . getBytes ( ) ) ) { disconnects + + ; }
2008-05-07 01:05:48 +02:00
peer . put ( " dct " , Long . toString ( System . currentTimeMillis ( ) ) ) ;
seedDB . addDisconnected ( peer ) ; // update info
}
2010-06-29 21:20:45 +02:00
yacyChannel . channels ( yacyChannel . PEERNEWS ) . addMessage ( new RSSMessage ( peer . getName ( ) + " left the network " , " " , " " ) ) ;
2005-04-07 21:19:42 +02:00
}
2008-08-02 14:12:04 +02:00
public void peerPing ( final yacySeed peer ) {
2006-03-20 21:31:51 +01:00
if ( peer = = null ) return ;
2005-04-07 21:19:42 +02:00
// this is called only if the peer has junior status
seedDB . addPotential ( peer ) ;
// perform all actions
2009-06-30 11:27:46 +02:00
processPeerArrival ( peer ) ;
2010-06-29 21:20:45 +02:00
yacyChannel . channels ( yacyChannel . PEERNEWS ) . addMessage ( new RSSMessage ( peer . getName ( ) + " sent me a ping " , " " , " " ) ) ;
2008-05-07 01:05:48 +02:00
}
2009-06-30 11:27:46 +02:00
private void processPeerArrival ( final yacySeed peer ) {
2008-08-02 14:12:04 +02:00
final String recordString = peer . get ( " news " , null ) ;
2008-05-07 01:05:48 +02:00
//System.out.println("### triggered news arrival from peer " + peer.getName() + ", news " + ((recordString == null) ? "empty" : "attached"));
if ( ( recordString = = null ) | | ( recordString . length ( ) = = 0 ) ) return ;
2008-08-02 14:12:04 +02:00
final String decodedString = de . anomic . tools . crypt . simpleDecode ( recordString , " " ) ;
2010-06-15 12:43:47 +02:00
final yacyNewsDB . Record record = this . newsPool . parseExternal ( decodedString ) ;
2008-05-07 01:05:48 +02:00
if ( record ! = null ) {
//System.out.println("### news arrival from peer " + peer.getName() + ", decoded=" + decodedString + ", record=" + recordString + ", news=" + record.toString());
2009-10-11 02:24:42 +02:00
final String cre1 = MapTools . string2map ( decodedString , " , " ) . get ( " cre " ) ;
final String cre2 = MapTools . string2map ( record . toString ( ) , " , " ) . get ( " cre " ) ;
2008-05-07 01:05:48 +02:00
if ( ( cre1 = = null ) | | ( cre2 = = null ) | | ( ! ( cre1 . equals ( cre2 ) ) ) ) {
System . out . println ( " ### ERROR - cre are not equal: cre1= " + cre1 + " , cre2= " + cre2 ) ;
return ;
}
try {
2008-06-04 23:34:57 +02:00
synchronized ( this . newsPool ) { this . newsPool . enqueueIncomingNews ( record ) ; }
2009-12-10 00:27:26 +01:00
} catch ( final Exception e ) {
2009-01-31 00:33:47 +01:00
Log . logSevere ( " YACY " , " processPeerArrival " , e ) ;
2008-05-07 01:05:48 +02:00
}
}
2005-04-07 21:19:42 +02:00
}
2005-12-22 02:01:46 +01:00
2008-08-02 14:12:04 +02:00
public void setUserAgent ( final String IP , final String userAgent ) {
2008-07-11 12:38:20 +02:00
if ( userAgents = = null ) return ; // case can happen during shutdown
2005-12-22 02:01:46 +01:00
userAgents . put ( IP , userAgent ) ;
}
2008-08-02 14:12:04 +02:00
public String getUserAgent ( final String IP ) {
final String userAgent = userAgents . get ( IP ) ;
2005-12-22 02:01:46 +01:00
return ( userAgent = = null ) ? " " : userAgent ;
}
2008-11-06 11:07:53 +01:00
2011-01-03 21:52:54 +01:00
/ * *
* Format a time inteval in milliseconds into a String of the form
* X ' day '[' s ' ] HH ':' mm
* /
public static String formatInterval ( final long millis ) {
try {
final long mins = millis / 60000 ;
2011-03-09 10:29:05 +01:00
final StringBuilder uptime = new StringBuilder ( 40 ) ;
2011-01-03 21:52:54 +01:00
final int uptimeDays = ( int ) ( Math . floor ( mins / 1440 . 0 ) ) ;
final int uptimeHours = ( int ) ( Math . floor ( mins / 60 . 0 ) % 24 ) ;
final int uptimeMins = ( int ) mins % 60 ;
uptime . append ( uptimeDays )
. append ( ( ( uptimeDays = = 1 ) ? " day " : " days " ) )
. append ( ( uptimeHours < 10 ) ? " 0 " : " " )
. append ( uptimeHours )
2011-03-09 10:29:05 +01:00
. append ( ':' )
2011-01-03 21:52:54 +01:00
. append ( ( uptimeMins < 10 ) ? " 0 " : " " )
. append ( uptimeMins ) ;
return uptime . toString ( ) ;
} catch ( final Exception e ) {
return " unknown " ;
}
}
2005-04-07 21:19:42 +02:00
}