1 # Written by Arno Bakker
2 # see LICENSE.txt for license information
6 from traceback import print_exc
8 from BaseLib.Core.BitTornado.bencode import bencode, bdecode
9 from BaseLib.Core.BitTornado.BT1.MessageID import *
11 from BaseLib.Core.Utilities.utilities import *
12 from BaseLib.Core.Utilities.unicode import str2unicode
16 MIN_OVERLAP_WAIT = 12.0*3600.0 # half a day in seconds
18 ICON_MAX_SIZE = 10*1024
20 class OverlapMsgHandler:
26 def register(self, overlay_bridge, launchmany):
28 print >> sys.stderr,"socnet: bootstrap: overlap"
29 self.mypermid = launchmany.session.get_permid()
30 self.session = launchmany.session
31 self.peer_db = launchmany.peer_db
32 self.superpeer_db = launchmany.superpeer_db
33 self.overlay_bridge = overlay_bridge
36 # Incoming SOCIAL_OVERLAP
38 def recv_overlap(self,permid,message,selversion):
41 oldict = bdecode(message[1:])
45 print >> sys.stderr,"socnet: SOCIAL_OVERLAP: error becoding"
48 if not isValidDict(oldict,permid):
52 self.process_overlap(permid,oldict)
55 def process_overlap(self,permid,oldict):
56 #self.print_hashdict(oldict['hashnetwork'])
58 # 1. Clean recently contacted admin
59 self.clean_recentpeers()
61 # 3. Save persinfo + hrwidinfo + ipinfo
62 if self.peer_db.hasPeer(permid):
63 save_ssocnet_peer(self,permid,oldict,False,False,False)
65 print >> sys.stderr,"socnet: overlap: peer unknown?! Weird, we just established connection"
68 if not (permid in self.recentpeers.keys()):
69 self.recentpeers[permid] = time()
70 self.reply_to_overlap(permid)
72 def clean_recentpeers(self):
74 for permid2,t in self.recentpeers.iteritems():
75 if (t+MIN_OVERLAP_WAIT) > time():
78 # print >> sys.stderr,"socnet: overlap: clean recent: not keeping",show_permid_short(permid2)
80 self.recentpeers = newdict
82 def reply_to_overlap(self,permid):
83 oldict = self.create_oldict()
84 self.send_overlap(permid,oldict)
87 # At overlay-connection establishment time.
89 def initiate_overlap(self,permid,locally_initiated):
90 self.clean_recentpeers()
91 if not (permid in self.recentpeers.keys() or permid in self.superpeer_db.getSuperPeers()):
93 # Make sure only one sends it
94 self.recentpeers[permid] = time()
95 self.reply_to_overlap(permid)
97 print >> sys.stderr,"socnet: overlap: active: he should initiate"
99 print >> sys.stderr,"socnet: overlap: active: peer recently contacted already"
104 def create_oldict(self):
107 * Personal info: name, picture, rwidhashes
109 Both are individually signed by us so dest can safely
110 propagate. We distinguish between what a peer said
111 is his IP+port and the information obtained from the network
112 or from other peers (i.e. BUDDYCAST)
115 nickname = self.session.get_nickname().encode("UTF-8")
116 persinfo = {'name':nickname}
117 # See if we can find icon
118 iconmime, icondata = self.session.get_mugshot()
120 persinfo.update({'icontype':iconmime, 'icondata':icondata})
123 oldict['persinfo'] = persinfo
125 #print >> sys.stderr, 'Overlap: Sending oldict: %s' % `oldict`
128 # print >> sys.stderr,"socnet: overlap: active: sending hashdict"
129 # self.print_hashdict(oldict['hashnetwork'])
134 def send_overlap(self,permid,oldict):
136 body = bencode(oldict)
137 ## Optimization: we know we're currently connected
138 self.overlay_bridge.send(permid, SOCIAL_OVERLAP + body,self.send_callback)
141 print_exc(file=sys.stderr)
144 def send_callback(self,exc,permid):
147 print >> sys.stderr,"socnet: SOCIAL_OVERLAP: error sending to",show_permid_short(permid),exc
154 def isValidDict(oldict,source_permid):
155 if not isinstance(oldict, dict):
157 print >> sys.stderr,"socnet: SOCIAL_OVERLAP: not a dict"
162 print >> sys.stderr,"socnet: SOCIAL_OVERLAP: keys",k
164 if not ('persinfo' in k) or not isValidPersinfo(oldict['persinfo'],False):
166 print >> sys.stderr,"socnet: SOCIAL_OVERLAP: key 'persinfo' missing or value wrong type in dict"
170 if key not in ['persinfo']:
172 print >> sys.stderr,"socnet: SOCIAL_OVERLAP: unknown key",key,"in dict"
179 def isValidPersinfo(persinfo,signed):
180 if not isinstance(persinfo,dict):
182 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: not a dict"
186 #print >> sys.stderr,"socnet: SOCIAL_*: persinfo: keys are",k
187 if not ('name' in k) or not isinstance(persinfo['name'],str):
189 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: key 'name' missing or value wrong type"
192 if 'icontype' in k and not isValidIconType(persinfo['icontype']):
194 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: key 'icontype' value wrong type"
197 if 'icondata' in k and not isValidIconData(persinfo['icondata']):
199 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: key 'icondata' value wrong type"
202 if ('icontype' in k and not ('icondata' in k)) or ('icondata' in k and not ('icontype' in k)):
204 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: key 'icontype' without 'icondata' or vice versa"
208 if not ('insert_time' in k) or not isinstance(persinfo['insert_time'],int):
210 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: key 'insert_time' missing or value wrong type"
214 if key not in ['name','icontype','icondata','insert_time']:
216 print >> sys.stderr,"socnet: SOCIAL_*: persinfo: unknown key",key,"in dict"
222 def isValidIconType(type):
223 """ MIME-type := type "/" subtype ... """
224 if not isinstance(type,str):
227 ridx = type.rfind('/')
228 return idx != -1 and idx == ridx
230 def isValidIconData(data):
231 if not isinstance(data,str):
235 # print >>sys.stderr,"socnet: SOCIAL_*: persinfo: IconData length is",len(data)
237 return len(data) <= ICON_MAX_SIZE
241 def save_ssocnet_peer(self,permid,record,persinfo_ignore,hrwidinfo_ignore,ipinfo_ignore):
242 """ This function is used by both BootstrapMsgHandler and
243 OverlapMsgHandler, and uses their database pointers. Hence the self
244 parameter. persinfo_ignore and ipinfo_ignore are booleans that
245 indicate whether to ignore the personal info, resp. ip info in
246 this record, because they were unsigned in the message and
247 we already received signed versions before.
249 if permid == self.mypermid:
253 if not persinfo_ignore:
254 persinfo = record['persinfo']
257 print >>sys.stderr,"socnet: Got persinfo",persinfo.keys()
258 if len(persinfo.keys()) > 1:
259 print >>sys.stderr,"socnet: Got persinfo THUMB THUMB THUMB THUMB"
261 # Arno, 2008-08-22: to avoid UnicodeDecode errors when commiting
263 name = str2unicode(persinfo['name'])
266 print >> sys.stderr,"socnet: SOCIAL_OVERLAP",show_permid_short(permid),`name`
268 if self.peer_db.hasPeer(permid):
269 self.peer_db.updatePeer(permid, name=name)
271 self.peer_db.addPeer(permid,{'name':name})
274 if 'icontype' in persinfo and 'icondata' in persinfo:
276 print >> sys.stderr,"socnet: saving icon for",show_permid_short(permid),`name`
277 self.peer_db.updatePeerIcon(permid, persinfo['icontype'],persinfo['icondata'])