Author: aum
Date: 2006-06-04 08:00:16 +0000 (Sun, 04 Jun 2006)
New Revision: 9040
Added:
trunk/apps/pyFreenet/fcp/freenetfs.py
trunk/apps/pyFreenet/fcp/xmlobject.py
trunk/apps/pyFreenet/manpages/
trunk/apps/pyFreenet/manpages/fcpgenkey.1
trunk/apps/pyFreenet/manpages/fcpgenkey.1.html
trunk/apps/pyFreenet/manpages/fcpget.1
trunk/apps/pyFreenet/manpages/fcpget.1.html
trunk/apps/pyFreenet/manpages/fcpput.1
trunk/apps/pyFreenet/manpages/fcpput.1.html
trunk/apps/pyFreenet/manpages/freesitemgr.1
trunk/apps/pyFreenet/manpages/freesitemgr.1.html
Modified:
trunk/apps/pyFreenet/CHANGELOG
trunk/apps/pyFreenet/INSTALL
trunk/apps/pyFreenet/code.leo
trunk/apps/pyFreenet/fcp/__init__.py
trunk/apps/pyFreenet/fcp/node.py
trunk/apps/pyFreenet/fcpget
trunk/apps/pyFreenet/fcpget.py
trunk/apps/pyFreenet/fcpput
trunk/apps/pyFreenet/fcpput.py
trunk/apps/pyFreenet/freedisk.py
trunk/apps/pyFreenet/setup.py
Log:
Implemented support for 'global' in fcpget/put
Modified: trunk/apps/pyFreenet/CHANGELOG
===================================================================
--- trunk/apps/pyFreenet/CHANGELOG 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/CHANGELOG 2006-06-04 08:00:16 UTC (rev 9040)
@@ -1,6 +1,16 @@
Revision history for PyFCP
+- Version 0.1.4
+
+ - added manpages for console programs
+ - improved mimetype determination algorithm for fcp put
+ - added 'freedisk', a rudimentary linux filesystem that maps freenet
+ into a mountable fs - limited functionality at present
+ - added support for use of env vars FCP_HOST and FCP_PORT for specifying
+ FCP host/port - useful for people who access FCP across a LAN, since
+ it avoids annoyance of having to specify -H or -P with each command
+
- Version 0.1.3
- added 'fcpget' and 'fcpput' command-line key retrieve/insert apps
Modified: trunk/apps/pyFreenet/INSTALL
===================================================================
--- trunk/apps/pyFreenet/INSTALL 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/INSTALL 2006-06-04 08:00:16 UTC (rev 9040)
@@ -2,10 +2,39 @@
System requirements:
-This package requires:
- - Python2.3 or later
- - access to a freenet FCP port, on same or other machine
+ This package requires:
+ - Python2.3 or later
+ - access to a freenet FCP port, on same or other machine
+ - third party module 'SSLCrypto' (source included here)
Installation:
- - become root, then type 'python setup.py install'
+ 1) Test if SSLCrypto is installed
+
+ If you already have the Python 'SSLCrypto' package installed,
+ you can skip to step 3.
+
+ You can test if SSLCrypto is installed by typing:
+
+ $ python -c "import SSLCrypto"
+
+ If the command completes quietly, then SSLCrypto is installed
+ and working. Otherwise, if you see something like 'ImportError:...',
+ you need to install SSLCrypto.
+
+ 2) Install SSLCrypto if needed
+
+ (i) go in to the 'dependencies' directory
+ (ii) unpack both the 'Pyrex...' and the 'SSLCrypto...' tarballs
+ (iii) cd into the 'Pyrex-...' directory, become root, then type:
+
+ python setup.py install
+
+ (iv) cd into the 'SSLCrypto...' directory, become root, then type:
+
+ python setup.py install
+
+ 3) Now, you should be able to install pyfcp and its applications.
+ To do this, get back into the toplevel pyfcp directory, then
+ become root, then type 'python setup.py install'
+
Modified: trunk/apps/pyFreenet/code.leo
===================================================================
--- trunk/apps/pyFreenet/code.leo 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/code.leo 2006-06-04 08:00:16 UTC (rev 9040)
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<leo_file>
-<leo_header file_format="2" tnodes="0" max_tnode_index="23" clone_windows="0"/>
-<globals body_outline_ratio="0.2633552015">
- <global_window_position top="40" left="121" height="659" width="1067"/>
+<leo_header file_format="2" tnodes="0" max_tnode_index="57" clone_windows="0"/>
+<globals body_outline_ratio="0.347234042553">
+ <global_window_position top="30" left="18" height="716" width="1175"/>
<global_log_window_position top="0" left="0" height="0" width="0"/>
</globals>
<preferences/>
@@ -10,9 +10,12 @@
<vnodes>
<v t="aum.20060506215300" a="E"><vh>PyFCP</vh></v>
<v t="aum.20060516115529"><vh>TODO</vh></v>
+<v t="aum.20060528180449" a="E"><vh>notes</vh>
+<v t="aum.20060528180449.1"><vh>freedisk</vh></v>
+</v>
<v t="aum.20060513180215" a="E"><vh>Release files</vh>
<v t="aum.20060513180215.1" tnodeList="aum.20060513180215.1"><vh>@nosent
README</vh></v>
-<v t="aum.20060522200735" a="V" tnodeList="aum.20060522200735"><vh>@nosent
README.freedisk</vh></v>
+<v t="aum.20060522200735" tnodeList="aum.20060522200735"><vh>@nosent
README.freedisk</vh></v>
<v t="aum.20060513180716" tnodeList="aum.20060513180716"><vh>@nosent
INSTALL</vh></v>
<v t="aum.20060513180932" tnodeList="aum.20060513180932"><vh>@nosent
AUTHORS</vh></v>
<v t="aum.20060513181137" tnodeList="aum.20060513181137"><vh>@nosent
COPYING</vh></v>
@@ -26,7 +29,7 @@
</v>
<v t="aum.20060513073239" a="E"><vh>Package 'fcp'</vh>
<v t="aum.20060516141235" tnodeList="aum.20060516141235"><vh>@nosent
__init__.py</vh></v>
-<v t="aum.20060506215707" a="E"
tnodeList="aum.20060506215707,aum.20060506215707.1,aum.20060506220237,aum.20060506215707.2,aum.20060506215707.3,aum.20060506220237.1,aum.20060506220237.2,aum.20060514223716,aum.20060506231352.1,aum.20060506231352,aum.20060507003931,aum.20060511001853,aum.20060521180804,aum.20060506224238,aum.20060514224855,aum.20060514224919,aum.20060514225725,aum.20060514223936,aum.20060514223822,aum.20060514223845,aum.20060514224020,aum.20060514124642,aum.20060514191601,aum.20060511205201,aum.20060506232639,aum.20060506232639.1,aum.20060511222538,aum.20060512101715,aum.20060511205201.1,aum.20060511205201.2,aum.20060506223545,aum.20060506224238.1,aum.20060506231352.2,aum.20060506220856,aum.20060506222005,aum.20060507124316,aum.20060511103841,aum.20060511103841.1,aum.20060511103952,aum.20060511103952.1,aum.20060514134235,aum.20060512181209,aum.20060514162944,aum.20060514124934,aum.20060512102840,aum.20060514164052,aum.20060509184020.1,aum.20060509184020.2,aum.20060509224119,aum.20060509224221"><vh>@nosent
node.py</vh>
+<v t="aum.20060506215707" a="E"
tnodeList="aum.20060506215707,aum.20060506215707.1,aum.20060506220237,aum.20060506215707.2,aum.20060506215707.3,aum.20060506220237.1,aum.20060506220237.2,aum.20060514223716,aum.20060506231352.1,aum.20060506231352,aum.20060507003931,aum.20060511001853,aum.20060521180804,aum.20060506224238,aum.20060514224855,aum.20060514224919,aum.20060514225725,aum.20060514223936,aum.20060514223822,aum.20060514223845,aum.20060514224020,aum.20060514124642,aum.20060514191601,aum.20060511205201,aum.20060506232639,aum.20060506232639.1,aum.20060511222538,aum.20060512101715,aum.20060511205201.1,aum.20060511205201.2,aum.20060506223545,aum.20060506224238.1,aum.20060506231352.2,aum.20060506220856,aum.20060506222005,aum.20060507124316,aum.20060511103841,aum.20060511103841.1,aum.20060511103952,aum.20060511103952.1,aum.20060514134235,aum.20060512181209,aum.20060514162944,aum.20060514124934,aum.20060512102840,aum.20060514164052,aum.20060509184020.1,aum.20060509184020.2,aum.20060509224119,aum.20060509224221,aum.20060603170554,aum.20060603231840,aum.20060603231840.1,aum.20060603231840.2"><vh>@nosent
node.py</vh>
<v t="aum.20060506215707.1"><vh>imports</vh></v>
<v t="aum.20060506220237"><vh>exceptions</vh></v>
<v t="aum.20060506215707.2"><vh>globals</vh></v>
@@ -82,8 +85,13 @@
<v t="aum.20060509184020.2"><vh>toBool</vh></v>
<v t="aum.20060509224119"><vh>readdir</vh></v>
<v t="aum.20060509224221"><vh>guessMimetype</vh></v>
+<v t="aum.20060603170554"><vh>uriIsPrivate</vh></v>
+<v t="aum.20060603231840" a="E"><vh>base64 stuff</vh>
+<v t="aum.20060603231840.1"><vh>base64encode</vh></v>
+<v t="aum.20060603231840.2"><vh>base64decode</vh></v>
</v>
</v>
+</v>
<v t="aum.20060511101147"
tnodeList="aum.20060511101147,aum.20060511113333,aum.20060511113333.1,aum.20060516143534,aum.20060511114439,aum.20060511114439.1,aum.20060512150118,aum.20060511114439.2,aum.20060511114604,aum.20060511114604.1,aum.20060511120059,aum.20060516184736,aum.20060516192715,aum.20060516200626,aum.20060516194958,aum.20060516194016,aum.20060511113333.3,aum.20060513071956,aum.20060507124316,aum.20060511130507,aum.20060516142202,aum.20060511120024"><vh>@nosent
sitemgr.py</vh>
<v t="aum.20060511113333"><vh>imports</vh></v>
<v t="aum.20060511113333.1"><vh>config</vh></v>
@@ -127,7 +135,120 @@
<v t="aum.20060507195029.1"><vh>main</vh></v>
<v t="aum.20060506224545"><vh>mainline</vh></v>
</v>
+<v t="aum.20060528175118" a="E"
tnodeList="aum.20060528175118,aum.20060528175118.1,aum.20060528175118.2,aum.20060528175118.3,aum.20060528175118.4,aum.20060528175118.5,aum.20060528175118.6,aum.20060528175118.7,aum.20060528175118.8,aum.20060528175118.9,aum.20060528175118.10,aum.20060528175118.11,aum.20060528175118.12,aum.20060528175118.13,aum.20060528175118.14,aum.20060528175118.15,aum.20060528175118.16,aum.20060528175118.17,aum.20060528175118.18,aum.20060528175118.19,aum.20060528175118.20,aum.20060603153411,aum.20060603160206,aum.20060528175118.21,aum.20060528175118.22,aum.20060528175118.23,aum.20060528175118.24"><vh>@file
xmlobject.py</vh>
+<v t="aum.20060528175118.1"><vh>imports</vh></v>
+<v t="aum.20060528175118.2"><vh>globals</vh></v>
+<v t="aum.20060528175118.3"><vh>exceptions</vh></v>
+<v t="aum.20060528175118.4" a="E"><vh>class XMLFile</vh>
+<v t="aum.20060528175118.5"><vh>__init__</vh></v>
+<v t="aum.20060528175118.6"><vh>save</vh></v>
+<v t="aum.20060528175118.7"><vh>saveAs</vh></v>
+<v t="aum.20060528175118.8"><vh>toxml</vh></v>
+<v t="aum.20060528175118.9"><vh>__len__</vh></v>
+<v t="aum.20060528175118.10"><vh>__getitem__</vh></v>
</v>
+<v t="aum.20060528175118.11" a="E"><vh>class XMLNode</vh>
+<v t="aum.20060528175118.12"><vh>__init__</vh></v>
+<v t="aum.20060528175118.13"><vh>_render</vh></v>
+<v t="aum.20060528175118.14"><vh>__repr__</vh></v>
+<v t="aum.20060528175118.15"><vh>__getattr__</vh></v>
+<v t="aum.20060528175118.16"><vh>__setattr__</vh></v>
+<v t="aum.20060528175118.17"><vh>_keys</vh></v>
+<v t="aum.20060528175118.18"><vh>__len__</vh></v>
+<v t="aum.20060528175118.19"><vh>__getitem__</vh></v>
+<v t="aum.20060528175118.20"><vh>_addNode</vh></v>
+<v t="aum.20060603153411"><vh>_getChild</vh></v>
+<v t="aum.20060603160206"><vh>_delChild</vh></v>
+<v t="aum.20060528175118.21"><vh>_addText</vh></v>
+<v t="aum.20060528175118.22"><vh>_addComment</vh></v>
+<v t="aum.20060528175118.23"><vh>_save</vh></v>
+<v t="aum.20060528175118.24"><vh>_toxml</vh></v>
+</v>
+</v>
+<v t="aum.20060521163823" a="E"
tnodeList="aum.20060521163823,aum.20060521163823.1,aum.20060521175433,aum.20060521175052,aum.20060521163823.2,aum.20060521163823.5,aum.20060521163823.3,aum.20060521175052.6,aum.20060521175052.4,aum.20060521175052.5,aum.20060521191057,aum.20060526071442,aum.20060526112020,aum.20060521232922,aum.20060521163823.4,aum.20060521185642,aum.20060521163823.14,aum.20060521163823.15,aum.20060521163823.25,aum.20060521163823.6,aum.20060527195652,aum.20060526163608,aum.20060604143559,aum.20060521163823.8,aum.20060521163823.13,aum.20060521163823.18,aum.20060521163823.17,aum.20060521163823.20,aum.20060521163823.21,aum.20060521163823.7,aum.20060521163823.23,aum.20060528214253,aum.20060528214707,aum.20060521163823.12,aum.20060521163823.10,aum.20060521163823.24,aum.20060521163823.11,aum.20060521163823.16,aum.20060521163823.9,aum.20060521163823.19,aum.20060521163823.22,aum.20060528221744,aum.20060530234330,aum.20060528221744.1,aum.20060530151504,aum.20060528221758,aum.20060530151453.1,aum.20060530151453,aum.20060530234330.1,aum.20060530234330.2,aum.20060521185946,aum.20060527114534,aum.20060527114743,aum.20060525194744,aum.20060522231936,aum.20060522225626,aum.20060521190048,aum.20060521190048.1,aum.20060525225133,aum.20060601233442,aum.20060525225133.1,aum.20060525225603,aum.20060525225713,aum.20060527140140.2,aum.20060526072230,aum.20060527114053,aum.20060530202714,aum.20060530202714.1,aum.20060530202714.2,aum.20060530202714.3,aum.20060525193858,aum.20060525194744.1,aum.20060529184826,aum.20060529123536,aum.20060521163823.26"><vh>@file
freenetfs.py</vh>
+<v t="aum.20060521163823.1"><vh>imports</vh></v>
+<v t="aum.20060521175433"><vh>globals</vh></v>
+<v t="aum.20060521175052"><vh>class ErrnoWrapper</vh></v>
+<v t="aum.20060521163823.2" a="E"><vh>class FreenetFS</vh>
+<v t="aum.20060521163823.5"><vh>attribs</vh></v>
+<v t="aum.20060521163823.3"><vh>__init__</vh></v>
+<v t="aum.20060521175052.6"><vh>run</vh></v>
+<v t="aum.20060521175052.4"><vh>GetContent</vh></v>
+<v t="aum.20060521175052.5"><vh>Invalidate</vh></v>
+<v t="aum.20060521191057"><vh>_loadConfig</vh></v>
+<v t="aum.20060526071442"><vh>setupFiles</vh></v>
+<v t="aum.20060526112020"><vh>connectToNode</vh></v>
+<v t="aum.20060521232922"><vh>log</vh></v>
+<v t="aum.20060521163823.4"><vh>mythread</vh></v>
+<v t="aum.20060521185642" a="E"><vh>fs primitives</vh>
+<v t="aum.20060521163823.14"><vh>chmod</vh></v>
+<v t="aum.20060521163823.15"><vh>chown</vh></v>
+<v t="aum.20060521163823.25"><vh>fsync</vh></v>
+<v t="aum.20060521163823.6" a="E"><vh>getattr</vh>
+<v t="aum.20060527195652"><vh><<generate keypair>></vh></v>
+<v t="aum.20060526163608"><vh><<retrieve/cache key>></vh></v>
+<v t="aum.20060604143559"><vh><<base64 command>></vh></v>
+</v>
+<v t="aum.20060521163823.8"><vh>getdir</vh></v>
+<v t="aum.20060521163823.13"><vh>link</vh></v>
+<v t="aum.20060521163823.18"><vh>mkdir</vh></v>
+<v t="aum.20060521163823.17"><vh>mknod</vh></v>
+<v t="aum.20060521163823.20"><vh>open</vh></v>
+<v t="aum.20060521163823.21"><vh>read</vh></v>
+<v t="aum.20060521163823.7"><vh>readlink</vh></v>
+<v t="aum.20060521163823.23" a="E"><vh>release</vh>
+<v t="aum.20060528214253"><vh><<insert to freenet>></vh></v>
+<v t="aum.20060528214707"><vh><<write to freedisk>></vh></v>
+</v>
+<v t="aum.20060521163823.12"><vh>rename</vh></v>
+<v t="aum.20060521163823.10"><vh>rmdir</vh></v>
+<v t="aum.20060521163823.24"><vh>statfs</vh></v>
+<v t="aum.20060521163823.11"><vh>symlink</vh></v>
+<v t="aum.20060521163823.16"><vh>truncate</vh></v>
+<v t="aum.20060521163823.9"><vh>unlink</vh></v>
+<v t="aum.20060521163823.19"><vh>utime</vh></v>
+<v t="aum.20060521163823.22"><vh>write</vh></v>
+</v>
+<v t="aum.20060528221744" a="E"><vh>freedisk methods</vh>
+<v t="aum.20060530234330"><vh>setupFreedisks</vh></v>
+<v t="aum.20060528221744.1"><vh>newDisk</vh></v>
+<v t="aum.20060530151504"><vh>addDisk</vh></v>
+<v t="aum.20060528221758"><vh>delDisk</vh></v>
+<v t="aum.20060530151453.1"><vh>commitDisk</vh></v>
+<v t="aum.20060530151453"><vh>updateDisk</vh></v>
+<v t="aum.20060530234330.1"><vh>getManifest</vh></v>
+<v t="aum.20060530234330.2"><vh>putManifest</vh></v>
+</v>
+<v t="aum.20060521185946"><vh>hashpath</vh></v>
+<v t="aum.20060527114534"><vh>addToCache</vh></v>
+<v t="aum.20060527114743"><vh>delFromCache</vh></v>
+<v t="aum.20060525194744"><vh>__getDirStat</vh></v>
+<v t="aum.20060522231936"><vh>statFromKw</vh></v>
+<v t="aum.20060522225626"><vh>statToDict</vh></v>
+<v t="aum.20060521190048"><vh>getReadURI</vh></v>
+<v t="aum.20060521190048.1"><vh>getWriteURI</vh></v>
+</v>
+<v t="aum.20060525225133" a="E"><vh>class FileRecord</vh>
+<v t="aum.20060601233442"><vh>attribs</vh></v>
+<v t="aum.20060525225133.1"><vh>__init__</vh></v>
+<v t="aum.20060525225603"><vh>__getattr__</vh></v>
+<v t="aum.20060525225713"><vh>__setattr__</vh></v>
+<v t="aum.20060527140140.2"><vh>write</vh></v>
+<v t="aum.20060526072230"><vh>addChild</vh></v>
+<v t="aum.20060527114053"><vh>delChild</vh></v>
+</v>
+<v t="aum.20060530202714" a="E"><vh>class FreediskMgr</vh>
+<v t="aum.20060530202714.1"><vh>__init__</vh></v>
+<v t="aum.20060530202714.2"><vh>update</vh></v>
+<v t="aum.20060530202714.3"><vh>commit</vh></v>
+</v>
+<v t="aum.20060525193858"><vh>pathToInode</vh></v>
+<v t="aum.20060525194744.1"><vh>timeNow</vh></v>
+<v t="aum.20060529184826"><vh>usage</vh></v>
+<v t="aum.20060529123536"><vh>main</vh></v>
+<v t="aum.20060521163823.26"><vh>mainline</vh></v>
+</v>
+</v>
<v t="aum.20060521111625" a="E"><vh>Client Apps</vh>
<v t="aum.20060513073239.2"><vh>freesitemgr</vh>
<v t="aum.20060516145032" a="E"
tnodeList="aum.20060516145032,aum.20060516145032.1,aum.20060514132715,aum.20060514132715.1,aum.20060516150511,aum.20060516184736.1,aum.20060516193650,aum.20060516153119,aum.20060516143534.1,aum.20060516144850,aum.20060516143534.2,aum.20060514132715.2,aum.20060514132715.3"><vh>@nosent
freesitemgr.py</vh>
@@ -171,10 +292,10 @@
<v t="aum.20060515200029"><vh>mainline</vh></v>
</v>
</v>
-<v t="aum.20060521111625.1"><vh>get/put/genkey</vh>
-<v t="aum.20060521133455"><vh>fcpget</vh>
+<v t="aum.20060521111625.1" a="E"><vh>get/put/genkey</vh>
+<v t="aum.20060521133455" a="E"><vh>fcpget</vh>
<v t="aum.20060521133455.1" a="E"
tnodeList="aum.20060521133455.1,aum.20060521133455.2,aum.20060521111727.1,aum.20060521131205,aum.20060521131205.1,aum.20060521131205.2,aum.20060521111727.2,aum.20060521111727.3"><vh>@nosent
fcpget</vh>
-<v t="aum.20060521133455.2"><vh>fcpget code</vh>
+<v t="aum.20060521133455.2" a="E"><vh>fcpget code</vh>
<v t="aum.20060521111727.1"><vh>imports</vh></v>
<v t="aum.20060521131205"><vh>globals</vh></v>
<v t="aum.20060521131205.1"><vh>usage</vh></v>
@@ -183,7 +304,7 @@
<v t="aum.20060521111727.3"><vh>mainline</vh></v>
</v>
</v>
-<v t="aum.20060521111727" a="E"
tnodeList="aum.20060521111727,aum.20060521133455.2,aum.20060521111727.1,aum.20060521131205,aum.20060521131205.1,aum.20060521131205.2,aum.20060521111727.2,aum.20060521111727.3"><vh>@nosent
fcpget.py</vh>
+<v t="aum.20060521111727"
tnodeList="aum.20060521111727,aum.20060521133455.2,aum.20060521111727.1,aum.20060521131205,aum.20060521131205.1,aum.20060521131205.2,aum.20060521111727.2,aum.20060521111727.3"><vh>@nosent
fcpget.py</vh>
<v t="aum.20060521133455.2"><vh>fcpget code</vh>
<v t="aum.20060521111727.1"><vh>imports</vh></v>
<v t="aum.20060521131205"><vh>globals</vh></v>
@@ -196,16 +317,16 @@
</v>
<v t="aum.20060521134332" a="E"><vh>fcpput</vh>
<v t="aum.20060521134332.1" a="E"
tnodeList="aum.20060521134332.1,aum.20060521134737,aum.20060521134737.1,aum.20060521134737.2,aum.20060521134737.3,aum.20060521134737.4,aum.20060521134737.5,aum.20060521134737.6"><vh>@nosent
fcpput</vh>
-<v t="aum.20060521134737"><vh>fcpput code</vh>
+<v t="aum.20060521134737" a="E"><vh>fcpput code</vh>
<v t="aum.20060521134737.1"><vh>imports</vh></v>
<v t="aum.20060521134737.2"><vh>globals</vh></v>
<v t="aum.20060521134737.3"><vh>usage</vh></v>
-<v t="aum.20060521134737.4"><vh>help</vh></v>
+<v t="aum.20060521134737.4" a="V"><vh>help</vh></v>
<v t="aum.20060521134737.5"><vh>main</vh></v>
<v t="aum.20060521134737.6"><vh>mainline</vh></v>
</v>
</v>
-<v t="aum.20060521135828" a="E"
tnodeList="aum.20060521135828,aum.20060521134737,aum.20060521134737.1,aum.20060521134737.2,aum.20060521134737.3,aum.20060521134737.4,aum.20060521134737.5,aum.20060521134737.6"><vh>@nosent
fcpput.py</vh>
+<v t="aum.20060521135828"
tnodeList="aum.20060521135828,aum.20060521134737,aum.20060521134737.1,aum.20060521134737.2,aum.20060521134737.3,aum.20060521134737.4,aum.20060521134737.5,aum.20060521134737.6"><vh>@nosent
fcpput.py</vh>
<v t="aum.20060521134737"><vh>fcpput code</vh>
<v t="aum.20060521134737.1"><vh>imports</vh></v>
<v t="aum.20060521134737.2"><vh>globals</vh></v>
@@ -216,9 +337,9 @@
</v>
</v>
</v>
-<v t="aum.20060521182836" a="E"><vh>fcpgenkey</vh>
-<v t="aum.20060521183025" a="E"
tnodeList="aum.20060521183025,aum.20060521183025.1,aum.20060521183025.2,aum.20060521183025.3,aum.20060521183025.4,aum.20060521183025.5,aum.20060521183025.6,aum.20060521183025.7"><vh>@nosent
fcpgenkey</vh>
-<v t="aum.20060521183025.1"><vh>fcpgenkey code</vh>
+<v t="aum.20060521182836"><vh>fcpgenkey</vh>
+<v t="aum.20060521183025"
tnodeList="aum.20060521183025,aum.20060521183025.1,aum.20060521183025.2,aum.20060521183025.3,aum.20060521183025.4,aum.20060521183025.5,aum.20060521183025.6,aum.20060521183025.7"><vh>@nosent
fcpgenkey</vh>
+<v t="aum.20060521183025.1" a="E"><vh>fcpgenkey code</vh>
<v t="aum.20060521183025.2"><vh>imports</vh></v>
<v t="aum.20060521183025.3"><vh>globals</vh></v>
<v t="aum.20060521183025.4"><vh>usage</vh></v>
@@ -240,74 +361,116 @@
</v>
</v>
<v t="aum.20060521163241" a="E"><vh>freedisk</vh>
-<v t="aum.20060521163823" a="E"
tnodeList="aum.20060521163823,aum.20060521163823.1,aum.20060521175433,aum.20060521175052,aum.20060521175052.1,aum.20060521175052.2,aum.20060521175052.3,aum.20060521175052.4,aum.20060521175052.5,aum.20060521175052.6,aum.20060521163823.2,aum.20060521163823.5,aum.20060521163823.3,aum.20060521191057,aum.20060526071442,aum.20060526112020,aum.20060521232922,aum.20060521163823.4,aum.20060521185642,aum.20060521163823.6,aum.20060527195652,aum.20060526163608,aum.20060526163608.1,aum.20060521163823.7,aum.20060521163823.8,aum.20060521163823.9,aum.20060521163823.10,aum.20060521163823.11,aum.20060521163823.12,aum.20060521163823.13,aum.20060521163823.14,aum.20060521163823.15,aum.20060521163823.16,aum.20060521163823.17,aum.20060521163823.18,aum.20060521163823.19,aum.20060521163823.20,aum.20060521163823.21,aum.20060521163823.22,aum.20060521163823.23,aum.20060521163823.24,aum.20060521163823.25,aum.20060521185946,aum.20060527114534,aum.20060527114743,aum.20060525194744,aum.20060522231936,aum.20060522225626,aum.20060521190048,aum.20060521190048.1,aum.20060525225133,aum.20060525225133.1,aum.20060525225603,aum.20060525225713,aum.20060527140140.2,aum.20060526072230,aum.20060527114053,aum.20060525193858,aum.20060525194744.1,aum.20060521163823.26"><vh>@file
freedisk.py</vh>
-<v t="aum.20060521163823.1"><vh>imports</vh></v>
-<v t="aum.20060521175433"><vh>globals</vh></v>
-<v t="aum.20060521175052"><vh>class ErrnoWrapper</vh></v>
-<v t="aum.20060521175052.1"><vh>class Fuse</vh>
-<v t="aum.20060521175052.2"><vh>attribs</vh></v>
-<v t="aum.20060521175052.3"><vh>__init__</vh></v>
-<v t="aum.20060521175052.4"><vh>GetContent</vh></v>
-<v t="aum.20060521175052.5"><vh>Invalidate</vh></v>
-<v t="aum.20060521175052.6"><vh>main</vh></v>
+<v t="aum.20060529191729" tnodeList="aum.20060529191729"><vh>@file
mount.freenetfs</vh></v>
+<v t="aum.20060602094531" a="E"><vh>Front ends</vh>
+<v t="aum.20060530170840" a="EO"
tnodeList="aum.20060530170840,aum.20060529123536.1,aum.20060529163723,aum.20060529163723.1,aum.20060603114446,aum.20060604194409,aum.20060604194834,aum.20060603114247,aum.20060530143459.3,aum.20060530143459.4,aum.20060530143459.5,aum.20060530143459.6,aum.20060530143459.7,aum.20060530143459.8,aum.20060530143459.9,aum.20060603164555,aum.20060604144241,aum.20060603121718,aum.20060603125105,aum.20060603121718.1,aum.20060603121848,aum.20060603122324,aum.20060603125848,aum.20060603132557,aum.20060603131227,aum.20060603154804,aum.20060603155318,aum.20060603162815,aum.20060603155642,aum.20060603125405,aum.20060603125405.1,aum.20060529164147,aum.20060529164147.1,aum.20060530160322,aum.20060531160838,aum.20060603100604,aum.20060603100604.1,aum.20060603100604.2,aum.20060604143852,aum.20060603125812,aum.20060603132247,aum.20060529163723.2,aum.20060530142805.1,aum.20060530143459,aum.20060530143459.2,aum.20060529163723.4"><vh>@file
freedisk.py</vh>
+<v t="aum.20060529123536.1" a="E"><vh>freedisk app</vh>
+<v t="aum.20060529163723"><vh>imports</vh></v>
+<v t="aum.20060529163723.1"><vh>globals</vh></v>
+<v t="aum.20060603114446" a="E"><vh>class FreediskMgr</vh>
+<v t="aum.20060604194409"><vh>__init__</vh></v>
+<v t="aum.20060604194834"><vh>execute</vh></v>
+<v t="aum.20060603114247"><vh>cmd_init</vh></v>
+<v t="aum.20060530143459.3"><vh>cmd_start</vh></v>
+<v t="aum.20060530143459.4"><vh>cmd_stop</vh></v>
+<v t="aum.20060530143459.5"><vh>cmd_new</vh></v>
+<v t="aum.20060530143459.6"><vh>cmd_add</vh></v>
+<v t="aum.20060530143459.7"><vh>cmd_del</vh></v>
+<v t="aum.20060530143459.8"><vh>cmd_update</vh></v>
+<v t="aum.20060530143459.9"><vh>cmd_commit</vh></v>
+<v t="aum.20060603164555"><vh>cmd_list</vh></v>
+<v t="aum.20060604144241"><vh>cmd_cmd</vh></v>
</v>
-<v t="aum.20060521163823.2" a="E"><vh>class FreenetFS</vh>
-<v t="aum.20060521163823.5"><vh>attribs</vh></v>
-<v t="aum.20060521163823.3"><vh>__init__</vh></v>
-<v t="aum.20060521191057"><vh>loadConfig</vh></v>
-<v t="aum.20060526071442"><vh>setupFiles</vh></v>
-<v t="aum.20060526112020"><vh>connectToNode</vh></v>
-<v t="aum.20060521232922"><vh>log</vh></v>
-<v t="aum.20060521163823.4"><vh>mythread</vh></v>
-<v t="aum.20060521185642" a="E"><vh>fs primitives</vh>
-<v t="aum.20060521163823.6" a="E"><vh>getattr</vh>
-<v t="aum.20060527195652"><vh><<generate keypair>></vh></v>
-<v t="aum.20060526163608"><vh><<retrieve/cache key>></vh></v>
-<v t="aum.20060526163608.1"><vh><<try host fs>></vh></v>
+<v t="aum.20060603121718"><vh>class FreediskConfig</vh>
+<v t="aum.20060603125105"><vh>attribs</vh></v>
+<v t="aum.20060603121718.1"><vh>__init__</vh></v>
+<v t="aum.20060603121848"><vh>load</vh></v>
+<v t="aum.20060603122324"><vh>create</vh></v>
+<v t="aum.20060603125848"><vh>save</vh></v>
+<v t="aum.20060603132557"><vh>abort</vh></v>
+<v t="aum.20060603131227"><vh>setPassword</vh></v>
+<v t="aum.20060603154804"><vh>addDisk</vh></v>
+<v t="aum.20060603155318"><vh>getDisk</vh></v>
+<v t="aum.20060603162815"><vh>getDisks</vh></v>
+<v t="aum.20060603155642"><vh>delDisk</vh></v>
+<v t="aum.20060603125405"><vh>__getattr__</vh></v>
+<v t="aum.20060603125405.1"><vh>__setattr__</vh></v>
</v>
-<v t="aum.20060521163823.7"><vh>readlink</vh></v>
-<v t="aum.20060521163823.8"><vh>getdir</vh></v>
-<v t="aum.20060521163823.9"><vh>unlink</vh></v>
-<v t="aum.20060521163823.10"><vh>rmdir</vh></v>
-<v t="aum.20060521163823.11"><vh>symlink</vh></v>
-<v t="aum.20060521163823.12"><vh>rename</vh></v>
-<v t="aum.20060521163823.13"><vh>link</vh></v>
-<v t="aum.20060521163823.14"><vh>chmod</vh></v>
-<v t="aum.20060521163823.15"><vh>chown</vh></v>
-<v t="aum.20060521163823.16"><vh>truncate</vh></v>
-<v t="aum.20060521163823.17"><vh>mknod</vh></v>
-<v t="aum.20060521163823.18"><vh>mkdir</vh></v>
-<v t="aum.20060521163823.19"><vh>utime</vh></v>
-<v t="aum.20060521163823.20"><vh>open</vh></v>
-<v t="aum.20060521163823.21"><vh>read</vh></v>
-<v t="aum.20060521163823.22"><vh>write</vh></v>
-<v t="aum.20060521163823.23"><vh>release</vh></v>
-<v t="aum.20060521163823.24"><vh>statfs</vh></v>
-<v t="aum.20060521163823.25"><vh>fsync</vh></v>
+<v t="aum.20060529164147"><vh>usage</vh></v>
+<v t="aum.20060529164147.1"><vh>help</vh></v>
+<v t="aum.20060530160322"><vh>removeDirAndContents</vh></v>
+<v t="aum.20060531160838"><vh>status</vh></v>
+<v t="aum.20060603100604"><vh>encrypt</vh></v>
+<v t="aum.20060603100604.1"><vh>decrypt</vh></v>
+<v t="aum.20060603100604.2"><vh>getpasswd</vh></v>
+<v t="aum.20060604143852"><vh>doFsCommand</vh></v>
+<v t="aum.20060603125812"><vh>ipython</vh></v>
+<v t="aum.20060603132247"><vh>getyesno</vh></v>
+<v t="aum.20060529163723.2" a="E"><vh>main</vh>
+<v t="aum.20060530142805.1"><vh><<set defaults>></vh></v>
+<v t="aum.20060530143459"><vh><<process args>></vh></v>
+<v t="aum.20060530143459.2" a="E"><vh><<execute command>></vh></v>
</v>
-<v t="aum.20060521185946"><vh>hashpath</vh></v>
-<v t="aum.20060527114534"><vh>addToCache</vh></v>
-<v t="aum.20060527114743"><vh>delFromCache</vh></v>
-<v t="aum.20060525194744"><vh>getDirStat</vh></v>
-<v t="aum.20060522231936"><vh>statFromKw</vh></v>
-<v t="aum.20060522225626"><vh>statToDict</vh></v>
-<v t="aum.20060521190048"><vh>getReadURI</vh></v>
-<v t="aum.20060521190048.1"><vh>getWriteURI</vh></v>
+<v t="aum.20060529163723.4"><vh>mainline</vh></v>
</v>
-<v t="aum.20060525225133" a="E"><vh>class FileRecord</vh>
-<v t="aum.20060525225133.1"><vh>__init__</vh></v>
-<v t="aum.20060525225603"><vh>__getattr__</vh></v>
-<v t="aum.20060525225713"><vh>__setattr__</vh></v>
-<v t="aum.20060527140140.2"><vh>write</vh></v>
-<v t="aum.20060526072230"><vh>addChild</vh></v>
-<v t="aum.20060527114053"><vh>delChild</vh></v>
</v>
-<v t="aum.20060525193858"><vh>pathToInode</vh></v>
-<v t="aum.20060525194744.1"><vh>timeNow</vh></v>
-<v t="aum.20060521163823.26"><vh>mainline</vh></v>
+<v t="aum.20060530170840.1" a="O"
tnodeList="aum.20060530170840.1,aum.20060529123536.1,aum.20060529163723,aum.20060529163723.1,aum.20060603114446,aum.20060604194409,aum.20060604194834,aum.20060603114247,aum.20060530143459.3,aum.20060530143459.4,aum.20060530143459.5,aum.20060530143459.6,aum.20060530143459.7,aum.20060530143459.8,aum.20060530143459.9,aum.20060603164555,aum.20060604144241,aum.20060603121718,aum.20060603125105,aum.20060603121718.1,aum.20060603121848,aum.20060603122324,aum.20060603125848,aum.20060603132557,aum.20060603131227,aum.20060603154804,aum.20060603155318,aum.20060603162815,aum.20060603155642,aum.20060603125405,aum.20060603125405.1,aum.20060529164147,aum.20060529164147.1,aum.20060530160322,aum.20060531160838,aum.20060603100604,aum.20060603100604.1,aum.20060603100604.2,aum.20060604143852,aum.20060603125812,aum.20060603132247,aum.20060529163723.2,aum.20060530142805.1,aum.20060530143459,aum.20060530143459.2,aum.20060529163723.4"><vh>@file
freedisk</vh>
+<v t="aum.20060529123536.1" a="E"><vh>freedisk app</vh>
+<v t="aum.20060529163723"><vh>imports</vh></v>
+<v t="aum.20060529163723.1"><vh>globals</vh></v>
+<v t="aum.20060603114446" a="E"><vh>class FreediskMgr</vh>
+<v t="aum.20060604194409"><vh>__init__</vh></v>
+<v t="aum.20060604194834"><vh>execute</vh></v>
+<v t="aum.20060603114247"><vh>cmd_init</vh></v>
+<v t="aum.20060530143459.3"><vh>cmd_start</vh></v>
+<v t="aum.20060530143459.4"><vh>cmd_stop</vh></v>
+<v t="aum.20060530143459.5"><vh>cmd_new</vh></v>
+<v t="aum.20060530143459.6"><vh>cmd_add</vh></v>
+<v t="aum.20060530143459.7"><vh>cmd_del</vh></v>
+<v t="aum.20060530143459.8"><vh>cmd_update</vh></v>
+<v t="aum.20060530143459.9"><vh>cmd_commit</vh></v>
+<v t="aum.20060603164555"><vh>cmd_list</vh></v>
+<v t="aum.20060604144241"><vh>cmd_cmd</vh></v>
</v>
+<v t="aum.20060603121718"><vh>class FreediskConfig</vh>
+<v t="aum.20060603125105"><vh>attribs</vh></v>
+<v t="aum.20060603121718.1"><vh>__init__</vh></v>
+<v t="aum.20060603121848"><vh>load</vh></v>
+<v t="aum.20060603122324"><vh>create</vh></v>
+<v t="aum.20060603125848"><vh>save</vh></v>
+<v t="aum.20060603132557"><vh>abort</vh></v>
+<v t="aum.20060603131227"><vh>setPassword</vh></v>
+<v t="aum.20060603154804"><vh>addDisk</vh></v>
+<v t="aum.20060603155318"><vh>getDisk</vh></v>
+<v t="aum.20060603162815"><vh>getDisks</vh></v>
+<v t="aum.20060603155642"><vh>delDisk</vh></v>
+<v t="aum.20060603125405"><vh>__getattr__</vh></v>
+<v t="aum.20060603125405.1"><vh>__setattr__</vh></v>
</v>
+<v t="aum.20060529164147"><vh>usage</vh></v>
+<v t="aum.20060529164147.1"><vh>help</vh></v>
+<v t="aum.20060530160322"><vh>removeDirAndContents</vh></v>
+<v t="aum.20060531160838"><vh>status</vh></v>
+<v t="aum.20060603100604"><vh>encrypt</vh></v>
+<v t="aum.20060603100604.1"><vh>decrypt</vh></v>
+<v t="aum.20060603100604.2"><vh>getpasswd</vh></v>
+<v t="aum.20060604143852"><vh>doFsCommand</vh></v>
+<v t="aum.20060603125812"><vh>ipython</vh></v>
+<v t="aum.20060603132247"><vh>getyesno</vh></v>
+<v t="aum.20060529163723.2" a="E"><vh>main</vh>
+<v t="aum.20060530142805.1"><vh><<set defaults>></vh></v>
+<v t="aum.20060530143459"><vh><<process args>></vh></v>
+<v t="aum.20060530143459.2" a="E"><vh><<execute command>></vh></v>
</v>
+<v t="aum.20060529163723.4"><vh>mainline</vh></v>
+</v>
+</v>
+</v>
+<v t="aum.20060602094531.1" a="E"><vh>test</vh>
+<v t="aum.20060602094531.2" tnodeList="aum.20060602094531.2"><vh>@file
fdtest.py</vh></v>
+</v>
+</v>
+</v>
<v t="aum.20060513073239.5" a="E"><vh>Test files</vh>
<v t="aum.20060526123909" tnodeList="aum.20060526123909"><vh>@file
fstest.c</vh></v>
<v t="aum.20060511003500" tnodeList="aum.20060511003500"><vh>@file
test.py</vh></v>
@@ -422,9 +585,10 @@
<v t="aum.20060509223528.104"><vh>guessMimetype</vh></v>
<v t="aum.20060509223528.105"><vh>dbr</vh></v>
<v t="aum.20060509223528.106"><vh>setLogCallback</vh></v>
-<v t="aum.20060509223528.107"><vh>base64 stuff</vh></v>
+<v t="aum.20060509223528.107" a="E"><vh>base64 stuff</vh>
<v t="aum.20060509223528.108"><vh>str2b64</vh></v>
<v t="aum.20060509223528.109"><vh>b642str</vh></v>
+</v>
<v t="aum.20060509223528.110"><vh>num2bits</vh></v>
<v t="aum.20060509223528.111"><vh>str2bits</vh></v>
<v t="aum.20060509223528.112"><vh>bits2str</vh></v>
@@ -465,13 +629,19 @@
</t>
<t tx="aum.20060506215707.1">import sys, os, socket, time, thread
import threading, mimetypes, sha, Queue
-import select, traceback
+import select, traceback, base64
</t>
<t tx="aum.20060506215707.2"># where we can find the freenet node FCP port
defaultFCPHost = "127.0.0.1"
defaultFCPPort = 9481
+# may set environment vars for FCP host/port
+if os.environ.has_key("FCP_HOST"):
+ defaultFCPHost = os.environ["FCP_HOST"].strip()
+if os.environ.has_key("FCP_PORT"):
+ defaultFCPPort = int(os.environ["FCP_PORT"].strip())
+
# poll timeout period for manager thread
pollTimeout = 0.1
#pollTimeout = 3
@@ -494,6 +664,8 @@
DETAIL = 5
DEBUG = 6
+defaultVerbosity = ERROR
+
</t>
<t tx="aum.20060506215707.3">class FCPNode:
"""
@@ -582,8 +754,10 @@
Keyword Arguments:
- name - name of client to use with reqs, defaults to random. This
is crucial if you plan on making persistent requests
- - host - hostname, defaults to defaultFCPHost
- - port - port number, defaults to defaultFCPPort
+ - host - hostname, defaults to environment variable FCP_HOST, and
+ if this doesn't exist, then defaultFCPHost
+ - port - port number, defaults to environment variable FCP_PORT, and
+ if this doesn't exist, then defaultFCPPort
- logfile - a pathname or writable file object, to which log messages
should be written, defaults to stdout
- verbosity - how detailed the log messages should be, defaults to 0
@@ -604,9 +778,11 @@
"""
# grab and save parms
+ env = os.environ
self.name = kw.get('clientName', self._getUniqueId())
- self.host = kw.get('host', defaultFCPHost)
- self.port = kw.get('port', defaultFCPPort)
+ self.host = kw.get('host', env.get("FCP_HOST", defaultFCPHost))
+ self.port = kw.get('port', env.get("FCP_PORT", defaultFCPPort))
+ self.port = int(self.port)
# set up the logger
logfile = kw.get('logfile', None) or sys.stdout
@@ -616,7 +792,7 @@
raise Exception("Bad logfile '%s', must be pathname or file
object" % logfile)
logfile = file(logfile, "a")
self.logfile = logfile
- self.verbosity = kw.get('verbosity', 0)
+ self.verbosity = kw.get('verbosity', defaultVerbosity)
# try to connect to node
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -900,8 +1076,8 @@
else:
opts["DSOnly"] = "false"
- if uri.startswith("freenet:CHK@") or uri.startswith("CHK@"):
- uri = os.path.splitext(uri)[0]
+# if uri.startswith("freenet:CHK@") or uri.startswith("CHK@"):
+# uri = os.path.splitext(uri)[0]
opts['URI'] = uri
opts['MaxRetries'] = kw.get("maxretries", 3)
@@ -1070,8 +1246,33 @@
raise Exception("Global requests must be persistent")
opts['URI'] = uri
- opts['Metadata.ContentType'] = kw.get("mimetype", "text/plain")
+
+ # determine a mimetype
+ mimetype = kw.get("mimetype", None)
+ if kw.has_key('mimetype'):
+ # got an explicit mimetype - use it
+ mimetype = kw['mimetype']
+ else:
+ # not explicitly given - figure one out
+ ext = os.path.splitext(uri)[1]
+ if not ext:
+ # no CHK@ file extension, try for filename
+ if kw.has_key('file'):
+ # try to grab a file extension from inserted file
+ ext = os.path.splitext(kw['file'])[1]
+ if not ext:
+ # last resort fallback
+ ext = ".txt"
+ # got some kind of 'file extension', convert to mimetype
+ try:
+ mimetype = mimetypes.guess_type(ext)[0] or "text/plain"
+ except:
+ mimetype = "text/plain"
+
+ # now can specify the mimetype
+ opts['Metadata.ContentType'] = mimetype
+
id = kw.pop("id", None)
if not id:
id = self._getUniqueId()
@@ -7278,6 +7479,9 @@
if cmd == 'ClientGet':
job.uri = kw['URI']
+ if cmd == 'ClientPut':
+ job.mimetype = kw['Metadata.ContentType']
+
self.clientReqQueue.put(job)
self._log(DEBUG, "_submitCmd: id=%s cmd=%s kw=%s" % (id, cmd,
str(kw)[:256]))
@@ -7509,13 +7713,42 @@
System requirements:
-This package requires:
- - Python2.3 or later
- - access to a freenet FCP port, on same or other machine
+ This package requires:
+ - Python2.3 or later
+ - access to a freenet FCP port, on same or other machine
+ - third party module 'SSLCrypto' (source included here)
Installation:
- - become root, then type 'python setup.py install'
+ 1) Test if SSLCrypto is installed
+
+ If you already have the Python 'SSLCrypto' package installed,
+ you can skip to step 3.
+
+ You can test if SSLCrypto is installed by typing:
+
+ $ python -c "import SSLCrypto"
+
+ If the command completes quietly, then SSLCrypto is installed
+ and working. Otherwise, if you see something like 'ImportError:...',
+ you need to install SSLCrypto.
+
+ 2) Install SSLCrypto if needed
+
+ (i) go in to the 'dependencies' directory
+ (ii) unpack both the 'Pyrex...' and the 'SSLCrypto...' tarballs
+ (iii) cd into the 'Pyrex-...' directory, become root, then type:
+
+ python setup.py install
+
+ (iv) cd into the 'SSLCrypto...' directory, become root, then type:
+
+ python setup.py install
+
+ 3) Now, you should be able to install pyfcp and its applications.
+ To do this, get back into the toplevel pyfcp directory, then
+ become root, then type 'python setup.py install'
+
</t>
<t tx="aum.20060513180932">@nocolor
The PyFCP modules and scripts were written
@@ -7552,6 +7785,16 @@
Revision history for PyFCP
+- Version 0.1.4
+
+ - added manpages for console programs
+ - improved mimetype determination algorithm for fcp put
+ - added 'freedisk', a rudimentary linux filesystem that maps freenet
+ into a mountable fs - limited functionality at present
+ - added support for use of env vars FCP_HOST and FCP_PORT for specifying
+ FCP host/port - useful for people who access FCP across a LAN, since
+ it avoids annoyance of having to specify -H or -P with each command
+
- Version 0.1.3
- added 'fcpget' and 'fcpput' command-line key retrieve/insert apps
@@ -7574,7 +7817,7 @@
import sys, os, commands
-version = "0.1.3"
+version = "0.1.5"
releaseDir = "pyfcp-%s" % version
tarball = releaseDir + ".tar.gz"
@@ -7596,12 +7839,15 @@
"AUTHORS", "README", "INSTALL", "COPYING", "BUGS", "CHANGELOG",
"setup.py",
"fcp",
+ "dependencies",
"freesitemgr", "freesitemgr.py",
"tutorial.py",
"fcpxmlrpc.cgi",
"fcpget.py", "fcpget",
"fcpput.py", "fcpput",
"fcpgenkey.py", "fcpgenkey",
+ "manpages",
+ "freedisk.py", "freedisk.conf",
"html",
]
@@ -7994,18 +8240,50 @@
<t tx="aum.20060515193950">"""
distutils installation script for pyfcp
"""
-import sys
+import sys, os
+# barf if prerequisite module 'SSLCrypto' is not installed
+try:
+ sys.stdout.write("Testing if SSLCrypto module is installed...")
+ sys.stdout.flush()
+ import SSLCrypto
+ print "ok!"
+except:
+ print "failed!"
+ print
+ print "You have not installed the SSLCrypto module"
+ print "Please refer to the INSTALL file in this directory"
+ print "and follow the instructions"
+ print
+ print "You can continue with this installation, but you will"
+ print "not have the protection of encrypted config files."
+ resp = raw_input("Continue installation anyway? [Y/n] ")
+ resp = resp.strip().lower() or "y"
+ resp = resp[0]
+ if resp == 'n':
+ print "Installation aborted"
+ sys.exit(1)
+ else:
+ print "Installing without encryption"
+
+# barf if user is not running this script as root
+if (os.getuid() != 0) and not sys.platform.lower().startswith("win"):
+ print "You must be root to do this installation"
+ sys.exit(1)
+
+
if sys.platform.lower().startswith("win"):
freesitemgrScript = "freesitemgr.py"
fcpgetScript = "fcpget.py"
fcpputScript = "fcpput.py"
fcpgenkeyScript = "fcpgenkey.py"
+ freediskScript = "freedisk.py"
else:
freesitemgrScript = "freesitemgr"
fcpgetScript = "fcpget"
fcpputScript = "fcpput"
fcpgenkeyScript = "fcpgenkey"
+ freediskScript = "freedisk"
from distutils.core import setup
setup(name="PyFCP",
@@ -8017,7 +8295,7 @@
packages = ['fcp'],
scripts = [freesitemgrScript, fcpgetScript, fcpputScript,
- fcpgenkeyScript,
+ fcpgenkeyScript, freediskScript,
],
@@ -8116,8 +8394,9 @@
from node import SILENT, FATAL, CRITICAL, ERROR, INFO, DETAIL, DEBUG
+import freenetfs
-__all__ = ['node', 'sitemgr', 'xmlrpc',
+__all__ = ['node', 'sitemgr', 'xmlrpc', 'freenetfs',
'FCPNode', 'JobTicket',
'ConnectionRefused', 'FCPException', 'FCPPutFailed',
'FCPProtocolError',
@@ -8514,6 +8793,7 @@
verbose = False
fcpHost = fcp.node.defaultFCPHost
fcpPort = fcp.node.defaultFCPPort
+ Global = False
opts = {
"Verbosity" : 0,
@@ -8523,8 +8803,8 @@
try:
cmdopts, args = getopt.getopt(
sys.argv[1:],
- "?hvH:P:",
- ["help", "verbose", "fcpHost=", "fcpPort=",
+ "?hvH:P:g",
+ ["help", "verbose", "fcpHost=", "fcpPort=", "global",
]
)
except getopt.GetoptError:
@@ -8553,6 +8833,9 @@
except:
usage("Invalid fcpPort argument %s" % repr(a))
+ if o in ("-g", "--global"):
+ opts['Global'] = "true"
+
# process args
nargs = len(args)
if nargs < 1 or nargs > 2:
@@ -8570,7 +8853,10 @@
# try to create the node
try:
- node = fcp.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity,
+ node = fcp.FCPNode(host=fcpHost,
+ port=fcpPort,
+ verbosity=verbosity,
+ Global=Global,
logfile=sys.stderr)
except:
if verbose:
@@ -8583,9 +8869,12 @@
except:
if verbose:
traceback.print_exc(file=sys.stderr)
+ node.shutdown()
sys.stderr.write("%s: Failed to retrieve key %s\n" % (progname,
repr(uri)))
sys.exit(1)
+ node.shutdown()
+
# try to dispose of the data
if outfile:
# figure out an extension, if none given
@@ -8664,7 +8953,12 @@
print " Connect to FCP service at host <hostname>"
print " -P, --fcpPort=<portnum>"
print " Connect to FCP service at port <portnum>"
-
+ print " -g, --global"
+ print " Do it on the FCP global queue"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
</t>
@@ -8737,6 +9031,12 @@
print " an attempt will be made to guess it from the filename. If no"
print " filename is given, or if this attempt fails, the mimetype"
print " 'text/plain' will be used as a fallback"
+ print " -g, --global"
+ print " Do it on the FCP global queue"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
@@ -8760,8 +9060,8 @@
try:
cmdopts, args = getopt.getopt(
sys.argv[1:],
- "?hvH:P:m:",
- ["help", "verbose", "fcpHost=", "fcpPort=", "mimetype=",
+ "?hvH:P:m:g",
+ ["help", "verbose", "fcpHost=", "fcpPort=", "mimetype=", "global",
]
)
except getopt.GetoptError:
@@ -8793,6 +9093,9 @@
if o in ("-m", "--mimetype"):
mimetype = a
+ if o in ("-g", "--global"):
+ opts['Global'] = "true"
+
# process args
nargs = len(args)
if nargs < 1 or nargs > 2:
@@ -8813,9 +9116,14 @@
base, ext = os.path.splitext(infile)
if ext:
mimetype = mimetypes.guess_type(ext)[0]
- if not mimetype:
- mimetype = "text/plain"
+ if mimetype:
+ # mimetype explicitly specified, or implied with input file,
+ # stick it in.
+ # otherwise, let FCPNode.put try to imply it from a uri's
+ # 'file extension' suffix
+ opts['mimetype'] = mimetype
+
# try to create the node
try:
node = fcp.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity,
@@ -8832,14 +9140,18 @@
try:
data = file(infile, "rb").read()
except:
+ node.shutdown()
usage("Failed to read input from file %s" % repr(infile))
# try to insert the key
try:
+ print "opts=%s" % str(opts)
uri = node.put(uri, data=data, **opts)
+ node.shutdown()
except:
if verbose:
traceback.print_exc(file=sys.stderr)
+ node.shutdown()
sys.stderr.write("%s: Failed to insert key %s\n" % (progname,
repr(uri)))
sys.exit(1)
@@ -8903,61 +9215,75 @@
except:
pass
-from _fuse import main, FuseGetContext, FuseInvalidate
-from string import join
+import _fuse
import sys
from errno import *
import fcp
+from fcp.xmlobject import XMLFile
+from fcp.node import guessMimetype, base64encode, base64decode
+
</t>
-<t tx="aum.20060521163823.2">class FreenetFS(Fuse):
+<t tx="aum.20060521163823.2">class FreenetFS:
@others
</t>
-<t tx="aum.20060521163823.3">def __init__(self, *args, **kw):
+<t tx="aum.20060521163823.3">def __init__(self, mountpoint, *args, **kw):
+ """
+ Create a freenetfs
+
+ Arguments:
+ - mountpoint - the dir in the filesystem at which to mount the fs
+ - other args get passed to fuse
+
+ Keywords:
+ - multithreaded - whether to run the fs multithreaded, default True
+ - fcpHost - hostname of FCP service
+ - fcpPort - port number of FCP service
+ - verbosity - defaults to fcp.DETAIL
+ - config - location of config file
+ - debug - whether to run in debug mode, default False
+ """
- Fuse.__init__(self, *args, **kw)
+ #self.log("init: args=%s kw=%s" % (args, kw))
- if 0:
- self.log("xmp.py:Xmp:mountpoint: %s" % repr(self.mountpoint))
- self.log("xmp.py:Xmp:unnamed mount options: %s" % self.optlist)
- self.log("xmp.py:Xmp:named mount options: %s" % self.optdict)
+ for k in ['multithreaded',
+ 'fcpHost',
+ 'fcpPort',
+ 'verbosity',
+ 'debug',
+ ]:
+ if kw.has_key(k):
+ v = kw.pop(k)
+ try:
+ v = int(v)
+ except:
+ pass
+
+ setattr(self, k, v)
- opts = self.optdict
+ self.optlist = list(args)
+ self.optdict = dict(kw)
- host = opts.get('host', fcpHost)
- port = opts.get('port', fcpPort)
- verbosity = int(opts.get('verbosity', defaultVerbosity))
+ self.mountpoint = mountpoint
+
+ #if not self.config:
+ # raise Exception("Missing 'config=filename.conf' argument")
- self.configfile = opts.get('config', None)
- if not self.configfile:
- raise Exception("Missing 'config=filename.conf' argument")
-
- self.loadConfig()
-
+ #self.loadConfig()
self.setupFiles()
+ self.setupFreedisks()
- self.fcpHost = host
- self.fcpPort = port
- self.fcpVerbosity = verbosity
-
- self.privKeyQueue = []
- self.privKeyLock = Lock()
- self.privKeypairQueue = []
- self.privKeypairLock = Lock()
-
- try:
- self.node = None
- self.connectToNode()
- except:
- #raise
- pass
-
# do stuff to set up your filesystem here, if you want
#thread.start_new_thread(self.mythread, ())
+ if 0:
+ self.log("xmp.py:Xmp:mountpoint: %s" % repr(self.mountpoint))
+ self.log("xmp.py:Xmp:unnamed mount options: %s" % self.optlist)
+ self.log("xmp.py:Xmp:named mount options: %s" % self.optdict)
+
</t>
<t tx="aum.20060521163823.4">def mythread(self):
@@ -8971,27 +9297,34 @@
# print "mythread: ticking"
</t>
-<t tx="aum.20060521163823.5">flags = 1
+<t tx="aum.20060521163823.5">_attrs = ['getattr', 'readlink', 'getdir',
'mknod', 'mkdir',
+ 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
+ 'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
+ 'statfs', 'fsync']
+multithreaded = 0
+flags = 1
+debug = False
+fcpHost = fcpHost
+fcpPort = fcpPort
+verbosity = defaultVerbosity
+allow_other = False
+kernel_cache = False
+config = os.path.join(os.path.expanduser("~"), ".freediskrc")
+
# Files and directories already present in the filesytem.
# Note - directories must end with "/"
initialFiles = [
"/",
-# "/cmd/",
-# "/cmd/genkey",
-# "/cmd/genkeypair",
"/get/",
"/put/",
- #"/cmd/invertprivatekey/",
"/keys/",
-# "/private/",
"/usr/",
+ "/cmds/",
]
chrFiles = [
- "/cmd/genkey",
- "/cmd/genkeypair",
]
</t>
@@ -8999,13 +9332,18 @@
rec = self.files.get(path, None)
if not rec:
+ # each of these code segments should assign a record to 'rec',
+ # or raise an IOError
+
# retrieving a key?
if path.startswith("/keys/"):
<<generate keypair>>
elif path.startswith("/get/"):
<<retrieve/cache key>>
+ elif path.startswith("/cmds/"):
+ <<base64 command>>
else:
- <<try host fs>>
+ raise IOError(errno.ENOENT, path)
self.log("getattr: path=%s" % path)
self.log(" mode=0%o" % rec.mode)
@@ -9053,6 +9391,8 @@
</t>
<t tx="aum.20060521163823.9">def unlink(self, path):
+ self.log("unlink: path=%s" % path)
+
# remove existing file?
if path.startswith("/get/") \
or path.startswith("/put/") \
@@ -9063,21 +9403,103 @@
self.delFromCache(rec)
return 0
+ if path.startswith("/usr"):
+ # remove a file within a freedisk
+
+ # barf if nonexistent
+ rec = self.files.get(path, None)
+ if not rec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf if removing dir
+ if rec.isdir:
+ raise IOError(errno.EISDIR, path)
+
+ # barf if trying to remove a . control file
+ bits = path.split("/")[2:]
+ diskPath = "/".join(path.split("/")[:3])
+ if len(bits) == 2 and bits[1] in freediskSpecialFiles:
+ raise IOError(errno.EACCES, path)
+
+ # barf if not on an existing freedisk
+ diskRec = self.files.get(diskPath, None)
+ if not diskRec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf if freedisk not writeable
+ if not diskRec.canwrite:
+ raise IOError(errno.EACCES, path)
+
+ # ok to delete
+ self.delFromCache(rec)
+
+ ret = 0
+ else:
+ raise IOError(errno.ENOENT, path)
+
# fallback on host fs
- ret = os.unlink(path)
- self.log("unlink: path=%s\n => %s" % (path, ret))
+ self.log("unlink: => %s" % ret)
return ret
</t>
<t tx="aum.20060521163823.10">def rmdir(self, path):
- ret = os.rmdir(path)
- self.log("rmdir: path=%s\n => %s" % (path, ret))
+ self.log("rmdir: path=%s" % path)
+
+ rec = self.files.get(path, None)
+
+ # barf if no such directory
+ if not rec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf if not a directory
+ if not rec.isdir:
+ raise IOError(errno.ENOTDIR, path)
+
+ # barf if not within freedisk mounts
+ if not path.startswith("/usr/"):
+ raise IOError(errno.EACCES, path)
+
+ # seek the freedisk record
+ bits = path.split("/")
+ diskPath = "/".join(bits[:3])
+ diskRec = self.files.get(diskPath, None)
+
+ # barf if nonexistent
+ if not diskRec:
+ raise IOError(errno.ENOENT, path)
+
+ # if a freedisk root, just delete
+ if path == diskPath:
+ # remove directory record
+ self.delFromCache(rec)
+
+ # and remove children
+ for k in self.files.keys():
+ if k.startswith(path+"/"):
+ del self.files[k]
+
+ return 0
+
+ # now, it's a subdir within a freedisk
+
+ # barf if non-empty
+ if rec.children:
+ raise IOError(errno.ENOTEMPTY, path)
+
+ # now, at last, can remove
+ self.delFromCache(rec)
+ ret = 0
+
+ self.log("rmdir: => %s" % ret)
+
return ret
</t>
<t tx="aum.20060521163823.11">def symlink(self, path, path1):
+ raise IOError(errno.EPERM, path)
+
ret = os.symlink(path, path1)
self.log("symlink: path=%s path1=%s\n => %s" % (path, path1, ret))
return ret
@@ -9092,6 +9514,8 @@
</t>
<t tx="aum.20060521163823.13">def link(self, path, path1):
+ raise IOError(errno.EPERM, path)
+
ret = os.link(path, path1)
self.log("link: path=%s path1=%s\n => %s" % (path, path1, ret))
return ret
@@ -9113,16 +9537,47 @@
</t>
<t tx="aum.20060521163823.16">def truncate(self, path, size):
- f = open(path, "w+")
- ret = f.truncate(size)
- self.log("truncate: path=%s size=%s\n => %s" % (path, size, ret))
+ self.log("truncate: path=%s size=%s" % (path, size))
+
+ if not path.startswith("/usr/"):
+ raise IOError(errno.EPERM, path)
+
+ parentPath, filename = os.path.split(path)
+
+ if os.path.split(parentPath)[0] != "/usr":
+ raise IOError(errno.EPERM, path)
+
+ rec = self.files.get(path, None)
+ if not rec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf at readonly files
+ if filename == '.status':
+ raise IOError(errno.EPERM, path)
+
+ rec.data = ""
+
+ ret = 0
+
+ self.log("truncate: => %s" % ret)
+
return ret
</t>
<t tx="aum.20060521163823.17">def mknod(self, path, mode, dev):
""" Python has no os.mknod, so we can only do some things """
+
+ if path == "/":
+ #return -EINVAL
+ raise IOError(errno.EEXIST, path)
+
+ parentPath = os.path.split(path)[0]
+ if parentPath in ['/', '/usr']:
+ #return -EINVAL
+ raise IOError(errno.EPERM, path)
+
# start key write, if needed
- if path.startswith("/put/"):
+ if parentPath == "/put":
# see if an existing file
if self.files.has_key(path):
@@ -9133,14 +9588,32 @@
perm=0644)
ret = 0
- else:
+ elif path.startswith("/usr/"):
+ # creating a file in a user dir
+
+ # barf if no write permission in dir
+ diskPath = "/".join(path.split("/")[:3])
+ diskRec = self.files.get(diskPath, None)
+ #if not diskRec:
+ # raise IOError(errno.ENOENT, path)
+ if diskRec and not diskRec.canwrite:
+ self.log("mknod: diskPath=%s" % diskPath)
+ raise IOError(errno.EPERM, path)
+
+ # create the record
+ rec = self.addToCache(path=path, isreg=True, perm=0644,
+ iswriting=True)
+ ret = 0
+
# fall back on host os
- if S_ISREG(mode):
- file(path, "w").close()
- ret = 0
- else:
- ret = -EINVAL
+ #if S_ISREG(mode):
+ # file(path, "w").close()
+ # ret = 0
+ else:
+ #ret = -EINVAL
+ raise IOError(errno.EPERM, path)
+
self.log("mknod: path=%s mode=0%o dev=%s\n => %s" % (
path, mode, dev, ret))
@@ -9149,10 +9622,52 @@
</t>
<t tx="aum.20060521163823.18">def mkdir(self, path, mode):
- ret = os.mkdir(path, mode)
- self.log("mkdir: path=%s mode=%s\n => %s" % (path, mode, ret))
- return ret
+ self.log("mkdir: path=%s mode=%s" % (path, mode))
+ # barf if directory exists
+ if self.files.has_key(path):
+ raise IOError(errno.EEXIST, path)
+
+ # barf if happening outside /usr/
+ if not path.startswith("/usr/"):
+ raise IOError(errno.EACCES, path)
+
+ parentPath = os.path.split(path)[0]
+
+ if parentPath == '/usr':
+ # creating a new freedisk
+
+ # create the directory record
+ rec = self.addToCache(path=path, isdir=True, perm=0555)
+
+ # create the pseudo-files within it
+ for name in freediskSpecialFiles:
+ subpath = os.path.join(path, name)
+ rec = self.addToCache(path=subpath, isreg=True, perm=0644)
+ if name == '.status':
+ rec.data = "idle"
+
+ # done here
+ return 0
+
+ elif path.startswith("/usr/"):
+ # creating a dir within a freedisk
+
+ # barf if no write permission in dir
+ diskPath = "/".join(path.split("/")[:3])
+ diskRec = self.files.get(diskPath, None)
+ #if not diskRec:
+ # self.log("mkdir: diskPath=%s" % diskPath)
+ # raise IOError(errno.ENOENT, path)
+ if diskRec and not diskRec.canwrite:
+ self.log("mkdir: diskPath=%s" % diskPath)
+ raise IOError(errno.EPERM, path)
+
+ # ok to create
+ self.addToCache(path=path, isdir=True, perm=0755)
+
+ return 0
+
</t>
<t tx="aum.20060521163823.19">def utime(self, path, times):
@@ -9176,8 +9691,13 @@
else:
# fall back to host fs
- os.close(os.open(path, flags))
+ raise IOError(errno.ENOENT, path)
+ for flag in [os.O_WRONLY, os.O_RDWR, os.O_APPEND]:
+ if flags & flag:
+ self.log("open: setting iswriting for %s" % path)
+ rec.iswriting = True
+
self.log("open: open of %s succeeded" % path)
# seems ok
@@ -9219,6 +9739,7 @@
# write to existing 'file'
rec.seek(off)
rec.write(buf)
+ rec.hasdata = True
else:
f = open(path, "r+")
f.seek(off)
@@ -9237,76 +9758,36 @@
if not rec:
return
- # if writing, save the thing
- if rec.iswriting:
- # what uri?
- rec.iswriting = False
- uri = os.path.split(path)[1]
+ filename = os.path.split(path)[1]
- # frigs to allow fancy CHK@ inserts
- if uri.startswith("CHK@"):
- putUri = "CHK@"
+ # ditch any encoded command files
+ if path.startswith("/cmds/"):
+ print "got file %s" % path
+ rec = self.files.get(path, None)
+ if rec:
+ self.delFromCache(rec)
else:
- putUri = uri
- ext = os.path.splitext(uri)[1]
+ print "eh? not in cache"
- try:
- self.log("release: inserting %s" % uri)
+ # if writing, save the thing
+ elif rec.iswriting:
+
+ self.log("release: %s: iswriting=True" % path)
- mimetype = fcp.node.guessMimetype(path)
- data = rec.data
+ # what uri?
+ rec.iswriting = False
- # empty the pseudo-file till a result is through
- rec.data = 'inserting'
+ print "Release: path=%s" % path
- self.connectToNode()
+ if path.startswith("/put/"):
+ <<insert to freenet>>
- #print "FIXME: data=%s" % repr(data)
+ elif path.startswith("/usr/"):
+ <<write to freedisk>>
- if _no_node:
- print "FIXME: not inserting"
- getUri = "NO_URI"
- else:
- # perform the insert
- getUri = self.node.put(
- putUri,
- data=data,
- mimetype=mimetype)
- # strip 'freenet:' prefix
- if getUri.startswith("freenet:"):
- getUri = getUri[8:]
-
- # restore file extension
- if getUri.startswith("CHK@"):
- getUri += ext
-
- # now cache the read-back
- self.addToCache(
- path="/get/"+getUri,
- data=data,
- perm=0444,
- isreg=True,
- )
-
- # and adjust the written file to reveal read uri
- rec.data = getUri
-
- self.log("release: inserted %s as %s ok" % (
- uri, mimetype))
-
- except:
- traceback.print_exc()
- rec.data = 'failed'
- self.log("release: insert of %s failed" % uri)
- raise IOError(errno.EIO, "Failed to insert")
-
- self.log("release: done with insertion")
- return 0
-
self.log("release: path=%s flags=%s" % (path, flags))
return 0
-
</t>
<t tx="aum.20060521163823.24">def statfs(self):
"""
@@ -9339,10 +9820,7 @@
</t>
<t tx="aum.20060521163823.26">if __name__ == '__main__':
- server = FreenetFS()
- server.multithreaded = 1;
- server.main()
-
+ main()
</t>
<t tx="aum.20060521175052">class ErrnoWrapper:
@@ -9353,106 +9831,59 @@
try:
return apply(self.func, args, kw)
except (IOError, OSError), detail:
- traceback.print_exc()
+ if showAllExceptions:
+ traceback.print_exc()
# Sometimes this is an int, sometimes an instance...
if hasattr(detail, "errno"): detail = detail.errno
return -detail
</t>
-<t tx="aum.20060521175052.1">class Fuse:
-
- @others
-</t>
-<t tx="aum.20060521175052.2">_attrs = ['getattr', 'readlink', 'getdir',
'mknod', 'mkdir',
- 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
- 'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
- 'statfs', 'fsync']
-
-flags = 0
-multithreaded = 0
-
-</t>
-<t tx="aum.20060521175052.3">def __init__(self, *args, **kw):
-
- # default attributes
- if args == ():
- # there is a self.optlist.append() later on, make sure it won't
- # bomb out.
- self.optlist = []
- else:
- self.optlist = args
- self.optdict = kw
-
- if len(self.optlist) == 1:
- self.mountpoint = self.optlist[0]
- else:
- self.mountpoint = None
-
- # grab command-line arguments, if any.
- # Those will override whatever parameters
- # were passed to __init__ directly.
- argv = sys.argv
- argc = len(argv)
-
- #self.log("argv=%s" % argv)
-
- ## physical thing to mount
- #self.configfile = argv[1]
-
- if argc > 2:
- # we've been given the mountpoint
- self.mountpoint = argv[2]
- if argc > 3:
- # we've received mount args
- optstr = argv[4]
- opts = optstr.split(",")
- for o in opts:
- try:
- k, v = o.split("=", 1)
- self.optdict[k] = v
- except:
- self.optlist.append(o)
-
-</t>
<t tx="aum.20060521175052.4">def GetContext(self):
- return FuseGetContext(self)
+ print "GetContext: called"
+ return _fuse.FuseGetContext(self)
</t>
<t tx="aum.20060521175052.5">def Invalidate(self, path):
- return FuseInvalidate(self, path)
+ print "Invalidate: called"
+ return _fuse.FuseInvalidate(self, path)
</t>
-<t tx="aum.20060521175052.6">def main(self):
+<t tx="aum.20060521175052.6">def run(self):
- d = {'mountpoint': self.mountpoint}
- d['multithreaded'] = self.multithreaded
- if hasattr( self, 'debug'):
- d['lopts'] = 'debug';
+ try:
+ self.node = None
+ self.connectToNode()
+ except:
+ #raise
+ pass
- #opts = self.optdict
- #for k in ['wsize', 'rsize']:
- # if opts.has_key(k):
- # d[k] = int(opts[k])
+ d = {'mountpoint': self.mountpoint,
+ 'multithreaded': self.multithreaded,
+ }
+ if self.debug:
+ d['lopts'] = 'debug'
+
k=[]
- if hasattr(self,'allow_other'):
- k.append('allow_other')
+ for opt in ['allow_other', 'kernel_cache']:
+ if getattr(self, opt):
+ k.append(opt)
+ if k:
+ d['kopts'] = ",".join(k)
- if hasattr(self,'kernel_cache'):
- k.append('kernel_cache')
-
- if len(k):
- d['kopts'] = join(k,',')
-
for a in self._attrs:
if hasattr(self,a):
d[a] = ErrnoWrapper(getattr(self, a))
- #apply(main, (), d)
- main(**d)
+ _fuse.main(**d)
+
</t>
-<t tx="aum.20060521175433">fcpHost = fcp.node.defaultFCPHost
+<t tx="aum.20060521175433">argv = sys.argv
+argc = len(argv)
+progname = argv[0]
+
+fcpHost = fcp.node.defaultFCPHost
fcpPort = fcp.node.defaultFCPPort
defaultVerbosity = fcp.DETAIL
@@ -9468,6 +9899,13 @@
# set this to disable hits to node, for debugging
_no_node = 0
+# special filenames in freedisk toplevel dirs
+freediskSpecialFiles = [
+ '.privatekey', '.publickey', '.cmd', '.status', ".passwd",
+ ]
+
+showAllExceptions = False
+
</t>
<t tx="aum.20060521180804">def invertprivate(self, privatekey):
"""
@@ -9518,8 +9956,8 @@
"""
print help options, then exit
"""
- print "%s: a simple command-line freenet keypair"
- print "generation command" % progname
+ print "%s: a simple command-line freenet keypair" % progname
+ print "generation command"
print
print "Generates a simple SSK keypair, and prints"
print "public key, then private key, each on its own line"
@@ -9535,6 +9973,10 @@
print " Connect to FCP service at host <hostname>"
print " -P, --fcpPort=<portnum>"
print " Connect to FCP service at port <portnum>"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
@@ -9600,6 +10042,8 @@
# grab the keypair
pub, priv = node.genkey()
+ node.shutdown()
+
# successful, return the uri
print pub
print priv
@@ -9644,7 +10088,7 @@
return self.privkey + self.hashpath(path) + "/0"
</t>
-<t tx="aum.20060521191057">def loadConfig(self):
+<t tx="aum.20060521191057">def _loadConfig(self):
"""
The 'physical device' argument to mount should be the pathname
of a configuration file, with 'name=val' lines, including the
@@ -9656,7 +10100,7 @@
opts = {}
# build a dict of all the 'name=value' pairs in config file
- for line in [l.strip() for l in file(self.configfile).readlines()]:
+ for line in [l.strip() for l in file(self.config).readlines()]:
if line == '' or line.startswith("#"):
continue
try:
@@ -9686,15 +10130,19 @@
try:
self.cachedir = opts['cachedir']
if not os.path.isdir(self.cachedir):
- raise hell
+ self.log("Creating cache directory %s" % self.cachedir)
+ os.makedirs(self.cachedir)
+ #raise hell
except:
raise Exception("config file %s: missing or invalid cache directory" \
% self.configfile)
</t>
<t tx="aum.20060521232922">def log(self, msg):
- if not quiet:
- print "freedisk:"+msg
+ #if not quiet:
+ # print "freedisk:"+msg
+ file("/tmp/freedisk.log", "a").write(msg+"\n")
+
</t>
<t tx="aum.20060522200735">@nocolor
--------------------------------------------------
@@ -9952,7 +10400,7 @@
return inode
</t>
-<t tx="aum.20060525194744">def getDirStat(self, path):
+<t tx="aum.20060525194744">def __getDirStat(self, path):
"""
returns a stat tuple for given path
"""
@@ -9971,9 +10419,15 @@
@others
</t>
-<t tx="aum.20060525225133.1">def __init__(self, statrec=None, **kw):
+<t tx="aum.20060525225133.1">def __init__(self, fs, statrec=None, **kw):
"""
"""
+ # copy keywords cos we'll be popping them
+ kw = dict(kw)
+
+ # save fs ref
+ self.fs = fs
+
# got a statrec arg?
if statrec:
# yes, extract main items
@@ -9996,51 +10450,56 @@
statrec = list(statrec)
# build mode mask
- mode = kw.get('mode', 0)
- if kw.get('isdir', False):
+ mode = kw.pop('mode', 0)
+ if kw.pop('isdir', False):
mode |= stat.S_IFDIR
- if kw.get('ischr', False):
+ if kw.pop('ischr', False):
mode |= stat.S_IFCHR
- if kw.get('isblk', False):
+ if kw.pop('isblk', False):
mode |= stat.S_IFBLK
- if kw.get('isreg', False):
+ if kw.pop('isreg', False):
mode |= stat.S_IFREG
- if kw.get('isfifo', False):
+ if kw.pop('isfifo', False):
mode |= stat.S_IFIFO
- if kw.get('islink', False):
+ if kw.pop('islink', False):
mode |= stat.S_IFLNK
- if kw.get('issock', False):
+ if kw.pop('issock', False):
mode |= stat.S_IFSOCK
# handle non-file-related keywords
- perm = kw.get('perm', 0)
+ perm = kw.pop('perm', 0)
mode |= perm
- path = kw['path']
+ # set path
+ path = kw.pop('path')
self.path = path
- self.stream = StringIO()
+ # set up data stream
+ if kw.has_key("data"):
+ self.stream = StringIO(kw.pop('data'))
+ self.hasdata = True
+ else:
+ self.stream = StringIO()
+
+ # find parent, if any
+ if path == '/':
+ self.parent = None
+ else:
+ parentPath = os.path.split(path)[0]
+ parentRec = fs.files[parentPath]
+ self.parent = parentRec
- data = kw.get('data', '')
- self.stream = StringIO(data)
-
- for key in ['iswriting']:
- if kw.has_key(key):
- setattr(self, key, kw[key])
-
# child files/dirs
self.children = []
- #print "FileRecord.__init__: path=%s" % path
-
# get inode number
inode = pathToInode(path)
#size = kw.get('size', 0)
now = timeNow()
- atime = kw.get('atime', now)
- mtime = kw.get('mtime', now)
- ctime = kw.get('ctime', now)
+ atime = kw.pop('atime', now)
+ mtime = kw.pop('mtime', now)
+ ctime = kw.pop('ctime', now)
#print "statrec[stat.ST_MODE]=%s" % statrec[stat.ST_MODE]
#print "mode=%s" % mode
@@ -10057,11 +10516,13 @@
statrec[stat.ST_ATIME] = atime
statrec[stat.ST_MTIME] = atime
statrec[stat.ST_CTIME] = atime
-
+
+ # throw remaining keywords into instance's attribs
+ self.__dict__.update(kw)
+
+ # finally, parent constructor, now that we have a complete stat list
list.__init__(self, statrec)
- self.iswriting = kw.get('iswriting', False)
-
</t>
<t tx="aum.20060525225603">def __getattr__(self, attr):
"""
@@ -10081,7 +10542,7 @@
if attr == 'isblk':
return stat.S_ISBLK(self.mode)
- if attr == 'isreg':
+ if attr in ['isreg', 'isfile']:
return stat.S_ISREG(self.mode)
if attr == 'isfifo':
@@ -10152,7 +10613,7 @@
self[stat.ST_MODE] |= stat.S_IFBLK
else:
self[stat.ST_MODE] &= ~stat.S_IFBLK
- elif attr == 'isreg':
+ elif attr in ['isreg', 'isfile']:
if val:
self[stat.ST_MODE] |= stat.S_IFREG
else:
@@ -10268,9 +10729,17 @@
"""
if self.node:
return
- self.node = fcp.FCPNode(host=self.fcpHost,
- port=self.fcpPort,
- verbosity=self.fcpVerbosity)
+
+ self.verbosity = fcp.DETAIL
+
+ try:
+ self.node = fcp.FCPNode(host=self.fcpHost,
+ port=self.fcpPort,
+ verbosity=self.verbosity)
+ except:
+ raise IOError(errno.EIO, "Failed to reach FCP service at %s:%s" % (
+ self.fcpHost, self.fcpPort))
+
#self.log("pubkey=%s" % self.pubkey)
#self.log("privkey=%s" % self.privkey)
#self.log("cachedir=%s" % self.cachedir)
@@ -10322,12 +10791,12 @@
try:
self.connectToNode()
mimetype, data = self.node.get(uri)
- rec = FileRecord(path=path,
- isreg=True,
- perm=0644,
- data=data,
- )
- self.addToCache(rec)
+ rec = self.addToCache(
+ path=path,
+ isreg=True,
+ perm=0644,
+ data=data,
+ )
except:
traceback.print_exc()
@@ -10335,13 +10804,6 @@
raise IOError(errno.ENOENT, path)
</t>
-<t tx="aum.20060526163608.1"># try the host filesystem
-print "getattr: no rec for %s, hitting main fs" % path
-rec = FileRecord(os.lstat(path), path=path)
-
-print rec
-
-</t>
<t tx="aum.20060527114053">def delChild(self, rec):
"""
Tries to remove a child entry
@@ -10360,7 +10822,7 @@
adds it to parent dir
"""
if rec == None:
- rec = FileRecord(**kw)
+ rec = FileRecord(self, **kw)
path = rec.path
@@ -10414,9 +10876,2010 @@
rec = self.addToCache(
path=path,
isreg=True,
- data=pubkey+"\n"+privkey,
+ data=pubkey+"\n"+privkey+"\n",
perm=0444,
)
+
</t>
+<t tx="aum.20060528175118">"""
+Allows XML files to be operated on like Python objects.
+
+Features:
+ - load XML source from file pathnames, readable file objects or raw strings
+ - add, get and set tag attributes like with python attributes
+ - iterate over nodes
+ - save the modified XMLFile or XMLObject to file
+
+Example XML file::
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <rapsheets>
+ <person name="John Smith" age="42">
+ <!-- John Smith has an appeal in process against his last
conviction -->
+ <crime name="Armed robbery" date="March 11, 1994"/>
+ <crime name="Aggravated burglary" date="June 9, 2001"/>
+ </person>
+ <person name="Mary Jones" age="33">
+ <crime name="Prostitution" date="January 8, 1997"/>
+ <crime name="Selling heroin" date="September 4, 2002"/>
+ <crime name="Manslaughter" date="December 21, 2004"/>
+ </person>
+ </rapsheets>
+
+Example usage::
+
+ >>> from xmlobject import XMLFile
+
+ >>> x = XMLFile(path="sample.xml)
+
+ >>> print x
+ <xmlobj.XMLFile instance at 0xb7ccc52c>
+
+ >>> print x.root
+ <XMLNode: rapsheets>
+
+ >>> print x.root._children
+ [<XMLNode: text>, <XMLNode: person>, <XMLNode: text>,
+ <XMLNode: person>, <XMLNode: text>]
+
+ >>> print x.root.person
+ [<XMLNode: person>, <XMLNode: person>]
+
+ >>> print x.root.person[0].name
+ John Smith
+
+ >>> john = x.root.person[0]
+
+ >>> john.height = 184
+
+ >>> c = john._addNode("crime")
+
+ >>> c.name = "Grand Theft Auto"
+
+ >>> c.date = "4 May, 2005"
+
+ >>> print x.toxml()
+ <?xml version="1.0" ?>
+ <rapsheets>
+ <person age="42" height="184" name="John Smith">
+ <!-- John Smith has an appeal in process against his last
conviction -->
+ <crime date="March 11, 1994" name="Armed robbery"/>
+ <crime date="June 9, 2001" name="Aggravated burglary"/>
+ <crime date="4 May, 2005" name="Grand Theft Auto"/></person>
+ <person age="33" name="Mary Jones">
+ <crime date="January 8, 1997" name="Prostitution"/>
+ <crime date="September 4, 2002" name="Selling heroin"/>
+ <crime date="December 21, 2004" name="Manslaughter"/>
+ </person>
+ </rapsheets>
+
+ >>>
+
+"""
+
+ at others</t>
+<t tx="aum.20060528175118.1">import sys, os
+import xml.dom
+import xml.dom.minidom
+from xml.dom.minidom import parse, parseString, getDOMImplementation
+
+</t>
+<t tx="aum.20060528175118.2">impl = getDOMImplementation()
+
+</t>
+<t tx="aum.20060528175118.3">class MissingRootTag(Exception):
+ """root tag name was not given"""
+
+class InvalidXML(Exception):
+ """failed to parse XML input"""
+
+class CannotSave(Exception):
+ """unable to save"""
+
+class InvalidNode(Exception):
+ """not a valid minidom node"""
+
+</t>
+<t tx="aum.20060528175118.4">class XMLFile:
+ """
+ Allows an xml file to be viewed and operated on
+ as a python object.
+
+ (If you're viewing the epydoc-generated HTML documentation, click the
'show private'
+ link at the top right of this page to see all the methods)
+
+ Holds the root node in the .root attribute, also in an attribute
+ with the same name as this root node.
+ """
+ @others
+
+</t>
+<t tx="aum.20060528175118.5">def __init__(self, **kw):
+ """
+ Create an XMLFile
+
+ Keywords:
+ - path - a pathname from which the file can be read
+ - file - an open file object from which the raw xml
+ can be read
+ - raw - the raw xml itself
+ - root - name of root tag, if not reading content
+
+ Usage scenarios:
+ 1. Working with existing content - you must supply input in
+ one of the following ways:
+ - 'path' must be an existing file, or
+ - 'file' must be a readable file object, or
+ - 'raw' must contain raw xml as a string
+ 2. Creating whole new content - you must give the name
+ of the root tag in the 'root' keyword
+
+ Notes:
+ - Keyword precedence governing existing content is:
+ 1. path (if existing file)
+ 2. file
+ 3. raw
+ - If working with existing content:
+ - if the 'root' is given, then the content's toplevel tag
+ MUST match the value given for 'root'
+ - trying to _save will raise an exception unless 'path'
+ has been given
+ - if not working with existing content:
+ - 'root' must be given
+ - _save() will raise an exception unless 'path' has been given
+ """
+ path = kw.get("path", None)
+ fobj = kw.get("file", None)
+ raw = kw.get("raw", None)
+ root = kw.get("root", None)
+
+ if path:
+ self.path = path
+ try:
+ fobj = file(path)
+ except IOError:
+ pass
+ else:
+ self.path = None
+
+ if fobj:
+ raw = fobj.read()
+
+ if raw:
+ self.dom = xml.dom.minidom.parseString(raw)
+ else:
+ # could not source content, so create a blank slate
+ if not root:
+ # in which case, must give a root node name
+ raise MissingRootTag(
+ "No existing content, so must specify root")
+
+ # ok, create a blank dom
+ self.dom = impl.createDocument(None, root, None)
+
+ # get the root node, save it as attributes 'root' and name of node
+ rootnode = self.dom.documentElement
+
+ # now validate root tag
+ if root:
+ if rootnode.nodeName != root:
+ raise IncorrectRootTag("Gave root='%s', input has root='%s'" % (
+ root, rootnode.nodeName))
+
+ # need this for recursion in XMLNode
+ self._childrenByName = {}
+ self._children = []
+
+ # add all the child nodes
+ for child in self.dom.childNodes:
+ childnode = XMLNode(self, child)
+ #print "compare %s to %s" % (rootnode, child)
+ if child == rootnode:
+ #print "found root"
+ self.root = childnode
+ setattr(self, rootnode.nodeName, self.root)
+
+</t>
+<t tx="aum.20060528175118.6">def save(self, where=None, obj=None):
+ """
+ Saves the document.
+
+ If argument 'where' is given, saves to it, otherwise
+ tries to save to the original given 'path' (or barfs)
+
+ Value can be a string (taken to be a file path), or an open
+ file object.
+ """
+ obj = obj or self.dom
+
+ if not where:
+ if self.path:
+ where = self.path
+
+ if isinstance(where, str):
+ where = file(where, "w")
+
+ if not where:
+ raise CannotSave("No save destination, and no original path")
+
+ where.write(obj.toxml())
+ where.flush()
+
+</t>
+<t tx="aum.20060528175118.7">def saveAs(self, path):
+ """
+ save this time, and all subsequent times, to filename 'path'
+ """
+ self.path = path
+ self.save()
+
+</t>
+<t tx="aum.20060528175118.8">def toxml(self):
+ return self.dom.toxml()
+
+</t>
+<t tx="aum.20060528175118.9">def __len__(self):
+ """
+ returns number of child nodes
+ """
+ return len(self._children)
+
+</t>
+<t tx="aum.20060528175118.10">def __getitem__(self, idx):
+ if isinstance(idx, int):
+ return self._children[idx]
+ else:
+ return self._childrenByName[idx]
+
+</t>
+<t tx="aum.20060528175118.11">class XMLNode:
+ """
+ This is the workhorse for the xml object interface
+
+ (If you're viewing the epydoc-generated HTML documentation, click the
'show private'
+ link at the top right of this page to see all the methods)
+
+ """
+ @others
+</t>
+<t tx="aum.20060528175118.12">def __init__(self, parent, node):
+ """
+ You shouldn't need to instantiate this directly
+ """
+ self._parent = parent
+ if isinstance(parent, XMLFile):
+ self._root = parent
+ else:
+ self._root = parent._root
+ self._node = node
+ self._childrenByName = {}
+ self._children = []
+
+ # add ourself to parent's children registry
+ parent._children.append(self)
+
+ # the deal with named subtags is that we store the first instance
+ # as itself, and with second and subsequent instances, we make a list
+ parentDict = self._parent._childrenByName
+ nodeName = node.nodeName
+ if not parentDict.has_key(nodeName):
+ parentDict[nodeName] = parent.__dict__[nodeName] = self
+ else:
+ if isinstance(parentDict[nodeName], XMLNode):
+ # this is the second child node of a given tag name, so convert
+ # the instance to a list
+ parentDict[nodeName] = parent.__dict__[nodeName] =
[parentDict[nodeName]]
+ parentDict[nodeName].append(self)
+
+ # figure out our type
+ self._value = None
+ if isinstance(node, xml.dom.minidom.Text):
+ self._type = "text"
+ self._value = node.nodeValue
+ elif isinstance(node, xml.dom.minidom.Element):
+ self._type = "node"
+ self._name = nodeName
+ elif isinstance(node, xml.dom.minidom.Comment):
+ self._type = "comment"
+ self._value = node.nodeValue
+ else:
+ raise InvalidNode("node class %s" % node.__class__)
+
+ # and wrap all the child nodes
+ for child in node.childNodes:
+ XMLNode(self, child)
+
+</t>
+<t tx="aum.20060528175118.13">def _render(self):
+ """
+ Produces well-formed XML of this node's contents,
+ indented as required
+ """
+ return self._node.toxml()
+
+</t>
+<t tx="aum.20060528175118.14">def __repr__(self):
+ if self._type == "node":
+ return "<XMLNode: %s>" % self._node.nodeName
+ else:
+ return "<XMLNode: %s>" % self._type
+
+</t>
+<t tx="aum.20060528175118.15">def __getattr__(self, attr):
+ """
+ Fetches an attribute or child node of this tag
+
+ If it's an attribute, then returns the attribute value as a string.
+
+ If a child node, then:
+ - if there is only one child node of that name, return it
+ - if there is more than one child node of that name, return a list
+ of child nodes of that tag name
+
+ Supports some magic attributes:
+ - _text - the value of the first child node of type text
+ """
+ #print "%s: __getattr__: attr=%s" % (self, attr)
+
+ # magic attribute to return text
+ if attr == '_text':
+ tnode = self['#text']
+ if isinstance(tnode, list):
+ tnode = tnode[0]
+ return tnode._value
+
+ if self._type in ['text', 'comment']:
+ if attr == '_value':
+ return self._node.nodeValue
+ else:
+ raise AttributeError(attr)
+
+ if self._node.hasAttribute(attr):
+ return self._node.getAttribute(attr)
+ elif self._childrenByName.has_key(attr):
+ return self._childrenByName[attr]
+
+ #elif attr == 'value':
+ # magic attribute
+
+ else:
+ raise AttributeError(attr)
+
+
+</t>
+<t tx="aum.20060528175118.16">def __setattr__(self, attr, val):
+ """
+ Change the value of an attribute of this tag
+
+ The magic attribute '_text' can be used to set the first child
+ text node's value
+
+ For example::
+
+ Consider:
+
+ <somenode>
+ <child>foo</child>
+ </somenode>
+
+ >>> somenode
+ <XMLNODE: somenode>
+ >>> somenode.child
+ <XMLNODE: child>
+ >>> somenode.child._text
+ 'foo'
+ >>> somenode._toxml()
+ u'<somenode><child>foo</child></somenode>'
+ >>> somenode.child._text = 'bar'
+ >>> somenode.child._text
+ 'bar'
+ >>> somenode.child._toxml()
+ u'<somenode><child>bar/child></somenode>'
+
+ """
+ if attr.startswith("_"):
+
+ # magic attribute for setting _text
+ if attr == '_text':
+ tnode = self['#text']
+ if isinstance(tnode, list):
+ tnode = tnode[0]
+ tnode._node.nodeValue = val
+ tnode._value = val
+ return
+
+ self.__dict__[attr] = val
+ elif self._type in ['text', 'comment']:
+ self._node.nodeValue = val
+ else:
+ # discern between attribute and child node
+ if self._childrenByName.has_key(attr):
+ raise Exception("Attribute Exists")
+ self._node.setAttribute(attr, str(val))
+
+</t>
+<t tx="aum.20060528175118.17">def _keys(self):
+ """
+ Return a list of attribute names
+ """
+ return self._node.attributes.keys()
+
+def _values(self):
+ """
+ Returns a list of (attrname, attrval) tuples for this tag
+ """
+ return [self._node.getAttribute(k) for k in self._node.attributes.keys()]
+
+def _items(self):
+ """
+ returns a list of attribute values for this tag
+ """
+ return [(k, self._node.getAttribute(k)) for k in
self._node.attributes.keys()]
+
+def _has_key(self, k):
+ """
+ returns True if this tag has an attribute of the given name
+ """
+ return self._node.hasAttribute(k) or self._childrenByName.has_key(k)
+
+def _get(self, k, default=None):
+ """
+ returns the value of attribute k, or default if no such attribute
+ """
+ if self._has_key(k):
+ return getattr(self, k)
+ else:
+ return default
+</t>
+<t tx="aum.20060528175118.18">def __len__(self):
+ """
+ returns number of child nodes
+ """
+ return len(self._children)
+
+</t>
+<t tx="aum.20060528175118.19">def __getitem__(self, idx):
+ """
+ if given key is numeric, return the nth child, otherwise
+ try to return the child tag (or list of child tags) having
+ the key as the tag name
+ """
+ #print "__getitem__: idx=%s" % str(idx)
+
+ if isinstance(idx, slice) or isinstance(idx, int):
+ return self._children[idx]
+ elif isinstance(idx, str):
+ return self._childrenByName[idx]
+ else:
+ raise IndexError(idx)
+
+</t>
+<t tx="aum.20060528175118.20">def _addNode(self, child):
+ """
+ Tries to append a child node to the tree, and returns it
+
+ Value of 'child' must be one of:
+ - a string (in which case it is taken to be the name
+ of the new node's tag)
+ - a dom object, in which case it will be wrapped and added
+ - an XMLNode object, in which case it will be added without
+ wrapping
+ """
+
+ if isinstance(child, XMLNode):
+
+ # add it to our children registry
+ self._children.append(child)
+
+ parentDict = self._childrenByName
+ nodeName = child._node.nodeName
+
+ if not parentDict.has_key(nodeName):
+ parentDict[nodeName] = self.__dict__[nodeName] = child
+ else:
+ if isinstance(parentDict[nodeName], XMLNode):
+ # this is the second child node of a given tag name, so convert
+ # the instance to a list
+ parentDict[nodeName] \
+ = self.__dict__[nodeName] \
+ = [parentDict[nodeName]]
+
+ parentDict[nodeName].append(child)
+
+ # and stick it in the dom
+ self._node.appendChild(child._node)
+
+ return child
+
+ elif isinstance(child, str):
+ childNode = self._root.dom.createElement(child)
+ self._node.appendChild(childNode)
+
+ elif isinstance(child, xml.dom.minidom.Element):
+ childNode = child
+ child = childNode.nodeName
+ self._node.appendChild(childNode)
+
+
+ return XMLNode(self, childNode)
+
+</t>
+<t tx="aum.20060528175118.21">def _addText(self, value):
+ """
+ Tries to append a child text node, with the given text, to the tree,
+ and returns the created node object
+ """
+ childNode = self._root.dom.createTextNode(value)
+ self._node.appendChild(childNode)
+ return XMLNode(self, childNode)
+
+</t>
+<t tx="aum.20060528175118.22">def _addComment(self, comment):
+ """
+ Tries to append a child comment node (with the given text value)
+ to the tree, and returns the create node object
+ """
+ childNode = self._root.dom.createCommentNode(comment)
+ self._node.appendChild(childNode)
+ return XMLNode(self, childNode)
+
+</t>
+<t tx="aum.20060528175118.23">def _save(self, where=None):
+ """
+ Generates well-formed XML from just this node, and saves it
+ to a file.
+
+ Argument 'where' is either an open file object, or a pathname
+
+ If 'where' is not given, then saves the entire document tree.
+ """
+ if not where:
+ self._root.save()
+ else:
+ self._root.save(where, self._node)
+
+</t>
+<t tx="aum.20060528175118.24">def _toxml(self):
+ """
+ renders just this node out to raw xml code
+ """
+ return self._node.toxml()
+
+</t>
+<t tx="aum.20060528180449">@nocolor
+
+pyfcp notes
+
+</t>
+<t tx="aum.20060528180449.1">notes on freedisk
+
+ - freedisk, n. A shareable disk residing within freenet, that can
+ be mounted within a freenetfs
+
+structure of a freedisk
+
+ - a single xml file containing all file/directory entries within
+ the disk
+
+ - top-level pseudo-files:
+ - /_cmd - write commands
+ - /_status - write status
+
+usage procedure
+
+ 1. mount it
+ - mkdir /mnt/freenet/usr/fred
+
+ 2. either:
+ - mount a new disk, by writing an SSK private key, eg:
+ freenet:SSK at yadayada/
+ to /mnt/freenet/usr/fred/_privatekey
+ or
+ - mount an existing disk read/write, by writing an
+ SSK private key, eg:
+ SSK at blahprivateblah/
+ to /mnt/freenet/usr/fred/_privatekey
+
+ then writing to /mnt/freenet/usr/fred/_cmd the line:
+ import
+
+ or
+ - mount an existing disk readonly, by writing an
+ SSK public key, eg:
+ SSK at blahpublicblah/
+ to /mnt/freenet/usr/fred/_publickey
+
+ then writing to /mnt/freenet/usr/fred/_cmd the line:
+ import
+
+ 3. If the disk is mounted with write permission, then write to
+ _cmd the line:
+ export
+
+ 4. when finished with it, do:
+ - rmdir /mnt/freenet/usr/fred
+
+status file
+
+ reading from /mnt/freenet/usr/fred/_status will return one of:
+ - idle - no syncing is occurring
+ - importing - synchronising inbound
+ - exporting - synchronising outbound
+
+xml file format
+
+ <freedisk name="mydiskname" owner="fred">
+ <!-- plain files have path, uri and size attributes -->
+ <node path="/fred.txt" uri="CHK at blahblahblah" size="2232"/>
+
+ <!-- directory nodes have paths ending with a '/', and no uri or
size-->
+ <node path="/mary/"/>
+
+ </freedisk>
+
+export of freedisk inserts as SSK
+
+</t>
+<t tx="aum.20060528214253"># insert directly to freenet as a key
+
+uri = os.path.split(path)[1]
+
+# frigs to allow fancy CHK@ inserts
+if uri.startswith("CHK@"):
+ putUri = "CHK@"
+else:
+ putUri = uri
+
+ext = os.path.splitext(uri)[1]
+
+try:
+ self.log("release: inserting %s" % uri)
+
+ mimetype = fcp.node.guessMimetype(path)
+ data = rec.data
+
+ # empty the pseudo-file till a result is through
+ rec.data = 'inserting'
+
+ self.connectToNode()
+
+ #print "FIXME: data=%s" % repr(data)
+
+ if _no_node:
+ print "FIXME: not inserting"
+ getUri = "NO_URI"
+ else:
+ # perform the insert
+ getUri = self.node.put(
+ putUri,
+ data=data,
+ mimetype=mimetype)
+
+ # strip 'freenet:' prefix
+ if getUri.startswith("freenet:"):
+ getUri = getUri[8:]
+
+ # restore file extension
+ if getUri.startswith("CHK@"):
+ getUri += ext
+
+ # now cache the read-back
+ self.addToCache(
+ path="/get/"+getUri,
+ data=data,
+ perm=0444,
+ isreg=True,
+ )
+
+ # and adjust the written file to reveal read uri
+ rec.data = getUri
+
+ self.log("release: inserted %s as %s ok" % (
+ uri, mimetype))
+
+except:
+ traceback.print_exc()
+ rec.data = 'failed'
+ self.log("release: insert of %s failed" % uri)
+ raise IOError(errno.EIO, "Failed to insert")
+self.log("release: done with insertion")
+
+</t>
+<t tx="aum.20060528214707"># releasing a file being written into a freedisk
+
+bits = path.split("/")
+
+self.log("release: bits=%s" % str(bits))
+
+if bits[0] == '' and bits[1] == 'usr':
+ diskName = bits[2]
+ fileName = bits[3]
+
+ self.log("diskName=%s fileName=%s" % (diskName, fileName))
+
+ if fileName == '.privatekey':
+ # written a private key, make the directory writeable
+ parentPath = os.path.split(path)[0]
+ parentRec = self.files[parentPath]
+ parentRec.canwrite = True
+ self.log("release: got privkey, mark dir %s read/write" % parentRec)
+
+ elif fileName == '.cmd':
+ # wrote a command
+
+ self.log("got release of .cmd")
+
+ cmd = rec.data.strip()
+ rec.data = ""
+
+ self.log("release: cmd=%s" % cmd)
+
+ # execute according to command
+ if cmd == 'commit':
+ self.commitDisk(diskName)
+ elif cmd == 'update':
+ self.updateDisk(diskName)
+ elif cmd == 'merge':
+ self.mergeDisk(diskName)
+
+</t>
+<t tx="aum.20060528221744"># methods for freedisk operations
+
+ at others
+
+</t>
+<t tx="aum.20060528221744.1">def newDisk(self, name, uri=None):
+ """
+ Adds (mounts) a freedisk within freenetfs
+
+ Arguments:
+ - name - name of disk - will be mounted in as /usr/<name>
+ - uri - a private SSK key URI. If not given, one will be
+ randomly generated
+ """
+
+</t>
+<t tx="aum.20060528221758">def delDisk(self, name):
+ """
+ drops a freedisk mount
+
+ Arguments:
+ - name - the name of the disk
+ """
+
+</t>
+<t tx="aum.20060529123536">def main():
+
+ kw = {}
+ args = []
+
+ if argc != 5:
+ usage("Bad argument count")
+
+ mountpoint = argv[2]
+
+ for o in argv[4].split(","):
+ try:
+ k, v = o.split("=", 1)
+ kw[k] = v
+ except:
+ args.append(o)
+
+ #kw['multithreaded'] = True
+ kw['multithreaded'] = False
+
+ if os.fork() == 0:
+ server = FreenetFS(mountpoint, *args, **kw)
+ server.run()
+
+</t>
+<t tx="aum.20060529123536.1">"""
+freedisk is a command-line utility for creating,
+mounting and synchronising freenet freedisks
+
+Invoke with -h for help
+"""
+ at others
+
+</t>
+<t tx="aum.20060529163723">import sys, os
+import getopt
+import traceback
+import time
+import sha
+import getpass
+
+try:
+ import fcp
+ from fcp import node, freenetfs
+ from fcp.xmlobject import XMLFile, XMLNode
+except:
+ print "** PyFCP core module 'fcp' not installed."
+ print "** Please refer to the INSTALL file within the PyFCP source package"
+ sys.exit(1)
+
+try:
+ import SSLCrypto
+
+
+except:
+ SSLCrypto = None
+ print "** WARNING! SSLCrypto module not installed"
+ print "** Please refer to the INSTALL file within the PyFCP source package"
+
+</t>
+<t tx="aum.20060529163723.1"># args shorthand
+argv = sys.argv
+argc = len(argv)
+progname = argv[0]
+
+# default config file stuff
+homedir = os.path.expanduser("~")
+configFile = os.path.join(homedir, ".freediskrc")
+
+defaultMountpoint = os.path.join(homedir, "freedisk")
+
+</t>
+<t tx="aum.20060529163723.2">def main():
+ """
+ Front end
+ """
+ <<global vars>>
+
+ <<set defaults>>
+
+ <<process args>>
+
+ <<get config>>
+
+ <<validate args>>
+
+ <<execute command>>
+
+</t>
+<t tx="aum.20060529163723.4">if __name__ == '__main__':
+ main()
+
+</t>
+<t tx="aum.20060529164147">def usage(msg=None, ret=1):
+ """
+ Prints usage message then exits
+ """
+ if msg:
+ sys.stderr.write(msg+"\n")
+ sys.stderr.write("Usage: %s [options] [<command> [<args>]]\n"
% progname)
+ sys.stderr.write("Type '%s -h' for help\n" % progname)
+ sys.exit(ret)
+
+</t>
+<t tx="aum.20060529164147.1">def help():
+ """
+ Display help info then exit
+ """
+ print "%s: manage a freenetfs filesystem" % progname
+ print "Usage: %s [<options>] <command> [<arguments>]" %
progname
+ print "Options:"
+ print " -h, --help Display this help"
+ print " -c, --config= Specify config file, default ~/.freediskrc"
+ print "Commands:"
+ print " init Edit configuration interactively"
+ print " mount Mount the freenetfs"
+ print " unmount Unmount the freenetfs"
+ print " new <name> Create a new freedisk of name
<name>"
+ print " A new keypair will be generated."
+ print " add <name> <URI> Add an existing freedisk of
name <name>"
+ print " and public key URI <URI>"
+ print " del <name> Remove freedisk of name <name>"
+ print " update <name> Sync freedisk <name> from
freenet"
+ print " commit <name> Commit freedisk <name> into
freenet"
+ print
+ print "Environment variables:"
+ print " FREEDISK_CONFIG - set this in place of '-c' argument"
+
+ sys.exit(0)
+
+</t>
+<t tx="aum.20060529184826">def usage(msg, ret=1):
+
+ print "Usage: %s mountpoint -o args" % progname
+
+ sys.exit(ret)
+
+</t>
+<t tx="aum.20060529191729">@first #!/usr/bin/env python
+ at language shell
+import fcp
+fcp.freenetfs.main()
+
+</t>
+<t tx="aum.20060530142805.1"># create defaults
+
+opts = {
+ 'debug' : False,
+ 'multithreaded' : False,
+ 'configFile' : configFile,
+ 'verbosity' : fcp.ERROR,
+ 'Verbosity' : 1023,
+ }
+
+</t>
+<t tx="aum.20060530143459"># process args
+
+try:
+ cmdopts, args = getopt.getopt(
+ sys.argv[1:],
+ "?hvc:dm",
+ ["help", "verbose",
+ "multithreaded",
+ "config=", "debug",
+ ]
+ )
+except getopt.GetoptError:
+ # print help information and exit:
+ usage()
+ sys.exit(2)
+
+#print cmdopts
+for o, a in cmdopts:
+
+ if o in ("-?", "-h", "--help"):
+ help()
+
+ if o in ("-v", "--verbose"):
+ opts['verbosity'] = fcp.node.DETAIL
+ opts['Verbosity'] = 1023
+ verbose = True
+
+ if o in ("-c", "--config"):
+ opts['configFile'] = a
+
+ if o in ("-d", "--debug"):
+ opts['debug'] = True
+
+ if o in ("-m", "--multithreaded"):
+ opts['multithreaded'] = True
+
+</t>
+<t tx="aum.20060530143459.2"># start a freenetfs mount
+if cmd in ['init', 'setup']:
+ <<init>>
+
+elif cmd in ['start', 'mount']:
+ <<start>>
+
+elif cmd in ['umount', 'unmount', 'stop']:
+ <<stop>>
+
+elif cmd == 'new':
+ <<new>>
+
+elif cmd == 'add':
+ <<add>>
+
+elif cmd == 'del':
+ <<del>>
+
+elif cmd == 'update':
+ <<update>>
+
+elif cmd == 'commit':
+ <<commit>>
+
+elif cmd == 'list':
+ <<list>>
+
+elif cmd == 'cmd':
+ <<cmd>>
+
+
+
+
+else:
+ usage("Unrecognised command: %s" % cmd)
+
+</t>
+<t tx="aum.20060530143459.3">
+print "starting freedisk service..."
+fs = freenetfs.FreenetFS(
+ conf.mountpoint,
+ fcpHost=conf.fcpHost,
+ fcpPort=conf.fcpPort,
+ verbosity=conf.fcpVerbosity,
+ debug=debug,
+ multithreaded=multithreaded,
+ )
+
+# spawn a process to run it
+if os.fork() == 0:
+ print "Mounting freenet fs at %s" % conf.mountpoint
+ fs.run()
+else:
+ # parent process
+ keyDir = os.path.join(conf.mountpoint, "keys")
+ print "Waiting for disk to come up..."
+ while not os.path.isdir(keyDir):
+ time.sleep(1)
+ disks = conf.getDisks()
+
+ if disks:
+ print "Freenetfs now mounted, adding existing disks..."
+ else:
+ print "Freenetfs now mounted, no freedisks at present"
+
+ for disk in disks:
+
+ diskPath = os.path.join(conf.mountpoint, "usr", disk.name)
+
+ # barf if a freedisk of that name is already mounted
+ if os.path.exists(diskPath):
+ usage("Freedisk %s seems to be already mounted" % disk.name)
+
+ # mkdir to create the freedisk dir
+ os.mkdir(diskPath)
+
+ pubKeyPath = os.path.join(diskPath, ".publickey")
+ privKeyPath = os.path.join(diskPath, ".privatekey")
+ passwdPath = os.path.join(diskPath, ".passwd")
+
+ # wait for the pseudo-files to come into existence
+ while not os.path.isfile(privKeyPath):
+ time.sleep(0.1)
+
+ # set the key and password
+ file(pubKeyPath, "w").write(disk.uri)
+ file(privKeyPath, "w").write(disk.privUri)
+ file(passwdPath, "w").write(disk.passwd)
+
+</t>
+<t tx="aum.20060530143459.4">os.system("umount %s" % conf.mountpoint)
+
+</t>
+<t tx="aum.20060530143459.5">#print "new: %s: NOT IMPLEMENTED" % diskname
+
+if os.path.exists(diskPath):
+ usage("Freedisk %s seems to be already mounted" % diskname)
+
+# get a password if desired
+passwd = getpasswd("Encrypt disk with password", True)
+
+# get a new private key
+keyDir = os.path.join(conf.mountpoint, "keys")
+if not os.path.isdir(keyDir):
+ print "No keys directory %s" % keyDir
+ print "Is your freenetfs mounted?"
+ usage("Freenetfs not mounted")
+keyName = "freedisk_%s_%s" % (diskname, int(time.time()*1000000))
+keyPath = os.path.join(keyDir, keyName)
+
+keys = file(keyPath).read().strip().split("\n")
+pubKey, privKey = [k.split("/")[0].split("freenet:")[-1] for k in keys]
+
+# mkdir to create the freedisk dir
+os.mkdir(diskPath)
+
+# wait for the pseudo-files to come into existence
+while not os.path.isfile(privKeyPath):
+ time.sleep(0.1)
+
+#status("About to write to %s" % privKeyPath)
+
+file(pubKeyPath, "w").write(pubKey)
+file(privKeyPath, "w").write(privKey)
+file(passwdPath, "w").write(passwd)
+
+# and, of course, update config
+conf.addDisk(diskname, pubKey, privKey, passwd)
+
+</t>
+<t tx="aum.20060530143459.6"># get uri
+if nargs < 3:
+ usage("add: Missing URI")
+uri = args[2]
+
+#print "add: %s: NOT IMPLEMENTED" % diskname
+
+# barf if a freedisk of that name is already mounted
+if os.path.exists(diskPath):
+ usage("Freedisk %s seems to be already mounted" % diskname)
+
+# mkdir to create the freedisk dir
+os.mkdir(diskPath)
+
+# wait for the pseudo-files to come into existence
+while not os.path.isfile(privKeyPath):
+ time.sleep(0.1)
+
+# set the keys
+
+if fcp.node.uriIsPrivate(uri):
+ path = privKeyPath
+else:
+ path = pubKeyPath
+f = file(path, "w")
+f.write(uri)
+f.flush()
+f.close()
+
+</t>
+<t tx="aum.20060530143459.7">disk = conf.getDisk(diskname)
+
+if not isinstance(disk, XMLNode):
+ usage("No such disk '%s'" % diskname)
+
+conf.delDisk(diskname)
+
+path = os.path.join(conf.mountpoint, "usr", diskname)
+os.rmdir(path)
+
+</t>
+<t tx="aum.20060530143459.8">print "update: %s: NOT IMPLEMENTED" % diskname
+
+f = file(cmdPath, "w")
+f.write("update")
+f.flush()
+f.close()
+
+</t>
+<t tx="aum.20060530143459.9">print "commit: %s: launching.." % diskname
+
+f = file(cmdPath, "w")
+f.write("commit")
+f.flush()
+f.close()
+
+</t>
+<t tx="aum.20060530151453">def updateDisk(self, name):
+ """
+ synchronises a freedisk FROM freenet
+
+ Arguments:
+ - name - the name of the disk
+ """
+ self.log("updateDisk: disk=%s" % name)
+
+ startTime = time.time()
+
+ # determine freedisk's absolute path within the freenetfs
+ rootPath = os.path.join("/usr", name)
+
+ # get the freedisk root's record, barf if nonexistent
+ rootRec = self.files.get(rootPath, None)
+ if not rootRec:
+ self.log("updateDisk: no disk '%s' mounted!" % name)
+ return
+
+ # determine pseudo-file paths
+ statusFile = self.files[os.path.join(rootPath, ".status")]
+ pubKeyFile = self.files[os.path.join(rootPath, ".publickey")]
+
+ # and get the private key, sans 'freenet:'
+ pubKey = pubKeyFile.data.split("freenet:")[-1]
+
+ # process further
+ pubKey = privKey.replace("SSK@", "USK@").split("/")[0] + "/" + name + "/0"
+
+ self.log("update: pubKey=%s" % pubKey)
+
+ # fetch manifest
+
+ # mark disk as readonly
+
+ # for each entry in manifest
+ # if not localfile has changed
+ # replace the file record
+
+</t>
+<t tx="aum.20060530151453.1">def commitDisk(self, name):
+ """
+ synchronises a freedisk TO freenet
+
+ Arguments:
+ - name - the name of the disk
+ """
+ self.log("commitDisk: disk=%s" % name)
+
+ startTime = time.time()
+
+ # determine freedisk's absolute path within the freenetfs
+ rootPath = os.path.join("/usr", name)
+
+ # get the freedisk root's record, barf if nonexistent
+ rootRec = self.files.get(rootPath, None)
+ if not rootRec:
+ self.log("commitDisk: no disk '%s' mounted!" % name)
+ return
+
+ # determine pseudo-file paths
+ statusFile = self.files[os.path.join(rootPath, ".status")]
+ privKeyFile = self.files[os.path.join(rootPath, ".privatekey")]
+ pubKeyFile = self.files[os.path.join(rootPath, ".publickey")]
+
+ # and get the private key, sans 'freenet:'
+ privKey = privKeyFile.data.split("freenet:")[-1]
+
+ # process further
+ privKey = privKey.replace("SSK@", "USK@").split("/")[0] + "/" + name + "/0"
+
+ self.log("commit: privKey=%s" % privKey)
+
+ if privKey.startswith("SSK@"):
+ # convert to USK
+ privKey = "USK" + privKey[3:] + "/0"
+
+ self.log("commitDisk: checking files in %s" % rootPath)
+
+ # update status
+ statusFile.data = "committing\nAnalysing files\n"
+
+ # get list of records of files within this freedisk
+ fileRecs = []
+ for f in self.files.keys():
+ # is file/dir within the freedisk?
+ if f.startswith(rootPath+"/"):
+ # yes, get its record
+ fileRec = self.files[f]
+
+ # is it a file, and not a special file?
+ if fileRec.isfile and (os.path.split(f)[1] not in
freediskSpecialFiles):
+ # yes, grab it
+ fileRecs.append(fileRec)
+
+ # now sort them
+ fileRecs.sort(lambda r1, r2: cmp(r1.path, r2.path))
+
+ statusFile.data = "committing\nConnecting to Freenet\n"
+
+ # make sure we have a node to talk to
+ self.connectToNode()
+ node = self.node
+
+ # now insert all these files
+ maxJobs = 5
+ jobsWaiting = fileRecs[:]
+ jobsRunning = []
+ jobsDone = []
+
+ # determine CHKs for all these jobs
+ for rec in jobsWaiting:
+ rec.mimetype = guessMimetype(rec.path)
+ rec.uri = node.put(
+ "CHK at file",
+ data=rec.data,
+ chkonly=True,
+ mimetype=rec.mimetype)
+
+ statusFile.data = "committing\nInserting Files\n"
+
+ # now, create the manifest
+ manifest = XMLFile(root="freedisk")
+ root = manifest.root
+ for rec in jobsWaiting:
+ fileNode = root._addNode("file")
+ fileNode.path = rec.path
+ fileNode.uri = rec.uri
+ try:
+ fileNode.mimetype = rec.mimetype
+ except:
+ fileNode.mimetype = "text/plain"
+ fileNode.hash = sha.new(rec.data).hexdigest()
+
+ # and add the manifest as a waiting job
+ manifestJob = node.put(
+ privKey,
+ data=manifest.toxml(),
+ mimetype="text/xml",
+ async=True,
+ )
+
+ #jobsRunning.append(manifestJob)
+ #manifestUri = manifestJob.wait()
+ #print "manifestUri=%s" % manifestUri
+ #time.sleep(6)
+
+ # the big insert/wait loop
+ while jobsWaiting or jobsRunning:
+ nWaiting = len(jobsWaiting)
+ nRunning = len(jobsRunning)
+ self.log("commit: %s waiting, %s running" % (nWaiting,nRunning))
+
+ statusFile.data = "committing\n%s files queued, %s inserting\n" % (
+ nWaiting, nRunning)
+
+ # launch jobs, if available, and if spare slots
+ while len(jobsRunning) < maxJobs and jobsWaiting:
+
+ rec = jobsWaiting.pop(0)
+
+ # if record has data, insert it, otherwise take as done
+ if rec.hasdata:
+ uri = rec.uri
+ if not uri:
+ uri = "CHK at somefile" + os.path.splitext(rec.path)[1]
+ job = node.put(uri, data=rec.data, async=True)
+ rec.job = job
+ jobsRunning.append(rec)
+ else:
+ # record should already have the hash, uri, mimetype
+ jobsDone.append(rec)
+
+ # check running jobs
+ for rec in jobsRunning:
+ if rec == manifestJob:
+ job = rec
+ else:
+ job = rec.job
+
+ if job.isComplete():
+ jobsRunning.remove(rec)
+
+ uri = job.wait()
+
+ if job != manifestJob:
+ rec.uri = uri
+ rec.job = None
+ jobsDone.append(rec)
+
+ # breathe!!
+ if jobsRunning:
+ time.sleep(5)
+ else:
+ time.sleep(1)
+
+ statusFile.data = "idle"
+
+ self.log("commitDisk: done, manifestUri=%s" % manifestJob.uri)
+
+ pubKeyFile.data = manifestJob.uri
+
+ endTime = time.time()
+ commitTime = endTime - startTime
+
+ self.log("commitDisk: commit completed in %s seconds" % commitTime)
+
+</t>
+<t tx="aum.20060530151504">def addDisk(self, name, uri):
+ """
+ Adds (mounts) a freedisk within freenetfs
+
+ Arguments:
+ - name - name of disk - will be mounted in as /usr/<name>
+ - uri - a public or private SSK key URI. Parsing of the key will
+ reveal whether it's public or private. If public, the freedisk
+ will be mounted read-only. If private, the freedisk will be
+ mounted read/write
+ """
+
+</t>
+<t tx="aum.20060530160322">def removeDirAndContents(path):
+
+ files = os.listdir(path)
+
+ for f in files:
+ fpath = os.path.join(path, f)
+ if os.path.isfile(fpath):
+ os.unlink(fpath)
+ elif os.path.isdir(fpath):
+ removeDirAndContents(fpath)
+ os.rmdir(path)
+
+</t>
+<t tx="aum.20060530170840">@first #! /usr/bin/env python
+ at language python
+ at others
+</t>
+<t tx="aum.20060530170840.1">@first #! /usr/bin/env python
+ at language python
+ at others
+
+</t>
+<t tx="aum.20060530202714">class FreediskMgr:
+ """
+ Gateway for mirroring a local directory to/from freenet
+ """
+ @others
+
+</t>
+<t tx="aum.20060530202714.1">def __init__(self, **kw):
+ """
+ Creates a freediskmgr object
+
+ Keywords:
+ - name - mandatory - the name of the disk
+ - fcpNode - mandatory - an FCPNode instance
+ - root - mandatory - the root directory
+ - publicKey - the freenet public key URI
+ - privateKey - the freenet private key URI
+ Notes:
+ - exactly one of publicKey, privateKey keywords must be given
+ """
+
+</t>
+<t tx="aum.20060530202714.2">def update(self):
+ """
+ Update from freenet to local directory
+ """
+
+</t>
+<t tx="aum.20060530202714.3">def commit(self):
+ """
+ commit from local directory into freenet
+ """
+
+</t>
+<t tx="aum.20060530234330">def setupFreedisks(self):
+ """
+ Initialises the freedisks
+ """
+ self.freedisks = {}
+
+</t>
+<t tx="aum.20060530234330.1">def getManifest(self, name):
+ """
+ Retrieves the manifest of a given disk
+ """
+</t>
+<t tx="aum.20060530234330.2">def putManifest(self, name):
+ """
+ Inserts a freedisk manifest into freenet
+ """
+</t>
+<t tx="aum.20060531160838">def status(msg):
+ sys.stdout.write(msg + "...")
+ time.sleep(1)
+ print
+
+
+</t>
+<t tx="aum.20060601233442"># default attribs, can be overwritten by
constructor keywords
+haschanged = False
+hasdata = False
+canwrite = False
+iswriting = False
+uri = None
+
+</t>
+<t tx="aum.20060602094531"></t>
+<t tx="aum.20060602094531.1"></t>
+<t tx="aum.20060602094531.2">@first #! /usr/bin/env python
+"""
+Small script to test freedisk
+"""
+import sys, os, time
+
+mountpoint = "/mnt/freenet"
+
+diskName = "fred"
+
+def sh(cmd):
+ print "Executing: %s" % cmd
+ os.system(cmd)
+ time.sleep(0.5)
+
+def mkfile(name):
+ path = os.path.join(mountpoint, "usr", diskName, name)
+ parent = os.path.split(path)[0]
+ if not os.path.isdir(parent):
+ os.makedirs(parent)
+ f = file(path, "w")
+ f.write(str(time.time()))
+ f.close()
+
+sh("freedisk start")
+sh("freedisk new fred")
+
+mkfile("file1.txt")
+mkfile("file2.html")
+mkfile("dir1/file3.txt")
+mkfile("dir1/file4.txt")
+mkfile("dir2/file5.txt")
+mkfile("dir3/file6.txt")
+mkfile("dir3/dir4/file7.txt")
+time.sleep(1)
+sh("freedisk commit fred")
+
+</t>
+<t tx="aum.20060603100604">def encrypt(passwd, s):
+
+ passwd = sha.new(passwd).digest()
+
+ if SSLCrypto:
+ # encrypt with blowfish 256, key=sha(password), IV=00000000
+ return SSLCrypto.blowfish(passwd).encrypt(s)
+ else:
+ # no encyrption available, return plaintext
+ return s
+
+</t>
+<t tx="aum.20060603100604.1">def decrypt(passwd, s):
+
+ passwd = sha.new(passwd).digest()
+
+ if SSLCrypto:
+ # decrypt with blowfish 256, key=sha(password), IV=00000000
+ return SSLCrypto.blowfish(passwd).decrypt(s)
+ else:
+ # no encyrption available, return plaintext
+ return s
+
+</t>
+<t tx="aum.20060603100604.2">def getpasswd(prompt="Password", confirm=False):
+
+ if not confirm:
+ return getpass.getpass(prompt+": ").strip()
+
+ while 1:
+ passwd = getpass.getpass(prompt+": ").strip()
+ if passwd:
+ passwd1 = getpasswd("Verify password").strip()
+ if passwd == passwd1:
+ break
+ print "passwords do not match, please try again"
+ else:
+ break
+
+ return passwd
+
+</t>
+<t tx="aum.20060603114247">def cmd_init(self, *args):
+
+ conf = self.conf
+
+ # initialise/change freedisk config
+
+ print "Freedisk configuration"
+ print
+ print "Your freedisk config will normally be stored in the file:"
+ print " %s" % self.configFile
+
+ # allow password change
+ if conf.passwd:
+ # got a password already
+ prmt = "Do you wish to change your config password"
+ else:
+ # new password
+ prmt = "Do you wish to encrypt this file"
+ if getyesno(prmt):
+ passwd = getpasswd("New Password", True)
+ conf.setPassword(passwd)
+ print "Password successfully changed"
+
+ # host parms
+ fcpHost = raw_input("Freenet FCP Hostname: [%s] " % conf.fcpHost).strip()
+ if fcpHost:
+ conf.fcpHost = fcpHost
+
+ fcpPort = raw_input("Freenet FCP Port: [%s] "% conf.fcpPort).strip()
+ if fcpPort:
+ conf.fcpPort = fcpPort
+
+ print "Freenet verbosity:"
+ print " (0=SILENT, 1=FATAL, 2=CRITICAL, 3=ERROR"
+ print " 4=INFO, 5=DETAIL, 6=DEBUG)"
+ v = raw_input("[%s] " % conf.fcpVerbosity).strip()
+ if v:
+ conf.fcpVerbosity = v
+
+ while 1:
+ m = raw_input("Mountpoint [%s] " % conf.mountpoint).strip() \
+ or conf.mountpoint
+ if m:
+ if not os.path.isdir(m):
+ print "No such directory '%s'" % m
+ elif not os.path.exists(m):
+ print "%s is not a directory" % m
+ else:
+ conf.mountpoint = m
+ mountpoint = m
+ break
+
+ print "Freedisk configuration successfully changed"
+
+</t>
+<t tx="aum.20060603114446">class FreediskMgr:
+ """
+ Freedisk manager class
+ """
+ @others
+
+</t>
+<t tx="aum.20060603121718">class FreediskConfig:
+ """
+ allows for loading/saving/changing freedisk configs
+ """
+ @others
+
+</t>
+<t tx="aum.20060603121718.1">def __init__(self, path, passwd=None):
+ """
+ Create a config object from file at 'path', if it exists
+ """
+ #print "FreediskConfig: path=%s" % path
+
+ self.path = path
+ self.passwd = passwd
+
+ if os.path.isfile(path):
+ self.load()
+ else:
+ self.create()
+
+ self.root = self.xml.root
+
+</t>
+<t tx="aum.20060603121848">def load(self):
+ """
+ Loads config from self.config
+ """
+ # get the raw xml, plain or encrypted
+ ciphertext = file(self.path, "rb").read()
+
+ plaintext = ciphertext
+
+ # try to wrap into xml object
+ try:
+ xml = self.xml = XMLFile(raw=plaintext)
+ except:
+ i = 0
+ while i < 3:
+ passwd = self.passwd = getpasswd("Freedisk config password")
+ plaintext = decrypt(self.passwd, ciphertext)
+ try:
+ xml = XMLFile(raw=plaintext)
+ break
+ except:
+ i += 1
+ continue
+ if i == 3:
+ self.abort()
+
+ self.xml = xml
+ self.root = xml.root
+
+</t>
+<t tx="aum.20060603122324">def create(self):
+ """
+ Creates a new config object
+ """
+ self.xml = XMLFile(root="freedisk")
+ root = self.root = self.xml.root
+
+ self.fcpHost = fcp.node.defaultFCPHost
+ self.fcpPort = fcp.node.defaultFCPPort
+ self.fcpVerbosity = fcp.node.defaultVerbosity
+ self.mountpoint = defaultMountpoint
+
+ self.save()
+
+</t>
+<t tx="aum.20060603125105">_intAttribs = ["fcpPort", "fcpVerbosity"]
+
+_strAttribs = ["fcpHost", "mountpoint"]
+
+</t>
+<t tx="aum.20060603125405">def __getattr__(self, attr):
+
+ if attr in self._intAttribs:
+ try:
+ return int(getattr(self.root, attr))
+ except:
+ raise AttributeError(attr)
+
+ elif attr in self._strAttribs:
+ try:
+ return str(getattr(self.root, attr))
+ except:
+ raise AttributeError(attr)
+
+ else:
+ raise AttributeError(attr)
+
+</t>
+<t tx="aum.20060603125405.1">def __setattr__(self, attr, val):
+
+ if attr in self._intAttribs:
+ val = str(val)
+ setattr(self.root, attr, val)
+ self.save()
+ elif attr in self._strAttribs:
+ setattr(self.root, attr, val)
+ self.save()
+ else:
+ self.__dict__[attr] = val
+
+</t>
+<t tx="aum.20060603125812">def ipython(o=None):
+
+ from IPython.Shell import IPShellEmbed
+
+ ipshell = IPShellEmbed()
+
+ ipshell() # this call anywhere in your program will start IPython
+
+</t>
+<t tx="aum.20060603125848">def save(self):
+
+ plain = self.xml.toxml()
+
+ if self.passwd:
+ cipher = encrypt(self.passwd, plain)
+ else:
+ cipher = plain
+
+ f = file(self.path, "wb")
+ f.write(cipher)
+ f.flush()
+ f.close()
+
+</t>
+<t tx="aum.20060603131227">def setPassword(self, passwd):
+
+ self.passwd = passwd
+ self.save()
+
+</t>
+<t tx="aum.20060603132247">def getyesno(prmt, dflt=True):
+
+ if dflt:
+ ynprmt = "[Y/n] "
+ else:
+ ynprmt = "[y/N] "
+
+ resp = raw_input(prmt + "? " + ynprmt).strip()
+ if not resp:
+ return dflt
+ resp = resp.lower()[0]
+ return resp == 'y'
+
+</t>
+<t tx="aum.20060603132557">def abort(self):
+
+ print "freedisk: Cannot decrypt freedisk config file '%s'" % self.path
+ print
+ print "If you truly can't remember the password, your only"
+ print "option now is to delete the config file and start again"
+ sys.exit(1)
+
+</t>
+<t tx="aum.20060603153411">def _getChild(self, name):
+ """
+ Returns a list of zero or more child nodes whose
+ tag name is <name>
+ """
+ try:
+ item = getattr(self, name)
+ except AttributeError:
+ return []
+
+ if not isinstance(item, list):
+ item = [item]
+
+ return item
+
+</t>
+<t tx="aum.20060603154804">def addDisk(self, name, uri, privUri, passwd):
+
+ d = self.getDisk(name)
+ if isinstance(d, XMLNode):
+ raise Exception("Disk '%s' already exists" % name)
+
+ diskNode = self.root._addNode("disk")
+ diskNode.name = name
+ diskNode.uri = uri
+ diskNode.privUri = privUri
+ diskNode.passwd = passwd
+
+ self.save()
+
+</t>
+<t tx="aum.20060603155318">def getDisk(self, name):
+ """
+ Returns a record for a freedisk of name <name>
+ """
+ disks = self.root._getChild("disk")
+
+ for d in disks:
+ if d.name == name:
+ return d
+
+ return None
+
+</t>
+<t tx="aum.20060603155642">def delDisk(self, name):
+ """
+ Removes disk of given name
+ """
+ d = self.getDisk(name)
+ if not isinstance(d, XMLNode):
+ raise Exception("No such freedisk '%s'" % name)
+
+ self.root._delChild(d)
+
+ self.save()
+
+</t>
+<t tx="aum.20060603160206">def _delChild(self, child):
+ """
+ Removes given child node
+ """
+ node = self
+ while True:
+ print "Trying to remove %s from %s" % (child, node)
+ if child in node._children:
+ print "removing"
+ node._children.remove(child)
+ node._node.removeChild(child._node)
+
+ for k,v in node._childrenByName.items():
+ if child == v:
+ del node._childrenByName[k]
+ elif isinstance(v, list):
+ if child in v:
+ v.remove(child)
+
+ if isinstance(node, XMLFile):
+ break
+
+ node = node._parent
+
+</t>
+<t tx="aum.20060603162815">def getDisks(self):
+ """
+ Returns all freedisk records
+ """
+ return self.root._getChild("disk")
+
+</t>
+<t tx="aum.20060603164555">disks = conf.getDisks()
+
+if disks:
+ print "Currently mounted freedisks:"
+ for d in disks:
+ print " %s:" % d.name
+ print " uri=%s" % d.uri
+ print " passwd=%s" % d.passwd
+else:
+ print "No freedisks mounted"
+
+</t>
+<t tx="aum.20060603170554">def uriIsPrivate(uri):
+ """
+ analyses an SSK URI, and determines if it is an SSK or USK private key
+ """
+ if uri.startswith("freenet:"):
+ uri = uri[8:]
+
+ if not (uri.startswith("SSK@") or uri.startswith("USK@")):
+ return False
+
+ # rip off any path stuff
+ uri = uri.split("/")[0]
+
+ # blunt rule of thumb - 2 commas is pubkey, 1 is privkey
+ if len(uri.split(",")) == 2:
+ return True
+
+ return False
+
+</t>
+<t tx="aum.20060603231840"># functions to encode/decode base64, freenet
alphabet
+ at others
+
+</t>
+<t tx="aum.20060603231840.1">def base64encode(raw):
+ """
+ Encodes a string to base64, using the Freenet alphabet
+ """
+ # encode using standard RFC1521 base64
+ enc = base64.encodestring(raw)
+
+ # convert the characters to freenet encoding scheme
+ enc = enc.replace("+", "~")
+ enc = enc.replace("/", "-")
+ enc = enc.replace("=", "_")
+ enc = enc.replace("\n", "")
+
+ return enc
+
+</t>
+<t tx="aum.20060603231840.2">def base64decode(enc):
+ """
+ Decodes a freenet-encoded base64 string back to a binary string
+
+ Arguments:
+ - enc - base64 string to decode
+ """
+ # convert from Freenet alphabet to RFC1521 format
+ enc = enc.replace("~", "+")
+ enc = enc.replace("-", "/")
+ enc = enc.replace("_", "=")
+
+ # now ready to decode
+ raw = base64.decodestring(enc)
+
+ return raw
+
+</t>
+<t tx="aum.20060604143559"># a command has been encoded via base64
+
+print "base64 command encoded into %s" % path
+
+cmdBase64 = path.split("/cmds/", 1)[-1]
+
+print "cmdBase64=%s" % cmdBase64
+
+cmd = base64decode(cmdBase64)
+
+print "cmd=%s" % cmd
+
+result = cmd + "\n" + "done\n"
+
+rec = self.addToCache(path=path, isreg=True, data=result, perm=0644)
+
+</t>
+<t tx="aum.20060604143852">def doFsCommand(cmd):
+ """
+ Executes a command via base64-encoded file
+ """
+ cmdBase64 = fcp.node.base64encode(cmd)
+ path = conf.mountpoint + "/cmds/" + cmdBase64
+ return file(path).read()
+
+</t>
+<t tx="aum.20060604144241">def cmd_cmd(self, *args):
+
+ # arbitrary command, for testing
+ cmd = " ".join(args)
+ print repr(doFsCommand(cmd))
+
+</t>
+<t tx="aum.20060604194409">def __init__(self, *args, **kw):
+
+ self.args = args
+ self.kw = kw
+
+ configFile = self.configFile = kw['configFile']
+ conf = self.conf = FreediskConfig(configFile)
+ #ipython(conf)
+
+
+ # validate args
+ nargs = len(args)
+ if nargs == 0:
+ usage("No command given")
+
+ cmd = self.cmd = args[0]
+
+ # barf if not 'init' and no config
+ if cmd != 'init' and not os.path.isfile(configFile):
+ usage("Config file %s does not exist\nRun '%s init' to create it" % (
+ configFile, progname))
+
+ # validate args count for cmds needing diskname arg
+ if cmd in ['new', 'add', 'del', 'update', 'commit']:
+ if nargs < 2:
+ usage("%s: Missing argument <freediskname>" % cmd)
+ diskname = self.diskname = args[1]
+
+ # get paths to freedisk dir and pseudo-files
+ self.diskPath = os.path.join(conf.mountpoint, "usr", diskname)
+ self.pubKeyPath = os.path.join(self.diskPath, ".publickey")
+ self.privKeyPath = os.path.join(self.diskPath, ".privatekey")
+ self.passwdPath = os.path.join(self.diskPath, ".passwd")
+ self.cmdPath = os.path.join(self.diskPath, ".cmd")
+ self.statusPath = os.path.join(self.diskPath, ".status")
+
+</t>
+<t tx="aum.20060604194834">def execute(self):
+ """
+ Executes the given command
+ """
+ cmd = self.cmd
+ method = getattr(self, "cmd_"+cmd, None)
+ if not method:
+ usage("Unrecognised command '%s'" % cmd)
+
+ return method(*self.args[1:])
+
+</t>
</tnodes>
</leo_file>
Modified: trunk/apps/pyFreenet/fcp/__init__.py
===================================================================
--- trunk/apps/pyFreenet/fcp/__init__.py 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/fcp/__init__.py 2006-06-04 08:00:16 UTC (rev
9040)
@@ -4,8 +4,9 @@
from node import SILENT, FATAL, CRITICAL, ERROR, INFO, DETAIL, DEBUG
+import freenetfs
-__all__ = ['node', 'sitemgr', 'xmlrpc',
+__all__ = ['node', 'sitemgr', 'xmlrpc', 'freenetfs',
'FCPNode', 'JobTicket',
'ConnectionRefused', 'FCPException', 'FCPPutFailed',
'FCPProtocolError',
Added: trunk/apps/pyFreenet/fcp/freenetfs.py
===================================================================
--- trunk/apps/pyFreenet/fcp/freenetfs.py 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/fcp/freenetfs.py 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,1922 @@
+#! /usr/bin/env python
+#@+leo-ver=4
+#@+node:@file freenetfs.py
+#@@first
+"""
+A FUSE-based filesystem for freenet
+
+Written May 2006 by aum
+
+Released under the GNU Lesser General Public License
+
+Requires:
+ - python2.3 or later
+ - FUSE kernel module installed and loaded
+ (apt-get install fuse-source, crack tarball, build and install)
+ - python2.3-fuse
+ - libfuse2
+"""
+
+#@+others
+#@+node:imports
+import sys, os, time, stat, errno
+from StringIO import StringIO
+import thread
+from threading import Lock
+import traceback
+from Queue import Queue
+import sha, md5
+from UserString import MutableString
+
+from errno import *
+from stat import *
+
+try:
+ import warnings
+ warnings.filterwarnings('ignore',
+ 'Python C API version mismatch',
+ RuntimeWarning,
+ )
+except:
+ pass
+
+import _fuse
+import sys
+from errno import *
+
+import fcp
+
+from fcp.xmlobject import XMLFile
+from fcp.node import guessMimetype, base64encode, base64decode
+
+#@-node:imports
+#@+node:globals
+argv = sys.argv
+argc = len(argv)
+progname = argv[0]
+
+fcpHost = fcp.node.defaultFCPHost
+fcpPort = fcp.node.defaultFCPPort
+
+defaultVerbosity = fcp.DETAIL
+
+quiet = 0
+
+myuid = os.getuid()
+mygid = os.getgid()
+
+inodes = {}
+inodesNext = 1
+
+# set this to disable hits to node, for debugging
+_no_node = 0
+
+# special filenames in freedisk toplevel dirs
+freediskSpecialFiles = [
+ '.privatekey', '.publickey', '.cmd', '.status', ".passwd",
+ ]
+
+showAllExceptions = False
+
+#@-node:globals
+#@+node:class ErrnoWrapper
+class ErrnoWrapper:
+
+ def __init__(self, func):
+ self.func = func
+
+ def __call__(self, *args, **kw):
+ try:
+ return apply(self.func, args, kw)
+ except (IOError, OSError), detail:
+ if showAllExceptions:
+ traceback.print_exc()
+ # Sometimes this is an int, sometimes an instance...
+ if hasattr(detail, "errno"): detail = detail.errno
+ return -detail
+
+
+#@-node:class ErrnoWrapper
+#@+node:class FreenetFS
+class FreenetFS:
+
+ #@ @+others
+ #@+node:attribs
+ _attrs = ['getattr', 'readlink', 'getdir', 'mknod', 'mkdir',
+ 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
+ 'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
+ 'statfs', 'fsync']
+
+ multithreaded = 0
+ flags = 1
+ debug = False
+ fcpHost = fcpHost
+ fcpPort = fcpPort
+ verbosity = defaultVerbosity
+ allow_other = False
+ kernel_cache = False
+ config = os.path.join(os.path.expanduser("~"), ".freediskrc")
+
+ # Files and directories already present in the filesytem.
+ # Note - directories must end with "/"
+
+ initialFiles = [
+ "/",
+ "/get/",
+ "/put/",
+ "/keys/",
+ "/usr/",
+ "/cmds/",
+ ]
+
+ chrFiles = [
+ ]
+
+ #@-node:attribs
+ #@+node:__init__
+ def __init__(self, mountpoint, *args, **kw):
+ """
+ Create a freenetfs
+
+ Arguments:
+ - mountpoint - the dir in the filesystem at which to mount the fs
+ - other args get passed to fuse
+
+ Keywords:
+ - multithreaded - whether to run the fs multithreaded, default True
+ - fcpHost - hostname of FCP service
+ - fcpPort - port number of FCP service
+ - verbosity - defaults to fcp.DETAIL
+ - config - location of config file
+ - debug - whether to run in debug mode, default False
+ """
+
+ #self.log("init: args=%s kw=%s" % (args, kw))
+
+ for k in ['multithreaded',
+ 'fcpHost',
+ 'fcpPort',
+ 'verbosity',
+ 'debug',
+ ]:
+ if kw.has_key(k):
+ v = kw.pop(k)
+ try:
+ v = int(v)
+ except:
+ pass
+
+ setattr(self, k, v)
+
+ self.optlist = list(args)
+ self.optdict = dict(kw)
+
+ self.mountpoint = mountpoint
+
+ #if not self.config:
+ # raise Exception("Missing 'config=filename.conf' argument")
+
+ #self.loadConfig()
+ self.setupFiles()
+ self.setupFreedisks()
+
+ # do stuff to set up your filesystem here, if you want
+ #thread.start_new_thread(self.mythread, ())
+
+ if 0:
+ self.log("xmp.py:Xmp:mountpoint: %s" % repr(self.mountpoint))
+ self.log("xmp.py:Xmp:unnamed mount options: %s" % self.optlist)
+ self.log("xmp.py:Xmp:named mount options: %s" % self.optdict)
+
+ #@-node:__init__
+ #@+node:run
+ def run(self):
+
+ try:
+ self.node = None
+ self.connectToNode()
+ except:
+ #raise
+ pass
+
+ d = {'mountpoint': self.mountpoint,
+ 'multithreaded': self.multithreaded,
+ }
+
+ if self.debug:
+ d['lopts'] = 'debug'
+
+ k=[]
+ for opt in ['allow_other', 'kernel_cache']:
+ if getattr(self, opt):
+ k.append(opt)
+ if k:
+ d['kopts'] = ",".join(k)
+
+ for a in self._attrs:
+ if hasattr(self,a):
+ d[a] = ErrnoWrapper(getattr(self, a))
+
+ _fuse.main(**d)
+
+ #@-node:run
+ #@+node:GetContent
+ def GetContext(self):
+ print "GetContext: called"
+ return _fuse.FuseGetContext(self)
+
+ #@-node:GetContent
+ #@+node:Invalidate
+ def Invalidate(self, path):
+ print "Invalidate: called"
+ return _fuse.FuseInvalidate(self, path)
+
+ #@-node:Invalidate
+ #@+node:_loadConfig
+ def _loadConfig(self):
+ """
+ The 'physical device' argument to mount should be the pathname
+ of a configuration file, with 'name=val' lines, including the
+ following items:
+ - publickey=<freenet public key URI>
+ - privatekey=<freenet private key URI> (optional, without which we
+ will have the fs mounted readonly
+ """
+ opts = {}
+
+ # build a dict of all the 'name=value' pairs in config file
+ for line in [l.strip() for l in file(self.config).readlines()]:
+ if line == '' or line.startswith("#"):
+ continue
+ try:
+ name, val = line.split("=", 1)
+ opts[name.strip()] = val.strip()
+ except:
+ pass
+
+ # mandate a pubkey
+ try:
+ self.pubkey = opts['pubkey'].replace("SSK@", "USK@").split("/")[0]
+ "/"
+ except:
+ raise Exception("Config file %s: missing or invalid publickey" \
+ % self.configfile)
+
+ # accept optional privkey
+ if opts.has_key("privkey"):
+
+ try:
+ self.privkey = opts['privkey'].replace("SSK@",
+ "USK@").split("/")[0] +
"/"
+ except:
+ raise Exception("Config file %s: invalid privkey" \
+ % self.configfile)
+
+ # mandate cachepath
+ try:
+ self.cachedir = opts['cachedir']
+ if not os.path.isdir(self.cachedir):
+ self.log("Creating cache directory %s" % self.cachedir)
+ os.makedirs(self.cachedir)
+ #raise hell
+ except:
+ raise Exception("config file %s: missing or invalid cache
directory" \
+ % self.configfile)
+
+ #@-node:_loadConfig
+ #@+node:setupFiles
+ def setupFiles(self):
+ """
+ Create initial file/directory layout, according
+ to attributes 'initialFiles' and 'chrFiles'
+ """
+ # easy map of files
+ self.files = {}
+
+ # now create records for initial files
+ for path in self.initialFiles:
+
+ # initial attribs
+ isReg = isDir = isChr = isSock = isFifo = False
+ perm = size = 0
+
+ # determine file type
+ if path.endswith("/"):
+ isDir = True
+ path = path[:-1]
+ if not path:
+ path = "/"
+ elif path in self.chrFiles:
+ # it's a char file
+ #isChr = True
+ isReg = True
+ perm |= 0666
+ size = 1024
+ else:
+ # by default, it's a regular file
+ isReg = True
+
+ # create permissions field
+ if isDir:
+ perm |= 0755
+ else:
+ perm |= 0444
+
+ # create record for this path
+ self.addToCache(
+ path=path,
+ perm=perm,
+ size=size,
+ isdir=isDir, isreg=isReg, ischr=isChr,
+ issock=isSock, isfifo=isFifo,
+ )
+
+ #@-node:setupFiles
+ #@+node:connectToNode
+ def connectToNode(self):
+ """
+ Attempts a connection to an fcp node
+ """
+ if self.node:
+ return
+
+ self.verbosity = fcp.DETAIL
+
+ try:
+ self.node = fcp.FCPNode(host=self.fcpHost,
+ port=self.fcpPort,
+ verbosity=self.verbosity)
+ except:
+ raise IOError(errno.EIO, "Failed to reach FCP service at %s:%s" % (
+ self.fcpHost, self.fcpPort))
+
+ #self.log("pubkey=%s" % self.pubkey)
+ #self.log("privkey=%s" % self.privkey)
+ #self.log("cachedir=%s" % self.cachedir)
+
+ #@-node:connectToNode
+ #@+node:log
+ def log(self, msg):
+ #if not quiet:
+ # print "freedisk:"+msg
+ file("/tmp/freedisk.log", "a").write(msg+"\n")
+
+ #@-node:log
+ #@+node:mythread
+ def mythread(self):
+
+ """
+ The beauty of the FUSE python implementation is that with the python
interp
+ running in foreground, you can have threads
+ """
+ self.log("mythread: started")
+ #while 1:
+ # time.sleep(120)
+ # print "mythread: ticking"
+
+ #@-node:mythread
+ #@+node:fs primitives
+ # primitives required for actual fs operations
+
+ #@+others
+ #@+node:chmod
+ def chmod(self, path, mode):
+
+ ret = os.chmod(path, mode)
+ self.log("chmod: path=%s mode=%s\n => %s" % (path, mode, ret))
+ return ret
+
+ #@-node:chmod
+ #@+node:chown
+ def chown(self, path, user, group):
+
+ ret = os.chown(path, user, group)
+ self.log("chmod: path=%s user=%s group=%s\n => %s" % (path, user,
group, ret))
+ return ret
+
+ #@-node:chown
+ #@+node:fsync
+ def fsync(self, path, isfsyncfile):
+
+ self.log("fsync: path=%s, isfsyncfile=%s" % (path, isfsyncfile))
+ return 0
+
+ #@-node:fsync
+ #@+node:getattr
+ def getattr(self, path):
+
+ rec = self.files.get(path, None)
+ if not rec:
+ # each of these code segments should assign a record to 'rec',
+ # or raise an IOError
+
+ # retrieving a key?
+ if path.startswith("/keys/"):
+ #@ <<generate keypair>>
+ #@+node:<<generate keypair>>
+ # generate a new keypair
+ self.connectToNode()
+ pubkey, privkey = self.node.genkey()
+ rec = self.addToCache(
+ path=path,
+ isreg=True,
+ data=pubkey+"\n"+privkey+"\n",
+ perm=0444,
+ )
+
+ #@-node:<<generate keypair>>
+ #@nl
+ elif path.startswith("/get/"):
+ #@ <<retrieve/cache key>>
+ #@+node:<<retrieve/cache key>>
+ # check the cache
+ if _no_node:
+ print "FIXME: returning IOerror"
+ raise IOError(errno.ENOENT, path)
+
+ # get a key
+ uri = path.split("/", 2)[-1]
+ try:
+ self.connectToNode()
+ mimetype, data = self.node.get(uri)
+ rec = self.addToCache(
+ path=path,
+ isreg=True,
+ perm=0644,
+ data=data,
+ )
+
+ except:
+ traceback.print_exc()
+ #print "ehhh?? path=%s" % path
+ raise IOError(errno.ENOENT, path)
+
+ #@-node:<<retrieve/cache key>>
+ #@nl
+ elif path.startswith("/cmds/"):
+ #@ <<base64 command>>
+ #@+node:<<base64 command>>
+ # a command has been encoded via base64
+
+ print "base64 command encoded into %s" % path
+
+ cmdBase64 = path.split("/cmds/", 1)[-1]
+
+ print "cmdBase64=%s" % cmdBase64
+
+ cmd = base64decode(cmdBase64)
+
+ print "cmd=%s" % cmd
+
+ result = cmd + "\n" + "done\n"
+
+ rec = self.addToCache(path=path, isreg=True, data=result,
perm=0644)
+
+ #@-node:<<base64 command>>
+ #@nl
+ else:
+ raise IOError(errno.ENOENT, path)
+
+ self.log("getattr: path=%s" % path)
+ self.log(" mode=0%o" % rec.mode)
+ self.log(" inode=0x%x" % rec.inode)
+ self.log(" dev=0x%x" % rec.dev)
+ self.log(" nlink=0x%x" % rec.nlink)
+ self.log(" uid=%d" % rec.uid)
+ self.log(" gid=%d" % rec.gid)
+ self.log(" size=%d" % rec.size)
+ self.log(" atime=%d" % rec.atime)
+ self.log(" mtime=%d" % rec.mtime)
+ self.log(" ctime=%d" % rec.ctime)
+ self.log("rec=%s" % str(rec))
+
+ return tuple(rec)
+
+ #@-node:getattr
+ #@+node:getdir
+ def getdir(self, path):
+
+ rec = self.files.get(path, None)
+
+ if rec:
+ files = [os.path.split(child.path)[-1] for child in rec.children]
+ files.sort()
+ if rec.isdir:
+ if path != "/":
+ files.insert(0, "..")
+ files.insert(0, ".")
+ else:
+ self.log("Hit main fs for %s" % path)
+ files = os.listdir(path)
+
+ ret = map(lambda x: (x,0), files)
+
+ self.log("getdir: path=%s\n => %s" % (path, ret))
+ return ret
+
+ #@-node:getdir
+ #@+node:link
+ def link(self, path, path1):
+
+ raise IOError(errno.EPERM, path)
+
+ ret = os.link(path, path1)
+ self.log("link: path=%s path1=%s\n => %s" % (path, path1, ret))
+ return ret
+
+ #@-node:link
+ #@+node:mkdir
+ def mkdir(self, path, mode):
+
+ self.log("mkdir: path=%s mode=%s" % (path, mode))
+
+ # barf if directory exists
+ if self.files.has_key(path):
+ raise IOError(errno.EEXIST, path)
+
+ # barf if happening outside /usr/
+ if not path.startswith("/usr/"):
+ raise IOError(errno.EACCES, path)
+
+ parentPath = os.path.split(path)[0]
+
+ if parentPath == '/usr':
+ # creating a new freedisk
+
+ # create the directory record
+ rec = self.addToCache(path=path, isdir=True, perm=0555)
+
+ # create the pseudo-files within it
+ for name in freediskSpecialFiles:
+ subpath = os.path.join(path, name)
+ rec = self.addToCache(path=subpath, isreg=True, perm=0644)
+ if name == '.status':
+ rec.data = "idle"
+
+ # done here
+ return 0
+
+ elif path.startswith("/usr/"):
+ # creating a dir within a freedisk
+
+ # barf if no write permission in dir
+ diskPath = "/".join(path.split("/")[:3])
+ diskRec = self.files.get(diskPath, None)
+ #if not diskRec:
+ # self.log("mkdir: diskPath=%s" % diskPath)
+ # raise IOError(errno.ENOENT, path)
+ if diskRec and not diskRec.canwrite:
+ self.log("mkdir: diskPath=%s" % diskPath)
+ raise IOError(errno.EPERM, path)
+
+ # ok to create
+ self.addToCache(path=path, isdir=True, perm=0755)
+
+ return 0
+
+ #@-node:mkdir
+ #@+node:mknod
+ def mknod(self, path, mode, dev):
+ """ Python has no os.mknod, so we can only do some things """
+
+ if path == "/":
+ #return -EINVAL
+ raise IOError(errno.EEXIST, path)
+
+ parentPath = os.path.split(path)[0]
+ if parentPath in ['/', '/usr']:
+ #return -EINVAL
+ raise IOError(errno.EPERM, path)
+
+ # start key write, if needed
+ if parentPath == "/put":
+
+ # see if an existing file
+ if self.files.has_key(path):
+ raise IOError(errno.EEXIST, path)
+
+ rec = self.addToCache(
+ path=path, isreg=True, iswriting=True,
+ perm=0644)
+ ret = 0
+
+ elif path.startswith("/usr/"):
+ # creating a file in a user dir
+
+ # barf if no write permission in dir
+ diskPath = "/".join(path.split("/")[:3])
+ diskRec = self.files.get(diskPath, None)
+ #if not diskRec:
+ # raise IOError(errno.ENOENT, path)
+ if diskRec and not diskRec.canwrite:
+ self.log("mknod: diskPath=%s" % diskPath)
+ raise IOError(errno.EPERM, path)
+
+ # create the record
+ rec = self.addToCache(path=path, isreg=True, perm=0644,
+ iswriting=True)
+ ret = 0
+
+ # fall back on host os
+ #if S_ISREG(mode):
+ # file(path, "w").close()
+ # ret = 0
+
+ else:
+ #ret = -EINVAL
+ raise IOError(errno.EPERM, path)
+
+ self.log("mknod: path=%s mode=0%o dev=%s\n => %s" % (
+ path, mode, dev, ret))
+
+ return ret
+
+ #@-node:mknod
+ #@+node:open
+ def open(self, path, flags):
+
+ self.log("open: path=%s flags=%s" % (path, flags))
+
+ # see if it's an existing file
+ rec = self.files.get(path, None)
+
+ if rec:
+ # barf if not regular file
+ if not (rec.isreg or rec.ischr):
+ self.log("open: %s is not regular file" % path)
+ raise IOError(errno.EIO, "Not a regular file: %s" % path)
+
+ else:
+ # fall back to host fs
+ raise IOError(errno.ENOENT, path)
+
+ for flag in [os.O_WRONLY, os.O_RDWR, os.O_APPEND]:
+ if flags & flag:
+ self.log("open: setting iswriting for %s" % path)
+ rec.iswriting = True
+
+ self.log("open: open of %s succeeded" % path)
+
+ # seems ok
+ return 0
+
+ #@-node:open
+ #@+node:read
+ def read(self, path, length, offset):
+ """
+ """
+ # forward to existing file if any
+ rec = self.files.get(path, None)
+ if rec:
+ rec.seek(offset)
+ buf = rec.read(length)
+
+ self.log("read: path=%s length=%s offset=%s\n => %s" % (
+ path, length, offset, len(buf)))
+ #print repr(buf)
+ return buf
+
+ else:
+ # fall back on host fs
+ f = open(path, "r")
+ f.seek(offset)
+ buf = f.read(length)
+
+ self.log("read: path=%s length=%s offset=%s\n => (%s bytes)" % (
+ path, length, offset, len(buf)))
+
+ return buf
+
+ #@-node:read
+ #@+node:readlink
+ def readlink(self, path):
+
+ ret = os.readlink(path)
+ self.log("readlink: path=%s\n => %s" % (path, ret))
+ return ret
+
+ #@-node:readlink
+ #@+node:release
+ def release(self, path, flags):
+
+ rec = self.files.get(path, None)
+ if not rec:
+ return
+
+ filename = os.path.split(path)[1]
+
+ # ditch any encoded command files
+ if path.startswith("/cmds/"):
+ print "got file %s" % path
+ rec = self.files.get(path, None)
+ if rec:
+ self.delFromCache(rec)
+ else:
+ print "eh? not in cache"
+
+ # if writing, save the thing
+ elif rec.iswriting:
+
+ self.log("release: %s: iswriting=True" % path)
+
+ # what uri?
+ rec.iswriting = False
+
+ print "Release: path=%s" % path
+
+ if path.startswith("/put/"):
+ #@ <<insert to freenet>>
+ #@+node:<<insert to freenet>>
+ # insert directly to freenet as a key
+
+ uri = os.path.split(path)[1]
+
+ # frigs to allow fancy CHK@ inserts
+ if uri.startswith("CHK@"):
+ putUri = "CHK@"
+ else:
+ putUri = uri
+
+ ext = os.path.splitext(uri)[1]
+
+ try:
+ self.log("release: inserting %s" % uri)
+
+ mimetype = fcp.node.guessMimetype(path)
+ data = rec.data
+
+ # empty the pseudo-file till a result is through
+ rec.data = 'inserting'
+
+ self.connectToNode()
+
+ #print "FIXME: data=%s" % repr(data)
+
+ if _no_node:
+ print "FIXME: not inserting"
+ getUri = "NO_URI"
+ else:
+ # perform the insert
+ getUri = self.node.put(
+ putUri,
+ data=data,
+ mimetype=mimetype)
+
+ # strip 'freenet:' prefix
+ if getUri.startswith("freenet:"):
+ getUri = getUri[8:]
+
+ # restore file extension
+ if getUri.startswith("CHK@"):
+ getUri += ext
+
+ # now cache the read-back
+ self.addToCache(
+ path="/get/"+getUri,
+ data=data,
+ perm=0444,
+ isreg=True,
+ )
+
+ # and adjust the written file to reveal read uri
+ rec.data = getUri
+
+ self.log("release: inserted %s as %s ok" % (
+ uri, mimetype))
+
+ except:
+ traceback.print_exc()
+ rec.data = 'failed'
+ self.log("release: insert of %s failed" % uri)
+ raise IOError(errno.EIO, "Failed to insert")
+ self.log("release: done with insertion")
+
+ #@-node:<<insert to freenet>>
+ #@nl
+
+ elif path.startswith("/usr/"):
+ #@ <<write to freedisk>>
+ #@+node:<<write to freedisk>>
+ # releasing a file being written into a freedisk
+
+ bits = path.split("/")
+
+ self.log("release: bits=%s" % str(bits))
+
+ if bits[0] == '' and bits[1] == 'usr':
+ diskName = bits[2]
+ fileName = bits[3]
+
+ self.log("diskName=%s fileName=%s" % (diskName, fileName))
+
+ if fileName == '.privatekey':
+ # written a private key, make the directory writeable
+ parentPath = os.path.split(path)[0]
+ parentRec = self.files[parentPath]
+ parentRec.canwrite = True
+ self.log("release: got privkey, mark dir %s
read/write" % parentRec)
+
+ elif fileName == '.cmd':
+ # wrote a command
+
+ self.log("got release of .cmd")
+
+ cmd = rec.data.strip()
+ rec.data = ""
+
+ self.log("release: cmd=%s" % cmd)
+
+ # execute according to command
+ if cmd == 'commit':
+ self.commitDisk(diskName)
+ elif cmd == 'update':
+ self.updateDisk(diskName)
+ elif cmd == 'merge':
+ self.mergeDisk(diskName)
+
+ #@-node:<<write to freedisk>>
+ #@nl
+
+
+ self.log("release: path=%s flags=%s" % (path, flags))
+ return 0
+ #@-node:release
+ #@+node:rename
+ def rename(self, path, path1):
+
+ ret = os.rename(path, path1)
+ self.log("rename: path=%s path1=%s\n => %s" % (path, path1, ret))
+ return ret
+
+ #@-node:rename
+ #@+node:rmdir
+ def rmdir(self, path):
+
+ self.log("rmdir: path=%s" % path)
+
+ rec = self.files.get(path, None)
+
+ # barf if no such directory
+ if not rec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf if not a directory
+ if not rec.isdir:
+ raise IOError(errno.ENOTDIR, path)
+
+ # barf if not within freedisk mounts
+ if not path.startswith("/usr/"):
+ raise IOError(errno.EACCES, path)
+
+ # seek the freedisk record
+ bits = path.split("/")
+ diskPath = "/".join(bits[:3])
+ diskRec = self.files.get(diskPath, None)
+
+ # barf if nonexistent
+ if not diskRec:
+ raise IOError(errno.ENOENT, path)
+
+ # if a freedisk root, just delete
+ if path == diskPath:
+ # remove directory record
+ self.delFromCache(rec)
+
+ # and remove children
+ for k in self.files.keys():
+ if k.startswith(path+"/"):
+ del self.files[k]
+
+ return 0
+
+ # now, it's a subdir within a freedisk
+
+ # barf if non-empty
+ if rec.children:
+ raise IOError(errno.ENOTEMPTY, path)
+
+ # now, at last, can remove
+ self.delFromCache(rec)
+ ret = 0
+
+ self.log("rmdir: => %s" % ret)
+
+ return ret
+
+ #@-node:rmdir
+ #@+node:statfs
+ def statfs(self):
+ """
+ Should return a tuple with the following 6 elements:
+ - blocksize - size of file blocks, in bytes
+ - totalblocks - total number of blocks in the filesystem
+ - freeblocks - number of free blocks
+ - totalfiles - total number of file inodes
+ - freefiles - nunber of free file inodes
+
+ Feel free to set any of the above values to 0, which tells
+ the kernel that the info is not available.
+ """
+ self.log("statfs: returning fictitious values")
+ blocks_size = 1024
+ blocks = 100000
+ blocks_free = 25000
+ files = 100000
+ files_free = 60000
+ namelen = 80
+
+ return (blocks_size, blocks, blocks_free, files, files_free, namelen)
+
+ #@-node:statfs
+ #@+node:symlink
+ def symlink(self, path, path1):
+
+ raise IOError(errno.EPERM, path)
+
+ ret = os.symlink(path, path1)
+ self.log("symlink: path=%s path1=%s\n => %s" % (path, path1, ret))
+ return ret
+
+ #@-node:symlink
+ #@+node:truncate
+ def truncate(self, path, size):
+
+ self.log("truncate: path=%s size=%s" % (path, size))
+
+ if not path.startswith("/usr/"):
+ raise IOError(errno.EPERM, path)
+
+ parentPath, filename = os.path.split(path)
+
+ if os.path.split(parentPath)[0] != "/usr":
+ raise IOError(errno.EPERM, path)
+
+ rec = self.files.get(path, None)
+ if not rec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf at readonly files
+ if filename == '.status':
+ raise IOError(errno.EPERM, path)
+
+ rec.data = ""
+
+ ret = 0
+
+ self.log("truncate: => %s" % ret)
+
+ return ret
+
+ #@-node:truncate
+ #@+node:unlink
+ def unlink(self, path):
+
+ self.log("unlink: path=%s" % path)
+
+ # remove existing file?
+ if path.startswith("/get/") \
+ or path.startswith("/put/") \
+ or path.startswith("/keys/"):
+ rec = self.files.get(path, None)
+ if not rec:
+ raise IOError(2, path)
+ self.delFromCache(rec)
+ return 0
+
+ if path.startswith("/usr"):
+ # remove a file within a freedisk
+
+ # barf if nonexistent
+ rec = self.files.get(path, None)
+ if not rec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf if removing dir
+ if rec.isdir:
+ raise IOError(errno.EISDIR, path)
+
+ # barf if trying to remove a . control file
+ bits = path.split("/")[2:]
+ diskPath = "/".join(path.split("/")[:3])
+ if len(bits) == 2 and bits[1] in freediskSpecialFiles:
+ raise IOError(errno.EACCES, path)
+
+ # barf if not on an existing freedisk
+ diskRec = self.files.get(diskPath, None)
+ if not diskRec:
+ raise IOError(errno.ENOENT, path)
+
+ # barf if freedisk not writeable
+ if not diskRec.canwrite:
+ raise IOError(errno.EACCES, path)
+
+ # ok to delete
+ self.delFromCache(rec)
+
+ ret = 0
+ else:
+ raise IOError(errno.ENOENT, path)
+
+ # fallback on host fs
+ self.log("unlink: => %s" % ret)
+ return ret
+
+ #@-node:unlink
+ #@+node:utime
+ def utime(self, path, times):
+
+ ret = os.utime(path, times)
+ self.log("utime: path=%s times=%s\n => %s" % (path, times, ret))
+ return ret
+
+ #@-node:utime
+ #@+node:write
+ def write(self, path, buf, off):
+
+ dataLen = len(buf)
+
+ rec = self.files.get(path, None)
+ if rec:
+ # write to existing 'file'
+ rec.seek(off)
+ rec.write(buf)
+ rec.hasdata = True
+ else:
+ f = open(path, "r+")
+ f.seek(off)
+ nwritten = f.write(buf)
+ f.flush()
+
+ self.log("write: path=%s buf=[%s bytes] off=%s" % (path, len(buf),
off))
+
+ #return nwritten
+ return dataLen
+
+ #@-node:write
+ #@-others
+
+ #@-node:fs primitives
+ #@+node:freedisk methods
+ # methods for freedisk operations
+
+ #@+others
+ #@+node:setupFreedisks
+ def setupFreedisks(self):
+ """
+ Initialises the freedisks
+ """
+ self.freedisks = {}
+
+ #@-node:setupFreedisks
+ #@+node:newDisk
+ def newDisk(self, name, uri=None):
+ """
+ Adds (mounts) a freedisk within freenetfs
+
+ Arguments:
+ - name - name of disk - will be mounted in as /usr/<name>
+ - uri - a private SSK key URI. If not given, one will be
+ randomly generated
+ """
+
+ #@-node:newDisk
+ #@+node:addDisk
+ def addDisk(self, name, uri):
+ """
+ Adds (mounts) a freedisk within freenetfs
+
+ Arguments:
+ - name - name of disk - will be mounted in as /usr/<name>
+ - uri - a public or private SSK key URI. Parsing of the key will
+ reveal whether it's public or private. If public, the freedisk
+ will be mounted read-only. If private, the freedisk will be
+ mounted read/write
+ """
+
+ #@-node:addDisk
+ #@+node:delDisk
+ def delDisk(self, name):
+ """
+ drops a freedisk mount
+
+ Arguments:
+ - name - the name of the disk
+ """
+
+ #@-node:delDisk
+ #@+node:commitDisk
+ def commitDisk(self, name):
+ """
+ synchronises a freedisk TO freenet
+
+ Arguments:
+ - name - the name of the disk
+ """
+ self.log("commitDisk: disk=%s" % name)
+
+ startTime = time.time()
+
+ # determine freedisk's absolute path within the freenetfs
+ rootPath = os.path.join("/usr", name)
+
+ # get the freedisk root's record, barf if nonexistent
+ rootRec = self.files.get(rootPath, None)
+ if not rootRec:
+ self.log("commitDisk: no disk '%s' mounted!" % name)
+ return
+
+ # determine pseudo-file paths
+ statusFile = self.files[os.path.join(rootPath, ".status")]
+ privKeyFile = self.files[os.path.join(rootPath, ".privatekey")]
+ pubKeyFile = self.files[os.path.join(rootPath, ".publickey")]
+
+ # and get the private key, sans 'freenet:'
+ privKey = privKeyFile.data.split("freenet:")[-1]
+
+ # process further
+ privKey = privKey.replace("SSK@", "USK@").split("/")[0] + "/" + name +
"/0"
+
+ self.log("commit: privKey=%s" % privKey)
+
+ if privKey.startswith("SSK@"):
+ # convert to USK
+ privKey = "USK" + privKey[3:] + "/0"
+
+ self.log("commitDisk: checking files in %s" % rootPath)
+
+ # update status
+ statusFile.data = "committing\nAnalysing files\n"
+
+ # get list of records of files within this freedisk
+ fileRecs = []
+ for f in self.files.keys():
+ # is file/dir within the freedisk?
+ if f.startswith(rootPath+"/"):
+ # yes, get its record
+ fileRec = self.files[f]
+
+ # is it a file, and not a special file?
+ if fileRec.isfile and (os.path.split(f)[1] not in
freediskSpecialFiles):
+ # yes, grab it
+ fileRecs.append(fileRec)
+
+ # now sort them
+ fileRecs.sort(lambda r1, r2: cmp(r1.path, r2.path))
+
+ statusFile.data = "committing\nConnecting to Freenet\n"
+
+ # make sure we have a node to talk to
+ self.connectToNode()
+ node = self.node
+
+ # now insert all these files
+ maxJobs = 5
+ jobsWaiting = fileRecs[:]
+ jobsRunning = []
+ jobsDone = []
+
+ # determine CHKs for all these jobs
+ for rec in jobsWaiting:
+ rec.mimetype = guessMimetype(rec.path)
+ rec.uri = node.put(
+ "CHK at file",
+ data=rec.data,
+ chkonly=True,
+ mimetype=rec.mimetype)
+
+ statusFile.data = "committing\nInserting Files\n"
+
+ # now, create the manifest
+ manifest = XMLFile(root="freedisk")
+ root = manifest.root
+ for rec in jobsWaiting:
+ fileNode = root._addNode("file")
+ fileNode.path = rec.path
+ fileNode.uri = rec.uri
+ try:
+ fileNode.mimetype = rec.mimetype
+ except:
+ fileNode.mimetype = "text/plain"
+ fileNode.hash = sha.new(rec.data).hexdigest()
+
+ # and add the manifest as a waiting job
+ manifestJob = node.put(
+ privKey,
+ data=manifest.toxml(),
+ mimetype="text/xml",
+ async=True,
+ )
+
+ #jobsRunning.append(manifestJob)
+ #manifestUri = manifestJob.wait()
+ #print "manifestUri=%s" % manifestUri
+ #time.sleep(6)
+
+ # the big insert/wait loop
+ while jobsWaiting or jobsRunning:
+ nWaiting = len(jobsWaiting)
+ nRunning = len(jobsRunning)
+ self.log("commit: %s waiting, %s running" % (nWaiting,nRunning))
+
+ statusFile.data = "committing\n%s files queued, %s inserting\n" % (
+ nWaiting, nRunning)
+
+ # launch jobs, if available, and if spare slots
+ while len(jobsRunning) < maxJobs and jobsWaiting:
+
+ rec = jobsWaiting.pop(0)
+
+ # if record has data, insert it, otherwise take as done
+ if rec.hasdata:
+ uri = rec.uri
+ if not uri:
+ uri = "CHK at somefile" + os.path.splitext(rec.path)[1]
+ job = node.put(uri, data=rec.data, async=True)
+ rec.job = job
+ jobsRunning.append(rec)
+ else:
+ # record should already have the hash, uri, mimetype
+ jobsDone.append(rec)
+
+ # check running jobs
+ for rec in jobsRunning:
+ if rec == manifestJob:
+ job = rec
+ else:
+ job = rec.job
+
+ if job.isComplete():
+ jobsRunning.remove(rec)
+
+ uri = job.wait()
+
+ if job != manifestJob:
+ rec.uri = uri
+ rec.job = None
+ jobsDone.append(rec)
+
+ # breathe!!
+ if jobsRunning:
+ time.sleep(5)
+ else:
+ time.sleep(1)
+
+ statusFile.data = "idle"
+
+ self.log("commitDisk: done, manifestUri=%s" % manifestJob.uri)
+
+ pubKeyFile.data = manifestJob.uri
+
+ endTime = time.time()
+ commitTime = endTime - startTime
+
+ self.log("commitDisk: commit completed in %s seconds" % commitTime)
+
+ #@-node:commitDisk
+ #@+node:updateDisk
+ def updateDisk(self, name):
+ """
+ synchronises a freedisk FROM freenet
+
+ Arguments:
+ - name - the name of the disk
+ """
+ self.log("updateDisk: disk=%s" % name)
+
+ startTime = time.time()
+
+ # determine freedisk's absolute path within the freenetfs
+ rootPath = os.path.join("/usr", name)
+
+ # get the freedisk root's record, barf if nonexistent
+ rootRec = self.files.get(rootPath, None)
+ if not rootRec:
+ self.log("updateDisk: no disk '%s' mounted!" % name)
+ return
+
+ # determine pseudo-file paths
+ statusFile = self.files[os.path.join(rootPath, ".status")]
+ pubKeyFile = self.files[os.path.join(rootPath, ".publickey")]
+
+ # and get the private key, sans 'freenet:'
+ pubKey = pubKeyFile.data.split("freenet:")[-1]
+
+ # process further
+ pubKey = privKey.replace("SSK@", "USK@").split("/")[0] + "/" + name +
"/0"
+
+ self.log("update: pubKey=%s" % pubKey)
+
+ # fetch manifest
+
+ # mark disk as readonly
+
+ # for each entry in manifest
+ # if not localfile has changed
+ # replace the file record
+
+ #@-node:updateDisk
+ #@+node:getManifest
+ def getManifest(self, name):
+ """
+ Retrieves the manifest of a given disk
+ """
+ #@-node:getManifest
+ #@+node:putManifest
+ def putManifest(self, name):
+ """
+ Inserts a freedisk manifest into freenet
+ """
+ #@-node:putManifest
+ #@-others
+
+ #@-node:freedisk methods
+ #@+node:hashpath
+ def hashpath(self, path):
+
+ return sha.new(path).hexdigest()
+
+ #@-node:hashpath
+ #@+node:addToCache
+ def addToCache(self, rec=None, **kw):
+ """
+ Tries to 'cache' a given file/dir record, and
+ adds it to parent dir
+ """
+ if rec == None:
+ rec = FileRecord(self, **kw)
+
+ path = rec.path
+
+ # barf if file/dir already exists
+ if self.files.has_key(path):
+ self.log("addToCache: already got %s !!!" % path)
+ return
+
+ #print "path=%s" % path
+
+ # if not root, add to parent
+ if path != '/':
+ parentPath = os.path.split(path)[0]
+ parentRec = self.files.get(parentPath, None)
+ parentRec.addChild(rec)
+ if not parentRec:
+ self.log("addToCache: no parent of %s ?!?!" % path)
+ return
+
+ # ok, add to our table
+ self.files[path] = rec
+
+ # done
+ return rec
+
+ #@-node:addToCache
+ #@+node:delFromCache
+ def delFromCache(self, rec):
+ """
+ Tries to remove file/dir record from cache
+ """
+ path = rec.path
+ parentPath = os.path.split(path)[0]
+
+ if self.files.has_key(path):
+ del self.files[path]
+
+ parentRec = self.files.get(parentPath, None)
+ if parentRec:
+ parentRec.delChild(rec)
+
+ #@-node:delFromCache
+ #@+node:__getDirStat
+ def __getDirStat(self, path):
+ """
+ returns a stat tuple for given path
+ """
+ return FileRecord(mode=0700, path=path, isdir=True)
+
+ #@-node:__getDirStat
+ #@+node:statFromKw
+ def statFromKw(self, **kw):
+ """
+ Constructs a stat tuple from keywords
+ """
+ tup = [0] * 10
+
+ # build mode mask
+ mode = kw.get('mode', 0)
+ if kw.get('isdir', False):
+ mode |= stat.S_IFDIR
+ if kw.get('ischr', False):
+ mode |= stat.S_IFCHR
+ if kw.get('isblk', False):
+ mode |= stat.S_IFBLK
+ if kw.get('isreg', False):
+ mode |= stat.S_IFREG
+ if kw.get('isfifo', False):
+ mode |= stat.S_IFIFO
+ if kw.get('islink', False):
+ mode |= stat.S_IFLNK
+ if kw.get('issock', False):
+ mode |= stat.S_IFSOCK
+
+ path = kw['path']
+
+ # get inode number
+ inode = self.pathToInode(path)
+
+ dev = 0
+
+ nlink = 1
+ uid = myuid
+ gid = mygid
+ size = 0
+ atime = mtime = ctime = timeNow()
+
+ return (mode, inode, dev, nlink, uid, gid, size, atime, mtime, ctime)
+
+ # st_mode, st_ino, st_dev, st_nlink,
+ # st_uid, st_gid, st_size,
+ # st_atime, st_mtime, st_ctime
+
+ #@-node:statFromKw
+ #@+node:statToDict
+ def statToDict(self, info):
+ """
+ Converts a tuple returned by a stat call into
+ a dict with keys:
+
+ - isdir
+ - ischr
+ - isblk
+ - isreg
+ - isfifo
+ - islnk
+ - issock
+ - mode
+ - inode
+ - dev
+ - nlink
+ - uid
+ - gid
+ - size
+ - atime
+ - mtime
+ - ctime
+ """
+ print "statToDict: info=%s" % str(info)
+
+ mode = info[stat.ST_MODE]
+ return {
+ 'isdir' : stat.S_ISDIR(mode),
+ 'ischr' : stat.S_ISCHR(mode),
+ 'isblk' : stat.S_ISBLK(mode),
+ 'isreg' : stat.S_ISREG(mode),
+ 'isfifo' : stat.S_ISFIFO(mode),
+ 'islink' : stat.S_ISLNK(mode),
+ 'issock' : stat.S_ISSOCK(mode),
+ 'mode' : mode,
+ 'inode' : info[stat.ST_INO],
+ 'dev' : info[stat.ST_DEV],
+ 'nlink' : info[stat.ST_NLINK],
+ 'uid' : info[stat.ST_UID],
+ 'gid' : info[stat.ST_GID],
+ 'size' : info[stat.ST_SIZE],
+ 'atime' : info[stat.ST_ATIME],
+ 'mtime' : info[stat.ST_MTIME],
+ 'ctime' : info[stat.ST_CTIME],
+ }
+
+ #@-node:statToDict
+ #@+node:getReadURI
+ def getReadURI(self, path):
+ """
+ Converts to a pathname to a freenet URI for insertion,
+ using public key
+ """
+ return self.pubkey + self.hashpath(path) + "/0"
+
+ #@-node:getReadURI
+ #@+node:getWriteURI
+ def getWriteURI(self, path):
+ """
+ Converts to a pathname to a freenet URI for insertion,
+ using private key if any
+ """
+ if not self.privkey:
+ raise Exception("cannot write: no private key")
+
+ return self.privkey + self.hashpath(path) + "/0"
+
+ #@-node:getWriteURI
+ #@-others
+
+#@-node:class FreenetFS
+#@+node:class FileRecord
+class FileRecord(list):
+ """
+ Encapsulates the info for a file, and can
+ be returned by getattr
+ """
+ #@ @+others
+ #@+node:attribs
+ # default attribs, can be overwritten by constructor keywords
+ haschanged = False
+ hasdata = False
+ canwrite = False
+ iswriting = False
+ uri = None
+
+ #@-node:attribs
+ #@+node:__init__
+ def __init__(self, fs, statrec=None, **kw):
+ """
+ """
+ # copy keywords cos we'll be popping them
+ kw = dict(kw)
+
+ # save fs ref
+ self.fs = fs
+
+ # got a statrec arg?
+ if statrec:
+ # yes, extract main items
+ dev = statrec[stat.ST_DEV]
+ nlink = statrec[stat.ST_NLINK]
+ uid = statrec[stat.ST_UID]
+ gid = statrec[stat.ST_GID]
+ size = statrec[stat.ST_SIZE]
+ else:
+ # no, fudge a new one
+ statrec = [0,0,0,0,0,0,0,0,0,0]
+ dev = 0
+ nlink = 1
+ uid = myuid
+ gid = mygid
+ size = 0
+
+ # convert tuple to list if need be
+ if not hasattr(statrec, '__setitem__'):
+ statrec = list(statrec)
+
+ # build mode mask
+ mode = kw.pop('mode', 0)
+ if kw.pop('isdir', False):
+ mode |= stat.S_IFDIR
+ if kw.pop('ischr', False):
+ mode |= stat.S_IFCHR
+ if kw.pop('isblk', False):
+ mode |= stat.S_IFBLK
+ if kw.pop('isreg', False):
+ mode |= stat.S_IFREG
+ if kw.pop('isfifo', False):
+ mode |= stat.S_IFIFO
+ if kw.pop('islink', False):
+ mode |= stat.S_IFLNK
+ if kw.pop('issock', False):
+ mode |= stat.S_IFSOCK
+
+ # handle non-file-related keywords
+ perm = kw.pop('perm', 0)
+ mode |= perm
+
+ # set path
+ path = kw.pop('path')
+ self.path = path
+
+ # set up data stream
+ if kw.has_key("data"):
+ self.stream = StringIO(kw.pop('data'))
+ self.hasdata = True
+ else:
+ self.stream = StringIO()
+
+ # find parent, if any
+ if path == '/':
+ self.parent = None
+ else:
+ parentPath = os.path.split(path)[0]
+ parentRec = fs.files[parentPath]
+ self.parent = parentRec
+
+ # child files/dirs
+ self.children = []
+
+ # get inode number
+ inode = pathToInode(path)
+
+ #size = kw.get('size', 0)
+ now = timeNow()
+ atime = kw.pop('atime', now)
+ mtime = kw.pop('mtime', now)
+ ctime = kw.pop('ctime', now)
+
+ #print "statrec[stat.ST_MODE]=%s" % statrec[stat.ST_MODE]
+ #print "mode=%s" % mode
+
+ statrec[stat.ST_MODE] |= mode
+ statrec[stat.ST_INO] = inode
+ statrec[stat.ST_DEV] = dev
+ statrec[stat.ST_NLINK] = nlink
+ statrec[stat.ST_UID] = uid
+ statrec[stat.ST_GID] = gid
+
+ statrec[stat.ST_SIZE] = len(self.stream.getvalue())
+
+ statrec[stat.ST_ATIME] = atime
+ statrec[stat.ST_MTIME] = atime
+ statrec[stat.ST_CTIME] = atime
+
+ # throw remaining keywords into instance's attribs
+ self.__dict__.update(kw)
+
+ # finally, parent constructor, now that we have a complete stat list
+ list.__init__(self, statrec)
+
+ #@-node:__init__
+ #@+node:__getattr__
+ def __getattr__(self, attr):
+ """
+ Support read of pseudo-attributes:
+ - mode, isdir, ischr, isblk, isreg, isfifo, islnk, issock,
+ - inode, dev, nlink, uid, gid, size, atime, mtime, ctime
+ """
+ if attr == 'mode':
+ return self[stat.ST_MODE]
+
+ if attr == 'isdir':
+ return stat.S_ISDIR(self.mode)
+
+ if attr == 'ischr':
+ return stat.S_ISCHR(self.mode)
+
+ if attr == 'isblk':
+ return stat.S_ISBLK(self.mode)
+
+ if attr in ['isreg', 'isfile']:
+ return stat.S_ISREG(self.mode)
+
+ if attr == 'isfifo':
+ return stat.S_ISFIFO(self.mode)
+
+ if attr == 'islnk':
+ return stat.S_ISLNK(self.mode)
+
+ if attr == 'issock':
+ return stat.S_ISSOCK(self.mode)
+
+ if attr == 'inode':
+ return self[stat.ST_INO]
+
+ if attr == 'dev':
+ return self[stat.ST_DEV]
+
+ if attr == 'nlink':
+ return self[stat.ST_NLINK]
+
+ if attr == 'uid':
+ return self[stat.ST_UID]
+
+ if attr == 'gid':
+ return self[stat.ST_GID]
+
+ if attr == 'size':
+ return self[stat.ST_SIZE]
+
+ if attr == 'atime':
+ return self[stat.ST_ATIME]
+
+ if attr == 'mtime':
+ return self[stat.ST_ATIME]
+
+ if attr == 'ctime':
+ return self[stat.ST_ATIME]
+
+ if attr == 'data':
+ return self.stream.getvalue()
+
+ try:
+ return getattr(self.stream, attr)
+ except:
+ pass
+
+ raise AttributeError(attr)
+
+ #@-node:__getattr__
+ #@+node:__setattr__
+ def __setattr__(self, attr, val):
+ """
+ Support write of pseudo-attributes:
+ - mode, isdir, ischr, isblk, isreg, isfifo, islnk, issock,
+ - inode, dev, nlink, uid, gid, size, atime, mtime, ctime
+ """
+ if attr == 'isdir':
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFDIR
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFDIR
+ elif attr == 'ischr':
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFCHR
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFCHR
+ elif attr == 'isblk':
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFBLK
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFBLK
+ elif attr in ['isreg', 'isfile']:
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFREG
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFREG
+ elif attr == 'isfifo':
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFIFO
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFIFO
+ elif attr == 'islnk':
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFLNK
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFLNK
+ elif attr == 'issock':
+ if val:
+ self[stat.ST_MODE] |= stat.S_IFSOCK
+ else:
+ self[stat.ST_MODE] &= ~stat.S_IFSOCK
+
+ elif attr == 'mode':
+ self[stat.ST_MODE] = val
+ elif attr == 'inode':
+ self[stat.ST_IMO] = val
+ elif attr == 'dev':
+ self[stat.ST_DEV] = val
+ elif attr == 'nlink':
+ self[stat.ST_NLINK] = val
+ elif attr == 'uid':
+ self[stat.ST_UID] = val
+ elif attr == 'gid':
+ self[stat.ST_GID] = val
+ elif attr == 'size':
+ self[stat.ST_SIZE] = val
+ elif attr == 'atime':
+ self[stat.ST_ATIME] = val
+ elif attr == 'mtime':
+ self[stat.ST_MTIME] = val
+ elif attr == 'ctime':
+ self[stat.ST_CTIME] = val
+
+ elif attr == 'data':
+ oldPos = self.stream.tell()
+ self.stream = StringIO(val)
+ self.stream.seek(min(oldPos, len(val)))
+ self.size = len(val)
+
+ else:
+ self.__dict__[attr] = val
+
+ #@-node:__setattr__
+ #@+node:write
+ def write(self, buf):
+
+ self.stream.write(buf)
+ self.size = len(self.stream.getvalue())
+
+ #@-node:write
+ #@+node:addChild
+ def addChild(self, rec):
+ """
+ Adds a child file rec as a child of this rec
+ """
+ if not isinstance(rec, FileRecord):
+ raise Exception("Not a FileRecord: %s" % rec)
+
+ self.children.append(rec)
+ self.size += 1
+
+ #@-node:addChild
+ #@+node:delChild
+ def delChild(self, rec):
+ """
+ Tries to remove a child entry
+ """
+ if rec in self.children:
+ self.children.remove(rec)
+ self.size -= 1
+
+ else:
+ print "eh? trying to remove %s from %s" % (rec.path, self.path)
+
+ #@-node:delChild
+ #@-others
+
+#@-node:class FileRecord
+#@+node:class FreediskMgr
+class FreediskMgr:
+ """
+ Gateway for mirroring a local directory to/from freenet
+ """
+ #@ @+others
+ #@+node:__init__
+ def __init__(self, **kw):
+ """
+ Creates a freediskmgr object
+
+ Keywords:
+ - name - mandatory - the name of the disk
+ - fcpNode - mandatory - an FCPNode instance
+ - root - mandatory - the root directory
+ - publicKey - the freenet public key URI
+ - privateKey - the freenet private key URI
+ Notes:
+ - exactly one of publicKey, privateKey keywords must be given
+ """
+
+ #@-node:__init__
+ #@+node:update
+ def update(self):
+ """
+ Update from freenet to local directory
+ """
+
+ #@-node:update
+ #@+node:commit
+ def commit(self):
+ """
+ commit from local directory into freenet
+ """
+
+ #@-node:commit
+ #@-others
+
+#@-node:class FreediskMgr
+#@+node:pathToInode
+def pathToInode(path):
+ """
+ Comes up with a unique inode number given a path
+ """
+ # try for existing known path/inode
+ inode = inodes.get(path, None)
+ if inode != None:
+ return inode
+
+ # try hashing the path to 32bit
+ inode = int(md5.new(path).hexdigest()[:7], 16)
+
+ # and ensure it's unique
+ while inodes.has_key(inode):
+ inode += 1
+
+ # register it
+ inodes[path] = inode
+
+ # done
+ return inode
+
+#@-node:pathToInode
+#@+node:timeNow
+def timeNow():
+ return int(time.time()) & 0xffffffffL
+
+#@-node:timeNow
+#@+node:usage
+def usage(msg, ret=1):
+
+ print "Usage: %s mountpoint -o args" % progname
+
+ sys.exit(ret)
+
+#@-node:usage
+#@+node:main
+def main():
+
+ kw = {}
+ args = []
+
+ if argc != 5:
+ usage("Bad argument count")
+
+ mountpoint = argv[2]
+
+ for o in argv[4].split(","):
+ try:
+ k, v = o.split("=", 1)
+ kw[k] = v
+ except:
+ args.append(o)
+
+ #kw['multithreaded'] = True
+ kw['multithreaded'] = False
+
+ if os.fork() == 0:
+ server = FreenetFS(mountpoint, *args, **kw)
+ server.run()
+
+#@-node:main
+#@+node:mainline
+if __name__ == '__main__':
+
+ main()
+#@-node:mainline
+#@-others
+
+#@-node:@file freenetfs.py
+#@-leo
Modified: trunk/apps/pyFreenet/fcp/node.py
===================================================================
--- trunk/apps/pyFreenet/fcp/node.py 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/fcp/node.py 2006-06-04 08:00:16 UTC (rev 9040)
@@ -15,7 +15,7 @@
import sys, os, socket, time, thread
import threading, mimetypes, sha, Queue
-import select, traceback
+import select, traceback, base64
class ConnectionRefused(Exception):
"""
@@ -53,6 +53,12 @@
defaultFCPHost = "127.0.0.1"
defaultFCPPort = 9481
+# may set environment vars for FCP host/port
+if os.environ.has_key("FCP_HOST"):
+ defaultFCPHost = os.environ["FCP_HOST"].strip()
+if os.environ.has_key("FCP_PORT"):
+ defaultFCPPort = int(os.environ["FCP_PORT"].strip())
+
# poll timeout period for manager thread
pollTimeout = 0.1
#pollTimeout = 3
@@ -75,6 +81,8 @@
DETAIL = 5
DEBUG = 6
+defaultVerbosity = ERROR
+
class FCPNode:
"""
Represents an interface to a freenet node via its FCP port,
@@ -126,8 +134,10 @@
Keyword Arguments:
- name - name of client to use with reqs, defaults to random. This
is crucial if you plan on making persistent requests
- - host - hostname, defaults to defaultFCPHost
- - port - port number, defaults to defaultFCPPort
+ - host - hostname, defaults to environment variable FCP_HOST, and
+ if this doesn't exist, then defaultFCPHost
+ - port - port number, defaults to environment variable FCP_PORT,
and
+ if this doesn't exist, then defaultFCPPort
- logfile - a pathname or writable file object, to which log
messages
should be written, defaults to stdout
- verbosity - how detailed the log messages should be, defaults to 0
@@ -148,9 +158,11 @@
"""
# grab and save parms
+ env = os.environ
self.name = kw.get('clientName', self._getUniqueId())
- self.host = kw.get('host', defaultFCPHost)
- self.port = kw.get('port', defaultFCPPort)
+ self.host = kw.get('host', env.get("FCP_HOST", defaultFCPHost))
+ self.port = kw.get('port', env.get("FCP_PORT", defaultFCPPort))
+ self.port = int(self.port)
# set up the logger
logfile = kw.get('logfile', None) or sys.stdout
@@ -160,7 +172,7 @@
raise Exception("Bad logfile '%s', must be pathname or file
object" % logfile)
logfile = file(logfile, "a")
self.logfile = logfile
- self.verbosity = kw.get('verbosity', 0)
+ self.verbosity = kw.get('verbosity', defaultVerbosity)
# try to connect to node
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -313,8 +325,8 @@
else:
opts["DSOnly"] = "false"
- if uri.startswith("freenet:CHK@") or uri.startswith("CHK@"):
- uri = os.path.splitext(uri)[0]
+ # if uri.startswith("freenet:CHK@") or uri.startswith("CHK@"):
+ # uri = os.path.splitext(uri)[0]
opts['URI'] = uri
opts['MaxRetries'] = kw.get("maxretries", 3)
@@ -393,8 +405,33 @@
raise Exception("Global requests must be persistent")
opts['URI'] = uri
- opts['Metadata.ContentType'] = kw.get("mimetype", "text/plain")
+
+ # determine a mimetype
+ mimetype = kw.get("mimetype", None)
+ if kw.has_key('mimetype'):
+ # got an explicit mimetype - use it
+ mimetype = kw['mimetype']
+ else:
+ # not explicitly given - figure one out
+ ext = os.path.splitext(uri)[1]
+ if not ext:
+ # no CHK@ file extension, try for filename
+ if kw.has_key('file'):
+ # try to grab a file extension from inserted file
+ ext = os.path.splitext(kw['file'])[1]
+ if not ext:
+ # last resort fallback
+ ext = ".txt"
+ # got some kind of 'file extension', convert to mimetype
+ try:
+ mimetype = mimetypes.guess_type(ext)[0] or "text/plain"
+ except:
+ mimetype = "text/plain"
+
+ # now can specify the mimetype
+ opts['Metadata.ContentType'] = mimetype
+
id = kw.pop("id", None)
if not id:
id = self._getUniqueId()
@@ -813,6 +850,9 @@
if cmd == 'ClientGet':
job.uri = kw['URI']
+ if cmd == 'ClientPut':
+ job.mimetype = kw['Metadata.ContentType']
+
self.clientReqQueue.put(job)
self._log(DEBUG, "_submitCmd: id=%s cmd=%s kw=%s" % (id, cmd,
str(kw)[:256]))
@@ -1411,6 +1451,59 @@
if m == None:
m = "text/plain"
return m
+def uriIsPrivate(uri):
+ """
+ analyses an SSK URI, and determines if it is an SSK or USK private key
+ """
+ if uri.startswith("freenet:"):
+ uri = uri[8:]
+
+ if not (uri.startswith("SSK@") or uri.startswith("USK@")):
+ return False
+
+ # rip off any path stuff
+ uri = uri.split("/")[0]
+ # blunt rule of thumb - 2 commas is pubkey, 1 is privkey
+ if len(uri.split(",")) == 2:
+ return True
+
+ return False
+# functions to encode/decode base64, freenet alphabet
+def base64encode(raw):
+ """
+ Encodes a string to base64, using the Freenet alphabet
+ """
+ # encode using standard RFC1521 base64
+ enc = base64.encodestring(raw)
+
+ # convert the characters to freenet encoding scheme
+ enc = enc.replace("+", "~")
+ enc = enc.replace("/", "-")
+ enc = enc.replace("=", "_")
+ enc = enc.replace("\n", "")
+ return enc
+
+def base64decode(enc):
+ """
+ Decodes a freenet-encoded base64 string back to a binary string
+
+ Arguments:
+ - enc - base64 string to decode
+ """
+ # convert from Freenet alphabet to RFC1521 format
+ enc = enc.replace("~", "+")
+ enc = enc.replace("-", "/")
+ enc = enc.replace("_", "=")
+
+ # now ready to decode
+ raw = base64.decodestring(enc)
+
+ return raw
+
+
+
+
+
Added: trunk/apps/pyFreenet/fcp/xmlobject.py
===================================================================
--- trunk/apps/pyFreenet/fcp/xmlobject.py 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/fcp/xmlobject.py 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,640 @@
+#@+leo-ver=4
+#@+node:@file xmlobject.py
+"""
+Allows XML files to be operated on like Python objects.
+
+Features:
+ - load XML source from file pathnames, readable file objects or raw strings
+ - add, get and set tag attributes like with python attributes
+ - iterate over nodes
+ - save the modified XMLFile or XMLObject to file
+
+Example XML file::
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <rapsheets>
+ <person name="John Smith" age="42">
+ <!-- John Smith has an appeal in process against his last conviction
-->
+ <crime name="Armed robbery" date="March 11, 1994"/>
+ <crime name="Aggravated burglary" date="June 9, 2001"/>
+ </person>
+ <person name="Mary Jones" age="33">
+ <crime name="Prostitution" date="January 8, 1997"/>
+ <crime name="Selling heroin" date="September 4, 2002"/>
+ <crime name="Manslaughter" date="December 21, 2004"/>
+ </person>
+ </rapsheets>
+
+Example usage::
+
+ >>> from xmlobject import XMLFile
+
+ >>> x = XMLFile(path="sample.xml)
+
+ >>> print x
+ <xmlobj.XMLFile instance at 0xb7ccc52c>
+
+ >>> print x.root
+ <XMLNode: rapsheets>
+
+ >>> print x.root._children
+ [<XMLNode: text>, <XMLNode: person>, <XMLNode: text>,
+ <XMLNode: person>, <XMLNode: text>]
+
+ >>> print x.root.person
+ [<XMLNode: person>, <XMLNode: person>]
+
+ >>> print x.root.person[0].name
+ John Smith
+
+ >>> john = x.root.person[0]
+
+ >>> john.height = 184
+
+ >>> c = john._addNode("crime")
+
+ >>> c.name = "Grand Theft Auto"
+
+ >>> c.date = "4 May, 2005"
+
+ >>> print x.toxml()
+ <?xml version="1.0" ?>
+ <rapsheets>
+ <person age="42" height="184" name="John Smith">
+ <!-- John Smith has an appeal in process against his last conviction
-->
+ <crime date="March 11, 1994" name="Armed robbery"/>
+ <crime date="June 9, 2001" name="Aggravated burglary"/>
+ <crime date="4 May, 2005" name="Grand Theft Auto"/></person>
+ <person age="33" name="Mary Jones">
+ <crime date="January 8, 1997" name="Prostitution"/>
+ <crime date="September 4, 2002" name="Selling heroin"/>
+ <crime date="December 21, 2004" name="Manslaughter"/>
+ </person>
+ </rapsheets>
+
+ >>>
+
+"""
+
+#@+others
+#@+node:imports
+import sys, os
+import xml.dom
+import xml.dom.minidom
+from xml.dom.minidom import parse, parseString, getDOMImplementation
+
+#@-node:imports
+#@+node:globals
+impl = getDOMImplementation()
+
+#@-node:globals
+#@+node:exceptions
+class MissingRootTag(Exception):
+ """root tag name was not given"""
+
+class InvalidXML(Exception):
+ """failed to parse XML input"""
+
+class CannotSave(Exception):
+ """unable to save"""
+
+class InvalidNode(Exception):
+ """not a valid minidom node"""
+
+#@-node:exceptions
+#@+node:class XMLFile
+class XMLFile:
+ """
+ Allows an xml file to be viewed and operated on
+ as a python object.
+
+ (If you're viewing the epydoc-generated HTML documentation, click the
'show private'
+ link at the top right of this page to see all the methods)
+
+ Holds the root node in the .root attribute, also in an attribute
+ with the same name as this root node.
+ """
+ #@ @+others
+ #@+node:__init__
+ def __init__(self, **kw):
+ """
+ Create an XMLFile
+
+ Keywords:
+ - path - a pathname from which the file can be read
+ - file - an open file object from which the raw xml
+ can be read
+ - raw - the raw xml itself
+ - root - name of root tag, if not reading content
+
+ Usage scenarios:
+ 1. Working with existing content - you must supply input in
+ one of the following ways:
+ - 'path' must be an existing file, or
+ - 'file' must be a readable file object, or
+ - 'raw' must contain raw xml as a string
+ 2. Creating whole new content - you must give the name
+ of the root tag in the 'root' keyword
+
+ Notes:
+ - Keyword precedence governing existing content is:
+ 1. path (if existing file)
+ 2. file
+ 3. raw
+ - If working with existing content:
+ - if the 'root' is given, then the content's toplevel tag
+ MUST match the value given for 'root'
+ - trying to _save will raise an exception unless 'path'
+ has been given
+ - if not working with existing content:
+ - 'root' must be given
+ - _save() will raise an exception unless 'path' has been given
+ """
+ path = kw.get("path", None)
+ fobj = kw.get("file", None)
+ raw = kw.get("raw", None)
+ root = kw.get("root", None)
+
+ if path:
+ self.path = path
+ try:
+ fobj = file(path)
+ except IOError:
+ pass
+ else:
+ self.path = None
+
+ if fobj:
+ raw = fobj.read()
+
+ if raw:
+ self.dom = xml.dom.minidom.parseString(raw)
+ else:
+ # could not source content, so create a blank slate
+ if not root:
+ # in which case, must give a root node name
+ raise MissingRootTag(
+ "No existing content, so must specify root")
+
+ # ok, create a blank dom
+ self.dom = impl.createDocument(None, root, None)
+
+ # get the root node, save it as attributes 'root' and name of node
+ rootnode = self.dom.documentElement
+
+ # now validate root tag
+ if root:
+ if rootnode.nodeName != root:
+ raise IncorrectRootTag("Gave root='%s', input has root='%s'" %
(
+ root, rootnode.nodeName))
+
+ # need this for recursion in XMLNode
+ self._childrenByName = {}
+ self._children = []
+
+ # add all the child nodes
+ for child in self.dom.childNodes:
+ childnode = XMLNode(self, child)
+ #print "compare %s to %s" % (rootnode, child)
+ if child == rootnode:
+ #print "found root"
+ self.root = childnode
+ setattr(self, rootnode.nodeName, self.root)
+
+ #@-node:__init__
+ #@+node:save
+ def save(self, where=None, obj=None):
+ """
+ Saves the document.
+
+ If argument 'where' is given, saves to it, otherwise
+ tries to save to the original given 'path' (or barfs)
+
+ Value can be a string (taken to be a file path), or an open
+ file object.
+ """
+ obj = obj or self.dom
+
+ if not where:
+ if self.path:
+ where = self.path
+
+ if isinstance(where, str):
+ where = file(where, "w")
+
+ if not where:
+ raise CannotSave("No save destination, and no original path")
+
+ where.write(obj.toxml())
+ where.flush()
+
+ #@-node:save
+ #@+node:saveAs
+ def saveAs(self, path):
+ """
+ save this time, and all subsequent times, to filename 'path'
+ """
+ self.path = path
+ self.save()
+
+ #@-node:saveAs
+ #@+node:toxml
+ def toxml(self):
+ return self.dom.toxml()
+
+ #@-node:toxml
+ #@+node:__len__
+ def __len__(self):
+ """
+ returns number of child nodes
+ """
+ return len(self._children)
+
+ #@-node:__len__
+ #@+node:__getitem__
+ def __getitem__(self, idx):
+ if isinstance(idx, int):
+ return self._children[idx]
+ else:
+ return self._childrenByName[idx]
+
+ #@-node:__getitem__
+ #@-others
+
+#@-node:class XMLFile
+#@+node:class XMLNode
+class XMLNode:
+ """
+ This is the workhorse for the xml object interface
+
+ (If you're viewing the epydoc-generated HTML documentation, click the
'show private'
+ link at the top right of this page to see all the methods)
+
+ """
+ #@ @+others
+ #@+node:__init__
+ def __init__(self, parent, node):
+ """
+ You shouldn't need to instantiate this directly
+ """
+ self._parent = parent
+ if isinstance(parent, XMLFile):
+ self._root = parent
+ else:
+ self._root = parent._root
+ self._node = node
+ self._childrenByName = {}
+ self._children = []
+
+ # add ourself to parent's children registry
+ parent._children.append(self)
+
+ # the deal with named subtags is that we store the first instance
+ # as itself, and with second and subsequent instances, we make a list
+ parentDict = self._parent._childrenByName
+ nodeName = node.nodeName
+ if not parentDict.has_key(nodeName):
+ parentDict[nodeName] = parent.__dict__[nodeName] = self
+ else:
+ if isinstance(parentDict[nodeName], XMLNode):
+ # this is the second child node of a given tag name, so convert
+ # the instance to a list
+ parentDict[nodeName] = parent.__dict__[nodeName] =
[parentDict[nodeName]]
+ parentDict[nodeName].append(self)
+
+ # figure out our type
+ self._value = None
+ if isinstance(node, xml.dom.minidom.Text):
+ self._type = "text"
+ self._value = node.nodeValue
+ elif isinstance(node, xml.dom.minidom.Element):
+ self._type = "node"
+ self._name = nodeName
+ elif isinstance(node, xml.dom.minidom.Comment):
+ self._type = "comment"
+ self._value = node.nodeValue
+ else:
+ raise InvalidNode("node class %s" % node.__class__)
+
+ # and wrap all the child nodes
+ for child in node.childNodes:
+ XMLNode(self, child)
+
+ #@-node:__init__
+ #@+node:_render
+ def _render(self):
+ """
+ Produces well-formed XML of this node's contents,
+ indented as required
+ """
+ return self._node.toxml()
+
+ #@-node:_render
+ #@+node:__repr__
+ def __repr__(self):
+ if self._type == "node":
+ return "<XMLNode: %s>" % self._node.nodeName
+ else:
+ return "<XMLNode: %s>" % self._type
+
+ #@-node:__repr__
+ #@+node:__getattr__
+ def __getattr__(self, attr):
+ """
+ Fetches an attribute or child node of this tag
+
+ If it's an attribute, then returns the attribute value as a string.
+
+ If a child node, then:
+ - if there is only one child node of that name, return it
+ - if there is more than one child node of that name, return a list
+ of child nodes of that tag name
+
+ Supports some magic attributes:
+ - _text - the value of the first child node of type text
+ """
+ #print "%s: __getattr__: attr=%s" % (self, attr)
+
+ # magic attribute to return text
+ if attr == '_text':
+ tnode = self['#text']
+ if isinstance(tnode, list):
+ tnode = tnode[0]
+ return tnode._value
+
+ if self._type in ['text', 'comment']:
+ if attr == '_value':
+ return self._node.nodeValue
+ else:
+ raise AttributeError(attr)
+
+ if self._node.hasAttribute(attr):
+ return self._node.getAttribute(attr)
+ elif self._childrenByName.has_key(attr):
+ return self._childrenByName[attr]
+
+ #elif attr == 'value':
+ # magic attribute
+
+ else:
+ raise AttributeError(attr)
+
+
+ #@-node:__getattr__
+ #@+node:__setattr__
+ def __setattr__(self, attr, val):
+ """
+ Change the value of an attribute of this tag
+
+ The magic attribute '_text' can be used to set the first child
+ text node's value
+
+ For example::
+
+ Consider:
+
+ <somenode>
+ <child>foo</child>
+ </somenode>
+
+ >>> somenode
+ <XMLNODE: somenode>
+ >>> somenode.child
+ <XMLNODE: child>
+ >>> somenode.child._text
+ 'foo'
+ >>> somenode._toxml()
+ u'<somenode><child>foo</child></somenode>'
+ >>> somenode.child._text = 'bar'
+ >>> somenode.child._text
+ 'bar'
+ >>> somenode.child._toxml()
+ u'<somenode><child>bar/child></somenode>'
+
+ """
+ if attr.startswith("_"):
+
+ # magic attribute for setting _text
+ if attr == '_text':
+ tnode = self['#text']
+ if isinstance(tnode, list):
+ tnode = tnode[0]
+ tnode._node.nodeValue = val
+ tnode._value = val
+ return
+
+ self.__dict__[attr] = val
+ elif self._type in ['text', 'comment']:
+ self._node.nodeValue = val
+ else:
+ # discern between attribute and child node
+ if self._childrenByName.has_key(attr):
+ raise Exception("Attribute Exists")
+ self._node.setAttribute(attr, str(val))
+
+ #@-node:__setattr__
+ #@+node:_keys
+ def _keys(self):
+ """
+ Return a list of attribute names
+ """
+ return self._node.attributes.keys()
+
+ def _values(self):
+ """
+ Returns a list of (attrname, attrval) tuples for this tag
+ """
+ return [self._node.getAttribute(k) for k in
self._node.attributes.keys()]
+
+ def _items(self):
+ """
+ returns a list of attribute values for this tag
+ """
+ return [(k, self._node.getAttribute(k)) for k in
self._node.attributes.keys()]
+
+ def _has_key(self, k):
+ """
+ returns True if this tag has an attribute of the given name
+ """
+ return self._node.hasAttribute(k) or self._childrenByName.has_key(k)
+
+ def _get(self, k, default=None):
+ """
+ returns the value of attribute k, or default if no such attribute
+ """
+ if self._has_key(k):
+ return getattr(self, k)
+ else:
+ return default
+ #@-node:_keys
+ #@+node:__len__
+ def __len__(self):
+ """
+ returns number of child nodes
+ """
+ return len(self._children)
+
+ #@-node:__len__
+ #@+node:__getitem__
+ def __getitem__(self, idx):
+ """
+ if given key is numeric, return the nth child, otherwise
+ try to return the child tag (or list of child tags) having
+ the key as the tag name
+ """
+ #print "__getitem__: idx=%s" % str(idx)
+
+ if isinstance(idx, slice) or isinstance(idx, int):
+ return self._children[idx]
+ elif isinstance(idx, str):
+ return self._childrenByName[idx]
+ else:
+ raise IndexError(idx)
+
+ #@-node:__getitem__
+ #@+node:_addNode
+ def _addNode(self, child):
+ """
+ Tries to append a child node to the tree, and returns it
+
+ Value of 'child' must be one of:
+ - a string (in which case it is taken to be the name
+ of the new node's tag)
+ - a dom object, in which case it will be wrapped and added
+ - an XMLNode object, in which case it will be added without
+ wrapping
+ """
+
+ if isinstance(child, XMLNode):
+
+ # add it to our children registry
+ self._children.append(child)
+
+ parentDict = self._childrenByName
+ nodeName = child._node.nodeName
+
+ if not parentDict.has_key(nodeName):
+ parentDict[nodeName] = self.__dict__[nodeName] = child
+ else:
+ if isinstance(parentDict[nodeName], XMLNode):
+ # this is the second child node of a given tag name, so
convert
+ # the instance to a list
+ parentDict[nodeName] \
+ = self.__dict__[nodeName] \
+ = [parentDict[nodeName]]
+
+ parentDict[nodeName].append(child)
+
+ # and stick it in the dom
+ self._node.appendChild(child._node)
+
+ return child
+
+ elif isinstance(child, str):
+ childNode = self._root.dom.createElement(child)
+ self._node.appendChild(childNode)
+
+ elif isinstance(child, xml.dom.minidom.Element):
+ childNode = child
+ child = childNode.nodeName
+ self._node.appendChild(childNode)
+
+
+ return XMLNode(self, childNode)
+
+ #@-node:_addNode
+ #@+node:_getChild
+ def _getChild(self, name):
+ """
+ Returns a list of zero or more child nodes whose
+ tag name is <name>
+ """
+ try:
+ item = getattr(self, name)
+ except AttributeError:
+ return []
+
+ if not isinstance(item, list):
+ item = [item]
+
+ return item
+
+ #@-node:_getChild
+ #@+node:_delChild
+ def _delChild(self, child):
+ """
+ Removes given child node
+ """
+ node = self
+ while True:
+ print "Trying to remove %s from %s" % (child, node)
+ if child in node._children:
+ print "removing"
+ node._children.remove(child)
+ node._node.removeChild(child._node)
+
+ for k,v in node._childrenByName.items():
+ if child == v:
+ del node._childrenByName[k]
+ elif isinstance(v, list):
+ if child in v:
+ v.remove(child)
+
+ if isinstance(node, XMLFile):
+ break
+
+ node = node._parent
+
+ #@-node:_delChild
+ #@+node:_addText
+ def _addText(self, value):
+ """
+ Tries to append a child text node, with the given text, to the tree,
+ and returns the created node object
+ """
+ childNode = self._root.dom.createTextNode(value)
+ self._node.appendChild(childNode)
+ return XMLNode(self, childNode)
+
+ #@-node:_addText
+ #@+node:_addComment
+ def _addComment(self, comment):
+ """
+ Tries to append a child comment node (with the given text value)
+ to the tree, and returns the create node object
+ """
+ childNode = self._root.dom.createCommentNode(comment)
+ self._node.appendChild(childNode)
+ return XMLNode(self, childNode)
+
+ #@-node:_addComment
+ #@+node:_save
+ def _save(self, where=None):
+ """
+ Generates well-formed XML from just this node, and saves it
+ to a file.
+
+ Argument 'where' is either an open file object, or a pathname
+
+ If 'where' is not given, then saves the entire document tree.
+ """
+ if not where:
+ self._root.save()
+ else:
+ self._root.save(where, self._node)
+
+ #@-node:_save
+ #@+node:_toxml
+ def _toxml(self):
+ """
+ renders just this node out to raw xml code
+ """
+ return self._node.toxml()
+
+ #@-node:_toxml
+ #@-others
+#@-node:class XMLNode
+#@-others
+#@nonl
+#@-node:@file xmlobject.py
+#@-leo
Modified: trunk/apps/pyFreenet/fcpget
===================================================================
--- trunk/apps/pyFreenet/fcpget 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/fcpget 2006-06-04 08:00:16 UTC (rev 9040)
@@ -47,7 +47,12 @@
print " Connect to FCP service at host <hostname>"
print " -P, --fcpPort=<portnum>"
print " Connect to FCP service at port <portnum>"
-
+ print " -g, --global"
+ print " Do it on the FCP global queue"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
def main():
@@ -59,6 +64,7 @@
verbose = False
fcpHost = fcp.node.defaultFCPHost
fcpPort = fcp.node.defaultFCPPort
+ Global = False
opts = {
"Verbosity" : 0,
@@ -68,8 +74,8 @@
try:
cmdopts, args = getopt.getopt(
sys.argv[1:],
- "?hvH:P:",
- ["help", "verbose", "fcpHost=", "fcpPort=",
+ "?hvH:P:g",
+ ["help", "verbose", "fcpHost=", "fcpPort=", "global",
]
)
except getopt.GetoptError:
@@ -98,6 +104,9 @@
except:
usage("Invalid fcpPort argument %s" % repr(a))
+ if o in ("-g", "--global"):
+ opts['Global'] = "true"
+
# process args
nargs = len(args)
if nargs < 1 or nargs > 2:
@@ -115,7 +124,10 @@
# try to create the node
try:
- node = fcp.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity,
+ node = fcp.FCPNode(host=fcpHost,
+ port=fcpPort,
+ verbosity=verbosity,
+ Global=Global,
logfile=sys.stderr)
except:
if verbose:
@@ -128,9 +140,12 @@
except:
if verbose:
traceback.print_exc(file=sys.stderr)
+ node.shutdown()
sys.stderr.write("%s: Failed to retrieve key %s\n" % (progname,
repr(uri)))
sys.exit(1)
+ node.shutdown()
+
# try to dispose of the data
if outfile:
# figure out an extension, if none given
Modified: trunk/apps/pyFreenet/fcpget.py
===================================================================
--- trunk/apps/pyFreenet/fcpget.py 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/fcpget.py 2006-06-04 08:00:16 UTC (rev 9040)
@@ -47,7 +47,12 @@
print " Connect to FCP service at host <hostname>"
print " -P, --fcpPort=<portnum>"
print " Connect to FCP service at port <portnum>"
-
+ print " -g, --global"
+ print " Do it on the FCP global queue"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
def main():
@@ -59,6 +64,7 @@
verbose = False
fcpHost = fcp.node.defaultFCPHost
fcpPort = fcp.node.defaultFCPPort
+ Global = False
opts = {
"Verbosity" : 0,
@@ -68,8 +74,8 @@
try:
cmdopts, args = getopt.getopt(
sys.argv[1:],
- "?hvH:P:",
- ["help", "verbose", "fcpHost=", "fcpPort=",
+ "?hvH:P:g",
+ ["help", "verbose", "fcpHost=", "fcpPort=", "global",
]
)
except getopt.GetoptError:
@@ -98,6 +104,9 @@
except:
usage("Invalid fcpPort argument %s" % repr(a))
+ if o in ("-g", "--global"):
+ opts['Global'] = "true"
+
# process args
nargs = len(args)
if nargs < 1 or nargs > 2:
@@ -115,7 +124,10 @@
# try to create the node
try:
- node = fcp.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity,
+ node = fcp.FCPNode(host=fcpHost,
+ port=fcpPort,
+ verbosity=verbosity,
+ Global=Global,
logfile=sys.stderr)
except:
if verbose:
@@ -128,9 +140,12 @@
except:
if verbose:
traceback.print_exc(file=sys.stderr)
+ node.shutdown()
sys.stderr.write("%s: Failed to retrieve key %s\n" % (progname,
repr(uri)))
sys.exit(1)
+ node.shutdown()
+
# try to dispose of the data
if outfile:
# figure out an extension, if none given
Modified: trunk/apps/pyFreenet/fcpput
===================================================================
--- trunk/apps/pyFreenet/fcpput 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/fcpput 2006-06-04 08:00:16 UTC (rev 9040)
@@ -50,6 +50,12 @@
print " an attempt will be made to guess it from the filename. If no"
print " filename is given, or if this attempt fails, the mimetype"
print " 'text/plain' will be used as a fallback"
+ print " -g, --global"
+ print " Do it on the FCP global queue"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
@@ -72,8 +78,8 @@
try:
cmdopts, args = getopt.getopt(
sys.argv[1:],
- "?hvH:P:m:",
- ["help", "verbose", "fcpHost=", "fcpPort=", "mimetype=",
+ "?hvH:P:m:g",
+ ["help", "verbose", "fcpHost=", "fcpPort=", "mimetype=", "global",
]
)
except getopt.GetoptError:
@@ -105,6 +111,9 @@
if o in ("-m", "--mimetype"):
mimetype = a
+ if o in ("-g", "--global"):
+ opts['Global'] = "true"
+
# process args
nargs = len(args)
if nargs < 1 or nargs > 2:
@@ -125,9 +134,14 @@
base, ext = os.path.splitext(infile)
if ext:
mimetype = mimetypes.guess_type(ext)[0]
- if not mimetype:
- mimetype = "text/plain"
+ if mimetype:
+ # mimetype explicitly specified, or implied with input file,
+ # stick it in.
+ # otherwise, let FCPNode.put try to imply it from a uri's
+ # 'file extension' suffix
+ opts['mimetype'] = mimetype
+
# try to create the node
try:
node = fcp.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity,
@@ -144,14 +158,18 @@
try:
data = file(infile, "rb").read()
except:
+ node.shutdown()
usage("Failed to read input from file %s" % repr(infile))
# try to insert the key
try:
+ print "opts=%s" % str(opts)
uri = node.put(uri, data=data, **opts)
+ node.shutdown()
except:
if verbose:
traceback.print_exc(file=sys.stderr)
+ node.shutdown()
sys.stderr.write("%s: Failed to insert key %s\n" % (progname,
repr(uri)))
sys.exit(1)
Modified: trunk/apps/pyFreenet/fcpput.py
===================================================================
--- trunk/apps/pyFreenet/fcpput.py 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/fcpput.py 2006-06-04 08:00:16 UTC (rev 9040)
@@ -50,6 +50,12 @@
print " an attempt will be made to guess it from the filename. If no"
print " filename is given, or if this attempt fails, the mimetype"
print " 'text/plain' will be used as a fallback"
+ print " -g, --global"
+ print " Do it on the FCP global queue"
+ print
+ print "Environment:"
+ print " Instead of specifying -H and/or -P, you can define the
environment"
+ print " variables FCP_HOST and/or FCP_PORT respectively"
sys.exit(0)
@@ -72,8 +78,8 @@
try:
cmdopts, args = getopt.getopt(
sys.argv[1:],
- "?hvH:P:m:",
- ["help", "verbose", "fcpHost=", "fcpPort=", "mimetype=",
+ "?hvH:P:m:g",
+ ["help", "verbose", "fcpHost=", "fcpPort=", "mimetype=", "global",
]
)
except getopt.GetoptError:
@@ -105,6 +111,9 @@
if o in ("-m", "--mimetype"):
mimetype = a
+ if o in ("-g", "--global"):
+ opts['Global'] = "true"
+
# process args
nargs = len(args)
if nargs < 1 or nargs > 2:
@@ -125,9 +134,14 @@
base, ext = os.path.splitext(infile)
if ext:
mimetype = mimetypes.guess_type(ext)[0]
- if not mimetype:
- mimetype = "text/plain"
+ if mimetype:
+ # mimetype explicitly specified, or implied with input file,
+ # stick it in.
+ # otherwise, let FCPNode.put try to imply it from a uri's
+ # 'file extension' suffix
+ opts['mimetype'] = mimetype
+
# try to create the node
try:
node = fcp.FCPNode(host=fcpHost, port=fcpPort, verbosity=verbosity,
@@ -144,14 +158,18 @@
try:
data = file(infile, "rb").read()
except:
+ node.shutdown()
usage("Failed to read input from file %s" % repr(infile))
# try to insert the key
try:
+ print "opts=%s" % str(opts)
uri = node.put(uri, data=data, **opts)
+ node.shutdown()
except:
if verbose:
traceback.print_exc(file=sys.stderr)
+ node.shutdown()
sys.stderr.write("%s: Failed to insert key %s\n" % (progname,
repr(uri)))
sys.exit(1)
Modified: trunk/apps/pyFreenet/freedisk.py
===================================================================
--- trunk/apps/pyFreenet/freedisk.py 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/freedisk.py 2006-06-04 08:00:16 UTC (rev 9040)
@@ -2,1300 +2,807 @@
#@+leo-ver=4
#@+node:@file freedisk.py
#@@first
+#@@language python
+#@+others
+#@+node:freedisk app
"""
-A FUSE-based filesystem for freenet
+freedisk is a command-line utility for creating,
+mounting and synchronising freenet freedisks
-Written May 2006 by aum
-
-Released under the GNU Lesser General Public License
-
-Requires:
- - python2.3 or later
- - FUSE kernel module installed and loaded
- (apt-get install fuse-source, crack tarball, build and install)
- - python2.3-fuse
- - libfuse2
+Invoke with -h for help
"""
-
#@+others
#@+node:imports
-import sys, os, time, stat, errno
-from StringIO import StringIO
-import thread
-from threading import Lock
+import sys, os
+import getopt
import traceback
-from Queue import Queue
-import sha, md5
-from UserString import MutableString
+import time
+import sha
+import getpass
-from errno import *
-from stat import *
-
try:
- import warnings
- warnings.filterwarnings('ignore',
- 'Python C API version mismatch',
- RuntimeWarning,
- )
+ import fcp
+ from fcp import node, freenetfs
+ from fcp.xmlobject import XMLFile, XMLNode
except:
- pass
-
-from _fuse import main, FuseGetContext, FuseInvalidate
-from string import join
-import sys
-from errno import *
+ print "** PyFCP core module 'fcp' not installed."
+ print "** Please refer to the INSTALL file within the PyFCP source package"
+ sys.exit(1)
-import fcp
+try:
+ import SSLCrypto
+
+except:
+ SSLCrypto = None
+ print "** WARNING! SSLCrypto module not installed"
+ print "** Please refer to the INSTALL file within the PyFCP source package"
+
#@-node:imports
#@+node:globals
-fcpHost = fcp.node.defaultFCPHost
-fcpPort = fcp.node.defaultFCPPort
+# args shorthand
+argv = sys.argv
+argc = len(argv)
+progname = argv[0]
-defaultVerbosity = fcp.DETAIL
+# default config file stuff
+homedir = os.path.expanduser("~")
+configFile = os.path.join(homedir, ".freediskrc")
-quiet = 0
+defaultMountpoint = os.path.join(homedir, "freedisk")
-myuid = os.getuid()
-mygid = os.getgid()
-
-inodes = {}
-inodesNext = 1
-
-# set this to disable hits to node, for debugging
-_no_node = 0
-
#@-node:globals
-#@+node:class ErrnoWrapper
-class ErrnoWrapper:
+#@+node:class FreediskMgr
+class FreediskMgr:
+ """
+ Freedisk manager class
+ """
+ #@ @+others
+ #@-others
- def __init__(self, func):
- self.func = func
-
- def __call__(self, *args, **kw):
- try:
- return apply(self.func, args, kw)
- except (IOError, OSError), detail:
- traceback.print_exc()
- # Sometimes this is an int, sometimes an instance...
- if hasattr(detail, "errno"): detail = detail.errno
- return -detail
-
-
-#@-node:class ErrnoWrapper
-#@+node:class Fuse
-class Fuse:
-
+#@-node:class FreediskMgr
+#@+node:class FreediskConfig
+class FreediskConfig:
+ """
+ allows for loading/saving/changing freedisk configs
+ """
#@ @+others
#@+node:attribs
- _attrs = ['getattr', 'readlink', 'getdir', 'mknod', 'mkdir',
- 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
- 'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
- 'statfs', 'fsync']
+ _intAttribs = ["fcpPort", "fcpVerbosity"]
- flags = 0
- multithreaded = 0
+ _strAttribs = ["fcpHost", "mountpoint"]
#@-node:attribs
#@+node:__init__
- def __init__(self, *args, **kw):
+ def __init__(self, path, passwd=None):
+ """
+ Create a config object from file at 'path', if it exists
+ """
+ #print "FreediskConfig: path=%s" % path
- # default attributes
- if args == ():
- # there is a self.optlist.append() later on, make sure it won't
- # bomb out.
- self.optlist = []
+ self.path = path
+ self.passwd = passwd
+
+ if os.path.isfile(path):
+ self.load()
else:
- self.optlist = args
- self.optdict = kw
+ self.create()
- if len(self.optlist) == 1:
- self.mountpoint = self.optlist[0]
- else:
- self.mountpoint = None
-
- # grab command-line arguments, if any.
- # Those will override whatever parameters
- # were passed to __init__ directly.
- argv = sys.argv
- argc = len(argv)
+ self.root = self.xml.root
- #self.log("argv=%s" % argv)
+ #@-node:__init__
+ #@+node:load
+ def load(self):
+ """
+ Loads config from self.config
+ """
+ # get the raw xml, plain or encrypted
+ ciphertext = file(self.path, "rb").read()
- ## physical thing to mount
- #self.configfile = argv[1]
+ plaintext = ciphertext
- if argc > 2:
- # we've been given the mountpoint
- self.mountpoint = argv[2]
- if argc > 3:
- # we've received mount args
- optstr = argv[4]
- opts = optstr.split(",")
- for o in opts:
+ # try to wrap into xml object
+ try:
+ xml = self.xml = XMLFile(raw=plaintext)
+ except:
+ i = 0
+ while i < 3:
+ passwd = self.passwd = getpasswd("Freedisk config password")
+ plaintext = decrypt(self.passwd, ciphertext)
try:
- k, v = o.split("=", 1)
- self.optdict[k] = v
+ xml = XMLFile(raw=plaintext)
+ break
except:
- self.optlist.append(o)
+ i += 1
+ continue
+ if i == 3:
+ self.abort()
- #@-node:__init__
- #@+node:GetContent
- def GetContext(self):
- return FuseGetContext(self)
+ self.xml = xml
+ self.root = xml.root
- #@-node:GetContent
- #@+node:Invalidate
- def Invalidate(self, path):
- return FuseInvalidate(self, path)
+ #@-node:load
+ #@+node:create
+ def create(self):
+ """
+ Creates a new config object
+ """
+ self.xml = XMLFile(root="freedisk")
+ root = self.root = self.xml.root
- #@-node:Invalidate
- #@+node:main
- def main(self):
+ self.fcpHost = fcp.node.defaultFCPHost
+ self.fcpPort = fcp.node.defaultFCPPort
+ self.fcpVerbosity = fcp.node.defaultVerbosity
+ self.mountpoint = defaultMountpoint
- d = {'mountpoint': self.mountpoint}
- d['multithreaded'] = self.multithreaded
- if hasattr( self, 'debug'):
- d['lopts'] = 'debug';
+ self.save()
- #opts = self.optdict
- #for k in ['wsize', 'rsize']:
- # if opts.has_key(k):
- # d[k] = int(opts[k])
+ #@-node:create
+ #@+node:save
+ def save(self):
- k=[]
- if hasattr(self,'allow_other'):
- k.append('allow_other')
+ plain = self.xml.toxml()
- if hasattr(self,'kernel_cache'):
- k.append('kernel_cache')
+ if self.passwd:
+ cipher = encrypt(self.passwd, plain)
+ else:
+ cipher = plain
+
+ f = file(self.path, "wb")
+ f.write(cipher)
+ f.flush()
+ f.close()
- if len(k):
- d['kopts'] = join(k,',')
+ #@-node:save
+ #@+node:abort
+ def abort(self):
- for a in self._attrs:
- if hasattr(self,a):
- d[a] = ErrnoWrapper(getattr(self, a))
- #apply(main, (), d)
- main(**d)
+ print "freedisk: Cannot decrypt freedisk config file '%s'" % self.path
+ print
+ print "If you truly can't remember the password, your only"
+ print "option now is to delete the config file and start again"
+ sys.exit(1)
- #@-node:main
- #@-others
-#@-node:class Fuse
-#@+node:class FreenetFS
-class FreenetFS(Fuse):
-
- #@ @+others
- #@+node:attribs
- flags = 1
+ #@-node:abort
+ #@+node:setPassword
+ def setPassword(self, passwd):
+
+ self.passwd = passwd
+ self.save()
- # Files and directories already present in the filesytem.
- # Note - directories must end with "/"
+ #@-node:setPassword
+ #@+node:addDisk
+ def addDisk(self, name, uri, privUri, passwd):
- initialFiles = [
- "/",
- # "/cmd/",
- # "/cmd/genkey",
- # "/cmd/genkeypair",
- "/get/",
- "/put/",
- #"/cmd/invertprivatekey/",
- "/keys/",
- # "/private/",
- "/usr/",
- ]
+ d = self.getDisk(name)
+ if isinstance(d, XMLNode):
+ raise Exception("Disk '%s' already exists" % name)
+
+ diskNode = self.root._addNode("disk")
+ diskNode.name = name
+ diskNode.uri = uri
+ diskNode.privUri = privUri
+ diskNode.passwd = passwd
+
+ self.save()
- chrFiles = [
- "/cmd/genkey",
- "/cmd/genkeypair",
- ]
-
- #@-node:attribs
- #@+node:__init__
- def __init__(self, *args, **kw):
-
- Fuse.__init__(self, *args, **kw)
-
- if 0:
- self.log("xmp.py:Xmp:mountpoint: %s" % repr(self.mountpoint))
- self.log("xmp.py:Xmp:unnamed mount options: %s" % self.optlist)
- self.log("xmp.py:Xmp:named mount options: %s" % self.optdict)
-
- opts = self.optdict
-
- host = opts.get('host', fcpHost)
- port = opts.get('port', fcpPort)
- verbosity = int(opts.get('verbosity', defaultVerbosity))
-
- self.configfile = opts.get('config', None)
- if not self.configfile:
- raise Exception("Missing 'config=filename.conf' argument")
-
- self.loadConfig()
-
- self.setupFiles()
-
- self.fcpHost = host
- self.fcpPort = port
- self.fcpVerbosity = verbosity
-
- self.privKeyQueue = []
- self.privKeyLock = Lock()
- self.privKeypairQueue = []
- self.privKeypairLock = Lock()
-
- try:
- self.node = None
- self.connectToNode()
- except:
- #raise
- pass
-
- # do stuff to set up your filesystem here, if you want
- #thread.start_new_thread(self.mythread, ())
-
- #@-node:__init__
- #@+node:loadConfig
- def loadConfig(self):
+ #@-node:addDisk
+ #@+node:getDisk
+ def getDisk(self, name):
"""
- The 'physical device' argument to mount should be the pathname
- of a configuration file, with 'name=val' lines, including the
- following items:
- - publickey=<freenet public key URI>
- - privatekey=<freenet private key URI> (optional, without which we
- will have the fs mounted readonly
+ Returns a record for a freedisk of name <name>
"""
- opts = {}
+ disks = self.root._getChild("disk")
+
+ for d in disks:
+ if d.name == name:
+ return d
+
+ return None
- # build a dict of all the 'name=value' pairs in config file
- for line in [l.strip() for l in file(self.configfile).readlines()]:
- if line == '' or line.startswith("#"):
- continue
- try:
- name, val = line.split("=", 1)
- opts[name.strip()] = val.strip()
- except:
- pass
-
- # mandate a pubkey
- try:
- self.pubkey = opts['pubkey'].replace("SSK@", "USK@").split("/")[0]
+ "/"
- except:
- raise Exception("Config file %s: missing or invalid publickey" \
- % self.configfile)
-
- # accept optional privkey
- if opts.has_key("privkey"):
-
- try:
- self.privkey = opts['privkey'].replace("SSK@",
- "USK@").split("/")[0] +
"/"
- except:
- raise Exception("Config file %s: invalid privkey" \
- % self.configfile)
-
- # mandate cachepath
- try:
- self.cachedir = opts['cachedir']
- if not os.path.isdir(self.cachedir):
- raise hell
- except:
- raise Exception("config file %s: missing or invalid cache
directory" \
- % self.configfile)
-
- #@-node:loadConfig
- #@+node:setupFiles
- def setupFiles(self):
+ #@-node:getDisk
+ #@+node:getDisks
+ def getDisks(self):
"""
- Create initial file/directory layout, according
- to attributes 'initialFiles' and 'chrFiles'
+ Returns all freedisk records
"""
- # easy map of files
- self.files = {}
+ return self.root._getChild("disk")
- # now create records for initial files
- for path in self.initialFiles:
-
- # initial attribs
- isReg = isDir = isChr = isSock = isFifo = False
- perm = size = 0
-
- # determine file type
- if path.endswith("/"):
- isDir = True
- path = path[:-1]
- if not path:
- path = "/"
- elif path in self.chrFiles:
- # it's a char file
- #isChr = True
- isReg = True
- perm |= 0666
- size = 1024
- else:
- # by default, it's a regular file
- isReg = True
-
- # create permissions field
- if isDir:
- perm |= 0755
- else:
- perm |= 0444
-
- # create record for this path
- self.addToCache(
- path=path,
- perm=perm,
- size=size,
- isdir=isDir, isreg=isReg, ischr=isChr,
- issock=isSock, isfifo=isFifo,
- )
-
- #@-node:setupFiles
- #@+node:connectToNode
- def connectToNode(self):
+ #@-node:getDisks
+ #@+node:delDisk
+ def delDisk(self, name):
"""
- Attempts a connection to an fcp node
+ Removes disk of given name
"""
- if self.node:
- return
- self.node = fcp.FCPNode(host=self.fcpHost,
- port=self.fcpPort,
- verbosity=self.fcpVerbosity)
- #self.log("pubkey=%s" % self.pubkey)
- #self.log("privkey=%s" % self.privkey)
- #self.log("cachedir=%s" % self.cachedir)
+ d = self.getDisk(name)
+ if not isinstance(d, XMLNode):
+ raise Exception("No such freedisk '%s'" % name)
+
+ self.root._delChild(d)
- #@-node:connectToNode
- #@+node:log
- def log(self, msg):
- if not quiet:
- print "freedisk:"+msg
- #@-node:log
- #@+node:mythread
- def mythread(self):
+ self.save()
- """
- The beauty of the FUSE python implementation is that with the python
interp
- running in foreground, you can have threads
- """
- self.log("mythread: started")
- #while 1:
- # time.sleep(120)
- # print "mythread: ticking"
+ #@-node:delDisk
+ #@+node:__getattr__
+ def __getattr__(self, attr):
+
+ if attr in self._intAttribs:
+ try:
+ return int(getattr(self.root, attr))
+ except:
+ raise AttributeError(attr)
- #@-node:mythread
- #@+node:fs primitives
- # primitives required for actual fs operations
+ elif attr in self._strAttribs:
+ try:
+ return str(getattr(self.root, attr))
+ except:
+ raise AttributeError(attr)
- #@+others
- #@+node:getattr
- def getattr(self, path):
-
- rec = self.files.get(path, None)
- if not rec:
- # retrieving a key?
- if path.startswith("/keys/"):
- #@ <<generate keypair>>
- #@+node:<<generate keypair>>
- # generate a new keypair
- self.connectToNode()
- pubkey, privkey = self.node.genkey()
- rec = self.addToCache(
- path=path,
- isreg=True,
- data=pubkey+"\n"+privkey,
- perm=0444,
- )
- #@-node:<<generate keypair>>
- #@nl
- elif path.startswith("/get/"):
- #@ <<retrieve/cache key>>
- #@+node:<<retrieve/cache key>>
- # check the cache
- if _no_node:
- print "FIXME: returning IOerror"
- raise IOError(errno.ENOENT, path)
-
- # get a key
- uri = path.split("/", 2)[-1]
- try:
- self.connectToNode()
- mimetype, data = self.node.get(uri)
- rec = FileRecord(path=path,
- isreg=True,
- perm=0644,
- data=data,
- )
- self.addToCache(rec)
-
- except:
- traceback.print_exc()
- #print "ehhh?? path=%s" % path
- raise IOError(errno.ENOENT, path)
-
- #@-node:<<retrieve/cache key>>
- #@nl
- else:
- #@ <<try host fs>>
- #@+node:<<try host fs>>
- # try the host filesystem
- print "getattr: no rec for %s, hitting main fs" % path
- rec = FileRecord(os.lstat(path), path=path)
-
- print rec
-
- #@-node:<<try host fs>>
- #@nl
-
- self.log("getattr: path=%s" % path)
- self.log(" mode=0%o" % rec.mode)
- self.log(" inode=0x%x" % rec.inode)
- self.log(" dev=0x%x" % rec.dev)
- self.log(" nlink=0x%x" % rec.nlink)
- self.log(" uid=%d" % rec.uid)
- self.log(" gid=%d" % rec.gid)
- self.log(" size=%d" % rec.size)
- self.log(" atime=%d" % rec.atime)
- self.log(" mtime=%d" % rec.mtime)
- self.log(" ctime=%d" % rec.ctime)
- self.log("rec=%s" % str(rec))
-
- return tuple(rec)
-
- #@-node:getattr
- #@+node:readlink
- def readlink(self, path):
-
- ret = os.readlink(path)
- self.log("readlink: path=%s\n => %s" % (path, ret))
- return ret
-
- #@-node:readlink
- #@+node:getdir
- def getdir(self, path):
-
- rec = self.files.get(path, None)
-
- if rec:
- files = [os.path.split(child.path)[-1] for child in rec.children]
- files.sort()
- if rec.isdir:
- if path != "/":
- files.insert(0, "..")
- files.insert(0, ".")
else:
- self.log("Hit main fs for %s" % path)
- files = os.listdir(path)
+ raise AttributeError(attr)
- ret = map(lambda x: (x,0), files)
-
- self.log("getdir: path=%s\n => %s" % (path, ret))
- return ret
-
- #@-node:getdir
- #@+node:unlink
- def unlink(self, path):
-
- # remove existing file?
- if path.startswith("/get/") \
- or path.startswith("/put/") \
- or path.startswith("/keys/"):
- rec = self.files.get(path, None)
- if not rec:
- raise IOError(2, path)
- self.delFromCache(rec)
- return 0
-
- # fallback on host fs
- ret = os.unlink(path)
- self.log("unlink: path=%s\n => %s" % (path, ret))
- return ret
-
- #@-node:unlink
- #@+node:rmdir
- def rmdir(self, path):
-
- ret = os.rmdir(path)
- self.log("rmdir: path=%s\n => %s" % (path, ret))
- return ret
-
- #@-node:rmdir
- #@+node:symlink
- def symlink(self, path, path1):
-
- ret = os.symlink(path, path1)
- self.log("symlink: path=%s path1=%s\n => %s" % (path, path1, ret))
- return ret
-
- #@-node:symlink
- #@+node:rename
- def rename(self, path, path1):
-
- ret = os.rename(path, path1)
- self.log("rename: path=%s path1=%s\n => %s" % (path, path1, ret))
- return ret
-
- #@-node:rename
- #@+node:link
- def link(self, path, path1):
-
- ret = os.link(path, path1)
- self.log("link: path=%s path1=%s\n => %s" % (path, path1, ret))
- return ret
-
- #@-node:link
- #@+node:chmod
- def chmod(self, path, mode):
-
- ret = os.chmod(path, mode)
- self.log("chmod: path=%s mode=%s\n => %s" % (path, mode, ret))
- return ret
-
- #@-node:chmod
- #@+node:chown
- def chown(self, path, user, group):
-
- ret = os.chown(path, user, group)
- self.log("chmod: path=%s user=%s group=%s\n => %s" % (path, user,
group, ret))
- return ret
-
- #@-node:chown
- #@+node:truncate
- def truncate(self, path, size):
-
- f = open(path, "w+")
- ret = f.truncate(size)
- self.log("truncate: path=%s size=%s\n => %s" % (path, size, ret))
- return ret
-
- #@-node:truncate
- #@+node:mknod
- def mknod(self, path, mode, dev):
- """ Python has no os.mknod, so we can only do some things """
- # start key write, if needed
- if path.startswith("/put/"):
-
- # see if an existing file
- if self.files.has_key(path):
- raise IOError(errno.EEXIST, path)
-
- rec = self.addToCache(
- path=path, isreg=True, iswriting=True,
- perm=0644)
- ret = 0
-
- else:
- # fall back on host os
- if S_ISREG(mode):
- file(path, "w").close()
- ret = 0
- else:
- ret = -EINVAL
-
- self.log("mknod: path=%s mode=0%o dev=%s\n => %s" % (
- path, mode, dev, ret))
-
- return ret
-
- #@-node:mknod
- #@+node:mkdir
- def mkdir(self, path, mode):
-
- ret = os.mkdir(path, mode)
- self.log("mkdir: path=%s mode=%s\n => %s" % (path, mode, ret))
- return ret
-
- #@-node:mkdir
- #@+node:utime
- def utime(self, path, times):
-
- ret = os.utime(path, times)
- self.log("utime: path=%s times=%s\n => %s" % (path, times, ret))
- return ret
-
- #@-node:utime
- #@+node:open
- def open(self, path, flags):
-
- self.log("open: path=%s flags=%s" % (path, flags))
-
- # see if it's an existing file
- rec = self.files.get(path, None)
+ #@-node:__getattr__
+ #@+node:__setattr__
+ def __setattr__(self, attr, val):
- if rec:
- # barf if not regular file
- if not (rec.isreg or rec.ischr):
- self.log("open: %s is not regular file" % path)
- raise IOError(errno.EIO, "Not a regular file: %s" % path)
-
+ if attr in self._intAttribs:
+ val = str(val)
+ setattr(self.root, attr, val)
+ self.save()
+ elif attr in self._strAttribs:
+ setattr(self.root, attr, val)
+ self.save()
else:
- # fall back to host fs
- os.close(os.open(path, flags))
+ self.__dict__[attr] = val
- self.log("open: open of %s succeeded" % path)
+ #@-node:__setattr__
+ #@-others
+
+#@-node:class FreediskConfig
+#@+node:usage
+def usage(msg=None, ret=1):
+ """
+ Prints usage message then exits
+ """
+ if msg:
+ sys.stderr.write(msg+"\n")
+ sys.stderr.write("Usage: %s [options] [<command> [<args>]]\n" % progname)
+ sys.stderr.write("Type '%s -h' for help\n" % progname)
+ sys.exit(ret)
+
+#@-node:usage
+#@+node:help
+def help():
+ """
+ Display help info then exit
+ """
+ print "%s: manage a freenetfs filesystem" % progname
+ print "Usage: %s [<options>] <command> [<arguments>]" % progname
+ print "Options:"
+ print " -h, --help Display this help"
+ print " -c, --config= Specify config file, default ~/.freediskrc"
+ print "Commands:"
+ print " init Edit configuration interactively"
+ print " mount Mount the freenetfs"
+ print " unmount Unmount the freenetfs"
+ print " new <name> Create a new freedisk of name <name>"
+ print " A new keypair will be generated."
+ print " add <name> <URI> Add an existing freedisk of name <name>"
+ print " and public key URI <URI>"
+ print " del <name> Remove freedisk of name <name>"
+ print " update <name> Sync freedisk <name> from freenet"
+ print " commit <name> Commit freedisk <name> into freenet"
+ print
+ print "Environment variables:"
+ print " FREEDISK_CONFIG - set this in place of '-c' argument"
+
+ sys.exit(0)
+
+#@-node:help
+#@+node:removeDirAndContents
+def removeDirAndContents(path):
- # seems ok
- return 0
+ files = os.listdir(path)
- #@-node:open
- #@+node:read
- def read(self, path, length, offset):
- """
- """
- # forward to existing file if any
- rec = self.files.get(path, None)
- if rec:
- rec.seek(offset)
- buf = rec.read(length)
-
- self.log("read: path=%s length=%s offset=%s\n => %s" % (
- path, length, offset, len(buf)))
- #print repr(buf)
- return buf
-
+ for f in files:
+ fpath = os.path.join(path, f)
+ if os.path.isfile(fpath):
+ os.unlink(fpath)
+ elif os.path.isdir(fpath):
+ removeDirAndContents(fpath)
+ os.rmdir(path)
+
+#@-node:removeDirAndContents
+#@+node:status
+def status(msg):
+ sys.stdout.write(msg + "...")
+ time.sleep(1)
+ print
+
+
+#@-node:status
+#@+node:encrypt
+def encrypt(passwd, s):
+
+ passwd = sha.new(passwd).digest()
+
+ if SSLCrypto:
+ # encrypt with blowfish 256, key=sha(password), IV=00000000
+ return SSLCrypto.blowfish(passwd).encrypt(s)
+ else:
+ # no encyrption available, return plaintext
+ return s
+
+#@-node:encrypt
+#@+node:decrypt
+def decrypt(passwd, s):
+
+ passwd = sha.new(passwd).digest()
+
+ if SSLCrypto:
+ # decrypt with blowfish 256, key=sha(password), IV=00000000
+ return SSLCrypto.blowfish(passwd).decrypt(s)
+ else:
+ # no encyrption available, return plaintext
+ return s
+
+#@-node:decrypt
+#@+node:getpasswd
+def getpasswd(prompt="Password", confirm=False):
+
+ if not confirm:
+ return getpass.getpass(prompt+": ").strip()
+
+ while 1:
+ passwd = getpass.getpass(prompt+": ").strip()
+ if passwd:
+ passwd1 = getpasswd("Verify password").strip()
+ if passwd == passwd1:
+ break
+ print "passwords do not match, please try again"
else:
- # fall back on host fs
- f = open(path, "r")
- f.seek(offset)
- buf = f.read(length)
+ break
+
+ return passwd
+
+#@-node:getpasswd
+#@+node:doFsCommand
+def doFsCommand(cmd):
+ """
+ Executes a command via base64-encoded file
+ """
+ cmdBase64 = fcp.node.base64encode(cmd)
+ path = conf.mountpoint + "/cmds/" + cmdBase64
+ return file(path).read()
+
+#@-node:doFsCommand
+#@+node:ipython
+def ipython(o=None):
+
+ from IPython.Shell import IPShellEmbed
+
+ ipshell = IPShellEmbed()
+
+ ipshell() # this call anywhere in your program will start IPython
+
+#@-node:ipython
+#@+node:getyesno
+def getyesno(prmt, dflt=True):
- self.log("read: path=%s length=%s offset=%s\n => (%s bytes)" % (
- path, length, offset, len(buf)))
+ if dflt:
+ ynprmt = "[Y/n] "
+ else:
+ ynprmt = "[y/N] "
+
+ resp = raw_input(prmt + "? " + ynprmt).strip()
+ if not resp:
+ return dflt
+ resp = resp.lower()[0]
+ return resp == 'y'
+
+#@-node:getyesno
+#@+node:main
+def main():
+ """
+ Front end
+ """
+ #@ <<global vars>>
+ #@+node:<<global vars>>
+ # some globals
- return buf
+ global Verbosity, verbose, configFile, conf
- #@-node:read
- #@+node:write
- def write(self, path, buf, off):
+ #@-node:<<global vars>>
+ #@nl
+
+ #@ <<set defaults>>
+ #@+node:<<set defaults>>
+ # create defaults
- dataLen = len(buf)
+ debug = False
+ multithreaded = False
- rec = self.files.get(path, None)
- if rec:
- # write to existing 'file'
- rec.seek(off)
- rec.write(buf)
- else:
- f = open(path, "r+")
- f.seek(off)
- nwritten = f.write(buf)
- f.flush()
+ #@-node:<<set defaults>>
+ #@nl
+
+ #@ <<process args>>
+ #@+node:<<process args>>
+ # process args
- self.log("write: path=%s buf=[%s bytes] off=%s" % (path, len(buf),
off))
+ try:
+ cmdopts, args = getopt.getopt(
+ sys.argv[1:],
+ "?hvc:dm",
+ ["help", "verbose",
+ "multithreaded",
+ "config=", "debug",
+ ]
+ )
+ except getopt.GetoptError:
+ # print help information and exit:
+ usage()
+ sys.exit(2)
+ output = None
+ verbose = False
- #return nwritten
- return dataLen
+ #print cmdopts
+ for o, a in cmdopts:
- #@-node:write
- #@+node:release
- def release(self, path, flags):
+ if o in ("-?", "-h", "--help"):
+ help()
- rec = self.files.get(path, None)
- if not rec:
- return
+ if o in ("-v", "--verbose"):
+ verbosity = fcp.node.DETAIL
+ opts['Verbosity'] = 1023
+ verbose = True
- # if writing, save the thing
- if rec.iswriting:
- # what uri?
- rec.iswriting = False
- uri = os.path.split(path)[1]
+ if o in ("-c", "--config"):
+ configFile = a
- # frigs to allow fancy CHK@ inserts
- if uri.startswith("CHK@"):
- putUri = "CHK@"
- else:
- putUri = uri
- ext = os.path.splitext(uri)[1]
+ if o in ("-d", "--debug"):
+ debug = True
- try:
- self.log("release: inserting %s" % uri)
+ if o in ("-m", "--multithreaded"):
+ multithreaded = True
- mimetype = fcp.node.guessMimetype(path)
- data = rec.data
+ #@-node:<<process args>>
+ #@nl
+
+ #@ <<get config>>
+ #@+node:<<get config>>
+ # load config, if any
- # empty the pseudo-file till a result is through
- rec.data = 'inserting'
+ #print "loading freedisk config"
- self.connectToNode()
+ conf = FreediskConfig(configFile)
- #print "FIXME: data=%s" % repr(data)
+ #ipython(conf)
- if _no_node:
- print "FIXME: not inserting"
- getUri = "NO_URI"
- else:
- # perform the insert
- getUri = self.node.put(
- putUri,
- data=data,
- mimetype=mimetype)
+ #@-node:<<get config>>
+ #@nl
- # strip 'freenet:' prefix
- if getUri.startswith("freenet:"):
- getUri = getUri[8:]
+ #@ <<validate args>>
+ #@+node:<<validate args>>
+ # validate args
- # restore file extension
- if getUri.startswith("CHK@"):
- getUri += ext
+ nargs = len(args)
+ if nargs == 0:
+ usage("No command given")
- # now cache the read-back
- self.addToCache(
- path="/get/"+getUri,
- data=data,
- perm=0444,
- isreg=True,
- )
-
- # and adjust the written file to reveal read uri
- rec.data = getUri
+ cmd = args[0]
- self.log("release: inserted %s as %s ok" % (
- uri, mimetype))
+ # barf if not 'init' and no config
+ if cmd != 'init' and not os.path.isfile(configFile):
+ usage("Config file %s does not exist\nRun '%s init' to create it" % (
+ configFile, progname))
- except:
- traceback.print_exc()
- rec.data = 'failed'
- self.log("release: insert of %s failed" % uri)
- raise IOError(errno.EIO, "Failed to insert")
+ # validate args count for cmds needing diskname arg
+ if cmd in ['new', 'add', 'del', 'update', 'commit']:
+ if nargs < 2:
+ usage("%s: Missing argument <freediskname>" % cmd)
+ diskname = args[1]
- self.log("release: done with insertion")
- return 0
+ # get paths to freedisk dir and pseudo-files
+ diskPath = os.path.join(conf.mountpoint, "usr", diskname)
+ pubKeyPath = os.path.join(diskPath, ".publickey")
+ privKeyPath = os.path.join(diskPath, ".privatekey")
+ passwdPath = os.path.join(diskPath, ".passwd")
+ cmdPath = os.path.join(diskPath, ".cmd")
+ statusPath = os.path.join(diskPath, ".status")
- self.log("release: path=%s flags=%s" % (path, flags))
- return 0
-
- #@-node:release
- #@+node:statfs
- def statfs(self):
- """
- Should return a tuple with the following 6 elements:
- - blocksize - size of file blocks, in bytes
- - totalblocks - total number of blocks in the filesystem
- - freeblocks - number of free blocks
- - totalfiles - total number of file inodes
- - freefiles - nunber of free file inodes
-
- Feel free to set any of the above values to 0, which tells
- the kernel that the info is not available.
- """
- self.log("statfs: returning fictitious values")
- blocks_size = 1024
- blocks = 100000
- blocks_free = 25000
- files = 100000
- files_free = 60000
- namelen = 80
-
- return (blocks_size, blocks, blocks_free, files, files_free, namelen)
-
- #@-node:statfs
- #@+node:fsync
- def fsync(self, path, isfsyncfile):
-
- self.log("fsync: path=%s, isfsyncfile=%s" % (path, isfsyncfile))
- return 0
-
- #@-node:fsync
- #@-others
-
- #@-node:fs primitives
- #@+node:hashpath
- def hashpath(self, path):
+ #@-node:<<validate args>>
+ #@nl
+
+ #@ <<execute command>>
+ #@+node:<<execute command>>
+ # start a freenetfs mount
+ if cmd in ['init', 'setup']:
+ #@ <<init>>
+ #@+node:<<init>>
+ # initialise/change freedisk config
- return sha.new(path).hexdigest()
-
- #@-node:hashpath
- #@+node:addToCache
- def addToCache(self, rec=None, **kw):
- """
- Tries to 'cache' a given file/dir record, and
- adds it to parent dir
- """
- if rec == None:
- rec = FileRecord(**kw)
-
- path = rec.path
-
- # barf if file/dir already exists
- if self.files.has_key(path):
- self.log("addToCache: already got %s !!!" % path)
- return
-
- #print "path=%s" % path
-
- # if not root, add to parent
- if path != '/':
- parentPath = os.path.split(path)[0]
- parentRec = self.files.get(parentPath, None)
- parentRec.addChild(rec)
- if not parentRec:
- self.log("addToCache: no parent of %s ?!?!" % path)
- return
-
- # ok, add to our table
- self.files[path] = rec
-
- # done
- return rec
-
- #@-node:addToCache
- #@+node:delFromCache
- def delFromCache(self, rec):
- """
- Tries to remove file/dir record from cache
- """
- path = rec.path
- parentPath = os.path.split(path)[0]
+ print "Freedisk configuration"
+ print
+ print "Your freedisk config will normally be stored in the file:"
+ print " %s" % configFile
- if self.files.has_key(path):
- del self.files[path]
+ # allow password change
+ if conf.passwd:
+ # got a password already
+ prmt = "Do you wish to change your config password"
+ else:
+ # new password
+ prmt = "Do you wish to encrypt this file"
+ if getyesno(prmt):
+ passwd = getpasswd("New Password", True)
+ conf.setPassword(passwd)
+ print "Password successfully changed"
- parentRec = self.files.get(parentPath, None)
- if parentRec:
- parentRec.delChild(rec)
-
- #@-node:delFromCache
- #@+node:getDirStat
- def getDirStat(self, path):
- """
- returns a stat tuple for given path
- """
- return FileRecord(mode=0700, path=path, isdir=True)
-
- #@-node:getDirStat
- #@+node:statFromKw
- def statFromKw(self, **kw):
- """
- Constructs a stat tuple from keywords
- """
- tup = [0] * 10
-
- # build mode mask
- mode = kw.get('mode', 0)
- if kw.get('isdir', False):
- mode |= stat.S_IFDIR
- if kw.get('ischr', False):
- mode |= stat.S_IFCHR
- if kw.get('isblk', False):
- mode |= stat.S_IFBLK
- if kw.get('isreg', False):
- mode |= stat.S_IFREG
- if kw.get('isfifo', False):
- mode |= stat.S_IFIFO
- if kw.get('islink', False):
- mode |= stat.S_IFLNK
- if kw.get('issock', False):
- mode |= stat.S_IFSOCK
-
- path = kw['path']
-
- # get inode number
- inode = self.pathToInode(path)
+ # host parms
+ fcpHost = raw_input("Freenet FCP Hostname: [%s] " %
conf.fcpHost).strip()
+ if fcpHost:
+ conf.fcpHost = fcpHost
- dev = 0
+ fcpPort = raw_input("Freenet FCP Port: [%s] "% conf.fcpPort).strip()
+ if fcpPort:
+ conf.fcpPort = fcpPort
- nlink = 1
- uid = myuid
- gid = mygid
- size = 0
- atime = mtime = ctime = timeNow()
-
- return (mode, inode, dev, nlink, uid, gid, size, atime, mtime, ctime)
-
- # st_mode, st_ino, st_dev, st_nlink,
- # st_uid, st_gid, st_size,
- # st_atime, st_mtime, st_ctime
-
- #@-node:statFromKw
- #@+node:statToDict
- def statToDict(self, info):
- """
- Converts a tuple returned by a stat call into
- a dict with keys:
-
- - isdir
- - ischr
- - isblk
- - isreg
- - isfifo
- - islnk
- - issock
- - mode
- - inode
- - dev
- - nlink
- - uid
- - gid
- - size
- - atime
- - mtime
- - ctime
- """
- print "statToDict: info=%s" % str(info)
-
- mode = info[stat.ST_MODE]
- return {
- 'isdir' : stat.S_ISDIR(mode),
- 'ischr' : stat.S_ISCHR(mode),
- 'isblk' : stat.S_ISBLK(mode),
- 'isreg' : stat.S_ISREG(mode),
- 'isfifo' : stat.S_ISFIFO(mode),
- 'islink' : stat.S_ISLNK(mode),
- 'issock' : stat.S_ISSOCK(mode),
- 'mode' : mode,
- 'inode' : info[stat.ST_INO],
- 'dev' : info[stat.ST_DEV],
- 'nlink' : info[stat.ST_NLINK],
- 'uid' : info[stat.ST_UID],
- 'gid' : info[stat.ST_GID],
- 'size' : info[stat.ST_SIZE],
- 'atime' : info[stat.ST_ATIME],
- 'mtime' : info[stat.ST_MTIME],
- 'ctime' : info[stat.ST_CTIME],
- }
-
- #@-node:statToDict
- #@+node:getReadURI
- def getReadURI(self, path):
- """
- Converts to a pathname to a freenet URI for insertion,
- using public key
- """
- return self.pubkey + self.hashpath(path) + "/0"
-
- #@-node:getReadURI
- #@+node:getWriteURI
- def getWriteURI(self, path):
- """
- Converts to a pathname to a freenet URI for insertion,
- using private key if any
- """
- if not self.privkey:
- raise Exception("cannot write: no private key")
+ print "Freenet verbosity:"
+ print " (0=SILENT, 1=FATAL, 2=CRITICAL, 3=ERROR"
+ print " 4=INFO, 5=DETAIL, 6=DEBUG)"
+ v = raw_input("[%s] " % conf.fcpVerbosity).strip()
+ if v:
+ conf.fcpVerbosity = v
- return self.privkey + self.hashpath(path) + "/0"
+ while 1:
+ m = raw_input("Mountpoint [%s] " % conf.mountpoint).strip() \
+ or conf.mountpoint
+ if m:
+ if not os.path.isdir(m):
+ print "No such directory '%s'" % m
+ elif not os.path.exists(m):
+ print "%s is not a directory" % m
+ else:
+ conf.mountpoint = m
+ mountpoint = m
+ break
+
+ print "Freedisk configuration successfully changed"
+
+ #@-node:<<init>>
+ #@nl
- #@-node:getWriteURI
- #@-others
-
-#@-node:class FreenetFS
-#@+node:class FileRecord
-class FileRecord(list):
- """
- Encapsulates the info for a file, and can
- be returned by getattr
- """
- #@ @+others
- #@+node:__init__
- def __init__(self, statrec=None, **kw):
- """
- """
- # got a statrec arg?
- if statrec:
- # yes, extract main items
- dev = statrec[stat.ST_DEV]
- nlink = statrec[stat.ST_NLINK]
- uid = statrec[stat.ST_UID]
- gid = statrec[stat.ST_GID]
- size = statrec[stat.ST_SIZE]
+ elif cmd in ['start', 'mount']:
+ #@ <<start>>
+ #@+node:<<start>>
+ print "starting freedisk service..."
+ fs = freenetfs.FreenetFS(
+ conf.mountpoint,
+ fcpHost=conf.fcpHost,
+ fcpPort=conf.fcpPort,
+ verbosity=conf.fcpVerbosity,
+ debug=debug,
+ multithreaded=multithreaded,
+ )
+
+ # spawn a process to run it
+ if os.fork() == 0:
+ print "Mounting freenet fs at %s" % conf.mountpoint
+ fs.run()
else:
- # no, fudge a new one
- statrec = [0,0,0,0,0,0,0,0,0,0]
- dev = 0
- nlink = 1
- uid = myuid
- gid = mygid
- size = 0
-
- # convert tuple to list if need be
- if not hasattr(statrec, '__setitem__'):
- statrec = list(statrec)
-
- # build mode mask
- mode = kw.get('mode', 0)
- if kw.get('isdir', False):
- mode |= stat.S_IFDIR
- if kw.get('ischr', False):
- mode |= stat.S_IFCHR
- if kw.get('isblk', False):
- mode |= stat.S_IFBLK
- if kw.get('isreg', False):
- mode |= stat.S_IFREG
- if kw.get('isfifo', False):
- mode |= stat.S_IFIFO
- if kw.get('islink', False):
- mode |= stat.S_IFLNK
- if kw.get('issock', False):
- mode |= stat.S_IFSOCK
-
- # handle non-file-related keywords
- perm = kw.get('perm', 0)
- mode |= perm
-
- path = kw['path']
- self.path = path
-
- self.stream = StringIO()
-
- data = kw.get('data', '')
- self.stream = StringIO(data)
-
- for key in ['iswriting']:
- if kw.has_key(key):
- setattr(self, key, kw[key])
-
- # child files/dirs
- self.children = []
+ # parent process
+ keyDir = os.path.join(conf.mountpoint, "keys")
+ print "Waiting for disk to come up..."
+ while not os.path.isdir(keyDir):
+ time.sleep(1)
+ disks = conf.getDisks()
- #print "FileRecord.__init__: path=%s" % path
-
- # get inode number
- inode = pathToInode(path)
+ if disks:
+ print "Freenetfs now mounted, adding existing disks..."
+ else:
+ print "Freenetfs now mounted, no freedisks at present"
- #size = kw.get('size', 0)
- now = timeNow()
- atime = kw.get('atime', now)
- mtime = kw.get('mtime', now)
- ctime = kw.get('ctime', now)
+ for disk in disks:
+
+ diskPath = os.path.join(conf.mountpoint, "usr", disk.name)
+
+ # barf if a freedisk of that name is already mounted
+ if os.path.exists(diskPath):
+ usage("Freedisk %s seems to be already mounted" %
disk.name)
+
+ # mkdir to create the freedisk dir
+ os.mkdir(diskPath)
+
+ pubKeyPath = os.path.join(diskPath, ".publickey")
+ privKeyPath = os.path.join(diskPath, ".privatekey")
+ passwdPath = os.path.join(diskPath, ".passwd")
+
+ # wait for the pseudo-files to come into existence
+ while not os.path.isfile(privKeyPath):
+ time.sleep(0.1)
+
+ # set the key and password
+ file(pubKeyPath, "w").write(disk.uri)
+ file(privKeyPath, "w").write(disk.privUri)
+ file(passwdPath, "w").write(disk.passwd)
+
+ #@-node:<<start>>
+ #@nl
- #print "statrec[stat.ST_MODE]=%s" % statrec[stat.ST_MODE]
- #print "mode=%s" % mode
-
- statrec[stat.ST_MODE] |= mode
- statrec[stat.ST_INO] = inode
- statrec[stat.ST_DEV] = dev
- statrec[stat.ST_NLINK] = nlink
- statrec[stat.ST_UID] = uid
- statrec[stat.ST_GID] = gid
-
- statrec[stat.ST_SIZE] = len(self.stream.getvalue())
-
- statrec[stat.ST_ATIME] = atime
- statrec[stat.ST_MTIME] = atime
- statrec[stat.ST_CTIME] = atime
+ elif cmd in ['umount', 'unmount', 'stop']:
+ #@ <<stop>>
+ #@+node:<<stop>>
+ os.system("umount %s" % conf.mountpoint)
- list.__init__(self, statrec)
+ #@-node:<<stop>>
+ #@nl
- self.iswriting = kw.get('iswriting', False)
+ elif cmd == 'new':
+ #@ <<new>>
+ #@+node:<<new>>
+ #print "new: %s: NOT IMPLEMENTED" % diskname
- #@-node:__init__
- #@+node:__getattr__
- def __getattr__(self, attr):
- """
- Support read of pseudo-attributes:
- - mode, isdir, ischr, isblk, isreg, isfifo, islnk, issock,
- - inode, dev, nlink, uid, gid, size, atime, mtime, ctime
- """
- if attr == 'mode':
- return self[stat.ST_MODE]
+ if os.path.exists(diskPath):
+ usage("Freedisk %s seems to be already mounted" % diskname)
+
+ # get a password if desired
+ passwd = getpasswd("Encrypt disk with password", True)
+
+ # get a new private key
+ keyDir = os.path.join(conf.mountpoint, "keys")
+ if not os.path.isdir(keyDir):
+ print "No keys directory %s" % keyDir
+ print "Is your freenetfs mounted?"
+ usage("Freenetfs not mounted")
+ keyName = "freedisk_%s_%s" % (diskname, int(time.time()*1000000))
+ keyPath = os.path.join(keyDir, keyName)
+
+ keys = file(keyPath).read().strip().split("\n")
+ pubKey, privKey = [k.split("/")[0].split("freenet:")[-1] for k in keys]
+
+ # mkdir to create the freedisk dir
+ os.mkdir(diskPath)
+
+ # wait for the pseudo-files to come into existence
+ while not os.path.isfile(privKeyPath):
+ time.sleep(0.1)
+
+ #status("About to write to %s" % privKeyPath)
+
+ file(pubKeyPath, "w").write(pubKey)
+ file(privKeyPath, "w").write(privKey)
+ file(passwdPath, "w").write(passwd)
+
+ # and, of course, update config
+ conf.addDisk(diskname, pubKey, privKey, passwd)
+
+ #@-node:<<new>>
+ #@nl
- if attr == 'isdir':
- return stat.S_ISDIR(self.mode)
-
- if attr == 'ischr':
- return stat.S_ISCHR(self.mode)
-
- if attr == 'isblk':
- return stat.S_ISBLK(self.mode)
-
- if attr == 'isreg':
- return stat.S_ISREG(self.mode)
-
- if attr == 'isfifo':
- return stat.S_ISFIFO(self.mode)
-
- if attr == 'islnk':
- return stat.S_ISLNK(self.mode)
-
- if attr == 'issock':
- return stat.S_ISSOCK(self.mode)
-
- if attr == 'inode':
- return self[stat.ST_INO]
+ elif cmd == 'add':
+ #@ <<add>>
+ #@+node:<<add>>
+ # get uri
+ if nargs < 3:
+ usage("add: Missing URI")
+ uri = args[2]
- if attr == 'dev':
- return self[stat.ST_DEV]
+ #print "add: %s: NOT IMPLEMENTED" % diskname
- if attr == 'nlink':
- return self[stat.ST_NLINK]
+ # barf if a freedisk of that name is already mounted
+ if os.path.exists(diskPath):
+ usage("Freedisk %s seems to be already mounted" % diskname)
- if attr == 'uid':
- return self[stat.ST_UID]
+ # mkdir to create the freedisk dir
+ os.mkdir(diskPath)
+
+ # wait for the pseudo-files to come into existence
+ while not os.path.isfile(privKeyPath):
+ time.sleep(0.1)
+
+ # set the keys
+
+ if fcp.node.uriIsPrivate(uri):
+ path = privKeyPath
+ else:
+ path = pubKeyPath
+ f = file(path, "w")
+ f.write(uri)
+ f.flush()
+ f.close()
+
+ #@-node:<<add>>
+ #@nl
- if attr == 'gid':
- return self[stat.ST_GID]
-
- if attr == 'size':
- return self[stat.ST_SIZE]
+ elif cmd == 'del':
+ #@ <<del>>
+ #@+node:<<del>>
+ disk = conf.getDisk(diskname)
- if attr == 'atime':
- return self[stat.ST_ATIME]
+ if not isinstance(disk, XMLNode):
+ usage("No such disk '%s'" % diskname)
- if attr == 'mtime':
- return self[stat.ST_ATIME]
+ conf.delDisk(diskname)
- if attr == 'ctime':
- return self[stat.ST_ATIME]
+ path = os.path.join(conf.mountpoint, "usr", diskname)
+ os.rmdir(path)
+
+ #@-node:<<del>>
+ #@nl
- if attr == 'data':
- return self.stream.getvalue()
+ elif cmd == 'update':
+ #@ <<update>>
+ #@+node:<<update>>
+ print "update: %s: NOT IMPLEMENTED" % diskname
- try:
- return getattr(self.stream, attr)
- except:
- pass
+ f = file(cmdPath, "w")
+ f.write("update")
+ f.flush()
+ f.close()
+
+ #@-node:<<update>>
+ #@nl
- raise AttributeError(attr)
+ elif cmd == 'commit':
+ #@ <<commit>>
+ #@+node:<<commit>>
+ print "commit: %s: launching.." % diskname
+
+ f = file(cmdPath, "w")
+ f.write("commit")
+ f.flush()
+ f.close()
+
+ #@-node:<<commit>>
+ #@nl
- #@-node:__getattr__
- #@+node:__setattr__
- def __setattr__(self, attr, val):
- """
- Support write of pseudo-attributes:
- - mode, isdir, ischr, isblk, isreg, isfifo, islnk, issock,
- - inode, dev, nlink, uid, gid, size, atime, mtime, ctime
- """
- if attr == 'isdir':
- if val:
- self[stat.ST_MODE] |= stat.S_IFDIR
- else:
- self[stat.ST_MODE] &= ~stat.S_IFDIR
- elif attr == 'ischr':
- if val:
- self[stat.ST_MODE] |= stat.S_IFCHR
- else:
- self[stat.ST_MODE] &= ~stat.S_IFCHR
- elif attr == 'isblk':
- if val:
- self[stat.ST_MODE] |= stat.S_IFBLK
- else:
- self[stat.ST_MODE] &= ~stat.S_IFBLK
- elif attr == 'isreg':
- if val:
- self[stat.ST_MODE] |= stat.S_IFREG
- else:
- self[stat.ST_MODE] &= ~stat.S_IFREG
- elif attr == 'isfifo':
- if val:
- self[stat.ST_MODE] |= stat.S_IFIFO
- else:
- self[stat.ST_MODE] &= ~stat.S_IFIFO
- elif attr == 'islnk':
- if val:
- self[stat.ST_MODE] |= stat.S_IFLNK
- else:
- self[stat.ST_MODE] &= ~stat.S_IFLNK
- elif attr == 'issock':
- if val:
- self[stat.ST_MODE] |= stat.S_IFSOCK
- else:
- self[stat.ST_MODE] &= ~stat.S_IFSOCK
-
- elif attr == 'mode':
- self[stat.ST_MODE] = val
- elif attr == 'inode':
- self[stat.ST_IMO] = val
- elif attr == 'dev':
- self[stat.ST_DEV] = val
- elif attr == 'nlink':
- self[stat.ST_NLINK] = val
- elif attr == 'uid':
- self[stat.ST_UID] = val
- elif attr == 'gid':
- self[stat.ST_GID] = val
- elif attr == 'size':
- self[stat.ST_SIZE] = val
- elif attr == 'atime':
- self[stat.ST_ATIME] = val
- elif attr == 'mtime':
- self[stat.ST_MTIME] = val
- elif attr == 'ctime':
- self[stat.ST_CTIME] = val
-
- elif attr == 'data':
- oldPos = self.stream.tell()
- self.stream = StringIO(val)
- self.stream.seek(min(oldPos, len(val)))
- self.size = len(val)
-
+ elif cmd == 'list':
+ #@ <<list>>
+ #@+node:<<list>>
+ disks = conf.getDisks()
+
+ if disks:
+ print "Currently mounted freedisks:"
+ for d in disks:
+ print " %s:" % d.name
+ print " uri=%s" % d.uri
+ print " passwd=%s" % d.passwd
else:
- self.__dict__[attr] = val
+ print "No freedisks mounted"
+
+ #@-node:<<list>>
+ #@nl
- #@-node:__setattr__
- #@+node:write
- def write(self, buf):
+ elif cmd == 'cmd':
+ #@ <<cmd>>
+ #@+node:<<cmd>>
+ # arbitrary command, for testing
- self.stream.write(buf)
- self.size = len(self.stream.getvalue())
+ cmd = " ".join(args[1:])
+
+ print repr(doFsCommand(cmd))
+
+ #@-node:<<cmd>>
+ #@nl
- #@-node:write
- #@+node:addChild
- def addChild(self, rec):
- """
- Adds a child file rec as a child of this rec
- """
- if not isinstance(rec, FileRecord):
- raise Exception("Not a FileRecord: %s" % rec)
- self.children.append(rec)
- self.size += 1
- #@-node:addChild
- #@+node:delChild
- def delChild(self, rec):
- """
- Tries to remove a child entry
- """
- if rec in self.children:
- self.children.remove(rec)
- self.size -= 1
- else:
- print "eh? trying to remove %s from %s" % (rec.path, self.path)
+ else:
+ usage("Unrecognised command: %s" % cmd)
- #@-node:delChild
- #@-others
+ #@-node:<<execute command>>
+ #@nl
-#@-node:class FileRecord
-#@+node:pathToInode
-def pathToInode(path):
- """
- Comes up with a unique inode number given a path
- """
- # try for existing known path/inode
- inode = inodes.get(path, None)
- if inode != None:
- return inode
-
- # try hashing the path to 32bit
- inode = int(md5.new(path).hexdigest()[:7], 16)
-
- # and ensure it's unique
- while inodes.has_key(inode):
- inode += 1
-
- # register it
- inodes[path] = inode
-
- # done
- return inode
-
-#@-node:pathToInode
-#@+node:timeNow
-def timeNow():
- return int(time.time()) & 0xffffffffL
-
-#@-node:timeNow
+#@-node:main
#@+node:mainline
if __name__ == '__main__':
+ main()
- server = FreenetFS()
- server.multithreaded = 1;
- server.main()
-
#@-node:mainline
#@-others
+#@-node:freedisk app
+#@-others
#@-node:@file freedisk.py
#@-leo
Added: trunk/apps/pyFreenet/manpages/fcpgenkey.1
===================================================================
--- trunk/apps/pyFreenet/manpages/fcpgenkey.1 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/manpages/fcpgenkey.1 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,69 @@
+.TH "fcpgenkey" "1" "0.1.4" "aum" "pyfcp - Freenet FCP tools"
+.SH "NAME"
+.LP
+fcpgenkey \- generate a single Freenet SSK@/USK@ keypair
+
+.SH "SYNTAX"
+.LP
+\fBfcpput\fP [\fIoptions\fP]
+.SH "DESCRIPTION"
+.LP
+fcpgenkey is a simple command\-line FCP client program for generating
+new Freenet SSK@ or USK@ public/private keypairs.
+
+The keypair generated is written to standard output stream, with
+the public key, then a newline, then the private key.
+
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help information and exit
+.TP
+
+\fB\-v\fR, \fB\-\-verbose\fR
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+.TP
+
+\fB\-H\fR, \fB\-\-fcpHost=<hostname>\fR
+Use FCP interface at host <hostname>,
+defaults to 127.0.0.1
+.TP
+
+\fB\-P\fR, \fB\-\-fcpPort=<port>\fR
+Use FCP interface at port <port>
+.TP
+
+.LP
+
+.SH "FILES"
+.TP
+\fBnone\fP
+.SH "ENVIRONMENT VARIABLES"
+.LP
+.TP
+\fBFCP_HOST\fP
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '\-H'.
+.TP
+\fBFCP_PORT\fP
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '\-P'.
+
+.LP
+
+.SH "EXAMPLES"
+.TP
+\fBfcpgenkey > keys.txt\fP
+Generate a keypair and save it to \fIkeys.txt\fP
+
+.LP
+
+.SH "AUTHORS"
+.LP
+aum <david at rebirthing.co.nz>
+.SH "SEE ALSO"
+.LP
+fcpget(1) fcpput(1) freesitemgr(1)
+
Added: trunk/apps/pyFreenet/manpages/fcpgenkey.1.html
===================================================================
--- trunk/apps/pyFreenet/manpages/fcpgenkey.1.html 2006-06-04 05:58:50 UTC
(rev 9039)
+++ trunk/apps/pyFreenet/manpages/fcpgenkey.1.html 2006-06-04 08:00:16 UTC
(rev 9040)
@@ -0,0 +1,126 @@
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Man page of fcpgenkey</TITLE>
+</HEAD><BODY>
+<H1>fcpgenkey</H1>
+Section: pyfcp - Freenet FCP tools (1)<BR>Updated: 0.1.4<BR><A
HREF="#index">Index</A>
+<A HREF="/cgi-bin/man/man2html">Return to Main Contents</A><HR>
+
+<A NAME="lbAB"> </A>
+<H2>NAME</H2>
+
+<P>
+
+fcpgenkey - generate a single Freenet SSK@/USK@ keypair
+<P>
+<A NAME="lbAC"> </A>
+<H2>SYNTAX</H2>
+
+<P>
+
+<B>fcpput</B> [<I>options</I>]
+<A NAME="lbAD"> </A>
+<H2>DESCRIPTION</H2>
+
+<P>
+
+fcpgenkey is a simple command-line FCP client program for generating
+new Freenet SSK@ or USK@ public/private keypairs.
+<P>
+The keypair generated is written to standard output stream, with
+the public key, then a newline, then the private key.
+<P>
+<A NAME="lbAE"> </A>
+<H2>OPTIONS</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>-h</B>, <B>--help</B><DD>
+Print help information and exit
+<DT><DD>
+<B>-v</B>, <B>--verbose</B>
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+<DT><DD>
+<B>-H</B>, <B>--fcpHost=<hostname></B>
+Use FCP interface at host <hostname>,
+defaults to 127.0.0.1
+<DT><DD>
+<B>-P</B>, <B>--fcpPort=<port></B>
+Use FCP interface at port <port>
+<DT><DD>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAF"> </A>
+<H2>FILES</H2>
+
+<DL COMPACT>
+<DT><B>none</B><DD>
+</DL>
+<A NAME="lbAG"> </A>
+<H2>ENVIRONMENT VARIABLES</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>FCP_HOST</B><DD>
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '-H'.
+<DT><B>FCP_PORT</B><DD>
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '-P'.
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAH"> </A>
+<H2>EXAMPLES</H2>
+
+<DL COMPACT>
+<DT><B>fcpgenkey > keys.txt</B><DD>
+Generate a keypair and save it to <I>keys.txt</I>
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAI"> </A>
+<H2>AUTHORS</H2>
+
+<P>
+
+aum <<A HREF="mailto:david at rebirthing.co.nz">david at
rebirthing.co.nz</A>>
+<A NAME="lbAJ"> </A>
+<H2>SEE ALSO</H2>
+
+<P>
+
+<A HREF="/cgi-bin/man/man2html?1+fcpget">fcpget</A>(1) <A
HREF="/cgi-bin/man/man2html?1+fcpput">fcpput</A>(1) <A
HREF="/cgi-bin/man/man2html?1+freesitemgr">freesitemgr</A>(1)
+<P>
+<P>
+
+<HR>
+<A NAME="index"> </A><H2>Index</H2>
+<DL>
+<DT><A HREF="#lbAB">NAME</A><DD>
+<DT><A HREF="#lbAC">SYNTAX</A><DD>
+<DT><A HREF="#lbAD">DESCRIPTION</A><DD>
+<DT><A HREF="#lbAE">OPTIONS</A><DD>
+<DT><A HREF="#lbAF">FILES</A><DD>
+<DT><A HREF="#lbAG">ENVIRONMENT VARIABLES</A><DD>
+<DT><A HREF="#lbAH">EXAMPLES</A><DD>
+<DT><A HREF="#lbAI">AUTHORS</A><DD>
+<DT><A HREF="#lbAJ">SEE ALSO</A><DD>
+</DL>
+<HR>
+This document was created by
+<A HREF="/cgi-bin/man/man2html">man2html</A>,
+using the manual pages.<BR>
+Time: 01:47:06 GMT, May 28, 2006
+</BODY>
+</HTML>
Added: trunk/apps/pyFreenet/manpages/fcpget.1
===================================================================
--- trunk/apps/pyFreenet/manpages/fcpget.1 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/manpages/fcpget.1 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,71 @@
+.TH "fcpget" "1" "0.1.4" "aum" "pyfcp - Freenet FCP tools"
+.SH "NAME"
+.LP
+fcpget \- Retrieve a single key from Freenet
+.SH "SYNTAX"
+.LP
+\fBfcpget\fP [\fIoptions\fP] \fIkey_uri\fP [\fIfilename\fP]
+.SH "DESCRIPTION"
+.LP
+fcpget is a simple command\-line FCP client program for retrieving
+single keys from Freenet.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help information and exit
+.TP
+
+\fB\-v\fR, \fB\-\-verbose\fR
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+.TP
+
+\fB\-H\fR, \fB\-\-fcpHost=<hostname>\fR
+Use FCP interface at host <hostname>,
+defaults to 127.0.0.1
+.TP
+
+\fB\-P\fR, \fB\-\-fcpPort=<port>\fR
+Use FCP interface at port <port>
+.TP
+
+
+.LP
+
+.SH "FILES"
+.TP
+\fBnone\fP
+.SH "ENVIRONMENT VARIABLES"
+.LP
+.TP
+\fBFCP_HOST\fP
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '\-H'.
+.TP
+\fBFCP_PORT\fP
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '\-P'.
+
+.LP
+
+.SH "EXAMPLES"
+.TP
+\fBfcpget KSK at somekey\fP
+Retrieve key URI \fIKSK at somekey\fP from Freenet, and print its
+data to standard output.
+
+.TP
+\fBfcpget KSK at somekey filename.html\fP
+Retrieve key URI \fIKSK at somekey\fP from Freenet, and save its
+data to \fIfilename.html\fP
+
+.LP
+
+.SH "AUTHORS"
+.LP
+aum <david at rebirthing.co.nz>
+.SH "SEE ALSO"
+.LP
+fcpput(1) fcpgenkey(1) freesitemgr(1)
+
Added: trunk/apps/pyFreenet/manpages/fcpget.1.html
===================================================================
--- trunk/apps/pyFreenet/manpages/fcpget.1.html 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/manpages/fcpget.1.html 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,127 @@
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Man page of fcpget</TITLE>
+</HEAD><BODY>
+<H1>fcpget</H1>
+Section: pyfcp - Freenet FCP tools (1)<BR>Updated: 0.1.4<BR><A
HREF="#index">Index</A>
+<A HREF="/cgi-bin/man/man2html">Return to Main Contents</A><HR>
+
+<A NAME="lbAB"> </A>
+<H2>NAME</H2>
+
+<P>
+
+fcpget - Retrieve a single key from Freenet
+<A NAME="lbAC"> </A>
+<H2>SYNTAX</H2>
+
+<P>
+
+<B>fcpget</B> [<I>options</I>] <I>key_uri</I> [<I>filename</I>]
+<A NAME="lbAD"> </A>
+<H2>DESCRIPTION</H2>
+
+<P>
+
+fcpget is a simple command-line FCP client program for retrieving
+single keys from Freenet.
+<A NAME="lbAE"> </A>
+<H2>OPTIONS</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>-h</B>, <B>--help</B><DD>
+Print help information and exit
+<DT><DD>
+<B>-v</B>, <B>--verbose</B>
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+<DT><DD>
+<B>-H</B>, <B>--fcpHost=<hostname></B>
+Use FCP interface at host <hostname>,
+defaults to 127.0.0.1
+<DT><DD>
+<B>-P</B>, <B>--fcpPort=<port></B>
+Use FCP interface at port <port>
+<DT><DD>
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAF"> </A>
+<H2>FILES</H2>
+
+<DL COMPACT>
+<DT><B>none</B><DD>
+</DL>
+<A NAME="lbAG"> </A>
+<H2>ENVIRONMENT VARIABLES</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>FCP_HOST</B><DD>
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '-H'.
+<DT><B>FCP_PORT</B><DD>
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '-P'.
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAH"> </A>
+<H2>EXAMPLES</H2>
+
+<DL COMPACT>
+<DT><B>fcpget <A HREF="mailto:KSK at somekey">KSK at somekey</A></B><DD>
+Retrieve key URI <I><A HREF="mailto:KSK at somekey">KSK at somekey</A></I>
from Freenet, and print its
+data to standard output.
+<P>
+<DT><B>fcpget <A HREF="mailto:KSK at somekey">KSK at somekey</A>
filename.html</B><DD>
+Retrieve key URI <I><A HREF="mailto:KSK at somekey">KSK at somekey</A></I>
from Freenet, and save its
+data to <I>filename.html</I>
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAI"> </A>
+<H2>AUTHORS</H2>
+
+<P>
+
+aum <<A HREF="mailto:david at rebirthing.co.nz">david at
rebirthing.co.nz</A>>
+<A NAME="lbAJ"> </A>
+<H2>SEE ALSO</H2>
+
+<P>
+
+<A HREF="/cgi-bin/man/man2html?1+fcpput">fcpput</A>(1) <A
HREF="/cgi-bin/man/man2html?1+fcpgenkey">fcpgenkey</A>(1) <A
HREF="/cgi-bin/man/man2html?1+freesitemgr">freesitemgr</A>(1)
+<P>
+<P>
+
+<HR>
+<A NAME="index"> </A><H2>Index</H2>
+<DL>
+<DT><A HREF="#lbAB">NAME</A><DD>
+<DT><A HREF="#lbAC">SYNTAX</A><DD>
+<DT><A HREF="#lbAD">DESCRIPTION</A><DD>
+<DT><A HREF="#lbAE">OPTIONS</A><DD>
+<DT><A HREF="#lbAF">FILES</A><DD>
+<DT><A HREF="#lbAG">ENVIRONMENT VARIABLES</A><DD>
+<DT><A HREF="#lbAH">EXAMPLES</A><DD>
+<DT><A HREF="#lbAI">AUTHORS</A><DD>
+<DT><A HREF="#lbAJ">SEE ALSO</A><DD>
+</DL>
+<HR>
+This document was created by
+<A HREF="/cgi-bin/man/man2html">man2html</A>,
+using the manual pages.<BR>
+Time: 01:47:06 GMT, May 28, 2006
+</BODY>
+</HTML>
Added: trunk/apps/pyFreenet/manpages/fcpput.1
===================================================================
--- trunk/apps/pyFreenet/manpages/fcpput.1 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/manpages/fcpput.1 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,87 @@
+.TH "fcpput" "1" "0.1.4" "aum" "pyfcp - Freenet FCP tools"
+.SH "NAME"
+.LP
+fcpput \- Insert single key, or directory, into Freenet
+.SH "SYNTAX"
+.LP
+\fBfcpput\fP [\fIoptions\fP] \fIkey_uri\fP [\fIfilename\fP]
+.SH "DESCRIPTION"
+.LP
+fcpput is a simple command\-line FCP client program for inserting
+keys or directories into Freenet.
+
+If the program completes successfully, it will print to standard
+output a \fIfreenet URI\fP under which the key can be later retrieved.
+
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help information and exit
+.TP
+
+\fB\-v\fR, \fB\-\-verbose\fR
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+.TP
+
+\fB\-H\fR, \fB\-\-fcpHost=<hostname>\fR
+Use FCP interface at host <hostname>,
+defaults to 127.0.0.1
+.TP
+
+\fB\-P\fR, \fB\-\-fcpPort=<port>\fR
+Use FCP interface at port <port>
+.TP
+
+\fB\-m\fR, \fB\-\-mimetype=<mimetype>\fR
+Specify the mimetype under which the key is to be
+inserted. If not specified, the program will try
+to guess a mimetype from the input file extension
+(if present), or from a 'file extension' suffix on
+the URI. If neither of these are present, default
+mimetype is text/plain.
+
+.LP
+
+.SH "FILES"
+.TP
+\fBnone\fP
+.SH "ENVIRONMENT VARIABLES"
+.LP
+.TP
+\fBFCP_HOST\fP
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '\-H'.
+.TP
+\fBFCP_PORT\fP
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '\-P'.
+
+.LP
+
+.SH "EXAMPLES"
+.TP
+\fBfcpput KSK at somekey filename.html\fP
+Insert file \fIfilename.html\fP as URI \fIKSK at somekey\fP
+
+.TP
+\fBsomeprog | fcpput KSK at somekey\fP
+Run a program, and insert its standard output
+as URI \fIKSK at somekey\fP
+
+.TP
+\fBfcpput \-H 192.168.10.4 CHK at fred.jpg somefile\fP
+Using the FCP service on host \fI192.168.10.4\fP,
+insert \fIsomefile\fP as a CHK, with the implied
+mimetype of \fIimage/jpeg\fP. The final CHK URI under
+which this key can be later retrieved will be printed to stdout.
+.LP
+
+.SH "AUTHORS"
+.LP
+aum <david at rebirthing.co.nz>
+.SH "SEE ALSO"
+.LP
+fcpget(1) fcpgenkey(1) freesitemgr(1)
+
Added: trunk/apps/pyFreenet/manpages/fcpput.1.html
===================================================================
--- trunk/apps/pyFreenet/manpages/fcpput.1.html 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/manpages/fcpput.1.html 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,142 @@
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Man page of fcpput</TITLE>
+</HEAD><BODY>
+<H1>fcpput</H1>
+Section: pyfcp - Freenet FCP tools (1)<BR>Updated: 0.1.4<BR><A
HREF="#index">Index</A>
+<A HREF="/cgi-bin/man/man2html">Return to Main Contents</A><HR>
+
+<A NAME="lbAB"> </A>
+<H2>NAME</H2>
+
+<P>
+
+fcpput - Insert single key, or directory, into Freenet
+<A NAME="lbAC"> </A>
+<H2>SYNTAX</H2>
+
+<P>
+
+<B>fcpput</B> [<I>options</I>] <I>key_uri</I> [<I>filename</I>]
+<A NAME="lbAD"> </A>
+<H2>DESCRIPTION</H2>
+
+<P>
+
+fcpput is a simple command-line FCP client program for inserting
+keys or directories into Freenet.
+<P>
+If the program completes successfully, it will print to standard
+output a <I>freenet URI</I> under which the key can be later retrieved.
+<P>
+<A NAME="lbAE"> </A>
+<H2>OPTIONS</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>-h</B>, <B>--help</B><DD>
+Print help information and exit
+<DT><DD>
+<B>-v</B>, <B>--verbose</B>
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+<DT><DD>
+<B>-H</B>, <B>--fcpHost=<hostname></B>
+Use FCP interface at host <hostname>,
+defaults to 127.0.0.1
+<DT><DD>
+<B>-P</B>, <B>--fcpPort=<port></B>
+Use FCP interface at port <port>
+<DT><DD>
+<B>-m</B>, <B>--mimetype=<mimetype></B>
+Specify the mimetype under which the key is to be
+inserted. If not specified, the program will try
+to guess a mimetype from the input file extension
+(if present), or from a 'file extension' suffix on
+the URI. If neither of these are present, default
+mimetype is text/plain.
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAF"> </A>
+<H2>FILES</H2>
+
+<DL COMPACT>
+<DT><B>none</B><DD>
+</DL>
+<A NAME="lbAG"> </A>
+<H2>ENVIRONMENT VARIABLES</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>FCP_HOST</B><DD>
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '-H'.
+<DT><B>FCP_PORT</B><DD>
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '-P'.
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAH"> </A>
+<H2>EXAMPLES</H2>
+
+<DL COMPACT>
+<DT><B>fcpput <A HREF="mailto:KSK at somekey">KSK at somekey</A>
filename.html</B><DD>
+Insert file <I>filename.html</I> as URI <I><A HREF="mailto:KSK at somekey">KSK
at somekey</A></I>
+<P>
+<DT><B>someprog | fcpput <A HREF="mailto:KSK at somekey">KSK at
somekey</A></B><DD>
+Run a program, and insert its standard output
+as URI <I><A HREF="mailto:KSK at somekey">KSK at somekey</A></I>
+<P>
+<DT><B>fcpput -H 192.168.10.4 <A HREF="mailto:CHK at fred.jpg">CHK at
fred.jpg</A> somefile</B><DD>
+Using the FCP service on host <I>192.168.10.4</I>,
+insert <I>somefile</I> as a CHK, with the implied
+mimetype of <I>image/jpeg</I>. The final CHK URI under
+which this key can be later retrieved will be printed to stdout.
+</DL>
+<P>
+
+<P>
+<A NAME="lbAI"> </A>
+<H2>AUTHORS</H2>
+
+<P>
+
+aum <<A HREF="mailto:david at rebirthing.co.nz">david at
rebirthing.co.nz</A>>
+<A NAME="lbAJ"> </A>
+<H2>SEE ALSO</H2>
+
+<P>
+
+<A HREF="/cgi-bin/man/man2html?1+fcpget">fcpget</A>(1) <A
HREF="/cgi-bin/man/man2html?1+fcpgenkey">fcpgenkey</A>(1) <A
HREF="/cgi-bin/man/man2html?1+freesitemgr">freesitemgr</A>(1)
+<P>
+<P>
+
+<HR>
+<A NAME="index"> </A><H2>Index</H2>
+<DL>
+<DT><A HREF="#lbAB">NAME</A><DD>
+<DT><A HREF="#lbAC">SYNTAX</A><DD>
+<DT><A HREF="#lbAD">DESCRIPTION</A><DD>
+<DT><A HREF="#lbAE">OPTIONS</A><DD>
+<DT><A HREF="#lbAF">FILES</A><DD>
+<DT><A HREF="#lbAG">ENVIRONMENT VARIABLES</A><DD>
+<DT><A HREF="#lbAH">EXAMPLES</A><DD>
+<DT><A HREF="#lbAI">AUTHORS</A><DD>
+<DT><A HREF="#lbAJ">SEE ALSO</A><DD>
+</DL>
+<HR>
+This document was created by
+<A HREF="/cgi-bin/man/man2html">man2html</A>,
+using the manual pages.<BR>
+Time: 01:47:06 GMT, May 28, 2006
+</BODY>
+</HTML>
Added: trunk/apps/pyFreenet/manpages/freesitemgr.1
===================================================================
--- trunk/apps/pyFreenet/manpages/freesitemgr.1 2006-06-04 05:58:50 UTC (rev
9039)
+++ trunk/apps/pyFreenet/manpages/freesitemgr.1 2006-06-04 08:00:16 UTC (rev
9040)
@@ -0,0 +1,87 @@
+.TH "freesitemgr" "1" "0.1.4" "aum" "pyfcp - Freenet FCP tools"
+.SH "NAME"
+.LP
+freesitemgr \- Insert or re\-insert freesites into freenet
+
+.SH "SYNTAX"
+.LP
+\fBfreesitemgr\fP [\fIoptions\fP] \fIcommand\fP [\fIarguments\fP]
+.SH "DESCRIPTION"
+.LP
+\fBfreesitemgr\fP is a simple command\-line freenet client program
+for inserting \fBfreesites\fP.
+
+Use of this program is controlled by the \fIcommand\fP argument
+you give it.
+
+run \fBfreesitemgr \-h\fP for a detailed help output.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help information and exit
+.TP
+
+\fB\-v\fR, \fB\-\-verbose\fR
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+.TP
+
+\fB\-q\fR, \fB\-\-quiet\fR
+Run quietly
+.TP
+
+\fB\-l\fR, \fB\-\-logfile\fR
+location of logfile (default /home/david/updatesites.log)
+.TP
+
+\fB\-s\fR, \fB\-\-single\-files\fR
+Insert each file of the freesite as single CHKs, and redirect
+to these from within the freesite manifest
+
+.LP
+
+.SH "COMMANDS"
+.LP
+
+\fBsetup\fP
+Create or modify the freesite config file (\fB~/.freesites\fP)
+interactively.
+
+\fBadd\fP
+Add a new freesite interactively
+
+\fBlist [<name>]\fP
+display a summary of all freesites, or a
+detailed report of one site if <name> given
+
+\fBremove <name>\fP
+remove given freesite
+
+\fBupdate\fP
+reinsert any freesites which have changed since
+they were last inserted
+
+
+.LP
+
+.SH "ENVIRONMENT VARIABLES"
+.LP
+.TP
+\fBFCP_HOST\fP
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '\-H'.
+.TP
+\fBFCP_PORT\fP
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '\-P'.
+
+.LP
+
+.SH "AUTHORS"
+.LP
+aum <david at rebirthing.co.nz>
+.SH "SEE ALSO"
+.LP
+fcpget(1) fcpput(1) fcpgenkey(1)
+
Added: trunk/apps/pyFreenet/manpages/freesitemgr.1.html
===================================================================
--- trunk/apps/pyFreenet/manpages/freesitemgr.1.html 2006-06-04 05:58:50 UTC
(rev 9039)
+++ trunk/apps/pyFreenet/manpages/freesitemgr.1.html 2006-06-04 08:00:16 UTC
(rev 9040)
@@ -0,0 +1,140 @@
+Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Man page of freesitemgr</TITLE>
+</HEAD><BODY>
+<H1>freesitemgr</H1>
+Section: pyfcp - Freenet FCP tools (1)<BR>Updated: 0.1.4<BR><A
HREF="#index">Index</A>
+<A HREF="/cgi-bin/man/man2html">Return to Main Contents</A><HR>
+
+<A NAME="lbAB"> </A>
+<H2>NAME</H2>
+
+<P>
+
+freesitemgr - Insert or re-insert freesites into freenet
+<P>
+<A NAME="lbAC"> </A>
+<H2>SYNTAX</H2>
+
+<P>
+
+<B>freesitemgr</B> [<I>options</I>] <I>command</I> [<I>arguments</I>]
+<A NAME="lbAD"> </A>
+<H2>DESCRIPTION</H2>
+
+<P>
+
+<B>freesitemgr</B> is a simple command-line freenet client program
+for inserting <B>freesites</B>.
+<P>
+Use of this program is controlled by the <I>command</I> argument
+you give it.
+<P>
+run <B>freesitemgr -h</B> for a detailed help output.
+<A NAME="lbAE"> </A>
+<H2>OPTIONS</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>-h</B>, <B>--help</B><DD>
+Print help information and exit
+<DT><DD>
+<B>-v</B>, <B>--verbose</B>
+Enable very verbose output, including FCP transcripts,
+to the stderr stream.
+<DT><DD>
+<B>-q</B>, <B>--quiet</B>
+Run quietly
+<DT><DD>
+<B>-l</B>, <B>--logfile</B>
+location of logfile (default /home/david/updatesites.log)
+<DT><DD>
+<B>-s</B>, <B>--single-files</B>
+Insert each file of the freesite as single CHKs, and redirect
+to these from within the freesite manifest
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAF"> </A>
+<H2>COMMANDS</H2>
+
+<P>
+
+<P>
+<B>setup</B>
+Create or modify the freesite config file (<B>~/.freesites</B>)
+interactively.
+<P>
+<B>add</B>
+Add a new freesite interactively
+<P>
+<B>list [<name>]</B>
+display a summary of all freesites, or a
+detailed report of one site if <name> given
+<P>
+<B>remove <name></B>
+remove given freesite
+<P>
+<B>update</B>
+reinsert any freesites which have changed since
+they were last inserted
+<P>
+<P>
+<P>
+
+<P>
+<A NAME="lbAG"> </A>
+<H2>ENVIRONMENT VARIABLES</H2>
+
+<P>
+
+<DL COMPACT>
+<DT><B>FCP_HOST</B><DD>
+Specifies the hostname of the FCP interface. This value
+will be used unless overridden with '-H'.
+<DT><B>FCP_PORT</B><DD>
+Specifies the port number of the FCP interface. This value
+will be used unless overridden with '-P'.
+<P>
+</DL>
+<P>
+
+<P>
+<A NAME="lbAH"> </A>
+<H2>AUTHORS</H2>
+
+<P>
+
+aum <<A HREF="mailto:david at rebirthing.co.nz">david at
rebirthing.co.nz</A>>
+<A NAME="lbAI"> </A>
+<H2>SEE ALSO</H2>
+
+<P>
+
+<A HREF="/cgi-bin/man/man2html?1+fcpget">fcpget</A>(1) <A
HREF="/cgi-bin/man/man2html?1+fcpput">fcpput</A>(1) <A
HREF="/cgi-bin/man/man2html?1+fcpgenkey">fcpgenkey</A>(1)
+<P>
+<P>
+
+<HR>
+<A NAME="index"> </A><H2>Index</H2>
+<DL>
+<DT><A HREF="#lbAB">NAME</A><DD>
+<DT><A HREF="#lbAC">SYNTAX</A><DD>
+<DT><A HREF="#lbAD">DESCRIPTION</A><DD>
+<DT><A HREF="#lbAE">OPTIONS</A><DD>
+<DT><A HREF="#lbAF">COMMANDS</A><DD>
+<DT><A HREF="#lbAG">ENVIRONMENT VARIABLES</A><DD>
+<DT><A HREF="#lbAH">AUTHORS</A><DD>
+<DT><A HREF="#lbAI">SEE ALSO</A><DD>
+</DL>
+<HR>
+This document was created by
+<A HREF="/cgi-bin/man/man2html">man2html</A>,
+using the manual pages.<BR>
+Time: 01:47:06 GMT, May 28, 2006
+</BODY>
+</HTML>
Modified: trunk/apps/pyFreenet/setup.py
===================================================================
--- trunk/apps/pyFreenet/setup.py 2006-06-04 05:58:50 UTC (rev 9039)
+++ trunk/apps/pyFreenet/setup.py 2006-06-04 08:00:16 UTC (rev 9040)
@@ -1,18 +1,50 @@
"""
distutils installation script for pyfcp
"""
-import sys
+import sys, os
+# barf if prerequisite module 'SSLCrypto' is not installed
+try:
+ sys.stdout.write("Testing if SSLCrypto module is installed...")
+ sys.stdout.flush()
+ import SSLCrypto
+ print "ok!"
+except:
+ print "failed!"
+ print
+ print "You have not installed the SSLCrypto module"
+ print "Please refer to the INSTALL file in this directory"
+ print "and follow the instructions"
+ print
+ print "You can continue with this installation, but you will"
+ print "not have the protection of encrypted config files."
+ resp = raw_input("Continue installation anyway? [Y/n] ")
+ resp = resp.strip().lower() or "y"
+ resp = resp[0]
+ if resp == 'n':
+ print "Installation aborted"
+ sys.exit(1)
+ else:
+ print "Installing without encryption"
+
+# barf if user is not running this script as root
+if (os.getuid() != 0) and not sys.platform.lower().startswith("win"):
+ print "You must be root to do this installation"
+ sys.exit(1)
+
+
if sys.platform.lower().startswith("win"):
freesitemgrScript = "freesitemgr.py"
fcpgetScript = "fcpget.py"
fcpputScript = "fcpput.py"
fcpgenkeyScript = "fcpgenkey.py"
+ freediskScript = "freedisk.py"
else:
freesitemgrScript = "freesitemgr"
fcpgetScript = "fcpget"
fcpputScript = "fcpput"
fcpgenkeyScript = "fcpgenkey"
+ freediskScript = "freedisk"
from distutils.core import setup
setup(name="PyFCP",
@@ -24,7 +56,7 @@
packages = ['fcp'],
scripts = [freesitemgrScript, fcpgetScript, fcpputScript,
- fcpgenkeyScript,
+ fcpgenkeyScript, freediskScript,
],