#! /usr/bin/env python
# -*- coding: utf-8 -*-
#-----------------------------------------------------------------------------
# Name: friend.py
# Purpose:
#
# Author: Jeremy Arendt
#
# Created: 2004/24/02
# RCS-ID: $Id: friend.py,v 1.6 2005/10/23 21:38:03 Inigo Exp $
# Copyright: (c) 2002
# Licence: See G3.LICENCE
#-----------------------------------------------------------------------------
#from g3peerid import *
from traceback import print_exc
from random import random,shuffle
from time import time
from os.path import join
import ConfigParser
from g3rpcclient import T_G3RPCCLient
from threading import Lock
from socket import gethostbyname
import re
import sys
import os
import os.path
if sys.platform == "win32":
win32_flag = True
else:
win32_flag = False
ADDFRIENDTEMP = 12
ADDFOETEMP = 10
REMOVEFOE = 11
GOTANNOUNCE = 0
GOTRENOUNCE = 9
class Friend:
def __init__(self, ip, peerid):
self.lastupdate = time()
self.name = peerid
self.peerid = peerid
self.ip = ip
self.sharedfiles = []
self.total_up = 0
self.total_down = 0
self.type = 0
self.online = False
self.temp = False
def AddSharedFile(self, port, infohash):
self.lastupdate = time()
for file in self.sharedfiles:
if file[0] == infohash and file[1] == port:
return
elif file[0] == infohash:
file[1] = port
return
elif file[1] == port:
file[0] = infohash
return
self.sharedfiles.append( [infohash, port] )
def RemoveSharedFile(self, port, infohash):
self.lastupdate = time()
for file in self.sharedfiles:
if file[0] == infohash and file[1] == port:
self.sharedfiles.remove(file)
return
def HasFile(self, infohash):
for hash, port in self.sharedfiles:
if hash == infohash:
return (hash, port)
return None
def PrintInfo(self):
print "name: %s" % GetPeerName(self.peerid)
print "ip: %s" % self.ip
print "id: %s" % self.peerid
print "sharedfiles: %s" % self.sharedfiles
def IsMyFriend(self):
return self.type
class Foe:
def __init__(self, ip, name, temp=True):
self.ip = ip
self.temp = temp
self.name = name
class MyFriend(Friend, T_G3RPCCLient):
def __init__(self, ip, peerid, btconfig):
Friend.__init__(self, ip, peerid)
T_G3RPCCLient.__init__(self, btconfig, ip)
self.type = 1
def AnnounceHashes(self, infohashes):
for h in infohashes:
self.Announce(self.ip, h[2], h[1], h[0])
def RenounceHashes(self, infohashes):
for h in infohashes:
self.Renounce(self.ip, h[2], h[1], h[0])
class FriendList:
def __init__(self, btconfig):
self.config = {'timeout': 90}
self.friends = []
self.foes = []
self.last_live_files = []
self.btconfig = btconfig
path = self.btconfig.Get("path")
if win32_flag:
self.friends_file = join(path, "friends.ini")
else:
self.friends_file = join(os.path.expanduser('~/.Rufus'), 'friends.ini')
self.index = 0
self.mod_lock = Lock()
self.rr_pivot = 0
def __iter__(self):
self.index = 0
return self
def __len__(self):
return len(self.friends)
def next(self):
i = self.index
if len(self.friends) == 0 or i == len(self.friends):
raise StopIteration
return
self.index += 1
return self.friends[i]
def StartBroadcaster(self):
self.broadcaster.Start()
def StopBroadcaster(self):
self.broadcaster.Stop()
def Save(self):
# write out to file
print 'Saving Friend List'
try:
cp = ConfigParser.ConfigParser()
cp.add_section('friends')
i = 0
for f in self.friends:
if f.IsMyFriend() and not f.temp:
cp.set('friends', str(i), [f.name, f.ip])
i += 1
cp.add_section('foes')
i = 0
for f in self.foes:
if not f.temp:
cp.set('foes', str(i), [f.name, f.ip])
i += 1
file = open(self.friends_file, 'w')
cp.write(file)
file.close()
except IOError, e:
print "ERROR: writing to friends ini file -", str(e)
except:
print "ERROR: writing to friends ini file"
def Load(self):
if os.path.exists(self.friends_file):
#regex to check for IP address
ipregex = re.compile("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")
# load from file
print 'Loading Friend List'
try:
cp = ConfigParser.ConfigParser()
file = open(self.friends_file, 'r')
cp.readfp(file)
items = cp.items('friends')
for key, param in items:
param = eval(param)
name = param[0]
ip = param[1]
if not ipregex.match( param[1] ):
try:
ip = gethostbyname(param[1])
except:
ip = "0.0.0.0" #if error with DNS lookup set ip to this
self.AddFriend(ip, CreatePeerId(name), True, False)
items = cp.items('foes')
for key, param in items:
param = eval(param)
name = param[0]
ip = param[1]
if not ipregex.match( param[1] ):
try:
ip = gethostbyname(param[1])
except:
ip = "0.0.0.0" #if error with DNS lookup set ip to this
self.AddFoe(ip, name, False)
file.close()
except IOError, e:
print "ERROR: accessing friends.ini - ", str(e)
except (ConfigParser.MissingSectionHeaderError, ConfigParser.NoSectionError), e:
print "ERROR: processing friends.ini - ", str(e)
def GotUpdate(self, ip, peerid, msgtype, data):
if msgtype == GOTANNOUNCE:
friend = self.AddFriend(ip, peerid)
friend.AddSharedFile(data[0], data[1])
elif msgtype == GOTRENOUNCE:
friend = self.AddFriend(ip, peerid)
friend.RemoveSharedFile(data[0], data[1])
if len(friend.sharedfiles) == 0 and not friend.IsMyFriend():
self.friends.remove(friend)
else:
friend.online = False
elif msgtype == ADDFOETEMP:
foe = self.AddFoe(ip, peerid, True)
elif msgtype == REMOVEFOE:
foe = self.RemoveFoe(ip)
elif msgtype == ADDFRIENDTEMP:
friend = self.AddFriend(ip, peerid, myfriend=True, online=True, temp=True)
friend.AddSharedFile(data[0], data[1])
def RemoveFoe(self, ip):
self.mod_lock.acquire(True)
for f in self.foes:
if f.ip == ip:
self.foes.remove(f)
break
self.mod_lock.release()
def AddFoe(self, ip, name, temp=True):
self.mod_lock.acquire(True)
foe = None
# check if already in list
for f in self.foes:
if f.ip == ip:
foe = f
break
if foe == None:
foe = Foe(ip, name, temp)
self.foes.append(foe)
foe.temp = temp
self.mod_lock.release()
return foe
def AddFriend(self, ip, peerid, myfriend=False, online=True, temp=False):
self.mod_lock.acquire(True)
friend = None
# check if already in list
for f in self.friends:
if f.ip == ip:
if f.type == 0 and myfriend == True:
#remove Friend if upgrading to MyFriend
self.friends.remove(f)
else:
friend = f
break
if friend == None:
if myfriend:
friend = MyFriend(ip, peerid, self.btconfig)
else:
friend = Friend(ip, peerid)
friend.temp = temp
self.friends.append(friend)
friend.online = online
self.mod_lock.release()
return friend
def RemoveFriend(self, friend):
self.mod_lock.acquire(True)
for f in self.friends:
if f.ip == friend.ip:
self.friends.remove(f)
break
self.mod_lock.release()
def RemoveByIP(self, ip):
self.mod_lock.acquire(True)
for f in self.friends:
if ip == f.ip:
self.friends.remove(f)
break
self.mod_lock.release()
# removes friends who haven't checked in recently
def Purge(self, live_files):
self.mod_lock.acquire(True)
i = 0
live_hashes = [h[0] for h in live_files]
while i < len(self.friends):
f = self.friends[i]
if f.lastupdate + self.config['timeout'] < time():
if f.temp:
found = False
#peerid, port, infohash
for h in f.sharedfiles:
if h[0] in live_hashes:
found = True
if not found:
self.friends.remove(f)
elif f.IsMyFriend():
f.online = False
f.sharedfiles = []
else:
self.friends.remove(f)
continue
i += 1
self.mod_lock.release()
# broadcasts live and dead infohashes
def Broadcast(self, live_files):
self.Purge(live_files)
self.mod_lock.acquire(True)
dead_files = []
for h in self.last_live_files:
if not h in live_files:
dead_files.append(h)
if len(live_files) > 0:
for f in self.friends:
if f.IsMyFriend() and not f.temp:
f.AnnounceHashes(live_files)
if len(dead_files) > 0:
for f in self.friends:
if f.IsMyFriend() and not f.temp:
f.RenounceHashes(dead_files)
self.last_live_files = live_files
self.mod_lock.release()
# The friends connections we need to stay connected to
def GetNeedsConnection(self, infohash):
self.mod_lock.acquire(True)
plist = []
for f in self.friends:
hash_port = f.HasFile(infohash)
if f.IsMyFriend() and f.online and not f.temp and hash_port:
plist.append((f.ip, hash_port[1], f.peerid))
self.mod_lock.release()
return plist
# returns a copy of a list of friends that are sharing the file specified in infohash
# this func is called from a the DL thread - use care
def GetInterested(self, infohash):
if not self.mod_lock.acquire(False):
return ({}, {})
friendlist = {}
foelist = {}
#round robin
j = len(self.friends)
i = self.rr_pivot
rotated_friends = self.friends[i:j] + self.friends[0:i]
self.rr_pivot += 1
for f in self.friends:
if f.IsMyFriend(): # and f.HasFile(infohash):
friendlist[f.ip] = True
for f in self.foes:
foelist[f.ip] = True
self.mod_lock.release()
return (friendlist, foelist)
if __name__ == "__main__":
pass
|