I've made a new version of freenetlib.py. It is just slightly modified from version 0.02. The main differences is that all instances of "FreeNet" in the module (including the class name) have been changed to Freenet, which appears to be the correct spelling. The other change is that there is optional data length autocalculation, which makes it easier to use. This is used by simply omitting the datalength parameter of the insert method of the class Freenet.
-- Travis Bemann Sendmail is still screwed up on my box. My email address is really bemann at execpc.com. -------------- next part -------------- #!/usr/bin/env python # A Freenet client module. May rapidly change during the development of # the Freenet protocal. # # freenetlib.py 0.03 # # Copyright (C) 2000 Travis Bemann, Itamar Shtull-Trauring # # 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. # # more information on Freenet is availiable at http://freenet.sourceforge.net/ import re import socket import string import os import whrandom import sha import struct import sys # Default values for Freenet init parameters FREENET_DEFAULT_HOST = 'localhost' FREENET_DEFAULT_PORT = 19114 FREENET_DEFAULT_SEARCH_DEPTH = 10 FREENET_VERSION = '1.2' FREENET_DEFAULT_MIN_DEPTH = 5 FREENET_DEFAULT_MAX_DEPTH = 30 FREENET_DEFAULT_KEEP_ALIVE = 1 FREENET_DEFAULT_AUTO_CONNECT = 1 DEBUG = 0 error_reply = 'freenetlib.error_reply' error_uniqueid = 'freenetlib.error_uniqueid' error_version = 'freenetlib.error_version' error_nodata = 'freenetlib.error_nodata' error_keyexists = 'freenetlib.error_keyexists' # Note - This is a slightly modified version of a bit of Python # code sent to me by Bill Trost <trost at cloud.rain.com>. # This is fast as in it is fast to use in a program, not that the # implementation used is necessarily fast. def quick_sha1(s): return "%X%X%X%X%X" % struct.unpack("!lllll", sha.new(s).digest()) class Freenet: # Initialize an instance # - host: host of FreeNet server # - port: port of FreeNet server # - search_depth: maximum key search depth # - min_depth: minimum starting message depth # - max_depth: maximum starting message depth # - keep_alive: whether to keep the connection open # - auto_connect: whether to handshake automatically def __init__(self, host = FREENET_DEFAULT_HOST, port = FREENET_DEFAULT_PORT, search_depth = FREENET_DEFAULT_SEARCH_DEPTH, min_depth = FREENET_DEFAULT_MIN_DEPTH, max_depth = FREENET_DEFAULT_MAX_DEPTH, keep_alive = FREENET_DEFAULT_KEEP_ALIVE, auto_connect = FREENET_DEFAULT_AUTO_CONNECT): self.host = host self.port = port if search_depth < 1: raise ValueError, \ 'search_depth must be larger than zero' self.search_depth = search_depth if min_depth < 1: raise ValueError, 'min_depth must be larger than zero' if max_depth < 1: raise ValueError, 'max_depth must be larger than zero' if min_depth > max_depth: raise ValueError, \ 'min_depth may not be larger than max_depth' self.min_depth = min_depth self.max_depth = max_depth self.keep_alive = keep_alive self.response_closed = 1 if auto_connect: self.connect() def sendmesg(self, type, items, datafile=None, datalength=0): """ Construct message from dictionary, with optional file for data """ if DEBUG: print "Sent:", type, items f = self.file f.write(type + '\n') for key, value in items.items(): f.write("%s=%s\n" % (key, value)) if datafile: if datalength: f.write("DataLength=%s\n" % datalength) f.write("Data\n") line = datafile.readline() while line: f.write(line) line = datafile.readline() else: datafile.seek(0, 2) datalength = datafile.tell() datafile.seek(0, 0) f.write('DataLength=%s\n' % datalength) f.write('Data\n') line = datafile.readline() while line: f.write(line) line = datafile.readline() else: f.write("EndMessage\n") f.flush() def recvmesg(self, savefile=None): """ Read a message, writing it to a given file if need be """ f = self.file dict = {} messagetype = string.rstrip(f.readline()) line = string.rstrip(f.readline()) while line and (line not in ['Data', 'EndMessage']): key, value = string.split(line, '=') dict[key] = value line = string.rstrip(f.readline()) if line == 'Data': #if not dict.has_key('DataLength'): # raise error_nodatalength, "Message has Data but no DataLength." line = f.readline() readlength = 0 while line: savefile.write(line) readlength = readlength + len(line) line = f.readline() #if readlength != int(dict['DataLength']): # raise error_nodata, """DataLength supposed to be %s, but # actual Data length is %s.""" % (dict['DataLength'], readlength) if DEBUG: print "Received:", messagetype, dict return messagetype, dict def getranduniqueid(self): """ Generate 64 bit hex id number """ max = pow(2, 16) - 1 result = "" for i in range(0, 4): result = result + ("%04x" % whrandom.randint(0, max)) # the server strip leading 0 from uniqueids, so we do the same while result[0] == '0': result = result[1:] return result def getranddepth(self): """ Returns a random number between self.min_depth and self.max_depth. """ return whrandom.randint(self.min_depth, self.max_depth) def connect(self): """ Connect to server """ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect(self.host, self.port) self.response_closed = 0 self.file = self.sock.makefile('rb+') self._handshake() def close(self): """ close from server """ self.response_closed = 1 self.sock.close() def _handshake(self): """ Handshake with server. """ uid = self.getranduniqueid() if self.keep_alive: keep_alive = 'true' else: keep_alive = 'false' self.sendmesg('HandshakeRequest', {'UniqueID': uid, 'Depth': 1, 'HopsToLive': self.search_depth, 'KeepAlive': keep_alive}) type, items = self.recvmesg() if type == 'HandshakeRequest': try: self.sendmesg('HandshakeReply', {'UniqueID': items['UniqueID'], 'Depth': items['Depth'], 'HopsToLive': items['HopsToLive'], 'KeepAlive': keep_alive, 'Version': FREENET_VERSION}) except KeyError: self.close() raise error_reply, \ 'Necessary request fields are missing' elif type == 'HandshakeReply': try: if items['Version'] != FREENET_VERSION: self.close() raise error_version, \ 'Incompatible FreeNet version' if items['UniqueID'] != uid: self.close() raise error_uniqueid, \ 'Incorrect unique ID' except KeyError: raise error_version, \ 'Necessary reply fields are missing' else: self.close() raise error_reply, 'Unexpected message type' def insert(self, key, datafile, datalength = None): """ Insert file under key key: key to insert file under (unencrypted) data: file data """ if not self.keep_alive and self.response_closed: self.connect() uid = self.getranduniqueid() if self.keep_alive: keep_alive = 'true' else: keep_alive = 'false' self.sendmesg('InsertRequest', {'UniqueID': uid, 'Depth': self.getranddepth(), 'HopsToLive': self.search_depth, 'KeepAlive': keep_alive, 'Source': 'tcp/127.0.0.1:19114', 'SearchKey': quick_sha1(key)}) type, items = self.recvmesg() if type == 'RequestFailed': if not self.keep_alive: self.close() raise error_reply, 'Request failed' elif type == 'DataReply': if not self.keep_alive: self.close() raise error_keyexists, 'Key already exists' elif type == 'TimedOut': if not self.keep_alive: self.close() raise error_nodata, \ 'Key already exists but no data was found' elif type == 'InsertReply': if items['UniqueID'] != uid: if not self.keep_alive: self.close() raise error_uniqueid, \ 'Unique ID mismatch' self.sendmesg('DataInsert', {'UniqueID': uid, 'Depth': self.getranddepth(), 'HopsToLive': self.search_depth, 'KeepAlive': keep_alive, 'Source': 'tcp/127.0.0.1:19114', 'DataSource': 'tcp/127.0.0.1:19114'}, datafile, datalength) else: if not self.keep_alive: self.close() raise error_reply, 'Unexpected message type' if not self.keep_alive: self.close() def request(self, key, savefile=sys.stdout): """ Request file under key key: key to request file from (unencrypted) returns: file data """ if not self.keep_alive and self.response_closed: self.connect() uid = self.getranduniqueid() if self.keep_alive: keep_alive = 'true' else: keep_alive = 'false' self.sendmesg('DataRequest', {'UniqueID': uid, 'Depth': self.getranddepth(), 'HopsToLive': self.search_depth, 'KeepAlive': keep_alive, 'Source': 'tcp/127.0.0.1:19114', 'SearchKey': quick_sha1(key)}) type, items = self.recvmesg(savefile) if type == 'RequestFailed': if not self.keep_alive: self.close() raise error_reply, 'Request failed' elif type == 'TimedOut': if not self.keep_alive: self.close() raise error_keyexists, \ 'Key exists but no data was found' elif type == 'DataReply': if items['UniqueID'] != uid: if not self.keep_alive: self.close() raise error_uniqueid, \ 'Unique ID mismatch' return None else: if not self.keep_alive: self.close() raise error_reply, 'Unexpected message type' return None # shouldn't happen(?) if __name__ == '__main__': fn = Freenet() fn.request("keyindex.pl")
