SF.net SVN: fclient: [79] trunk
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <ju...@us...> - 2008-01-27 02:16:50
|
Revision: 79
http://fclient.svn.sourceforge.net/fclient/?rev=79&view=rev
Author: jurner
Date: 2008-01-26 18:16:50 -0800 (Sat, 26 Jan 2008)
Log Message:
-----------
added sanbox with fcp rewrite
Added Paths:
-----------
trunk/sandbox/
trunk/sandbox/fcp/
trunk/sandbox/fcp/001.py
trunk/sandbox/fcp/LICENCE.MIT
trunk/sandbox/fcp/README
trunk/sandbox/fcp/__init__.py
trunk/sandbox/fcp/boards/
trunk/sandbox/fcp/boards/__init__.py
trunk/sandbox/fcp/boards/frost.py
trunk/sandbox/fcp/fcp2_0_client.py
trunk/sandbox/fcp/fcp2_0_config.py
trunk/sandbox/fcp/fcp2_0_consts.py
trunk/sandbox/fcp/fcp2_0_message.py
trunk/sandbox/fcp/fcp2_0_params.py
trunk/sandbox/fcp/fcp2_0_uri.py
trunk/sandbox/fcp/fcp_lib/
trunk/sandbox/fcp/fcp_lib/__init__.py
trunk/sandbox/fcp/fcp_lib/events.py
trunk/sandbox/fcp/fcp_lib/namespace.py
trunk/sandbox/fcp/fcp_lib/numbers.py
trunk/sandbox/fcp/fcp_lib/uuid.py
trunk/sandbox/fcp/oo1.py
trunk/sandbox/fcp/test_fcp/
trunk/sandbox/fcp/test_fcp/__init__.py
trunk/sandbox/fcp/test_fcp/dummy_socket.py
trunk/sandbox/fcp/test_fcp/dummy_socket.pyc
trunk/sandbox/fcp/test_fcp/test_fcp2_0_client.py
trunk/sandbox/fcp/test_fcp/test_fcp2_0_config.py
trunk/sandbox/fcp/test_fcp/test_fcp2_0_message.py
Added: trunk/sandbox/fcp/001.py
===================================================================
--- trunk/sandbox/fcp/001.py (rev 0)
+++ trunk/sandbox/fcp/001.py 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,107 @@
+
+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()
+
+
Added: trunk/sandbox/fcp/LICENCE.MIT
===================================================================
--- trunk/sandbox/fcp/LICENCE.MIT (rev 0)
+++ trunk/sandbox/fcp/LICENCE.MIT 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,20 @@
+ Fcp - a python wraper library for the freenet client protocol. See: [http://www.freenetproject.org]
+
+Copyright (c) 2008 J\xFCrgen Urner
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
Added: trunk/sandbox/fcp/README
===================================================================
--- trunk/sandbox/fcp/README (rev 0)
+++ trunk/sandbox/fcp/README 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,18 @@
+Fcp - a python wraper library for the freenet client protocol. See: [http://www.freenetproject.org]
+
+
+
+
+Version history:
+
+*******************************************************************
+0.1.0
+*******************************************************************
+news:
+
+ x.
+
+bugfixes:
+
+ x.
+
Added: trunk/sandbox/fcp/__init__.py
===================================================================
--- trunk/sandbox/fcp/__init__.py (rev 0)
+++ trunk/sandbox/fcp/__init__.py 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,11 @@
+"""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
Added: trunk/sandbox/fcp/boards/__init__.py
===================================================================
--- trunk/sandbox/fcp/boards/__init__.py (rev 0)
+++ trunk/sandbox/fcp/boards/__init__.py 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1 @@
+
Added: trunk/sandbox/fcp/boards/frost.py
===================================================================
--- trunk/sandbox/fcp/boards/frost.py (rev 0)
+++ trunk/sandbox/fcp/boards/frost.py 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,99 @@
+
+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())
+
+
+
+
+
+
+
+
Added: trunk/sandbox/fcp/fcp2_0_client.py
===================================================================
--- trunk/sandbox/fcp/fcp2_0_client.py (rev 0)
+++ trunk/sandbox/fcp/fcp2_0_client.py 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,1510 @@
+"""Freenet client protocol 2.0 implementation
+
+Compatibility: >= Freenet 0.7 Build #1084
+
+
+@newfield event, events
+
+
+@note: The client implementation never uses or watches the global queue. No (in words N-O)
+implementation should ever do so. Global is evil... so avoid it. Already filed a bug report regarding this.
+
+"""
+
+import atexit
+import base64
+import cPickle
+import logging
+import os
+import random
+import socket
+import subprocess
+import sys
+import 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(3)
+
+
+from fcp_lib import events, namespace, uuid
+
+
+del hack
+#<-- rel import hack
+
+import fcp2_0_message
+import fcp2_0_consts as consts
+
+
+logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
+#**********************************************************************
+# helpers
+#**********************************************************************
+def saveReadFile(fpath):
+ """Reads contents of a file in the savest manner possible
+ @param fpath: file to write
+ @return: contents of the file if successful, None otherwise
+ """
+ read = None
+ try:
+ fp = open(fpath, 'rb')
+ except: pass
+ else:
+ try:
+ read = fp.read()
+ except: pass
+ fp.close()
+ return read
+
+def saveRemoveFile(fpath):
+ """Savely removes a file
+ @param fpath: filepath of the file to remove or None
+ @return: True if the file was removed, False otherwise
+ """
+ if fpath is not None:
+ if os.path.isfile(fpath):
+ try:
+ os.remove(fpath)
+ except Exception, d:
+ pass
+ else:
+ return True
+ return False
+
+
+def saveWriteFile(fpath, data):
+ """Writes data to a file i the savest manner possible
+ @param fpath: file to write
+ @param data: data to write to file
+ @return: True if successful, False otherwise
+ """
+ written = False
+ try:
+ fp = open(fpath, 'wb')
+ except: pass
+ else:
+ try:
+ fp.write(data)
+ written = True
+ except:
+ fp.Close()
+ else:
+ fp.close()
+ return written
+
+#*************************************************************************************************
+#
+#*************************************************************************************************
+class FcpClient(object):
+
+ 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 = '32000'
+ SocketTimeout = 0.1
+ Version = '2.0'
+
+ from fcp2_0_config import Config
+ from fcp2_0_consts import (
+ ConnectReason,
+ DebugVerbosity,
+ DisconnectReason,
+ FcpTrue,
+ FcpFalse,
+ FetchError,
+ FilenameCollision,
+ InsertError,
+ LogMessages,
+ PeerNodeStatus,
+ PeerNoteType,
+ Persistence,
+ Priority,
+ ProtocolError,
+ ReturnType,
+ Verbosity,
+ )
+ from fcp2_0_message import Message
+ import fcp2_0_params as FcParams
+ from fcp2_0_uri import Uri
+
+
+
+ class Events(events.Events):
+ """All events the client supports"""
+ _events_ = (
+
+ 'Idle',
+
+ 'ClientConnected',
+ 'ClientDisconnected',
+
+ 'RequestCompleted', # the request is not removed neither from node nor client
+ 'RequestFailed', # the request is already removed from node and client
+ 'RequestModified',
+ 'RequestProgress',
+ 'RequestRestored',
+ 'RequestStarted',
+
+
+ # config related events
+ 'ConfigData',
+ 'NodeData',
+
+
+ #Peer related events
+ 'EndListPeers',
+ 'Peer',
+ 'PeerRemoved',
+ 'UnknownNodeIdentifier',
+ 'EndListPeerNotes',
+ 'PeerNote',
+
+
+ ###############################
+
+ 'ProtocolError',
+ 'PersistentGet',
+
+ # others
+ 'SSKKeypair',
+
+ )
+
+
+
+ def __init__(self,
+ connectionName=None,
+ debugVerbosity=None,
+ ):
+ """
+ @param conectionName: name of the connection
+ @param debugVerbosity: verbosity level for debugging. Default is L{Verbosity.Warning}
+
+ @ivar events: events the client supports
+ """
+ self._connectionName = connectionName
+ self._ddaTests = []
+ self._identifierMapping = {} # mapping from identifiers to FcRequestIdentifiers
+ self._requests = {}
+
+ self._log = logging.getLogger(self.__class__.__name__)
+ self._socket = None
+
+ self.events = self.Events()
+
+ self.setDebugVerbosity(self.DebugVerbosity.Warning if debugVerbosity is None else debugVerbosity)
+ atexit.register(self.close)
+
+
+ ###############################################################
+ ##
+ ## private methods
+ ##
+ ###############################################################
+ def _addFcParamsToRequest(self,
+ msg,
+ userData,
+ msgSubType,
+ initTime,
+ persistentUserData,
+ filenameCollision,
+ ):
+
+
+ # add additional params to msg
+ msg.params.update({
+
+ # persistent params that will go into identifier
+ 'FcSubType': msgSubType, # identifies sub message types
+ 'FcInitTime': self.fcpTime(initTime), # when was the request started?
+ 'FcFilenameCollision': filenameCollision, # handle fielanem collisions?
+ 'FcPersistentUserData': persistentUserData, # any user defined persistent data
+
+ # non persistent params
+ 'FcStatus': self.Message.StatusPending,
+ 'FcErrorMessage': None, # did an error occur?
+ 'FcUserData': userData, # any user defined runtime data here
+
+ # params for AllData
+ 'FcData': '', # if data was requested via requestData you will find it here
+
+ # params for SSKKeypair
+ 'FcInsertUri': None,
+ 'FcRequestUri': None,
+
+ # params from DataFound
+ 'FcMetadataContentType': '', # contecnt type
+ 'FcDataLength': '', # content size
+
+ # params from PersistentRequestModified
+ 'FcModified': {},
+
+
+ # params for SimpleProgress
+ 'FcProgressTotal': '0',
+ 'FcProgressRequired': '0',
+ 'FcProgressFailed': '0',
+ 'FcProgressFatalyFailed': '0',
+ 'FcProgressSucceeeded': '0',
+
+ })
+ return msg
+
+
+ def _registerRequest(self,
+ msg,
+ userData,
+ msgSubType,
+ initTime,
+ persistentUserData,
+ filenameCollision=consts.FilenameCollision.HandleNever,
+ ):
+ """Registers a message
+ @param msg: message to register for track keeping
+ @param msgSubType: one of the message sub type consts
+ @param requestIdentifier: (str)
+ @param userData: (str)
+ @param initTime: (python time)
+ @param handleCollisions: (bool)
+
+ @return: (str) uuid
+ @note: the identifier returned is unique to the client but may not be unique to the node
+ """
+
+ # we store FcRequestIdentifier and additional params in ClientToken
+ identifier = self.FcParams.newUuid()
+
+ # add additional params to msg
+ msg = self._addFcParamsToRequest(
+ msg,
+ userData,
+ msgSubType,
+ initTime,
+ persistentUserData,
+ filenameCollision,
+ )
+
+ msg['ClientToken'] = self.FcParams.messageToParams(msg)
+ msg['Identifier'] = identifier
+ self._requests[identifier] = msg
+
+
+
+ def _restorePersistentRequestFromNode(self, msg):
+ """Preps a message from node to be restored
+ @return: the restored message if everything went well, None otherwise
+ """
+ fcParams = self.FcParams.paramsFromRequest(msg)
+ if fcParams is None:
+ return None
+
+ # have to re-adjust initTime to python time
+ fcParams[self.FcParams.IInitTime] = self.pythonTime(fcParams[self.FcParams.IInitTime])
+
+ # add additional params to msg
+ msg = self._addFcParamsToRequest(
+ msg,
+ None, # userData,
+ fcParams[self.FcParams.ISubType],
+ fcParams[self.FcParams.IInitTime],
+ fcParams[self.FcParams.IPersistentUserData],
+ fcParams[self.FcParams.IFilenameCollision],
+ )
+
+ # fix some Fcp inconsistencies ClientGet vs. PersistentGet
+ if msg.name == self.Message.MessagePersistentGet:
+ del msg.params['Started']
+ if 'PersistenceType' in msg.params:
+ msg['Persistence'] = msg.params.pop('PersistenceType')
+
+ return msg
+
+
+
+ ###############################################################
+ ##
+ ## Fcp <--> Python mappings
+ ##
+ ###############################################################
+ def fcpBool(self, pythonBool):
+ """Converts a python bool to a fcp bool
+ @param pythonBool: (bool)
+ @return: (str) 'true' or 'false'
+ """
+ return self.FcpTrue if pythonBool else self.FcpFalse
+
+ def fcpTime(self, pythonTime):
+ """Converts a python time value to a fcp time value
+ @param fcpTime: (int, str) time to convert
+ @raise ValueError: if the python time could not be converted
+ @return: (int) fcp time
+ """
+ return pythonTime * 1000
+
+ def pythonBool(self, fcpBool):
+ """Converts a fcp bool to a python bool
+ @param pythonBool: 'true' or 'false'
+ @return: (bool) True or False
+ """
+ return fcpBool == self.FcpTrue
+
+ def pythonTime(self, fcpTime):
+ """Converts a fcp time value to a python time value
+ @param fcpTime: (int, str) time to convert
+ @raise ValueError: if the fcp time could not be converted
+ @return: (int) python time
+ """
+ return int(fcpTime) / 1000
+
+ ###############################################################
+ ##
+ ## connection related methods
+ ##
+ ###############################################################
+ def close(self):
+ """Closes the client
+ @note: make shure to call close() when done with the client
+ """
+ self._log.info(self.LogMessages.ClientClose)
+ if self._socket is not None:
+ self._socket.close()
+ self._socket = None
+
+ # clean left over tmp files
+ for initialRequest in self._ddaTests:
+ if initialRequest['FcTestDDA']['TmpFile'] is not None:
+ saveRemoveFile(initialRequest['FcTestDDA']['TmpFile'])
+
+
+ def connect(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
+ @event: Connected(event, params). Triggered as soon as the client is connected. Params
+ will be the parameters of the NodeHello message.
+ @return: (Message) NodeHello if successful, None otherwise for the next iteration
+ """
+ self._log.info(self.LogMessages.Connecting)
+
+ # poll untill freenet responds
+ timeElapsed = 0
+ while timeElapsed <= duration:
+
+ # try to Connect socket
+ if self._socket is not None:
+ self.close()
+ 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._log.info(self.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(
+ self.Message.MessageClientHello,
+ Name=self._connectionName if self._connectionName is not None else uuid.uuid_time(),
+ ExpectedVersion=self.Version,
+ )
+ while timeElapsed <= duration:
+ msg = self.next(dispatch=False)
+ if msg.name == self.Message.MessageClientSocketTimeout:
+ timeElapsed += self.SocketTimeout
+ yield None
+ elif msg.name == self.Message.MessageNodeHello:
+ self._log.debug(self.LogMessages.MessageReceived + msg.pprint())
+ self.events.ClientConnected(msg)
+ yield msg
+ raise StopIteration
+ else:
+ self._log.debug(self.LogMessages.MessageReceived + msg.pprint())
+ break
+ break
+
+ # continue polling
+ self._log.info(self.LogMessages.ConnectionRetry)
+ timeElapsed += timeout
+ time.sleep(timeout)
+
+ msg = self.Message(
+ self.Message.MessageClientDisconnected,
+ DisconnectReason=self.DisconnectReason.ConnectingFailed
+ )
+ self.events.ClientDisconnected(msg)
+ self._log.info(self.LogMessages.ConnectingFailed)
+ self.close()
+ raise StopIteration
+
+
+ def setDebugVerbosity(self, debugVerbosity):
+ """Sets the verbosity level of the client
+ @note: see L{Verbosity}
+ """
+ self._log.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: (string) 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
+
+ #########################################################
+ ##
+ ## 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
+ """
+ if msg.name == self.Message.MessageClientSocketTimeout:
+ return True
+ self._log.debug(self.LogMessages.MessageReceived + msg.pprint())
+
+ # check if we have a corrosponding initial message
+ fcRequestIdentifier = None
+ initialRequest = None
+
+ # 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 == self.Message.MessageIdentifierCollision:
+ if initialRequest is not None:
+ # 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'] = {self.Message.ModifiedRequestIdentifier: requestIdentifier}
+ self.events.RequestModified(initialRequest)
+ self.sendMessageEx(initialRequest)
+ return True
+
+
+ elif msg.name == self.Message.MessageProtocolError:
+ code = msg['Code']
+ if requestIdentifier is None:
+ #TODO: check how to handle this
+ raise self.ProtocolError(msg)
+
+ if initialRequest is None:
+ #TODO: check how to handle this
+ raise self.ProtocolError(msg)
+
+
+ if code == self.ProtocolError.ShuttingDown:
+ self.close()
+ msg = self.Message(
+ self.Message.MessageClientDisconnected,
+ DisconnectReason=DisconnectReason.Shutdown,
+ )
+ self.events.ClientDisconnect(msg)
+ return True
+
+
+ # handle DDA errors
+ elif code == self.ProtocolError.DDADenied:
+ ddaRequestMsg = self.Message(self.Message.MessageTestDDARequest)
+ if initialRequest.name == self.Message.MessageClientGet:
+ ddaRequestMsg['WantWriteDirectory'] = self.FcpTrue
+ directory = os.path.dirname(initialRequest['Filename'])
+ else:
+
+ #TODO: determine directory for other cases
+ raise RuntimeError(NotImplemented)
+
+ ddaRequestMsg['WantReadDirectory'] = self.FcpTrue
+ directory = None
+ ddaRequestMsg['Directory'] = directory
+
+
+ # add params required for testing
+ initialRequest['FcTestDDA'] = {
+ 'Directory': directory,
+ 'Replied': False,
+ 'TmpFile': None,
+ 'WantWrite': self.pythonBool(ddaRequestMsg.get('WantWriteDirectory', self.FcpFalse)),
+ 'ErrorMsg': msg,
+ }
+ self._ddaTests.append(initialRequest)
+ self.sendMessageEx(ddaRequestMsg)
+ return True
+
+
+ # handle filename collisions
+ elif code == self.ProtocolError.DiskTargetExists:
+ handleCollision = initialRequest.get('FcFilenameCollision', self.FilenameCollision.HandleNever)
+ collisionHandled = bool(handleCollision & self.FilenameCollision.CollisionHandled)
+
+ # rename filename
+ if handleCollision & self.FilenameCollision.HandleRename:
+ filename = initialRequest['Filename']
+ initialRequest['FcFilenameCollision'] |= self.FilenameCollision.CollisionHandled
+ newFilename = namespace.unique_filename(filename, extensions=1, ispostfixed=collisionHandled)
+ initialRequest['Filename'] = newFilename
+ initialRequest['FcModified'] = {self.Message.ModifiedRequestFilename: filename}
+ self.sendMessageEx(initialRequest)
+ self.events.RequestModified(initialRequest)
+ return True
+
+ # don't handle
+ else:
+ initialRequest['FcFilenameCollision'] &= ~self.FilenameCollision.CollisionHandled
+
+
+ # only requests should get through to here
+
+ # NOTE: Fcp already removed the request
+ del self._requests[requestIdentifier]
+ initialRequest['FcErrorMessage'] = msg
+ initialRequest['FcStatus'] = self.Message.StatusError | self.Message.StatusRemoved
+ self.events.RequestFailed(initialRequest)
+ 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 == self.Message.MessageTestDDAReply:
+ 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 = saveReadFile(fpathRead)
+ if readContent is None:
+ readContent = ''
+
+ # perform write test if necessary
+ fpathWrite = msg.params.get('WriteFilename', None)
+ if fpathWrite is not None:
+ written = saveWriteFile(fpathWrite, msg['ContentToWrite'])
+ if not written:
+ saveRemoveFile(fpathWrite)
+ else:
+ initialRequest['FcTestDDA']['TmpFile'] = fpathWrite
+
+ self.sendMessage(
+ self.Message.MessageTestDDAResponse,
+ Directory=msg['Directory'],
+ ReadContent=readContent,
+ )
+ return True
+
+
+ elif msg.name == self.Message.MessageTestDDAComplete:
+ # 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:
+ saveRemoveFile(initialRequest['FcTestDDA']['TmpFile'])
+ wantWrite = initialRequest.params['FcTestDDA']['WantWrite']
+ errorMsg = initialRequest['FcTestDDA']['ErrorMsg']
+ del initialRequest.params['FcTestDDA']
+
+ # check if test was sucessful
+ testFailed = False
+ if wantWrite:
+ testFailed = not self.pythonBool(msg.params.get('WriteDirectoryAllowed', self.FcpFalse) )
+ else:
+ testFailed = not self.pythonBool(msg.params.get('ReadDirectoryAllowed', self.FcpFalse) )
+
+ if testFailed:
+
+ #TODO: check if Fcp removed the request
+ #TODO: check if errorMsg gives reasonable feedback
+
+ del self._request[fcRequestIdentifier]
+ initialRequest['FcStatus'] = self.Message.StatusError | self.Message.StatusRemoved
+ initialRequest['FcErrorMessage'] = errorMsg
+ self.events.ProtocolError(initialRequest)
+ return True
+
+
+ # resend message
+ self.sendMessageEx(initialRequest)
+ return True
+
+
+
+ ####################################################
+ ##
+ ## config related
+ ##
+ ####################################################
+ elif msg.name == self.Message.MessageConfigData:
+ self.events.ConfigData(msg)
+ return True
+
+ elif msg.name == self.Message.MessageNodeData:
+ self.events.NodeData(msg)
+ return True
+
+
+ ####################################################
+ ##
+ ## get / put related
+ ##
+ ####################################################
+ elif msg.name == self.Message.MessageAllData:
+ if initialRequest is None:
+ return False
+
+ initialRequest['FcData'] = msg.data
+ self.events.RequestCompleted(initialRequest)
+ return True
+
+ elif msg.name == self.Message.MessageDataFound:
+
+ if initialRequest is None:
+ # something is going wrong
+ return False
+
+ initialRequest['FcStatus'] = self.Message.StatusComplete
+ initialRequest['FcMetadataContentType'] = msg.get('Metadata.ContentType', '')
+ initialRequest['FcDataLength'] = msg.get('DataLength', '')
+ self.events.RequestCompleted(initialRequest)
+ return True
+
+
+
+ elif msg.name == self.Message.MessageGetFailed:
+ code = msg['Code']
+ if initialRequest is None:
+ # something is going wrong
+ return False
+
+ # check if it is one of our requests for key information
+ if code == self.FetchError.TooBig and initialRequest['FcSubType'] == self.Message.SubTypeGetKeyInfo:
+ initialRequest['FcStatus'] = self.Message.StatusComplete
+ initialRequest['FcMetadataContentType'] = msg.get('ExpectedMetadata.ContentType', '')
+ initialRequest['FcDataLength'] = msg.get('ExpectedDataLength', '')
+ initialRequest['FcProgressCompleted'] = self.FcpTrue
+ #TODO: check if Fcp removed the request
+
+ self.events.RequestCompleted(initialRequest)
+ else:
+
+ #TODO: check if Fcp removed the request
+
+ initialRequest['FcErrorMessage'] = msg
+ initialRequest['FcStatus'] = self.Message.StatusError
+ self.events.RequestFailed(initialRequest)
+ return True
+
+
+
+ elif msg.name == self.Message.MessagePersistentGet:
+
+ # NOTE:
+ # Fcp does no good job in handling persistent requests and identifiers. Already dropped some
+ # notes and reports regarding this. See freenet-tech mailing list [Fcp notes and issues]
+
+ CancelPersistentRequests = 0 # for testing... if True, cancels all persistent requests
+
+ # unknown request... try to restore it
+ if initialRequest is None:
+ restoredRequest = self._restorePersistentRequestFromNode(msg)
+
+ # not one of our requests... so cancel it
+ if restoredRequest is None or CancelPersistentRequests:
+ self.sendMessage(
+ self.Message.MessageRemovePersistentRequest,
+ Identifier=msg['Identifier'],
+ Global=msg['Global'],
+ )
+ return True
+
+ restoredRequest.name = self.Message.MessageClientGet
+ self._requests[requestIdentifier] = restoredRequest
+ restoredRequest['FcStatus'] = self.Message.StatusStarted
+ self.events.RequestRestored(restoredRequest)
+ return True
+
+ # known request... filter out multiple PersistentGets
+ if initialRequest['FcStatus'] == self.Message.StatusPending:
+ initialRequest['FcStatus'] = self.Message.StatusStarted
+
+ #TODO: update initialRequest with params from PersistentGet?
+
+ self.events.RequestStarted(initialRequest)
+ return True
+
+ return True
+
+
+ elif msg.name == self.Message.MessagePersistentRequestModified:
+ 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[self.Message.ModifiedRequestPersistentUserData] = 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[self.Message.ModifiedRequestPriorityClass] = None
+ initialRequest['PriorityClass'] = priorityClass
+
+ initialRequest['FcModified'] = modified
+ self.events.RequestModified(initialRequest)
+ return True
+
+
+ elif msg.name == self.Message.MessagePersistentRequestRemoved:
+ if initialRequest is None:
+ return False
+
+ del self._requests[requestIdentifier]
+ return True
+
+
+ elif msg.name == self.Message.MessageSimpleProgress:
+ if initialRequest is None:
+ # something went wrong
+ 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
+
+ ####################################################
+ ##
+ ## Peer related messages
+ ##
+ ####################################################
+ elif msg.name == self.Message.MessageEndListPeers:
+ self.events.EndListPeers(msg)
+ return True
+
+ elif msg.name == self.Message.MessageEndListPeerNotes:
+ self.events.EndListPeerNotes(msg.params)
+ return True
+
+ elif msg.name == self.Message.MessagePeer:
+ self.events.Peer(msg)
+ return True
+
+ elif msg.name == self.Message.MessagePeerNote:
+ note = msg.get('NoteText', '')
+ if note:
+ note = base64.decodestring(note)
+ msg['NoteText'] = note
+ self.events.PeerNote(msg)
+ return True
+
+ elif msg.name == self.Message.MessagePeerRemoved:
+ self.events.PeerRemoved(msg)
+ return True
+
+ elif msg.name == self.Message.MessageUnknownNodeIdentifier:
+ self.events.UnknownNodeIdentifier(msg)
+ return True
+
+ ####################################################
+ ##
+ ## others
+ ##
+ ####################################################
+ elif msg.name == self.Message.MessageSSKKeypair:
+ if initialRequest is None:
+ return False
+
+ #TODO: maybe we need a mapping from SSKKeypair to pending request
+
+ initialRequest['Uri'] = msg['InsertUri']
+ initialRequest['FcRequestUri'] = msg['RequestUri']
+
+ self.sendMessageEx(initialRequest)
+ 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 instead of run() to run the client step by step
+ """
+ msg = self.Message.fromSocket(self._socket)
+ if msg.name == self.Message.MessageClientSocketDied:
+ if dispatch:
+ msg['DisconnectReason'] = self.DisconnectReason.SocketDied
+ self.events.ClientDisconnected(msg)
+ raise socket.error(msg['Details'])
+
+ elif msg.name == self.Message.MessageClientSocketTimeout:
+ if dispatch:
+ self.events.Idle(msg)
+
+ else:
+ if dispatch:
+ self.handleMessage(msg)
+ return msg
+
+
+ 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.
+ """
+ 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.
+ """
+ self._log.debug(self.LogMessages.MessageSend + msg.pprint())
+ try:
+ msg.send(self._socket)
+ except socket.error, d:
+ self._log.info(self.LogMessages.SocketDied)
+ self.close()
+
+ errorMsg = self.Message(
+ self.Message.MessageClientSocketDied,
+ DisconnectReason=self.DisconnectReason.SocketDied,
+ Exception=socket.error,
+ Details=d
+ )
+ self.events.ClientDisconnected(errorMsg)
+ raise socket.error(d)
+ return msg
+
+ #########################################################
+ ##
+ ## config related methods
+ ##
+ #########################################################
+ #TODO: WithDefault never returns defaults
+ def getConfig(self,
+ withCurrent=consts.FcpTrue,
+ withDefaults=consts.FcpTrue,
+ withExpertFlag=consts.FcpTrue,
+ withForceWriteFlag=consts.FcpTrue,
+ withSortOrder=consts.FcpTrue,
+ withShortDescription=consts.FcpTrue,
+ withLongDescription=consts.FcpTrue,
+ ):
+ """
+ @event: ConfigData(event, msg)
+ """
+ self.sendMessage(
+ self.Message.MessageGetConfig,
+ WithSortOrder=withSortOrder,
+ WithCurrent=withCurrent,
+ WithDefaults=withDefaults,
+ WithExpertFlag=withExpertFlag,
+ WithForceWriteFlag=withForceWriteFlag,
+ WithShortDescription=withShortDescription,
+ WithLongDescription=withLongDescription,
+ )
+
+ def getNode(self,
+ withPrivate=True,
+ withVolatile=True,
+ giveOpennetRef=True,
+ ):
+ """
+ @event: NodeData(event, msg)
+ """
+ self.sendMessage(
+ self.Message.MessageGetNode,
+ WithPrivate=self.fcpBool(withPrivate),
+ WithVolatile=self.fcpBool(withVolatile),
+ GiveOpennetRef=self.fcpBool(giveOpennetRef),
+ )
+
+
+ ########################################################
+ ##
+ ## ClientGet related methods
+ ##
+ ########################################################
+ def getData(self,
+ uri,
+
+ allowedMimeTypes=None,
+ binaryBlob=consts.FcpFalse,
+ dsOnly=consts.FcpFalse,
+ ignoreDS=consts.FcpFalse,
+ maxRetries=None,
+ maxSize=None,
+ persistence=consts.Persistence.Connection,
+ priorityClass=consts.Priority.Medium,
+
+ userData=None,
+ persistentUserData='',
+ ):
+ """Requests a file from the node
+
+ @param uri: uri of the file to request (may contain prefixes like 'freenet:' or 'http://')
+
+ @param allowedMimeTypes: (str) list of allowed mime types
+ @param binaryBlob: if FcpTrue, the file is retrieved as binary blob file
+ @param dsOnly: if FcpTrue, retrieves the file from the local data store only
+ @param ignoreDs: If FcpTrue, ignores the local data store
+ @param maxRetries: (int) maximum number of retries or -1 to retry forver or None to leave it to the node to decide
+ @param maxSize: (int) maximum size of the file in bytes or None to set no limited
+ @param persistence: persistence of the request as one of the L{consts.Persistence} constants
+ @param priorityClass: priority of the request as one of the L{consts.Priority} consts
+
+ @param userData: any non persistent data to associate to the request
+ @param persistentUserData: any string to associate to the request as persistent data
+
+ @return: (str) request identifier
+ @note: if a filename collision is handled a RequestFilenameChanged event is triggered
+ """
+
+ msg = self.Message(
+ self.Message.MessageClientGet,
+
+ BinaryBlob=binaryBlob,
+ Global=self.FcpFalse,
+ DSOnly=dsOnly,
+
+ Identifier=None,
+ IgnoreDS=ignoreDS,
+ Persistence=persistence,
+ PriorityClass=priorityClass,
+
+ ReturnType=self.ReturnType.Direct,
+ URI=self.Uri(uri).uri,
+ Verbosity=self.Verbosity.ReportProgress,
+
+ #MaxTempSize=whatever,
+ #TempFilename=whatever
+ )
+ if allowedMimeTypes is not None:
+ msg['AllowedMimeTypes'] = allowedMimeTypes
+ if maxRetries is not None:
+ msg['MaxRetries'] = maxRetries
+ if maxSize is not None:
+ msg['MaxSize'] = maxSize
+
+
+ self._registerRequest(
+ msg,
+ userData,
+ self.Message.SubTypeGetData,
+ time.time(),
+ persistentUserData,
+ )
+ self.sendMessageEx(msg)
+ return msg['Identifier']
+
+
+ def getFile(self,
+ uri,
+ filename,
+
+ allowedMimeTypes=None,
+ binaryBlob=consts.FcpFalse,
+ dsOnly=consts.FcpFalse,
+ ignoreDS=consts.FcpFalse,
+ maxRetries=None,
+ maxSize=None,
+ persistence=consts.Persistence.Connection,
+ priorityClass=consts.Priority.Medium,
+
+ userData=None,
+ persistentUserData='',
+ filenameCollision=consts.FilenameCollision.HandleNever,
+ ):
+ """Requests a file from the node
+
+ @param uri: uri of the file to request (may contain prefixes like 'freenet:' or 'http://')
+ @param filename: (full path) filename to store the file to
+
+ @param allowedMimeTypes: (str) list of allowed mime types
+ @param binaryBlob: if FcpTrue, the file is retrieved as binary blob file
+ @param dsOnly: if FcpTrue, retrieves the file from the local data store only
+ @param ignoreDs: If FcpTrue, ignores the local data store
+ @param maxRetries: (int) maximum number of retries or -1 to retry forver or None to leave it to the node to decide
+ @param maxSize: (int) maximum size of the file in bytes or None to set no limited
+ @param persistence: persistence of the request as one of the L{consts.Persistence} constants
+ @param priorityClass: priority of the request as one of the L{consts.Priority} consts
+
+ @param filenameCollision: what to do if the disk target alreaady exists. One of the FilenameCollision.* consts
+ @param userData: any non persistent data to associate to the request
+ @param persistentUserData: any string to associate to the request as persistent data
+
+ @return: (str) request identifier
+ @note: if a filename collision is handled a RequestFilenameChanged event is triggered
+ """
+
+ msg = self.Message(
+ self.Message.MessageClientGet,
+
+ BinaryBlob=binaryBlob,
+ Filename=filename,
+ Global=self.FcpFalse,
+ DSOnly=dsOnly,
+
+ Identifier=None,
+ IgnoreDS=ignoreDS,
+ Persistence=persistence,
+ PriorityClass=priorityClass,
+
+ ReturnType=self.ReturnType.Disk,
+ URI=self.Uri(uri).uri,
+ Verbosity=self.Verbosity.ReportProgress,
+
+ #MaxTempSize=whatever,
+ #TempFilename=whatever
+ )
+ if allowedMimeTypes is not None:
+ msg['AllowedMimeTypes'] = allowedMimeTypes
+ if maxRetries is not None:
+ msg['MaxRetries'] = maxRetries
+ if maxSize is not None:
+ msg['MaxSize'] = maxSize
+
+
+ self._registerRequest(
+ msg,
+ userData,
+ self.Message.SubTypeGetFile,
+ time.time(),
+ persistentUserData,
+ filenameCollision,
+ )
+ self.sendMessageEx(msg)
+ return msg['Identifier']
+
+
+ def getKeyInfo(self,
+ uri,
+
+ dsOnly=consts.FcpFalse,
+ ignoreDS=consts.FcpFalse,
+ persistence=consts.Persistence.Connection,
+ priorityClass=consts.Priority.Medium,
+
+ userData=None,
+ persistentUserData='',
+ ):
+ """Requests info about a key
+
+ @param uri: uri of the file to request (may contain prefixes like 'freenet:' or 'http://')
+
+ @param dsOnly: if FcpTrue, retrieves the file from the local data store only
+ @param ignoreDs: If FcpTrue, ignores the local data store
+ @param persistence: persistence of the request as one of the L{consts.Persistence} constants
+ @param priorityClass: priority of the request as one of the L{consts.Priority} consts
+ @param userData: any non persistent data to associate to the request
+ @param persistentUserData: any string to associate to the request as persistent data
+
+ @return: (str) request identifier
+ """
+
+ # how to retrieve meta info about a key?
+ # ...idea is to provoke a GetFailed (TooBig)
+
+ msg = self.Message(
+ self.Message.MessageClientGet,
+
+ DSOnly=dsOnly,
+ Global=self.FcpFalse,
+ Identifier=None,
+ IgnoreDS=ignoreDS,
+
+ MaxSize=self.MaxSizeKeyInfo,
+
+ Persistence=persistence,
+ PriorityClass=priorityClass,
+ ReturnType=self.ReturnType.Nothing,
+
+ URI=self.Uri(uri).uri,
+ Verbosity=self.Verbosity.ReportProgress,
+ )
+
+ self._registerRequest(
+ msg,
+ userData,
+ self.Message.SubTypeGetKeyInfo,
+ time.time(),
+ persistentUserData,
+ )
+ self.sendMessageEx(msg)
+ return msg['Identifier']
+
+ ########################################################
+ ##
+ ## ClientPut related methods
+ ##
+ ########################################################
+ def put(self):
+ pass
+
+
+
+
+ ########################################################
+ ##
+ ## request related methods
+ ##
+ ########################################################
+ def getRequest(self, identifier):
+ """Returns a (initial) message, given its identifier
+ @param identifier: identifier of the message
+ @return: L{Message}
+ """
+ return self._requests[identifier]
+
+
+ def getRequests(self):
+ """Returns all (initial) messages, currently known to the client
+ @return: list(messages)
+ """
+ return self._requests
+
+
+ def modifyRequest(self, requestIdentifier, persistentUserData=None, priorityClass=None):
+ """Modifies a request
+ @param identifier: identifier of the request to modify
+ @param clientToken: new client token or None
+ @param priorityClass: new priority or None
+
+ @note: a RequestModified event is triggered as soon as the request has actually been modified
+ """
+ initialRequest = self._requests[requestIdentifier]
+ msg = self.Message(
+ self.Message.MessageModifyPersistentRequest,
+ Identifier=initialRequest['Identifier'],
+ Global=self.FcpFalse,
+ )
+ if persistentUserData is not None:
+ initialRequest['FcPersistentUserData'] = persistentUserData
+ msg['ClientToken'] = self.FcParams.messageToParams(initialRequest)
+ if priorityClass is not None:
+ msg['PriorityClass'] = priorityClass
+ self.sendMessageEx(msg)
+
+
+ def removeRequest(self, requestIdentifier):
+ """Removes a request
+ @param identifier: (str) identifier of the request to remove
+
+ @note: a RequestRemoved event is triggered as soon as the request has actually been removed
+ """
+ initialRequest = self._requests[requestIdentifier]
+ initialRequest['FcStatus'] = self.Message.StatusRemoved
+ self.sendMessage(
+ self.Message.MessageRemovePersistentRequest,
+ Global=self.FcpFalse,
+ Identifier=requestIdentifier,
+ )
+
+ #TODO: check how Fcp responds when the identifier is unknwon or something else goes
+ # werong. Maybe a ProtocolError.NoSuchIdentifier ???
+
+ ########################################################
+ ##
+ ## Peer related methods
+ ##
+ ########################################################
+ def listPeer(self, identity):
+ self.jobClient.sendMessage(
+ self.Message.MessageListPeer,
+ NodeIdentifier=identity,
+ )
+
+
+ def listPeerNotes(self, identity):
+ """Lists all text notes associated to a peer
+ @param identifier: peer as returned in a call to L{peerList}
+ @event: ListPeerNote(event, params)
+ @event: EndListPeerNotes(event, params)
+ @note: listPeerNotes() is only available for darknet nodes
+ """
+ self.sendMessage(
+ self.Message.MessageListPeerNotes,
+ NodeIdentifier=identity
+ )
+
+
+ def listPeers(self, withMetaData=True, withVolantile=True):
+ """Lists all peers of the node
+ @param withMetaData: include meta data for each peer?
+ @param withVolantile: include volantile data for each peer?
+
+ @event: Peer(event, peer).
+ @event: EndListPeers(event, params).
+ """
+ self.sendMessage(
+ self.Message.MessageListPeers,
+ WithMetadata=self.fcpBool(withMetaData),
+ WithVolatile=self.fcpBool(withVolantile),
+ )
+
+
+ def modifyPeer(self, identity, allowLocalAddresses=None, isDisabled=None, isListenOnly=None):
+ msg = Message(
+ self.Message.MessageModifyPeer,
+ NodeIdentifier=identity,
+ )
+ if allowLocalAddresses is not None:
+ msg['AllowLocalAddresses'] = self.fcpBool(allowLocalAddresses)
+ if isDisabled is not None:
+ msg['isDisabled'] = self.fcpBool(isDisabled)
+ if isListenOnly is not None:
+ msg['isListenOnly'] = self.fcpBool(isListenOnly)
+ self.jobClient.sendMessageEx(msg)
+ self.sendMessageEx(msg)
+
+
+ def modifyPeerNote(self, identity, note):
+ self.sendMessage(
+ self.Message.MessageModifyPeerNote,
+ NodeIdentifier=identity,
+ #NOTE: currently fcp supports only this one type
+ PeerNoteType=self.PeerNoteType.Private,
+ NoteText=note
+ )
+
+
+ def removePeer(self, identity):
+ self.sendMessage(
+ self.Message.MessageRemovePeer,
+ NodeIdentifier=identity,
+ )
+
+ ##########################################################
+ ##
+ ## others
+ ##
+ ##########################################################
+ def generateSSK(self):
+ """
+ @event: SSKKeypair(event, params), triggered when the request is complete
+ @return: identifier of the request
+ """
+ while True:
+ identifier = uuid.uuid_time()
+ if identifier not in self._requests:
+ break
+ self.sendMessage(
+ self.Message.MessageGenerateSSK,
+ Identifier=identifier,
+ )
+ return identifier
+
+
+#*****************************************************************************
+#
+#*****************************************************************************
+if __name__ == '__main__':
+ c = FcpClient(
+ connectionName='test',
+ debugVerbosity=FcpClient.DebugVerbosity.Debug
+ )
+
+ for nodeHello in c.connect(): pass
+ if nodeHello is not None:
+
+ #for i in xrange(5):
+ # c.next()
+
+
+ def testGetData():
+ def cb(event, request):
+ print request['FcData']
+ c.events.RequestCompleted += cb
+
+ identifier = c.getData(
+ 'CHK@q4~2soHTd9SOINIoXmg~dn7LNUAOYzN1tHNHT3j4c9E,gcVRtoglEhgqN-DJolXPqJ4yX1f~1gBGh89HNWlFMWQ,AAIC--8/snow_002%20%2810%29.jpg',
+ #binaryBlob=c.FcpTrue,
+ )
+
+ for i in xrange(50):
+ c.next()
+
+ c.removeRequest(identifier)
+ for i in xrange(5):
+ c.next()
+
+ #testGetData()
+
+
+
+ def testGetFile():
+ filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test.jpg')
+
+ identifier = c.getFile(
+ 'CHK@q4~2soHTd9SOINIoXmg~dn7LNUAOYzN1tHNHT3j4c9E,gcVRtoglEhgqN-DJolXPqJ4yX1f~1gBGh89HNWlFMWQ,AAIC--8/snow_002%20%2810%29.jpg',
+ filename,
+ filenameCollision=c.FilenameCollision.HandleRename,
+ persistence=consts.Persistence.Forever,
+ )
+
+ for i in xrange(50):
+ c.next()
+
+ #c.removeRequest(identifier)
+ #for i in xrange(5):
+ # c.next()
+
+ testGetFile()
+
+
+
+ def testGetKeyInfo():
+ identifier = c.getKeyInfo(
+ 'CHK@q4~2soHTd9SOINIoXmg~dn7LNUAOYzN1tHNHT3j4c9E,gcVRtoglEhgqN-DJolXPqJ4yX1f~1gBGh89HNWlFMWQ,AAIC--8/snow_002%20%2810%29.jpg',
+ )
+
+ for i in xrange(50):
+ c.next()
+ c.removeRequest(identifier)
+ for i in xrange(5):
+ c.next()
+
+ #testgetKeyInfo()
+
+
+
+ def testConfigData():
+
+ from fcp2_0_config import Config
+
+ def cb(event, msg):
+ root=Config(configDataMsg=msg)
+ for node in root.walk():
+ print node.key()
+
+ c.events.ConfigData += cb
+ c.getConfig()
+ for i in xrange(10):
+ c.next()
+
+ #testConfigData()
+
+
+
+ def testNodeData():
+
+ def cb(event, msg):
+ pass
+
+
+ c.events.NodeData += cb
+ c.getNode()
+ for i in xrange(10):
+ c.next()
+
+ #testNodeData()
+
+
+ def testGenerateSSK():
+
+ #def cb(event, msg):
+ # pass
+
+
+ #c.events.NodeData += cb
+ c.generateSSK()
+ for i in xrange(10):
+ c.next()
+
+ #testGenerateSSK()
+
+
+
\ No newline at end of file
Added: trunk/sandbox/fcp/fcp2_0_config.py
===================================================================
--- trunk/sandbox/fcp/fcp2_0_config.py (rev 0)
+++ trunk/sandbox/fcp/fcp2_0_config.py 2008-01-27 02:16:50 UTC (rev 79)
@@ -0,0 +1,335 @@
+"""Sketch for fcp config tree"""
+
+#****************************************************************************************
+#
+#****************************************************************************************
+class ConfigItem(object):
+
+ def __init__(self, parent, name):
+ self.parent = parent
+ self.name = name
+ self.children = {}
+ self.values = {}
+
+ def key(self):
+ out = []
+ parent = self
+ while parent is not None:
+ if parent.name is not None:
+ out.append(parent.name)
+ parent = parent.parent
+ out.reverse()
+ return '.'.join(out)
+
+
+#****************************************************************************************
+#
+#****************************************************************************************
+class Config(object):
+
+ """Class representing fcp config tree
+
+ """
+ #{'current': {'valueType': (8, None), 'allowedHosts': 'true'}, 'default': {'valueType': (8, None), 'allowedHosts': 'true'}}
+
+ ValueClassCurrent = 'current'
+ ValueClassDefault = 'default'
+ ValueClassExpertFlag = 'expertFlag'
+ ValueClassForceWriteFlag = 'forceWriteFlag'
+ ValueClassShortDescription = 'shortDescription'
+ ValueClassLongDescription = 'longDescription'
+ ValueClassSortOrder = 'sortOrder'
+ ValueClassType = 'valueType'
+
+ ValueClassesFcp = (
+ ValueClassDefault,
+ ValueClassExpertFlag,
+ ValueClassForceWriteFlag,
+ ValueClassShortDescription,
+ ValueClassLongDescription,
+ )
+ ValueClassAll = ValueClassesFcp + (ValueClassType, )
+
+
+ ValueTypeUnknown = 0
+ ValueTypeBool = 1
+ ValueTypeInt = 2 # params: LowerLimit, UpperLimit
+ ValueTypeBytes = 3 # may have params: LowerLimit, UpperLimit
+ ValueTypeFilename = 4
+ ValueTypeDirname = 5
+ ValueTypePort = 6
+ ValueTypeIP = 7
+ ValueTypeIPList = 8
+ ValueTypeChoice = 9
+ ValueTypeChoiceDirname = 10
+ ValueTypePercent = 11
+ ValueTypeString = 12
+ ValueTypeUri = 13
+ ValueTypeStringList = 14
+
+
+ class ChoiceFProxyCss:
+ Clean = 'Clean'
+ Boxed = 'boxed'
+ GrayAndBlue = 'grayandblue'
+ Sky = 'sky'
+
+ ChoicesAll = (Clean, Boxed, GrayAndBlue, Sky)
+ ChoicesAllowMultiple = False
+
+ class ChoiceLoggerPriority:
+ Error = 'ERROR'
+ Normal = 'NORMAL'
+ Minor = 'MINOR'
+ Debug = 'DEBUG'
+
+ ChoicesAll = (Error, Normal, Minor, Debug)
+ ChoicesAllowMultiple = False
+
+ class ChoiceNodeDownloadAllowedDirs:
+ All = 'all'
+ Downloads = 'downloads'
+ Nowhere = ''
+
+ ChoicesAll = (All, Downloads, Nowhere)
+ ChoicesAllowMultiple = False
+
+ class ChoiceNodeUploadAllowedDirs:
+ All = 'all'
+ Nowhere = ''
+ ChoicesAll = (All, Nowhere)
+ ChoicesAllowMultiple = False
+
+ class ChoicePriorityPolicy:
+ Hard = 'HARD'
+ Soft = 'SOFT'
+
+ ChoicesAll = (Hard, Soft)
+ ChoicesAllowMultiple = False
+
+
+ class ChoiceSSLVersion:
+ Stl = 'STL'
+ SslV3 = 'SSLV3'
+ TlsV1 = 'TLSv1'
+
+ ChoicesAll = (Stl, SslV3, TlsV1)
+ ChoicesAllowMultiple = False
+
+
+ KeyConsole = 'console'
+ KeyFcp = 'fcp'
+ KeyFproxy = 'fproxy'
+ KeyLogger = 'logger'
+ KeyNode = 'node'
+ KeyNodeLoad = 'node.load'
+ KeyNodeOpennet = 'node.opennet'
+ KeyNodeScheduler = 'node.scheduler'
+ KeyNodeUpdaterer = 'node.updater'
+ KeyPluginmanager = 'pluginmanager'
+ KeyPluginmanager2 = 'pluginmanager2'
+ KeySSL = 'ssl'
+ KeyToadletSymlinker = 'toadletsymlinker'
+
+ Keys = {
+
+ 'console.allowedHosts': (ValueTypeIPList, None), # host names, single IPs CIDR-maskip IPs likee 192.168.0.0/24
+ 'console.bindTo': (ValueTypeIPList, None),
+ 'console.directEnabled': (ValueTypeBool, None),
+ 'console.enabled': (ValueTypeBool, None),
+ 'console.port': (ValueTypePort, None),
+ 'console.ssl': (ValueTypeBool, None),
+
+
+ 'fcp.allowedHosts': (ValueTypeIPList, None),
+ 'fcp.allowedHostsFullAccess': (ValueTypeIPList, None),
+ 'fcp.assumeDownloadDDAIsAllowed': (ValueTypeBool, None),
+ 'fcp.assumeUploadDDAIsAllowed': (ValueTypeBool, None),
+ 'fcp.bindTo': (ValueTypeIP, None),
+ 'fcp.enabled': (ValueTypeBool, None),
+ 'fcp.persistentDownloadsEnabled': (ValueTypeBool, None),
+ 'fcp.persistentDownloadsFile': (ValueTypeFilename, None),
+ 'fcp.persistentDownloadsInterval': (ValueTypeInt, (0, None)),
+ 'fcp.port': (ValueTypePort, None),
+ 'fcp.ssl': (ValueTypeBool, None),
+
+
+ 'fproxy.CSSOverride': (ValueTypeBool, None),
+ 'fproxy.advancedModeEnabled': (ValueTypeBool, None),
+ 'fproxy.allowedHosts': (ValueTypeIPList, None),
+ 'fproxy.allowedHostsFullAccess': (ValueTypeIPList, None),
+ 'fproxy.bindTo': (ValueTypeIPList, None),
+ 'fproxy.css': (ValueTypeChoice, ChoiceFProxyCss),
+ 'fproxy.doRobots': (ValueTypeBool, None),
+ 'fproxy.enabled': (ValueTypeBool, None),
+ 'fproxy.javascriptEnabled': (ValueTypeBool, None),
+ 'fproxy.port': (ValueTypePort, None),
+ 'fproxy.showPanicButton': (ValueTypeBool, None),
+ 'fproxy.ssl': (ValueTypeBool, None),
+
+
+ 'logger.dirname': (ValueTypeDirname, None),
+ 'logger.enabled': (ValueTypeBool, None),
+ 'logger.interval': (ValueTypeUnknown, None), # ??? 1HOUR ??
+ 'logger.maxCachedBytes': (ValueTypeBytes, None),
+ 'logger.maxCachedLines': (ValueTypeInt, (0, None)), # ???
+ 'logger.maxZippedLogsSize': (ValueTypeBytes, None), # ???
+ 'logger.priority': (ValueTypeChoice, ChoiceLoggerPriority),
+ 'logger.priorityDetail': (ValueTypeUnknown, None), # ???? Detailed priority thresholds
+
+
+ 'node.alwaysAllowLocalAddresses': (ValueTypeBool, None),
+ 'node.assumeNATed': (ValueTypeBool, None),
+ 'node.bindTo': (ValueTypeIP, None),
+ 'nod...
[truncated message content] |