SF.net SVN: fclient: [255] trunk/sandbox
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <ju...@us...> - 2008-02-26 09:51:02
|
Revision: 255
http://fclient.svn.sourceforge.net/fclient/?rev=255&view=rev
Author: jurner
Date: 2008-02-26 01:50:39 -0800 (Tue, 26 Feb 2008)
Log Message:
-----------
renames
Added Paths:
-----------
trunk/sandbox/fcp2/
trunk/sandbox/fcp2/__init__.py
trunk/sandbox/fcp2/boards/frost.py
trunk/sandbox/fcp2/client.py
trunk/sandbox/fcp2/config.py
trunk/sandbox/fcp2/consts.py
trunk/sandbox/fcp2/events.py
trunk/sandbox/fcp2/fcp_lib/events.py
trunk/sandbox/fcp2/fcp_lib/namespace.py
trunk/sandbox/fcp2/fcp_lib/numbers.py
trunk/sandbox/fcp2/fcp_lib/tools.py
trunk/sandbox/fcp2/message.py
trunk/sandbox/fcp2/params.py
trunk/sandbox/fcp2/scripts/
trunk/sandbox/fcp2/scripts/gen_docs.py
trunk/sandbox/fcp2/scripts/gen_messagecheatsheet.py
trunk/sandbox/fcp2/test_fcp/client.py
trunk/sandbox/fcp2/test_fcp/dummy_socket.py
trunk/sandbox/fcp2/test_fcp/test_all.py
trunk/sandbox/fcp2/test_fcp/test_client.py
trunk/sandbox/fcp2/test_fcp/test_config.py
trunk/sandbox/fcp2/test_fcp/test_message.py
trunk/sandbox/fcp2/types.py
trunk/sandbox/fcp2/uri.py
Removed Paths:
-------------
trunk/sandbox/fcp2/001.py
trunk/sandbox/fcp2/__init__.py
trunk/sandbox/fcp2/boards/frost.py
trunk/sandbox/fcp2/fcp2_0_client.py
trunk/sandbox/fcp2/fcp2_0_config.py
trunk/sandbox/fcp2/fcp2_0_consts.py
trunk/sandbox/fcp2/fcp2_0_message.py
trunk/sandbox/fcp2/fcp2_0_params.py
trunk/sandbox/fcp2/fcp2_0_uri.py
trunk/sandbox/fcp2/fcp_lib/events.py
trunk/sandbox/fcp2/fcp_lib/namespace.py
trunk/sandbox/fcp2/fcp_lib/numbers.py
trunk/sandbox/fcp2/oo1.py
trunk/sandbox/fcp2/scripts/gen_docs.py
trunk/sandbox/fcp2/scripts/gen_messagecheatsheet.py
trunk/sandbox/fcp2/test_fcp/dummy_socket.py
trunk/sandbox/fcp2/test_fcp/dummy_socket.pyc
trunk/sandbox/fcp2/test_fcp/test_fcp2_0_client.py
trunk/sandbox/fcp2/test_fcp/test_fcp2_0_config.py
trunk/sandbox/fcp2/test_fcp/test_fcp2_0_message.py
Copied: trunk/sandbox/fcp2 (from rev 79, trunk/sandbox/fcp)
Deleted: trunk/sandbox/fcp2/001.py
===================================================================
--- trunk/sandbox/fcp/001.py 2008-01-27 02:16:50 UTC (rev 79)
+++ trunk/sandbox/fcp2/001.py 2008-02-26 09:50:39 UTC (rev 255)
@@ -1,107 +0,0 @@
-
-import random
-import uuid
-#*********************************************************************************************
-#
-#*********************************************************************************************
-def validateFcpBool(value):
- if value in ('true', 'false'):
- return value
- return None
-
-def validateFloat(value):
- try:
- return float(value)
- except ValueError:
- return None
-
-def validateInt(value):
- try:
- return int(value)
- except ValueError:
- return None
-
-def validateUuid(value):
- result = uuid.UUID_EXACT_MATCH_PAT.match(value)
- if result:
- return result.group(0)
- return None
-
-#*********************************************************************************************
-#
-#*********************************************************************************************
-FcParamsSep = '\x01'
-
-FcParams = (
- ('FcSubType', validateInt),
- ('FcInitTime', validateFloat), # can not take it from uuid cos requests may be resend multiple times
- ('FcHandleCollisions', validateFcpBool),
- ('FcCollisionHandled', validateFcpBool),
- ('FcRequestIdentifier', validateUuid),
- ('FcRandomBytes', validateInt),
- )
-
-
-ISubType = 0
-IRequestIdentifier = 1
-IInitTime = 2
-IHandleCollisions = 3
-ICollisionHandled = 4
-IRandomBytes = 5
-
-def paramsFromRequest(msg):
- """
-
- >>> params = [1, 123.456, 'false', 'false', uuid.uuid_time(), 123456789]
- >>> identifier = FcParamsSep.join( [str(i) for i in params] )
- >>> result = paramsFromRequest({'Identifier': identifier})
- >>> result == params
- True
-
- """
- identifier = msg.get('Identifier', None)
- if identifier is None:
- return None
-
- params = identifier.split(FcParamsSep)
- if len(params) != len(FcParams):
- return None
-
- for i, (paramName, paramValidator) in enumerate(FcParams):
- result = paramValidator(params[i])
- if result is None:
- return None
- params[i] = result
-
- return params
-
-def identifierFromRequest(msg):
- """
-
- >>> msg = {'FcSubType':1, 'FcInitTime':1.234, 'FcHandleCollisions':'false', 'FcCollisionHandled':'flase', 'FcRequestIdentifier':uuid.uuid_time(), 'FcRandomBytes':1234567879}
- >>> identifierFromRequest(msg)
-
- """
-
-
- params = []
- for paramName, paramValidator in FcParams:
- params.append(msg[paramName])
-
- params[-1] = random.getrandbits(32) # add some random bits to be able to
- # handle IdentifierCollisions(FcRequestIdentifier
- # remains the same, identifier kown to node changes)
-
- return FcParamsSep.join( [str(i) for i in params] )
-
-
-s = '1\x011.234\x01false\x01flase\x01{fc0125cc-c76f-11dc-9099-fce464f183f6}\x011234567879'
-print len(s)
-#*********************************************************************************
-#
-#*********************************************************************************
-if __name__ == '__main__2':
- import doctest
- doctest.testmod()
-
-
Deleted: trunk/sandbox/fcp2/__init__.py
===================================================================
--- trunk/sandbox/fcp/__init__.py 2008-01-27 02:16:50 UTC (rev 79)
+++ trunk/sandbox/fcp2/__init__.py 2008-02-26 09:50:39 UTC (rev 255)
@@ -1,11 +0,0 @@
-"""Python wrapper for the freenet client protocol
-
-See: [http://www.freenetproject.org]
-"""
-
-
-__author__ = 'Juergen Urner'
-__copyright__ = '(c) 2008 - Juergen Urner'
-__emeil__ = 'jue...@go...'
-__licence__ = 'Mit'
-__version__ = '0.1'
\ No newline at end of file
Copied: trunk/sandbox/fcp2/__init__.py (from rev 241, trunk/sandbox/fcp/__init__.py)
===================================================================
--- trunk/sandbox/fcp2/__init__.py (rev 0)
+++ trunk/sandbox/fcp2/__init__.py 2008-02-26 09:50:39 UTC (rev 255)
@@ -0,0 +1,17 @@
+"""Python wrapper for the freenet client protocol
+
+See: [http://www.freenetproject.org] and [http://wiki.freenetproject.org/FreenetFCPSpec2Point0]
+"""
+
+
+__author__ = 'Juergen Urner'
+__copyright__ = '(c) 2008 - Juergen Urner'
+__email__ = 'jue...@ar...'
+__licence__ = 'Mit'
+__version__ = '0.1'
+
+#****************************************************************************************
+#
+#****************************************************************************************
+def getFcpClient():
+ pass
Deleted: trunk/sandbox/fcp2/boards/frost.py
===================================================================
--- trunk/sandbox/fcp/boards/frost.py 2008-01-27 02:16:50 UTC (rev 79)
+++ trunk/sandbox/fcp2/boards/frost.py 2008-02-26 09:50:39 UTC (rev 255)
@@ -1,99 +0,0 @@
-
-import os, sys, time
-
-#--> rel import hack
-class SysPathHack(object):
- def __init__(self, n):
- fpath = os.path.abspath(__file__)
- for i in xrange(n): fpath = os.path.dirname(fpath)
- sys.path.insert(0, fpath)
- def __del__(self): sys.path.pop(0)
-hack = SysPathHack(2)
-
-from fcp2_0_client import FcpClient
-
-
-del hack
-#<-- rel import hack
-#**********************************************************************
-#
-#**********************************************************************
-#c = FcpClient()
-#c.connect()
-
-
-
-#KSK@frost|message|news|*currentDate*-*boardName*-*slot*.xml
-
-class FrostDate(object):
-
- def __init__(self, year, month, day):
- self.year = year
- self.month = month
- self.day = day
-
- @classmethod
- def now(clss):
- t = time.gmtime(time.time())
- instance = clss(t.tm_year, t.tm_mon, t.tm_mday)
- return instance
-
- def toString(self):
- return '%s.%s.%s' % (self.year, self.month, self.day)
-
-
-
-
-class FrostBoard(object):
-
- def __init__(self):
-
- self.fcpClient = FcpClient(debugVerbosity=FcpClient.DebugVerbosity.Debug)
- for nodeHello in self.fcpClient.connect():
- pass
- self.fcpClient.events.RequestCompleted += self.handleRequestCompleted
-
-
- def handleRequestCompleted(self, event, request):
-
- self.fcpClient.removeRequest(request['Identifier'])
- print request['FcData']
-
-
-
- def readBoard(self, boardName, section, startDate=None):
-
- slot = -1
- while True:
- slot += 1
- if slot > 30: break
-
- ksk = 'KSK@frost|message|%s|%s-%s-%s.xml' % (section, startDate.toString(), boardName, slot)
- print ksk
-
- self.fcpClient.getData(
- uri=ksk,
- maxSize='33000'
- )
-
- for i in xrange(300):
- self.fcpClient.next()
-
-
-
-
-board = FrostBoard()
-
-
-t = FrostDate.now()
-print t.toString()
-
-board.readBoard('de.freenet', 'news', FrostDate.now())
-
-
-
-
-
-
-
-
Copied: trunk/sandbox/fcp2/boards/frost.py (from rev 200, trunk/sandbox/fcp/boards/frost.py)
===================================================================
--- trunk/sandbox/fcp2/boards/frost.py (rev 0)
+++ trunk/sandbox/fcp2/boards/frost.py 2008-02-26 09:50:39 UTC (rev 255)
@@ -0,0 +1,101 @@
+
+import os, sys, time
+
+#--> rel import hack
+class SysPathHack(object):
+ def __init__(self, n):
+ fpath = os.path.abspath(__file__)
+ for i in xrange(n): fpath = os.path.dirname(fpath)
+ sys.path.insert(0, fpath)
+ def __del__(self): sys.path.pop(0)
+hack = SysPathHack(2)
+
+from fcp2_0_client import FcpClient
+
+
+del hack
+#<-- rel import hack
+#**********************************************************************
+#
+#**********************************************************************
+#c = FcpClient()
+#c.connect()
+
+
+
+#KSK@frost|message|news|*currentDate*-*boardName*-*slot*.xml
+
+class FrostDate(object):
+
+ def __init__(self, year, month, day):
+ self.year = year
+ self.month = month
+ self.day = day
+
+ @classmethod
+ def now(clss):
+ t = time.gmtime(time.time())
+ instance = clss(t.tm_year, t.tm_mon, t.tm_mday)
+ return instance
+
+ def toString(self):
+ return '%s.%s.%s' % (self.year, self.month, self.day)
+
+
+
+
+class FrostBoard(object):
+
+ def __init__(self):
+
+ self.fcpClient = FcpClient(debugVerbosity=FcpClient.consts.DebugVerbosity.Debug)
+ nodeHello = self.fcpClient.connect()
+ if nodeHello is None:
+ return False
+
+ self.fcpClient.events.RequestCompleted += self.handleRequestCompleted
+
+
+ def handleRequestCompleted(self, event, request):
+
+ #self.fcpClient.removeRequest(request['Identifier'])
+ print request.data
+
+
+
+ def readBoard(self, boardName, section, startDate=None):
+
+ slot = -1
+ while True:
+ slot += 1
+ if slot > 10: break
+
+ ksk = 'KSK@frost|message|%s|%s-%s-%s.xml' % (section, startDate.toString(), boardName, slot)
+ print ksk
+
+ self.fcpClient.getData(
+ uri=ksk,
+ maxSize='33000'
+ )
+
+ for i in xrange(50):
+ self.fcpClient.next()
+
+
+
+if __name__ == '__main__':
+ board = FrostBoard()
+
+
+ t = FrostDate.now()
+ print t.toString()
+
+ board.readBoard('de.freenet', 'news', FrostDate.now())
+
+
+
+
+
+
+
+
Copied: trunk/sandbox/fcp2/client.py (from rev 254, trunk/sandbox/fcp/client.py)
===================================================================
--- trunk/sandbox/fcp2/client.py (rev 0)
+++ trunk/sandbox/fcp2/client.py 2008-02-26 09:50:39 UTC (rev 255)
@@ -0,0 +1,2307 @@
+"""Freenet client protocol 2.0 implementation
+
+Compatibility: >= Freenet 0.7 Build #1107
+
+
+@newfield event: Event, Events
+@newfield requestparam: RequestParam, RequestParams
+
+@note: The client implementation never uses or watches the global queue. No implementation
+should ever do so. Global is evil.
+@note: the client is not thread save.
+
+
+Sample code. Connect to the freenet node::
+ client = FcpClient()
+ nodeHello = client.connect()
+ if nodeHello is None:
+ pass
+ # something went wrong ..could not connect to the freenet node
+ else:
+ pass
+ # everything went well ..we are connected now
+
+Request data associated to a freenet key::
+ myRequestIdentifier = client.getData('CHK@ABCDE.......')
+ myRequest = c.getRequest(myIdentifier)
+ client.run()
+ print myRequest.data
+
+Usually you would connect handlers to client events to do processing or handle errors::
+ def handleSuccess(event, request):
+ print 'Here is the data:', request.data
+
+ def handleFailure(event, request):
+ print 'Too bad, something went wrong'
+
+ client.events.RequestCompleted += handleSuccess
+ client.events.RequestFailed += handleFailure
+
+ client.getData('CHK@ABCDE.......')
+ c.run()
+
+
+Instead of calling run() you may run the client step by step::
+ client.getData('CHK@ABCDE.......')
+ for i in xrange(50):
+ client.next()
+
+
+You may disconnect event handlers aswell::
+
+ client.events.RequestCompleted -= handleSuccess
+ client.events.RequestFailed -= handleFailure
+
+
+Multiple event handlers may be connected / disconnected at once::
+
+ client.events += (
+ (client.events.RequestCompleted, handleSuccess),
+ (client.events.RequestFailed, handleFailure)
+ )
+
+
+"""
+
+#Bug reports filed and open:
+#--------------------------------------------------------------------------------------------------------------------------------------------
+# [0001931: Send EndListPersistentRequests following client connect]
+#
+# PendingRequests currently get lost if a.) the node goes down b.) if the client goes down unexpectedly.
+# This affects IdentifierCollision + FilenameCollision + ClientPut when a SSK needs to be created first
+#
+# we can handle this case none short of maintaining a file keeping messages. But still there is the
+# problem of knowing when the node has actually registered a request. The node does not send
+# an EndListPersistentRequests on connect so it is impossible to tell when or if to restore one of
+# the pending requests we stored.
+#
+#FIX: None
+#---------------------------------------------------------------------------------------------------------------------------------------------
+# [0001893: CloseConnectionDuplicateClientName bug or feature?]
+#
+# CloseConnectionDuplicateClientName
+# currently fcp takes down a our connection if another client (...) uses the same connection name.
+#
+#FIX: None
+#----------------------------------------------------------------------------------------------------------------------------------------------
+# [0001888: explicite connection locale]
+#
+# Many strings are already translated by freenet, but there is no way to tell the node wich language
+# to use to talk to a client. Maybe a good idea, maybe not.
+#
+#FIX: None
+#-----------------------------------------------------------------------------------------------------------------------------------------------
+# [0001781: unregister directories registered via TestDDARequest]
+#
+# With current state of the art DDA handling it is not possiblr to unregister directories (may pile
+# up in memory over time).
+#
+#FIX: None
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# [0002019: Socket dies if first message is not ClientHello]
+#
+# minor one
+#
+#FIX: None
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# [0001965: Persistence vs PersistenceType]
+#
+# PersistentGet passes persistence field as "PersistenceType", PersistentPut as "Persistence"
+# already fixed this here in the client
+#
+# FIX: implemented in client
+#-------------------------------------------------------------------------------------------------------------------------------------------------
+# [0002015: Drop the global queue]
+#
+# this one is somewhat related to [0001931: Send EndListPersistentRequests following client connect]
+#
+# We never use or watch the global queue. It is to dangerous. But problems remain when it comes
+# to restoring persistent requests. Shure these are our requests? Worst case is a client with a colliding
+# connection name flooding our client with an unknown number of left overs.
+#
+#FIX: None (that is, this case is handled as savely as possible - except from possible slowdowns - but no
+# guarantee that no unknown request may slip through)
+#-------------------------------------------------------------------------------------------------------------------------------------------------
+# [0001894: HandleCollision field in ClientGet]
+#
+# minor one. When downloading a file, filename collisions may occur. Fcp does not handle these very well
+# It checks if the tempfile (filename ?) can be created newly when the request is started. IIRC In the final
+# rename of the tempfile to filename no check is done and filename will get overwritten.
+#
+#FIX: we handle collisions in the client as savely as possible. But no guarantee either when a colliding file
+# (...) finds his way into the download directory while downloading another.
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# [0002083: RemovePersistentRequest ignores unknown requests]
+#
+# minor one, but related to it a major one: you can not change the Priority of requests with
+# Persistence=conncetion
+#
+#FIX: workaround for the "related" part
+#------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+# Todos
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# clean up
+#
+# x. move saveWriteFile and friends to a separate module
+#
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# Fcp types vs. Python types
+#
+# x. Fcp seems to use kibibytes. Autoconvert to kilobytes?
+# x. time intervals
+#
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# logging
+#
+# x. should uris (...) always be visible in log output? Certainly in memory. Maybe a specialized
+# "save" logger could be useful (like for user feedback).
+#
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# runtime
+#
+# x. if the socket dies the client disconnects automatically, clearing all requests.
+# No idea how to handle this. Would require at least an EndListPersistentRequest
+# from the node to check wich requests arrived at the node ..and an auto resend
+# requests the node does not know about.
+#
+#------------------------------------------------------------------------------------------------------------------------------------------------
+# request status
+#
+# x. have to set a dedicated flag when a request is about to be modified or removed
+# Fcp gets confused if we disconnect emidiately after sending a modify or remove request
+# Curretnly the RequestStatus.Completed flag is removed and later set again to get some
+# control over the process.
+#
+# TODO: check if this is a bug in Fcp
+# NOTE: seems to be fixed in [build 1112], fixes removed
+#-------------------------------------------------------------------------------------------------------------------------------------------------
+import atexit
+import copy
+import logging
+import os
+import socket
+import subprocess
+import sys
+import time
+
+
+#--> rel import hack
+class _RelImportHack(object):
+ def __init__(self, n):
+ fpath = os.path.abspath(__file__)
+ for i in xrange(n): fpath = os.path.dirname(fpath)
+ sys.path.insert(0, fpath)
+ def __del__(self): sys.path.pop(0)
+hack = _RelImportHack(2)
+
+
+import fcp.fcp2_0_consts as consts
+from fcp.fcp2_0_events import Events
+from fcp.fcp2_0_config import Config
+from fcp.fcp2_0_message import Message
+import fcp.fcp2_0_params as FcParams
+from fcp.fcp2_0_uri import Uri
+
+from fcp.fcp_lib import namespace
+from fcp.fcp_lib import tools
+
+
+del hack
+#<-- rel import hack
+
+logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
+#*************************************************************************************************
+#
+#*************************************************************************************************
+class FcpClient(object):
+ """
+ @ivar events: events the client supports
+ """
+
+ DefaultFcpHost = os.environ.get('FCP_HOST', '127.0.0.1').strip()
+ try:
+ DefaultFcpPort = int(os.environ.get('FCP_PORT', '').strip())
+ except ValueError:
+ DefaultFcpPort = 9481
+
+
+
+ #TODO: check if required
+ # suggested by Mathew Toseland to use about 32k for mimeType requests
+ # basic sizes of keys are: 1k for SSks and 32k for CHKs
+ # without MaxSize DataFound will have DataLength set to 0 (?!)
+ MaxSizeKeyInfo = 32768
+ MinimumRunTime = 1 # minimum time (seconds) the client will run when run() is called (FIX: 0001931)
+ SocketTimeout = 0.1
+ ExpectedFcpVersion = 2.0
+ ExpectedNodeBuild = 1107
+
+ consts = consts
+ Config = Config
+ Message = Message
+ FcParams = FcParams
+ Uri = Uri
+
+
+ def __init__(self,
+ connectionName=None,
+ debugVerbosity=None,
+ ):
+ """
+ @param connectionName: name of the connection or None to use an arbitrary connection name
+ @param debugVerbosity: verbosity level for debugging. Default is L{consts.DebugVerbosity.Warning}
+
+ """
+ self._connectionName = self.setConnectionName(connectionName)
+ self._ddaTests = [] # currently running DDA tests (request0, ... requestN)
+ self._requests = {} # currently running requests (requestIdentifier --> request)
+
+ self._logEvent = logging.getLogger(consts.LoggerNames.ClientEvents)
+ self._logMessage = logging.getLogger(consts.LoggerNames.ClientMessages)
+ self._logRuntime = logging.getLogger(consts.LoggerNames.ClientRuntime)
+
+ self._nodeHelloMessage = None
+ self._socket = None
+
+ self.events = Events()
+ for event in self.events:
+ event += self._captureEvent
+
+ self.setDebugVerbosity(consts.DebugVerbosity.Warning if debugVerbosity is None else debugVerbosity)
+ atexit.register(self.close)
+
+
+ ###############################################################
+ ##
+ ## private methods
+ ##
+ ###############################################################
+ def _close(self, msg):
+ """Closes the client
+ @param msg: message to pass to the ClientDisconnected event or None to not inform listeners
+ """
+ self._logRuntime.info(consts.LogMessages.ClientClose)
+
+ # clean left over DDA test tmp files
+ for initialRequest in self._ddaTests:
+ if initialRequest['FcTestDDA'].get('TmpFile', None) is not None:
+ tools.saveRemoveFile(initialRequest['FcTestDDA']['TmpFile'])
+
+ self._ddaTests = []
+ self._requests = {}
+
+ if self._socket is None:
+ #TODO: complain or not?
+ pass
+ else:
+ self._socket.close()
+ self._socket = None
+ if msg is not None:
+ self.events.ClientDisconnected(msg)
+
+
+ def _finalizeRequest(self, msg, request, event):
+ """Finalzes a request
+ @param msg: message that is the reason for finalizing
+ @param request: request to finalize
+ @param event: event to trigger or None
+
+ @note: this method sets the requests L{consts.RequestStatus.RemovedFromQueue} and
+ L{consts.RequestStatus.Completed} flags accordingly
+ @note: Fcp removes Get / Put requests with Persistence == connection emidiately
+ from its queue. Same goes all requests on ProtocolError. We inform the caller
+ that the request has been completed and remove it fom our queue if necessary.
+ Non Get / Put requests will be removed in any case.
+ """
+ removeRequest = msg.name in (consts.Message.ProtocolError, consts.Message.PersistentRequestRemoved)
+ if not removeRequest:
+ #NOTE: non Get / Put related requests do not have a Persistence param
+ removeRequest = request.params.get('Persistence', consts.Persistence.Connection) == consts.Persistence.Connection
+ if removeRequest:
+ request['FcRequestStatus'] |= consts.RequestStatus.RemovedFromQueue
+
+ request['FcRequestStatus'] |= consts.RequestStatus.Completed
+ if event is not None:
+ event(request)
+
+ if removeRequest:
+ del self._requests[request['Identifier']]
+
+
+ def _registerRequest(self,
+ msg,
+ requestType,
+ userData=None,
+ identifier=None,
+ initTime=None,
+ persistentUserData='',
+ filenameCollision=consts.FilenameCollision.HandleNever,
+ ):
+ """Registers a request
+ @param msg: message to register
+ @param requestType: (L{consts.RequestType}) type of request to register
+ @param filenameCollision: (L{consts.FilenameCollision}) how to handle filename collisions.
+ Default is L{consts.FilenameCollision.HandleNever}
+ @param identifier: (str) identifier of the request or None to create a new one
+ @param initTime: (int) init time of the request or None to set it to now
+ @param persistentUserData: (str) anyuser defined persistent data
+ @param userData: (any) any user defined non persistent data
+
+ @return: (str) identifer of therequest
+ @note: the identifier returned is unique to the client but may not be unique to the node
+ """
+ identifier = self.FcParams.newUuid(uuids=self._requests) if identifier is None else identifier
+
+ if requestType & (consts.RequestType.MaskGet | consts.RequestType.MaskPut):
+
+ msg.params.update({
+
+ # persistent params that will go into identifier
+ 'FcRequestType': requestType, # identifies sub message types
+ 'FcInitTime': time.time() if initTime is None else initTime, # when was the request started?
+ 'FcFilenameCollision': filenameCollision, # handle fielanem collisions?
+ 'FcPersistentUserData': persistentUserData, # any user defined persistent data
+
+ # non persistent params
+ 'FcRequestStatus': consts.RequestStatus.Null,
+ 'FcErrorMessage': None, # error message in case an error occured
+ 'FcUserData': userData, # any user defined runtime data here
+
+ # params for SSKKeypair
+ 'FcPrivateKey': None,
+ 'FcPublicKey': None,
+
+ # params from DataFound
+ 'FcMetadataContentType': '', # contecnt type
+ 'FcDataLength': '', # content size
+
+ # params from PersistentRequestModified
+ 'FcModified': {},
+
+ # params for DDA test
+ 'FcTestDDA': {},
+
+ # params for SimpleProgress
+ 'FcProgressTotal': '0',
+ 'FcProgressRequired': '0',
+ 'FcProgressFailed': '0',
+ 'FcProgressFatalyFailed': '0',
+ 'FcProgressSucceeeded': '0',
+
+ })
+ # equip msg with some persistent pparams
+ if msg.get('ClientToken', None) is None:
+ msg['ClientToken'] = self.FcParams.messageToParams(msg)
+
+ elif requestType & consts.RequestType.MaskGenerateKeypair:
+ msg.params.update({
+ 'FcRequestType': requestType, # identifies sub message types
+ 'FcRequestStatus': consts.RequestStatus.Null,
+ 'FcInitTime': initTime, # when was the request started?
+ 'FcModified': {},
+
+ 'FcPrivateKey': None,
+ 'FcPublicKey': None,
+ })
+
+ elif requestType & consts.RequestType.PluginMessage:
+ msg.params.update({
+ 'FcRequestType': requestType, # identifies sub message types
+ 'FcRequestStatus': consts.RequestStatus.Null,
+ 'FcInitTime': initTime, # when was the request started?
+ 'FcModified': {},
+
+ 'FcPluginReply': None,
+ })
+
+
+
+ else:
+ msg.params.update({
+ 'FcRequestType': requestType, # identifies sub message types
+ 'FcRequestStatus': consts.RequestStatus.Null,
+ 'FcInitTime': initTime, # when was the request started?
+ 'FcModified': {},
+ })
+
+ msg['FcRequestStatus'] |= consts.RequestStatus.Null
+ msg['Identifier'] = identifier
+ self._requests[identifier] = msg
+
+
+ def _captureEvent(self, event, request):
+ if event != self.events.Idle:
+ self._logEvent.debug(consts.LogMessages.EventTriggered + event.name)
+
+ ###############################################################
+ ##
+ ## connection related methods
+ ##
+ ###############################################################
+ def close(self):
+ """Closes the client
+ @note: make shure to call close() when done with the client
+ """
+ msg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.Close,
+ Param=None,
+ )
+ self._close(msg)
+
+
+ def closeFreenet(self):
+ """Shuts down the freenet node"""
+ self.sendMessage(consts.Message.Shutdown)
+
+
+ def connect(self, host=DefaultFcpHost, port=DefaultFcpPort, duration=20, timeout=0.5):
+ """Connects to the freenet node
+ @param host: (str) host of th node
+ @param port: (int) port of the node
+ @param duration: (int) how many seconds try to connect before giving up
+ @param timeout: (int) how much time to wait before another attempt to connect
+
+ @return: (L{fcp2_0_message.Message}) NodeHello or None if no connection could be established
+ """
+ nodeHello = None
+ for nodeHello in self.iterConnect(host=host, port=port, duration=duration, timeout=timeout):
+ pass
+ return nodeHello
+
+
+ def iterConnect(self, host=DefaultFcpHost, port=DefaultFcpPort, duration=20, timeout=0.5):
+ """Iterator to stablish a connection to a freenet node
+ @param host: (str) host of th node
+ @param port: (int) port of the node
+ @param duration: (int) how many seconds try to connect before giving up
+ @param timeout: (int) how much time to wait before another attempt to connect
+
+ @return: (L{fcp2_0_message.Message}) NodeHello if successful, None otherwise for the next iteration
+
+ @event: ClientConnected(event, message) is triggered as soon as the client is connected
+ """
+ self._logRuntime.info(consts.LogMessages.Connecting)
+
+ # try to Connect socket
+ if self._socket is not None:
+ self._close(None)
+
+ # poll untill freenet responds
+ timeElapsed = 0
+ while timeElapsed < duration:
+
+ # try to Connect socket
+ if self._socket is not None:
+ self._close(None)
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.settimeout(self.SocketTimeout)
+ try:
+ self._socket.connect((host, port))
+ except socket.error, d:
+ yield None
+ else:
+ self._logRuntime.info(consts.LogMessages.Connected)
+
+ # send ClientHello and wait for NodeHello
+ #NOTE: thought I could leave ClientHelloing up to the caller
+ # but instad of responding with ClientHelloMustBeFirst
+ # as expected when not doing so, the node disconnects.
+ # So take it over here.
+ self.sendMessage(
+ consts.Message.ClientHello,
+ Name=self._connectionName,
+ ExpectedVersion=self.ExpectedFcpVersion,
+ )
+ while timeElapsed <= duration:
+ msg = self.next(dispatch=False)
+
+ if msg.name == consts.Message.ClientSocketTimeout:
+ timeElapsed += self.SocketTimeout
+ yield None
+
+ elif msg.name == consts.Message.NodeHello:
+ self._nodeHelloMessage = msg
+ # check if version is ok
+ if self.versionCheckNodeHello(msg):
+ self.events.ClientConnected(msg)
+ else:
+ disconnectMsg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.VersionMissmatch,
+ Param=msg,
+ )
+ self._close(disconnectMsg)
+ yield self._nodeHelloMessage
+ raise StopIteration
+
+ else:
+ self._logMessage.debug(consts.LogMessages.MessageReceived + msg.pprint())
+ break
+ break
+
+ # continue polling
+ self._logRuntime.info(consts.LogMessages.ConnectionRetry)
+ timeElapsed += timeout
+ time.sleep(timeout)
+
+ self._logRuntime.info(consts.LogMessages.ConnectingFailed)
+ disconnectMsg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.ConnectingFailed,
+ Param=None,
+ )
+ self._close(disconnectMsg)
+ raise StopIteration
+
+
+ def getConnectionName(self):
+ """Returns the connection name used by the client
+ @return: (str) connection name
+ """
+ return self._connectionName
+
+
+ def setConnectionName(self, connectionName=None):
+ """Sets the connection name to be used by the client
+ @param connectionName: (str) connection name or None to use an arbitrary connection name
+ @return: (str) connection name
+ """
+ self._connectionName = self.FcParams.newUuid() if connectionName is None else connectionName
+ return self._connectionName
+
+
+ def setDebugVerbosity(self, debugVerbosity):
+ """Sets the verbosity level of the client
+ @note: see L{consts.DebugVerbosity}
+ """
+ self._logEvent.setLevel(debugVerbosity)
+ self._logMessage.setLevel(debugVerbosity)
+ self._logRuntime.setLevel(debugVerbosity)
+
+
+ def startFreenet(self, cmdline):
+ """Starts freenet
+ @param cmdline: commandline to start freenet (like '/freenet/run.sh start' or 'c:\freenet\start.bat')
+ @return: (str) whatever freenet returns
+ """
+ #TODO: on windows it may be necessary to hide the command window
+ p = subprocess.Popen(
+ args=cmdline,
+ shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ stdout, stderr = p.communicate()
+ return stdout
+
+
+ def versionCheckNodeHello(self, nodeHelloMessage):
+ """Performa a version check of the client against the specified NodeHello message
+ @return: (bool) True if version is ok, False otherwise
+ @note: if this check returns False the client will emidiately disconnect in L{connect()}
+ and triggers a ClientDisconnected event. Overwrite to adjust
+ """
+ if nodeHelloMessage['FCPVersion'] >= self.ExpectedFcpVersion:
+ if nodeHelloMessage['Build'] >= self.ExpectedNodeBuild:
+ return True
+ return False
+
+ #########################################################
+ ##
+ ## runtime related methods
+ ##
+ #########################################################
+ def handleMessage(self, msg):
+ """Handles a message from the freenet node
+ @param msg: (Message) to handle
+ @return: True if the message was handled, False otherwise
+ """
+
+ CancelPersistentRequests = 0 # for testing... if True, cancels all PersistentRequests
+
+ # check if we have an initial request corrosponding to msg
+ requestIdentifier = msg.get('Identifier', None)
+ initialRequest = None if requestIdentifier is None else self._requests.get(requestIdentifier, None)
+ ####################################################
+ ##
+ ## errors
+ ##
+ ####################################################
+ if msg.name == consts.Message.IdentifierCollision:
+ if initialRequest is None:
+ return False
+
+ # resend request with new identifier
+ newIdentifier = self.FcParams.newUuid(uuids=self._requests)
+ self._requests[newIdentifier] = initialRequest
+ del self._requests[requestIdentifier]
+ initialRequest['Identifier'] = newIdentifier
+ initialRequest['FcModified'] = {consts.RequestModified.Identifier: requestIdentifier}
+ self.events.RequestModified(initialRequest)
+ self.sendMessageEx(initialRequest)
+ return True
+
+
+ elif msg.name == consts.Message.ProtocolError:
+ code = msg['Code']
+ if code == consts.ProtocolError.ShuttingDown:
+ disconnectMsg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.Shutdown,
+ Param=None,
+ )
+ self._close(disconnectMsg)
+ return True
+
+
+ if requestIdentifier is None:
+ self.events.ProtocolError(msg)
+ return True
+
+ if initialRequest is None:
+ return False
+
+
+ # handle DDA errors
+ elif code == consts.ProtocolError.DDADenied:
+ ddaRequestMsg = self.Message(consts.Message.TestDDARequest)
+ if initialRequest.name == consts.Message.ClientGet:
+ ddaRequestMsg['WantWriteDirectory'] = True
+ directory = os.path.dirname(initialRequest['Filename'])
+ else:
+ ddaRequestMsg['WantReadDirectory'] = True
+ directory = os.path.dirname(initialRequest['Filename'])
+ ddaRequestMsg['Directory'] = directory
+
+ # add params required for testing
+ initialRequest['FcTestDDA'] = {
+ 'Directory': directory,
+ 'Replied': False,
+ 'TmpFile': None,
+ 'WantWrite': ddaRequestMsg.get('WantWriteDirectory', False),
+ 'ErrorMsg': msg,
+ }
+ self._ddaTests.append(initialRequest)
+ self.sendMessageEx(ddaRequestMsg)
+ return True
+
+
+ # handle filename collisions
+ elif code == consts.ProtocolError.DiskTargetExists:
+ handleCollision = initialRequest.get('FcFilenameCollision', consts.FilenameCollision.HandleNever)
+ collisionHandled = bool(handleCollision & consts.FilenameCollision.CollisionHandled)
+
+ # rename filename
+ if handleCollision & consts.FilenameCollision.HandleRename:
+ filename = initialRequest['Filename']
+ initialRequest['FcFilenameCollision'] |= consts.FilenameCollision.CollisionHandled
+ newFilename = namespace.unique_filename(filename, extensions=1, ispostfixed=collisionHandled)
+ initialRequest['Filename'] = newFilename
+ initialRequest['FcModified'] = {consts.RequestModified.Filename: filename}
+ self.sendMessageEx(initialRequest)
+ self.events.RequestModified(initialRequest)
+ return True
+
+ # don't handle
+ else:
+ initialRequest['FcFilenameCollision'] &= ~consts.FilenameCollision.CollisionHandled
+
+
+ # handle plugin related request failures
+ elif code == consts.ProtocolError.NoSuchPlugin or code == consts.ProtocolError.AccessDenied:
+ if initialRequest.name == consts.Message.GetPluginInfo:
+ initialRequest['FcErrorMessage'] = msg
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Error
+ self._finalizeRequest(msg, initialRequest, self.events.PluginInfoFailed)
+ return True
+
+ # TODO: just a guess that FCPPluginMessage can trigger an AccessDenied error
+ elif initialRequest.name == consts.Message.FCPPluginMessage:
+ initialRequest['FcErrorMessage'] = msg
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Error
+ self._finalizeRequest(msg, initialRequest, self.events.PluginMessageFailed)
+ return True
+
+ # only requests should get through to here
+
+ # NOTE: Fcp already removed the request
+ initialRequest['FcErrorMessage'] = msg
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Error
+ self._finalizeRequest(msg, initialRequest, self.events.RequestFailed)
+ return True
+
+
+ ####################################################
+ ##
+ ## TestDDA
+ ##
+ ## We assume that a DDATest messages do not get mixed up. 1st TestDDARequest is first to
+ ## enter TestDDAReply and TestDDAComplete. Hopefuly the freenet devels will rework the
+ ## TestDDA drill.
+ ##
+ ####################################################
+ elif msg.name == consts.Message.TestDDAReply:
+ directory = msg['Directory']
+
+ # find message that triggered the call
+ for initialRequest in self._ddaTests:
+ if initialRequest['FcTestDDA']['Directory'] == directory:
+ if not initialRequest['FcTestDDA']['Replied']:
+ initialRequest['FcTestDDA']['Replied'] = True
+ break
+ else:
+ # fell through
+ raise ValueError('No inital message found in TestDDAReply')
+
+ # perform read test if necessary
+ fpathRead = msg.params.get('ReadFilename', None)
+ readContent = ''
+ if fpathRead is not None:
+ readContent = tools.saveReadFile(fpathRead)
+ if readContent is None:
+ readContent = ''
+
+ # perform write test if necessary
+ fpathWrite = msg.params.get('WriteFilename', None)
+ if fpathWrite is not None:
+ written = tools.saveWriteFile(fpathWrite, msg['ContentToWrite'])
+ if not written:
+ tools.saveRemoveFile(fpathWrite)
+ else:
+ initialRequest['FcTestDDA']['TmpFile'] = fpathWrite
+
+ self.sendMessage(
+ consts.Message.TestDDAResponse,
+ Directory=msg['Directory'],
+ ReadContent=readContent,
+ )
+ return True
+
+
+ elif msg.name == consts.Message.TestDDAComplete:
+ # clean up tmp file
+ directory = msg['Directory']
+
+ # find message that triggered the call
+ for initialRequest in self._ddaTests:
+ if initialRequest['FcTestDDA']['Directory'] == directory:
+ if initialRequest['FcTestDDA']['Replied']:
+ break
+ else:
+ # fell through
+ raise ValueError('No initial message found in TestDDAComplete')
+
+ # remove test and clean tmp data
+ self._ddaTests.remove(initialRequest)
+ if initialRequest['FcTestDDA']['TmpFile'] is not None:
+ tools.saveRemoveFile(initialRequest['FcTestDDA']['TmpFile'])
+ wantWrite = initialRequest.params['FcTestDDA']['WantWrite']
+
+ # check if test was sucessful
+ testFailed = False
+ if wantWrite:
+ testFailed = not msg.params.get('WriteDirectoryAllowed', False)
+ else:
+ testFailed = not msg.params.get('ReadDirectoryAllowed', False)
+
+ if testFailed:
+ #TODO: check if errorMsg gives reasonable feedback
+ initialRequest['FcRequestStatus'] = consts.RequestStatus.Error
+ initialRequest['FcErrorMessage'] = initialRequest['FcTestDDA']['ErrorMsg']
+ self._finalizeRequest(msg, initialRequest, self.events.RequestFailed)
+ return True
+
+ # else: resend message
+ self.sendMessageEx(initialRequest)
+ return True
+
+ ####################################################
+ ##
+ ## config related
+ ##
+ ####################################################
+ elif msg.name == consts.Message.ConfigData:
+ self.events.ConfigData(msg)
+ return True
+
+ elif msg.name == consts.Message.NodeData:
+ self.events.NodeData(msg)
+ return True
+
+ ####################################################
+ ##
+ ## get / put related
+ ##
+ ####################################################
+ elif msg.name == consts.Message.AllData:
+ if initialRequest is None:
+ return False
+
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ initialRequest.data = msg.data
+ self._finalizeRequest(msg, initialRequest, self.events.RequestCompleted)
+ return True
+
+ elif msg.name == consts.Message.DataFound:
+ if initialRequest is None:
+ return False
+
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ initialRequest['FcMetadataContentType'] = msg.get('Metadata.ContentType', '')
+ initialRequest['FcDataLength'] = msg.get('DataLength', '')
+
+ # except from GetData all requests are complete here. Next GetData will run through AllData...
+
+ # For GetData with persistence != connection the node sends no All Data message
+ # whatever that is good for ..fix this here to get all GetData request to complete on
+ # All Data.
+ if initialRequest['FcRequestType'] == consts.RequestType.GetData:
+ if initialRequest['Persistence'] != consts.Persistence.Connection:
+ self.sendMessage(
+ consts.Message.GetRequestStatus,
+ Identifier=initialRequest['Identifier'],
+ Global=False,
+ OnlyData=True
+ )
+ else:
+ self._finalizeRequest(msg, initialRequest, self.events.RequestCompleted)
+
+ return True
+
+
+ elif msg.name == consts.Message.GetFailed:
+ if initialRequest is None:
+ return False
+
+ code = msg['Code']
+ if code == consts.FetchError.Canceled:
+ return False
+
+ # check if it is one of our requests for key information
+ if code == consts.FetchError.TooBig and initialRequest['FcRequestType'] == consts.RequestType.GetKeyInfo:
+ initialRequest['FcMetadataContentType'] = msg.get('ExpectedMetadata.ContentType', '')
+ initialRequest['FcDataLength'] = msg.get('ExpectedDataLength', -1)
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ self._finalizeRequest(msg, initialRequest, self.events.RequestCompleted)
+ else:
+ initialRequest['FcErrorMessage'] = msg
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Error
+ self._finalizeRequest(msg, initialRequest, self.events.RequestFailed)
+ return True
+
+
+ elif msg.name == consts.Message.PersistentGet:
+
+ # unknown request... try to restore it
+ if initialRequest is None:
+ fcParams = self.FcParams.paramsFromRequest(msg)
+
+ # not one of our requests... so cancel it
+ if fcParams is None or CancelPersistentRequests:
+ self.sendMessage(
+ consts.Message.RemovePersistentRequest,
+ Identifier=requestIdentifier,
+ Global=msg['Global'],
+ )
+ return True
+
+ initialRequest = copy.deepcopy(msg)
+ self._registerRequest(
+ initialRequest,
+ fcParams[self.FcParams.IRequestType],
+ identifier=requestIdentifier,
+ initTime=fcParams[self.FcParams.IInitTime],
+ userData=None,
+ persistentUserData=fcParams[self.FcParams.IPersistentUserData],
+ filenameCollision=fcParams[self.FcParams.IFilenameCollision],
+ )
+
+ initialRequest.name = consts.Message.ClientGet
+ #FIX: remove Started param from PersistentGet / Put
+ del initialRequest.params['Started']
+ #FIX: [0001965: Persistence vs PersistenceType]
+ if 'PersistenceType' in initialRequest.params:
+ initialRequest['Persistence'] = initialRequest.params.pop('PersistenceType')
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Restored
+ self.events.RequestRestored(initialRequest)
+ return True
+
+ # known request ..we don't handle that
+ return False
+
+
+ elif msg.name == consts.Message.PersistentRequestModified:
+ if initialRequest is None:
+ return False
+
+ modified = {}
+
+ # check if PersistentUserData has changed
+ params = self.FcParams.paramsFromRequest(initialRequest)
+ if params is not None:
+ clientToken = msg.get('ClientToken', None)
+ if clientToken is not None:
+
+ #TODO: its more or less a guess that PersistentUserData has changed
+ # ...as long as no other param is changed at runtime we are ok
+ # otherwise we would have to set flags to indicate wich member
+ # of ClientToken changed. See --> modifyRequest()
+ modified[consts.RequestModified.PersistentUserData] = None
+ for i, fcParam in enumerate(self.FcParams.FcParams):
+ initialRequest[fcParam] = params[i]
+
+ # check if PriorityClass has changed
+ priorityClass = msg.get('PriorityClass', None)
+ if priorityClass is not None:
+ modified[consts.RequestModified.PriorityClass] = None
+ initialRequest['PriorityClass'] = priorityClass
+
+ initialRequest['FcModified'] = modified
+ self.events.RequestModified(initialRequest)
+ return True
+
+
+ elif msg.name == consts.Message.PersistentRequestRemoved:
+ if initialRequest is None:
+ return False
+
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Removed
+ self._finalizeRequest(msg, initialRequest, self.events.RequestRemoved)
+ return True
+
+
+ elif msg.name == consts.Message.SimpleProgress:
+ if initialRequest is None:
+ return False
+
+ initialRequest['FcProgressTotal'] = msg['Total']
+ initialRequest['FcProgressRequired'] = msg['Required']
+ initialRequest['FcProgressFailed'] = msg['Failed']
+ initialRequest['FcProgressFatalyFailed'] = msg['FatallyFailed']
+ initialRequest['FcProgressSucceeeded'] = msg['Succeeded']
+ self.events.RequestProgress(initialRequest)
+ return True
+
+ ## put related
+
+ elif msg.name == consts.Message.PersistentPut:
+
+ # unknown request... try to restore it
+ if initialRequest is None:
+ fcParams = self.FcParams.paramsFromRequest(msg)
+
+ # not one of our requests... so cancel it
+ if fcParams is None or CancelPersistentRequests:
+ self.sendMessage(
+ consts.Message.RemovePersistentRequest,
+ Identifier=requestIdentifier,
+ Global=msg['Global'],
+ )
+ return True
+
+ initialRequest = copy.deepcopy(msg)
+ requestType = fcParams[self.FcParams.IRequestType]
+ self._registerRequest(
+ initialRequest,
+ requestType,
+ identifier=requestIdentifier,
+ initTime=fcParams[self.FcParams.IInitTime],
+ userData=None,
+ persistentUserData=fcParams[self.FcParams.IPersistentUserData],
+ filenameCollision=fcParams[self.FcParams.IFilenameCollision],
+ )
+
+ # determine initial message name
+ if requestType in (consts.RequestType.PutData, consts.RequestType.PutFile):
+ initialRequest.name = consts.Message.ClientPut
+ elif requestType == consts.RequestType.PutDir:
+ initialRequest.name = consts.Message.ClientPutDiskDir
+ elif requestType == consts.RequestType.PutMultiple:
+ initialRequest.name = consts.Message.ClientPutComplexDir
+
+ #FIX: remove Started param from PersistentGet / Put
+ del initialRequest.params['Started']
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Restored
+ self.events.RequestRestored(initialRequest)
+ return True
+
+ # known request ..we don't handle that
+ return False
+
+
+ elif msg.name == consts.Message.PutFailed:
+ if initialRequest is None:
+ return False
+
+ code = msg['Code']
+ if code == consts.InsertError.Canceled:
+ return False
+
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Error
+ initialRequest['FcErrorMessage'] = msg
+ self._finalizeRequest(msg, initialRequest, self.events.RequestFailed)
+ return True
+
+
+ elif msg.name == consts.Message.PutFetchable:
+ if initialRequest is None:
+ # something went wrong
+ return False
+
+ self.events.RequestFetchable(initialRequest)
+ return True
+
+
+ elif msg.name == consts.Message.PutSuccessful:
+ if initialRequest is None:
+ return False
+ # TODO: StartupTime and CompletionTime are passed, but
+ # as long as no corrosponding params are passed in DataFound
+ # we ignore them
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ initialRequest['URI'] = msg['URI']
+ self._finalizeRequest(msg, initialRequest, self.events.RequestCompleted)
+ return True
+
+
+ elif msg.name == consts.Message.URIGenerated:
+ if initialRequest is None:
+ return False
+ initialRequest['URI'] = msg['URI']
+ return True
+
+ elif msg.name == consts.Message.FinishedCompression:
+ if initialRequest is None:
+ return False
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Compressed
+ self.events.RequestCompressionFinished(initialRequest)
+ return True
+
+ elif msg.name == consts.Message.StartedCompression:
+ if initialRequest is None:
+ return False
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Compressing
+ self.events.RequestCompressionStarted(initialRequest)
+ return True
+
+ ####################################################
+ ##
+ ## Peer related messages
+ ##
+ ####################################################
+ elif msg.name == consts.Message.EndListPeers:
+ self.events.EndListPeers(msg)
+ return True
+
+ elif msg.name == consts.Message.EndListPeerNotes:
+ self.events.EndListPeerNotes(msg.params)
+ return True
+
+ elif msg.name == consts.Message.Peer:
+ self.events.Peer(msg)
+ return True
+
+ elif msg.name == consts.Message.PeerNote:
+ self.events.PeerNote(msg)
+ return True
+
+ elif msg.name == consts.Message.PeerRemoved:
+ self.events.PeerRemoved(msg)
+ return True
+
+ elif msg.name == consts.Message.UnknownNodeIdentifier:
+ self.events.PeerUnknown(msg)
+ return True
+
+ elif msg.name == consts.Message.UnknownPeerNoteType:
+ self.events.PeerNoteTypeUnknown(msg)
+ return True
+ ####################################################
+ ##
+ ## plugins
+ ##
+ ####################################################
+ elif msg.name == consts.Message.PluginInfo:
+ if initialRequest is None:
+ return False
+
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ self._finalizeRequest(msg, initialRequest, self.events.PluginInfo)
+ return True
+
+ elif msg.name == consts.Message.FCPPluginReply:
+ if initialRequest is None:
+ return False
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ initialRequest['FcPluginReply'] = msg
+ self._finalizeRequest(msg, initialRequest, self.events.PluginMessage)
+ return True
+
+ ####################################################
+ ##
+ ## others
+ ##
+ ####################################################
+ elif msg.name == consts.Message.CloseConnectionDuplicateClientName:
+ disconnectMsg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.DuplicateClientName,
+ Param=None,
+ )
+ self._close(disconnectMsg)
+ return True
+
+
+ elif msg.name == consts.Message.SSKKeypair:
+ if initialRequest is None:
+ return False
+
+ #TODO:no idea if the node may pass uris with prefixes like 'freenet:'... strip it anyways
+ insertURI = self.Uri(msg['InsertURI']).uri
+ requestURI = self.Uri(msg['RequestURI']).uri
+
+ if initialRequest['FcRequestType'] == consts.RequestType.GenerateUSKKeypair:
+ insertURI = insertURI.replace(consts.KeyType.SSK, consts.KeyType.USK, 1)
+ requestURI = requestURI.replace(consts.KeyType.SSK, consts.KeyType.USK, 1)
+
+ initialRequest['FcPrivateKey'] = insertURI
+ initialRequest['FcPublicKey'] = requestURI
+ initialRequest['FcRequestStatus'] |= consts.RequestStatus.Success
+ self._finalizeRequest(msg, initialRequest, self.events.KeypairGenerated)
+ return True
+
+ elif msg.name == consts.Message.SubscribedUSKUpdate:
+ if initialRequest is None:
+ return False
+ self.events.USKUpdated(msg)
+ return True
+
+
+ # default
+ return False
+
+
+ def next(self, dispatch=True):
+ """Pumps the next message waiting
+ @param dispatch: if True the message is dispatched to L{handleMessage}
+ @note: use this method to run the client step by step. If you want to run the
+ client unconditionally use L{run}
+ """
+ msg = self.Message.fromSocket(self._socket)
+ if msg.name == consts.Message.ClientSocketDied:
+ self._logRuntime.critical(consts.LogMessages.SocketDied)
+ if dispatch:
+ disconnectMsg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.SocketDied,
+ Param=msg,
+ )
+ self._close(disconnectMsg)
+ #raise socket.error(msg['Param']['Details'])
+
+ elif msg.name == consts.Message.ClientSocketTimeout:
+ if dispatch:
+ self.events.Idle(msg)
+ else:
+ self._logMessage.debug(consts.LogMessages.MessageReceived + msg.pprint())
+ if dispatch:
+ self.handleMessage(msg)
+ return msg
+
+
+ def run(self):
+ """Runs the client unconditionally untill all requests have completed
+ @note: a KeyboardInterrupt will stop the client
+ """
+
+ #FIX: 0001931
+ # poll a few times to see if there are persistent requests waiting
+ t0 = time.time()
+ while time.time() - t0 <= self.MinimumRunTime:
+ try:
+ msg = self.next()
+ except KeyboardInterrupt:
+ self._logRuntime.info(consts.LogMessages.KeyboardInterrupt)
+ return
+ if msg.name == consts.Message.ClientSocketDied:
+ return
+
+ #n = 0
+ while True:
+ #n += 1
+ #if n > 50: break
+
+ # check if we have running requests. Assert False
+ haveRunningRequests = False
+ for request in self._requests.values():
+ if not request['FcRequestStatus'] & consts.RequestStatus.Completed:
+ haveRunningRequests = True
+ break
+
+ if not haveRunningRequests:
+ self._logRuntime.info(consts.LogMessages.AllRequestsCompleted)
+ break
+
+ try:
+ msg = self.next()
+ except KeyboardInterrupt:
+ self._logRuntime.info(consts.LogMessages.KeyboardInterrupt)
+ break
+
+ if msg.name == consts.Message.ClientSocketDied:
+ break
+
+
+ def sendMessage(self, name, data=None, **params):
+ """Sends a message to freenet
+ @param name: name of the message to send
+ @param data: data to atatch to the message
+ @param params: {para-name: param-calue, ...} of parameters to pass along
+ with the message (see freenet protocol)
+ @raise SocketError: if the socket connection to the node dies unexpectedly
+ If an error handler is passed to the client it is called emidiately before the error
+ is raised.
+
+ @note: you can use this method to send a message to the node, bypassing all
+ track keeping methods of the client
+ """
+ return self.sendMessageEx(self.Message(name, data=data, **params))
+
+
+ def sendMessageEx(self, msg):
+ """Sends a message to freenet
+ @param msg: (Message) message to send
+ @return: Message
+ @raise SocketError: if the socket connection to the node dies unexpectedly.
+ If an error handler is passed to the client it is called emidiately before the error
+ is raised.
+
+ @note: you can use this method to send a message to the node, bypassing all
+ track keeping methods of the client
+ """
+ self._logMessage.debug(consts.LogMessages.MessageSend + msg.pprint())
+ try:
+ msg.send(self._socket)
+ except socket.error, d:
+ self._logRuntime.critical(consts.LogMessages.SocketDied)
+ disconnectMsg = self.Message(
+ consts.Message.ClientDisconnected,
+ DisconnectReason=consts.DisconnectReason.SocketDied,
+ Param=self.Message(consts.Message.ClientSocketDied, Exception=socket.error, Details=d)
+ )
+ self._close(disconnectMsg)
+ raise socket.error(d)
+ return msg
+
+ #########################################################
+ ##
+ ## config related methods
+ ##
+ #########################################################
+ def getConfig(self,
+ withCurrent=True,
+ withDefaults=True,
+ withExpertFlag=True,
+ withForceWriteFlag=True,
+ withSortOrder=True,
+ withShortDescription=True,
+ withLongDescription=True,
+ ):
+ """
+ @event: ConfigData(event, msg)
+ """
+ self.sendMessage(
+ consts.Message.GetConfig,
+ WithSortOrder=withSortOrder,
+ WithCurrent=withCurrent,
+ WithDefaults=withDefaults,
+ WithExpertFlag=withExpertFlag,
+ WithForceWriteFlag=withForceWriteFlag,
+ WithShortDescription=withShortDescription,
+ WithLongDescription=withLongDescription,
+ )
+
+
+ def modifyConfig(self, params):
+ """Modifies node configuration values
+ @param params: (dict) containing parameters to modify
+ """
+ msg = self.Message(consts.Message.ModifyConfig)
+ msg.params = params
+ self.sendMessageEx(msg)
+
+
+ ########################################################
+ ##
+ ## ClientGet related methods
+ ##
+ ########################################################
+ def clientGet(self,
+ uri,
+ requestType,
+ userData,
+ persistentUserData,
+ filenameCollision,
+ **messageParams
+ ):
+ """Requests a key from the node
+ @param uri: (str) uri of the file to request (may contain prefixes like 'freenet:' or 'http://')
+ @param requestType: (L{consts.RequestType}) sub type of the message
+ @param userData: any non persistent data to associate to the request or None
+ @param persistentUserData: any string to associate to the request as persistent data or None
+ @param filenameCollision: what to do if the disk target alreaady exists. One of the FilenameCollision.* consts
+ @param messageParams: keyword arguments to pass along with the ClientGet message (uppercase first letter!!).
+ If the value of a keyword is None, it is ignored.
+
+ @return: (str) identifier of the request
+ """
+ uri = self.Uri(uri).uri
+ msg = self.Message(consts.Message.ClientGet, URI=uri)
+ for paramName, value in messageParams.items():
+ if value is not No...
[truncated message content] |