[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r3286 - in freeway: . native src/org/gnu/freeway src/org/gn
From: |
mdonoughe |
Subject: |
[GNUnet-SVN] r3286 - in freeway: . native src/org/gnu/freeway src/org/gnu/freeway/cwrappers src/org/gnu/freeway/cwrappers/util src/org/gnu/freeway/protocol src/org/gnu/freeway/protocol/fs src/org/gnu/freeway/protocol/fs/esed2 src/org/gnu/freeway/protocol/fs/swing src/org/gnu/freeway/server src/org/gnu/freeway/services src/org/gnu/freeway/services/c src/org/gnu/freeway/test src/org/gnu/freeway/util |
Date: |
Mon, 21 Aug 2006 04:54:25 -0700 (PDT) |
Author: mdonoughe
Date: 2006-08-21 04:53:42 -0700 (Mon, 21 Aug 2006)
New Revision: 3286
Added:
freeway/src/org/gnu/freeway/cwrappers/CDatastoreDatum.java
freeway/src/org/gnu/freeway/cwrappers/CDatastoreValue.java
freeway/src/org/gnu/freeway/cwrappers/CHashCode512.java
freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreDatum.java
freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreValue.java
freeway/src/org/gnu/freeway/cwrappers/ConstCHashCode512.java
freeway/src/org/gnu/freeway/protocol/fs/
freeway/src/org/gnu/freeway/protocol/fs/AFSProtocol.java
freeway/src/org/gnu/freeway/protocol/fs/BloomFilter2.java
freeway/src/org/gnu/freeway/protocol/fs/DBHandle.java
freeway/src/org/gnu/freeway/protocol/fs/EntryCallback.java
freeway/src/org/gnu/freeway/protocol/fs/FileIndex.java
freeway/src/org/gnu/freeway/protocol/fs/Handler.java
freeway/src/org/gnu/freeway/protocol/fs/IndexedFileNameCallback.java
freeway/src/org/gnu/freeway/protocol/fs/IndirectionTableEntry.java
freeway/src/org/gnu/freeway/protocol/fs/IterState.java
freeway/src/org/gnu/freeway/protocol/fs/LFS.java
freeway/src/org/gnu/freeway/protocol/fs/Manager.java
freeway/src/org/gnu/freeway/protocol/fs/Migration.java
freeway/src/org/gnu/freeway/protocol/fs/MySQLHandle.java
freeway/src/org/gnu/freeway/protocol/fs/NativeFSProtocol.java
freeway/src/org/gnu/freeway/protocol/fs/Policy2.java
freeway/src/org/gnu/freeway/protocol/fs/QueryManager.java
freeway/src/org/gnu/freeway/protocol/fs/QueryRecord.java
freeway/src/org/gnu/freeway/protocol/fs/Routing.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/
freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSConstants.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSUtils.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Block.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDelete3Hash.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDeleteChk.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSGetAvgPriority.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexFile.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexSuper.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsert3Hash.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertChk.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertSBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSLinkFile.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSNSQuery.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSQuery.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResult3Hash.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultChk.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultSBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexFile.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexSuper.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUploadFile.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/ChkHashes.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentEncoding.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentIndex.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/DBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteURI.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteUtil.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadURI.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadUtil.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/EncryptedSBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Extractor.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/FileIdentifier.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectory.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectoryDatabase.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/GeneralURI.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlockData.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/IOContext.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertURI.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertUtil.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertWrapper.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/KeyWords.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Listener.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/NSSearchResultCallback.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Node.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/NodeContext.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/P2P3HashResult.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PChkResult.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PNSQuery.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PQuery.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PSBlockResult.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Policy.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Priority.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressModel.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressStats.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/Pseudonym.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestContinuation.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestEntry.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestManager.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNode.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNodeCallback.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/SBlock.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchResultCallback.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchURI.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/SendNSQueryContext.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/SendQueriesContext.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/TestTerminateThread.java
freeway/src/org/gnu/freeway/protocol/fs/esed2/URI.java
freeway/src/org/gnu/freeway/protocol/fs/swing/
freeway/src/org/gnu/freeway/protocol/fs/swing/CreatePseudonymDialog.java
freeway/src/org/gnu/freeway/protocol/fs/swing/DaemonWindow.java
freeway/src/org/gnu/freeway/protocol/fs/swing/DeletePseudonymDialog.java
freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadAdapter.java
freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadModel.java
freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadWindow.java
freeway/src/org/gnu/freeway/protocol/fs/swing/InsertDialog.java
freeway/src/org/gnu/freeway/protocol/fs/swing/SController.java
freeway/src/org/gnu/freeway/protocol/fs/swing/SearchAdapter.java
freeway/src/org/gnu/freeway/protocol/fs/swing/SearchOverviewPanel.java
freeway/src/org/gnu/freeway/protocol/fs/swing/SearchPanel.java
freeway/src/org/gnu/freeway/protocol/fs/swing/SearchWindow.java
freeway/src/org/gnu/freeway/protocol/fs/swing/SelectDialog.java
freeway/src/org/gnu/freeway/protocol/fs/swing/StatsWindow.java
freeway/src/org/gnu/freeway/protocol/fs/swing/about.c
freeway/src/org/gnu/freeway/protocol/fs/swing/about.h
freeway/src/org/gnu/freeway/protocol/fs/swing/delete.c
freeway/src/org/gnu/freeway/protocol/fs/swing/delete.h
freeway/src/org/gnu/freeway/protocol/fs/swing/directory.c
freeway/src/org/gnu/freeway/protocol/fs/swing/directory.h
freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.c
freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.h
freeway/src/org/gnu/freeway/protocol/fs/swing/download.c
freeway/src/org/gnu/freeway/protocol/fs/swing/download.h
freeway/src/org/gnu/freeway/protocol/fs/swing/helper.c
freeway/src/org/gnu/freeway/protocol/fs/swing/helper.h
freeway/src/org/gnu/freeway/protocol/fs/swing/insert.c
freeway/src/org/gnu/freeway/protocol/fs/swing/insert.h
freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.c
freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.h
freeway/src/org/gnu/freeway/protocol/fs/swing/main.c
freeway/src/org/gnu/freeway/protocol/fs/swing/main.h
freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.c
freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.h
freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.c
freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.h
freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.c
freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.h
freeway/src/org/gnu/freeway/protocol/fs/swing/search.c
freeway/src/org/gnu/freeway/protocol/fs/swing/search.h
freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.c
freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.h
freeway/src/org/gnu/freeway/services/DatastoreService.java
freeway/src/org/gnu/freeway/services/c/DatastoreService.java
freeway/src/org/gnu/freeway/util/DatastoreValue.java
freeway/src/org/gnu/freeway/util/DatumIterator.java
Removed:
freeway/src/org/gnu/freeway/protocol/afs/
Modified:
freeway/
freeway/native/java.c
freeway/native/switch-table.c
freeway/native/switch-table.h
freeway/native/util.c
freeway/protocols.xml
freeway/src/org/gnu/freeway/GNUNetDaemon.java
freeway/src/org/gnu/freeway/GNUNetDirectory.java
freeway/src/org/gnu/freeway/GNUNetDownload.java
freeway/src/org/gnu/freeway/GNUNetInsert.java
freeway/src/org/gnu/freeway/GNUNetPseudonym.java
freeway/src/org/gnu/freeway/GNUNetSearch.java
freeway/src/org/gnu/freeway/GNUNetSwing.java
freeway/src/org/gnu/freeway/cwrappers/util/SwitchTableGenerator.java
freeway/src/org/gnu/freeway/server/CPluginLoader.java
freeway/src/org/gnu/freeway/server/NativeCallback.java
freeway/src/org/gnu/freeway/services/c/StatsService.java
freeway/src/org/gnu/freeway/test/MySQLTest.java
Log:
fixes in SwitchTableGenerator
work on native FS application and it's services
Property changes on: freeway
___________________________________________________________________
Name: svk:merge
- 9da852c3-611b-0410-9456-b3f9f3a8f88d:/local/freeway:68
ca0d4bff-9018-0410-8ce9-c5d843b21c37:/local/freeway:73
+ 9da852c3-611b-0410-9456-b3f9f3a8f88d:/local/freeway:70
ca0d4bff-9018-0410-8ce9-c5d843b21c37:/local/freeway:73
Modified: freeway/native/java.c
===================================================================
--- freeway/native/java.c 2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/native/java.c 2006-08-21 11:53:42 UTC (rev 3286)
@@ -135,6 +135,7 @@
MUTEX_CREATE(&threadsLock);
MUTEX_CREATE(&modulesLock);
registerThread(env, capi);
+ FREENONNULL(setConfigurationString("GNUNETD", "_MAGIC_", "NO"));
char * * jargs = NULL;
int jargLength;
GNUNET_ASSERT(env != NULL);
Modified: freeway/native/switch-table.c
===================================================================
--- freeway/native/switch-table.c 2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/native/switch-table.c 2006-08-21 11:53:42 UTC (rev 3286)
@@ -2,13 +2,18 @@
jobject * jargs;
int jargLength = (*env)->GetArrayLength(env, arguments);
if(jargLength > 0) {
- jargs = MALLOC(sizeof(jobject) * jargLength);
+ jargs = malloc(sizeof(jobject) * jargLength);
int jargsI;
for(jargsI = 0; jargsI < jargLength; jargsI++) {
jargs[jargsI] = (*env)->GetObjectArrayElement(env, arguments, jargsI);
}
}
switch (functionType) {
+ case 4: {
+ long long cret = ((FunctionType4)
((void**)m->moduleFptrStruct)[functionOffset])();
+ oret = convLongToCLong(cret, env);
+ break;
+ }
case 13: {
PointerObject * carg0 = convObjectToPtr(jargs[0], env);
int cret = ((FunctionType13)
((void**)m->moduleFptrStruct)[functionOffset])(carg0->pointer);
@@ -22,6 +27,15 @@
((FunctionType60) ((void**)m->moduleFptrStruct)[functionOffset])(carg0,
carg1);
break;
}
+ case 85: {
+ PointerObject * carg0 = convObjectToPtr(jargs[0], env);
+ PointerObject * carg1 = convObjectToPtr(jargs[1], env);
+ int cret = ((FunctionType85)
((void**)m->moduleFptrStruct)[functionOffset])(carg0->pointer, carg1->pointer);
+ updateObjectFromPtr(jargs[1], carg1, env);
+ updateObjectFromPtr(jargs[0], carg0, env);
+ oret = convIntToCInt(cret, env);
+ break;
+ }
case 42: {
int carg0 = convCIntToInt(jargs[0], env);
int carg1 = convCIntToInt(jargs[1], env);
@@ -38,4 +52,4 @@
GNUNET_ASSERT(0);
}
if(jargs != NULL)
- FREE(jargs);
+ free(jargs);
Modified: freeway/native/switch-table.h
===================================================================
--- freeway/native/switch-table.h 2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/native/switch-table.h 2006-08-21 11:53:42 UTC (rev 3286)
@@ -1,5 +1,7 @@
// This file was autogenerated by SwitchTableGenerator
+typedef long long (*FunctionType4)();
typedef int (*FunctionType13)(void * arg0);
typedef void (*FunctionType60)(int arg0, long long arg1);
+typedef int (*FunctionType85)(void * arg0, void * arg1);
typedef void (*FunctionType42)(int arg0, int arg1);
typedef long long (*FunctionType10)(int arg0);
Modified: freeway/native/util.c
===================================================================
--- freeway/native/util.c 2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/native/util.c 2006-08-21 11:53:42 UTC (rev 3286)
@@ -488,5 +488,6 @@
//TODO: fix this
int parseGnunetdCommandLine(int argc, char * argv[]) {
+ FREENONNULL(setConfigurationString("GNUNETD", "_MAGIC_", "YES"));
return OK;
}
Modified: freeway/protocols.xml
===================================================================
--- freeway/protocols.xml 2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/protocols.xml 2006-08-21 11:53:42 UTC (rev 3286)
@@ -1,8 +1,10 @@
-<jar destfile="${project.build}/protocol-afs.jar">
- <fileset dir="${project.build}/classes"
includes="org/gnu/freeway/protocol/afs/**"
excludes="org/gnu/freeway/protocol/afs/MySQLHandle*" />
+<jar destfile="${project.build}/protocol-fs.jar">
+ <fileset dir="${project.build}/classes"
includes="org/gnu/freeway/protocol/fs/**"
excludes="org/gnu/freeway/protocol/fs/MySQLHandle*" />
<fileset dir="${project.res}/swing" />
<manifest>
- <attribute name="Main-Class"
value="org.gnu.freeway.protocol.afs.AFSProtocol" />
+ <attribute name="Freeway-Name" value="FS" />
+ <attribute name="Freeway-Java-Class"
value="org.gnu.freeway.protocol.fs.AFSProtocol" />
+ <attribute name="Freeway-C-Class"
value="org.gnu.freeway.protocol.fs.NativeFSProtocol" />
</manifest>
</jar>
Modified: freeway/src/org/gnu/freeway/GNUNetDaemon.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetDaemon.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetDaemon.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -253,7 +253,7 @@
public static void main( String[] args )
{
//TODO: use args
- CPluginLoader._.initUtil(new String[] {}, VERSION);
+ CPluginLoader._.initUtil(new String[] {"gnunetd"}, VERSION);
launch(GNUNetDaemon.class,args);
}
Modified: freeway/src/org/gnu/freeway/GNUNetDirectory.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetDirectory.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetDirectory.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,8 +4,8 @@
package org.gnu.freeway;
-import org.gnu.freeway.protocol.afs.*;
-import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.fs.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.io.*;
Modified: freeway/src/org/gnu/freeway/GNUNetDownload.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetDownload.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetDownload.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,8 +4,8 @@
package org.gnu.freeway;
-import org.gnu.freeway.protocol.afs.*;
-import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.fs.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.io.*;
import org.gnu.freeway.util.net.*;
Modified: freeway/src/org/gnu/freeway/GNUNetInsert.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetInsert.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetInsert.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,8 +4,8 @@
package org.gnu.freeway;
-import org.gnu.freeway.protocol.afs.*;
-import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.fs.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.crypto.*;
import org.gnu.freeway.util.io.*;
Modified: freeway/src/org/gnu/freeway/GNUNetPseudonym.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetPseudonym.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetPseudonym.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,7 +4,7 @@
package org.gnu.freeway;
-import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.crypto.*;
import org.gnu.freeway.util.net.*;
Modified: freeway/src/org/gnu/freeway/GNUNetSearch.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetSearch.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetSearch.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,7 +4,7 @@
package org.gnu.freeway;
-import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.crypto.*;
import org.gnu.freeway.util.io.*;
Modified: freeway/src/org/gnu/freeway/GNUNetSwing.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetSwing.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/GNUNetSwing.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,8 +4,8 @@
package org.gnu.freeway;
-import org.gnu.freeway.protocol.afs.esed2.*;
-import org.gnu.freeway.protocol.afs.swing.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.protocol.fs.swing.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.io.*;
import org.gnu.freeway.util.net.*;
Added: freeway/src/org/gnu/freeway/cwrappers/CDatastoreDatum.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/CDatastoreDatum.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/CDatastoreDatum.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,54 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.cwrappers;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.gnu.freeway.cwrappers.util.CWrapper;
+import org.gnu.freeway.util.crypto.HostIdentity;
+import org.gnu.freeway.util.net.PersistentHelper;
+
+/**
+ * @file CDataStoreValue.java
+ * @brief
+ * @author mdonoughe
+ */
+public class CDatastoreDatum extends ConstCDatastoreDatum implements CWrapper {
+
+ private static final int KIND = CWrapper.PTR_KIND;
+
+ public CDatastoreDatum(byte[] serializedData) {
+ deserializeFromByteArray(serializedData);
+ }
+
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.cwrappers.util.CWrapper#deserializeFromByteArray(byte[])
+ */
+ public void deserializeFromByteArray(byte[] serializedData) {
+ byte[] keya = new byte[64];
+ byte[] valuea = new byte[24];
+ System.arraycopy(serializedData, 0, keya, 0, 64);
+ System.arraycopy(serializedData, 64, keya, 0, 24);
+ key = new CHashCode512(keya);
+ value = new CDatastoreValue(valuea);
+ }
+
+}
Added: freeway/src/org/gnu/freeway/cwrappers/CDatastoreValue.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/CDatastoreValue.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/CDatastoreValue.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,57 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.cwrappers;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.gnu.freeway.cwrappers.util.CWrapper;
+import org.gnu.freeway.util.crypto.HostIdentity;
+import org.gnu.freeway.util.net.PersistentHelper;
+
+/**
+ * @file CDataStoreValue.java
+ * @brief
+ * @author mdonoughe
+ */
+public class CDatastoreValue extends ConstCDatastoreValue implements CWrapper {
+
+ private static final int KIND = CWrapper.PTR_KIND;
+
+ public CDatastoreValue(byte[] serializedData) {
+ super(null);
+ deserializeFromByteArray(serializedData);
+ }
+
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.cwrappers.util.CWrapper#deserializeFromByteArray(byte[])
+ */
+ public void deserializeFromByteArray(byte[] serializedData) {
+ ByteBuffer buf = ByteBuffer.wrap(serializedData);
+ buf.order(ByteOrder.nativeOrder());
+ value.setSize(buf.getInt());
+ value.setType(buf.getInt());
+ value.setPrio(buf.getInt());
+ value.setAnonymityLevel(buf.getInt());
+ buf.order(ByteOrder.BIG_ENDIAN);
+ value.setExpirationTime(buf.getLong());
+ }
+
+}
Added: freeway/src/org/gnu/freeway/cwrappers/CHashCode512.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/CHashCode512.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/CHashCode512.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,58 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.cwrappers;
+
+import java.nio.ByteBuffer;
+
+import org.gnu.freeway.cwrappers.ConstCHostIdentity;
+import org.gnu.freeway.cwrappers.util.CWrapper;
+import org.gnu.freeway.util.crypto.HashCode512;
+import org.gnu.freeway.util.crypto.HostIdentity;
+import org.gnu.freeway.util.net.PersistentHelper;
+
+/**
+ * @file CHostIdentity.java
+ * @brief
+ * @author mdonoughe
+ */
+public class CHashCode512 extends ConstCHashCode512 implements CWrapper {
+
+ private static final int KIND = CWrapper.PTR_KIND;
+
+ public CHashCode512(byte[] serializedData) {
+ super(null);
+ deserializeFromByteArray(serializedData);
+ }
+
+ public CHashCode512(HashCode512 value) {
+ super(value);
+ }
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.cwrappers.util.CWrapper#deserializeFromByteArray(byte[])
+ */
+ public void deserializeFromByteArray(byte[] serializedData) {
+ value = (HashCode512)
PersistentHelper.readFully(HashCode512.class, ByteBuffer.wrap(serializedData));
+ }
+
+ public void setValue(HashCode512 value) {
+ this.value = value;
+ }
+
+}
Added: freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreDatum.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreDatum.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreDatum.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,59 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.cwrappers;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.gnu.freeway.cwrappers.util.CWrapper;
+import org.gnu.freeway.cwrappers.util.ConstCWrapper;
+import org.gnu.freeway.util.crypto.HostIdentity;
+import org.gnu.freeway.util.net.PersistentHelper;
+
+/**
+ * @file ConstCDataStoreValue.java
+ * @brief
+ * @author mdonoughe
+ */
+public class ConstCDatastoreDatum implements ConstCWrapper {
+ private static final int KIND = CWrapper.PTR_KIND;
+
+ ConstCHashCode512 key;
+ ConstCDatastoreValue value;
+
+ /* (non-Javadoc)
+ * @see org.gnu.freeway.cwrappers.util.ConstCWrapper#getSerializedSize()
+ */
+ public int getSerializedSize() {
+ return 88;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.cwrappers.util.ConstCWrapper#serializeToByteArray()
+ */
+ public byte[] serializeToByteArray() {
+ byte[] out = new byte[88];
+ ByteBuffer buf = ByteBuffer.wrap(out);
+ buf.put(key.serializeToByteArray());
+ buf.put(value.serializeToByteArray());
+ return buf.array();
+ }
+
+}
Added: freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreValue.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreValue.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/ConstCDatastoreValue.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,72 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.cwrappers;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.gnu.freeway.cwrappers.util.CWrapper;
+import org.gnu.freeway.cwrappers.util.ConstCWrapper;
+import org.gnu.freeway.util.DatastoreValue;
+import org.gnu.freeway.util.crypto.HostIdentity;
+import org.gnu.freeway.util.net.PersistentHelper;
+
+/**
+ * @file ConstCDataStoreValue.java
+ * @brief
+ * @author mdonoughe
+ */
+public class ConstCDatastoreValue implements ConstCWrapper {
+ private static final int KIND = CWrapper.PTR_KIND;
+
+ DatastoreValue value;
+
+ public ConstCDatastoreValue(DatastoreValue value) {
+ this.value = value;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gnu.freeway.cwrappers.util.ConstCWrapper#getSerializedSize()
+ */
+ public int getSerializedSize() {
+ return 24;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.cwrappers.util.ConstCWrapper#serializeToByteArray()
+ */
+ public byte[] serializeToByteArray() {
+ byte[] out = new byte[24];
+ ByteBuffer buf = ByteBuffer.wrap(out);
+ buf.order(ByteOrder.nativeOrder());
+ buf.putInt(value.getSize());
+ buf.putInt(value.getType());
+ buf.putInt(value.getPrio());
+ buf.putInt(value.getAnonymityLevel());
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putLong(value.getExpirationTime());
+ return buf.array();
+ }
+
+ public DatastoreValue getValue() {
+ return value;
+ }
+
+}
Added: freeway/src/org/gnu/freeway/cwrappers/ConstCHashCode512.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/ConstCHashCode512.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/ConstCHashCode512.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,60 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.cwrappers;
+
+import org.gnu.freeway.cwrappers.util.CWrapper;
+import org.gnu.freeway.cwrappers.util.ConstCWrapper;
+import org.gnu.freeway.util.crypto.HashCode512;
+import org.gnu.freeway.util.crypto.HostIdentity;
+import org.gnu.freeway.util.net.PersistentHelper;
+
+/**
+ * @file ConstCHostIdentity.java
+ * @brief
+ * @author mdonoughe
+ */
+public class ConstCHashCode512 implements ConstCWrapper {
+
+ private static final int KIND = CWrapper.PTR_KIND;
+
+ protected HashCode512 value;
+
+ public ConstCHashCode512(HashCode512 value) {
+ this.value = value;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gnu.freeway.cwrappers.util.ConstCWrapper#getSerializedSize()
+ */
+ public int getSerializedSize() {
+ return 64;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.cwrappers.util.ConstCWrapper#serializeToByteArray()
+ */
+ public byte[] serializeToByteArray() {
+ return PersistentHelper.toBytes(value);
+ }
+
+ public HashCode512 getValue() {
+ return value;
+ }
+}
Modified: freeway/src/org/gnu/freeway/cwrappers/util/SwitchTableGenerator.java
===================================================================
--- freeway/src/org/gnu/freeway/cwrappers/util/SwitchTableGenerator.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/cwrappers/util/SwitchTableGenerator.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -553,6 +553,13 @@
// write every method to the buffer
for(int i = 0; i < methods.length; i++) {
+ Iterator temp = null;
+ String tempStr = null;
+ for(temp = order.iterator(); temp.hasNext(); tempStr =
(String) temp.next())
+ if(methods[i].getName().equals(tempStr))
+ break;
+ if(!methods[i].getName().equals(tempStr))
+ continue;
StringBuffer buffer = new StringBuffer();
StringBuffer arrayBuffer = new StringBuffer();
buffer.append(" public " +
cleanClassName(methods[i].getReturnType().getName(), imports) + " " +
methods[i].getName() + "(");
@@ -644,6 +651,8 @@
public static int getArgumentType(Class c) {
if ("void".equals(c.getName()))
return CWrapper.VOID_KIND;
+ else if(c.isArray())
+ return CWrapper.PTR_KIND;
try {
Field f = getPrivateField(c, "KIND");
f.setAccessible(true);
@@ -657,14 +666,11 @@
}
public static Field getPrivateField(Class c, String name) throws
NoSuchFieldException {
- if("void".equals(c.getName())) {
- System.exit(0);
- }
Field[] fields = c.getDeclaredFields();
for(int i = 0; i < fields.length; i++)
if(name.equals(fields[i].getName()))
return fields[i];
- throw new NoSuchFieldException(name);
+ throw new NoSuchFieldException(name + " in " +
c.getCanonicalName());
}
private static class SourceFilesFilter implements FilenameFilter {
Added: freeway/src/org/gnu/freeway/protocol/fs/AFSProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/AFSProtocol.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/AFSProtocol.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,240 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * main functions of the anonymous file sharing service
+ *
+ * AFS CORE. This is the code that is plugged into the GNUnet core to
+ * enable Anonymous File Sharing.
+ * support for ESED2 encoding of files
+ */
+
+public class AFSProtocol extends AbstractProtocol
+{
+ /**
+ * Just the version number of the AFS implementation.
+ * History:
+ *
+ * 1.x.x: initial version with triple hash and merkle tree
+ * 2.x.x: root node with mime-type, filename and version number
+ * 2.1.x: combined CHK/3HASH encoding with 25:1 super-nodes
+ * 2.2.x: with directories
+ * 3.0.x: with namespaces
+ */
+
+ public static final int VERSION = Utils.makeVersion(3,0,4);
+
+ /** */
+ private BloomFilter2 bloomFilter;
+
+ /** */
+ private FileIndex fileIndex;
+
+ /** */
+ private Policy policy;
+
+ /** */
+ private Handler handler;
+
+ /** */
+ private Manager manager;
+
+ /** */
+ private Routing routing;
+
+ /** */
+ private Migration migration;
+
+ /** */
+ private QueryManager queryManager;
+
+
+ public AFSProtocol()
+ {
+ super("AFS");
+ bloomFilter=new BloomFilter2();
+ fileIndex=new FileIndex();
+ policy=null;
+ handler=new Handler();
+ manager=new Manager();
+ routing=new Routing();
+ migration=new Migration(manager);
+ queryManager=new QueryManager();
+ }
+
+ public String toString()
+ {
+ return "AFS";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the AFS module. This method name must match
+ * the library name (libgnunet_XXX => initialize_XXX).
+ * @param capi
+ * @return false on errors
+ */
+
+ public boolean init( CoreForProtocol capi )
+ {
+ Prefs prefs;
+
+ super.init(capi);
+
+ prefs=capi.getApplication().getPreferences();
+ if (prefs.getInt("FS","QUOTA",0)<=0) {
+ log(Level.SEVERE,"You must specify a postive number for
the QUOTA in section FS.");
+ return false;
+ }
+
+ fileIndex.init(capi);
+
+ policy=new Policy(prefs,capi);
+
+ manager.init(capi);
+ bloomFilter.init(capi);
+ queryManager.init(capi);
+ routing.init(capi);
+ handler.init(capi);
+ migration.init(capi);
+
+ handler.policy=new Policy2(capi);
+ handler.singleBloomFilter=bloomFilter.singleBloomFilter;
+ handler.superBloomFilter=bloomFilter.superBloomFilter;
+ handler.fileIndex=fileIndex;
+ handler.manager=manager;
+ handler.routing=routing;
+
+ routing.policy=policy;
+ routing.manager=manager;
+ routing.queryManager=queryManager;
+ routing.superBloomFilter=bloomFilter.superBloomFilter;
+ routing.singleBloomFilter=bloomFilter.singleBloomFilter;
+ // **** fin beurk
+
+ // p2p handlers
+ addP2PHandler(P2PMessage.IS_QUERY,P2PQuery.class);
+ addP2PHandler(P2PMessage.IS_3HASH_RESULT,P2P3HashResult.class);
+ addP2PHandler(P2PMessage.IS_CHK_RESULT,P2PChkResult.class);
+ addP2PHandler(P2PMessage.IS_NSQUERY,P2PNSQuery.class);
+
addP2PHandler(P2PMessage.IS_SBLOCK_RESULT,P2PSBlockResult.class);
+
+ // c/s handlers
+ addCSHandler(CSMessage.IS_QUERY,CSQuery.class);
+ addCSHandler(CSMessage.IS_INSERT_CHK,CSInsertChk.class);
+ addCSHandler(CSMessage.IS_INSERT_3HASH,CSInsert3Hash.class);
+ addCSHandler(CSMessage.IS_INDEX_BLOCK,CSIndexBlock.class);
+ addCSHandler(CSMessage.IS_INDEX_FILE,CSIndexFile.class);
+ addCSHandler(CSMessage.IS_INDEX_SUPER,CSIndexSuper.class);
+ addCSHandler(CSMessage.IS_DELETE_CHK,CSDeleteChk.class);
+ addCSHandler(CSMessage.IS_DELETE_3HASH,CSDelete3Hash.class);
+ addCSHandler(CSMessage.IS_UNINDEX_BLOCK,CSUnindexBlock.class);
+ addCSHandler(CSMessage.IS_UNINDEX_FILE,CSUnindexFile.class);
+ addCSHandler(CSMessage.IS_UNINDEX_SUPER,CSUnindexSuper.class);
+ addCSHandler(CSMessage.IS_NSQUERY,CSNSQuery.class);
+ addCSHandler(CSMessage.IS_INSERT_SBLOCK,CSInsertSBlock.class);
+ addCSHandler(CSMessage.IS_UPLOAD_FILE,CSUploadFile.class);
+ addCSHandler(CSMessage.IS_LINK_FILE,CSLinkFile.class);
+
addCSHandler(CSMessage.IS_GET_AVG_PRIORITY,CSGetAvgPriority.class);
+ return true;
+ }
+
+ public void done()
+ {
+ super.done();
+ bloomFilter.done();
+ migration.done();
+ queryManager.done();
+ routing.done();
+ manager.done();
+ fileIndex.done();
+ policy.doneAnonymityPolicy();
+ }
+
+ protected boolean onCSMessage( CSSession client, CSMessage msg )
+ {
+ if (msg instanceof CSNSQuery) {
+ return
handler.csHandleRequestNSQuery(client,(CSNSQuery) msg);
+ }
+ if (msg instanceof CSInsertSBlock) {
+ return
handler.csHandleRequestInsertSBlock(client,(CSInsertSBlock) msg);
+ }
+ if (msg instanceof CSQuery) {
+ return handler.csHandleRequestQuery(client,(CSQuery)
msg);
+ }
+ if (msg instanceof CSInsertChk) {
+ return
handler.csHandleRequestInsertCHK(client,(CSInsertChk) msg);
+ }
+ if (msg instanceof CSInsert3Hash) {
+ return
handler.csHandleRequestInsert3HASH(client,(CSInsert3Hash) msg);
+ }
+ if (msg instanceof CSIndexBlock) {
+ return
handler.csHandleRequestIndexBlock(client,(CSIndexBlock) msg);
+ }
+ if (msg instanceof CSIndexFile) {
+ return
handler.csHandleRequestIndexFile(client,(CSIndexFile) msg);
+ }
+ if (msg instanceof CSIndexSuper) {
+ return
handler.csHandleRequestIndexSuper(client,(CSIndexSuper) msg);
+ }
+ if (msg instanceof CSDeleteChk) {
+ return
handler.csHandleRequestDeleteCHK(client,(CSDeleteChk) msg);
+ }
+ if (msg instanceof CSDelete3Hash) {
+ return
handler.csHandleRequestDelete3HASH(client,(CSDelete3Hash) msg);
+ }
+ if (msg instanceof CSUnindexBlock) {
+ return
handler.csHandleRequestUnindexBlock(client,(CSUnindexBlock) msg);
+ }
+ if (msg instanceof CSUnindexFile) {
+ return
handler.csHandleRequestUnindexFile(client,(CSUnindexFile) msg);
+ }
+ if (msg instanceof CSUnindexSuper) {
+ return
handler.csHandleRequestUnindexSuper(client,(CSUnindexSuper) msg);
+ }
+ if (msg instanceof CSUploadFile) {
+ return
handler.csHandleRequestUploadFile(client,(CSUploadFile) msg);
+ }
+ if (msg instanceof CSLinkFile) {
+ return
handler.csHandleRequestLinkFile(client,(CSLinkFile) msg);
+ }
+ if (msg instanceof CSGetAvgPriority) {
+ return
routing.csHandleRequestAvgPriority(client,(CSGetAvgPriority) msg);
+ }
+ return false;
+ }
+
+ protected boolean onP2PMessage( HostIdentity sender, P2PMessage msg )
+ {
+ if (msg instanceof P2PNSQuery) {
+ return handler.handleNSQUERY(sender,msg);
+ }
+ if (msg instanceof P2PSBlockResult) {
+ return handler.handleSBLOCK_CONTENT(sender,msg);
+ }
+ if (msg instanceof P2PQuery) {
+ return handler.handleQUERY(sender,msg);
+ }
+ if (msg instanceof P2P3HashResult) {
+ return handler.handle3HASH_CONTENT(sender,msg);
+ }
+ if (msg instanceof P2PChkResult) {
+ return handler.handleCHK_CONTENT(sender,msg);
+ }
+ return false;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/BloomFilter2.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/BloomFilter2.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/BloomFilter2.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,107 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * Bloomfilter implementation.
+ */
+
+public class BloomFilter2 extends LoggedObject
+{
+ public static final String BLOOMFILTER1 =
"content_bloomfilter";
+ public static final String BLOOMFILTER2 =
"keyword_bloomfilter";
+
+ /** Filters. */
+ public BloomFilter superBloomFilter;
+ public BloomFilter singleBloomFilter;
+
+
+ public BloomFilter2()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void init( CoreForProtocol capi )
+ {
+ ByteBuffer buf;
+ String fn,bf;
+ int quota,superbf_size,singlbf_size;
+
+ Statistics
stats=capi.getApplication().getStatistics();
+ Prefs prefs=capi.getApplication().getPreferences();
+
+ fn = prefs.getString("AFS","AFSDIR",null);
+ if (fn==null) {
+ trace("Configuration must specify directory for AFS
data in section AFS under AFSDIR.");
+ return;
+ }
+ new DirLocation(fn).create();
+
+ /* read existing quota, check if it changed */
+ quota = prefs.getInt("AFS","DISKQUOTA",0);
+
+ buf=prefs.getContent("AFS-DISKQUOTA");
+ if (buf==null || buf.capacity()!=4) {
+ buf=ByteBuffer.allocateDirect(4);
+ buf.putInt(quota);
+ prefs.putContent("AFS-DISKQUOTA",buf);
+ }
+ else if (buf.getInt()!=quota) {
+ log(Level.SEVERE,"AFS-Quota changed, run gnunet-convert
!");
+ return;
+ }
+ quota = quota * 1024; /* convert to kb */
+ singlbf_size = quota; /* 8 bit per entry/kb in DB */
+ superbf_size = quota;
+
+ bf=fn+"/"+BLOOMFILTER1;
+ superBloomFilter=BloomFilter.load(stats,bf,superbf_size,5); /*
approx. 3% false positives at max use */
+
+ bf=fn+"/"+BLOOMFILTER2;
+ singleBloomFilter=BloomFilter.load(stats,bf,singlbf_size,5); /*
approx. 3% false positives at max use */
+ }
+
+ public void done()
+ {
+ singleBloomFilter.free();
+ superBloomFilter.free();
+ }
+
+ public void bf_deleteEntryCallback( HashCode512 key, ContentIndex ce,
Object data, int datalen, Object closure )
+ {
+ switch (ce.type) {
+ case ContentIndex.LOOKUP_TYPE_CHK:
+ case ContentIndex.LOOKUP_TYPE_3HASH:
+ case ContentIndex.LOOKUP_TYPE_SBLOCK:
+ singleBloomFilter.delete(key);
+ break;
+ case ContentIndex.LOOKUP_TYPE_SUPER:
+ superBloomFilter.delete(key);
+ break;
+ case ContentIndex.LOOKUP_TYPE_CHKS:
+ break;
+ default:
+ log(Level.WARNING,"Bloom filter notified of
deletion of unexpected type of content entry: "+ce.type+".");
+ break;
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/DBHandle.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/DBHandle.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/DBHandle.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,170 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Handle for a high-level database (mysql, simple)
+ * mysql wrapper
+ */
+
+public interface DBHandle
+{
+ /**
+ * Open the database.
+ *
+ * @param prefs
+ * @param backend string used to distinguish multiple backends of
the same type.
+ * @param name parameter for naming the database configuration
(e.g. quota)
+ * @return the database handle
+ */
+
+ public boolean open( Prefs prefs, String backend, String name );
+
+ /**
+ * Close the database.
+ */
+
+ public void close();
+
+ /**
+ * Remove the database (entirely!). Also implicitly close database.
+ * Close and delete the database.
+ */
+
+ public void drop();
+
+ /**
+ * Read the contents of a bucket to a buffer.
+ * Read the contents of a block to a buffer. In the case of 3HASH
+ * query, readContent must also return the respective 2HASH in
+ * ce.hash [so that hash(ce.hash)==3HASH].
+ *
+ * @param query the query hash (3HASH o CHK)/the hashcode representing
the entry
+ * @param ce what to look for (will be modified on return)/the
meta-data of the entry (set)
+ * @param prio the amount to change priority of the entry if found/by
how much should the priority of the content be changed
+ * @return the content on success, null on failure
+ */
+
+ public byte[] readContent( HashCode512 query, ContentIndex ce, int prio
);
+
+ /**
+ * Write content to a file. Check for reduncancy and eventually
+ * append.
+ * Write content to the db. Overwrites existing data.
+ * If ce.type is LOOKUP_TYPE_3HASH, ce.hash will contain
+ * a double hash which must be converted to 3HASH, later to be
+ * retrievable by 3HASH, but the 2HASH must be stored so it can
+ * be retrieved by readContent(). For indexed content,
+ * ce.fileOffset and ce.fileNameIndex must be stored.
+ * Note that block can be null for on-demand encoded content
+ * (in this case, len must also be 0).
+ * Write content to the db. Overwrites existing data.
+ * If ce.type is LOOKUP_TYPE_3HASH, ce.hash will contain
+ * a double hash which must be converted to 3HASH, later to be
+ * retrievable by 3HASH, but the 2HASH must be stored so it can
+ * be retrieved by readContent(). For indexed content,
+ * ce.fileOffset and ce.fileNameIndex must be stored.
+ * Note that block can be null for on-demand encoded content
+ * (in this case, len must also be 0).
+ *
+ * @param ce information related to the block to store/the meta-data
for the entry/the meta-data for the entry
+ * @param block the data to store
+ * @param offset offset
+ * @param length the size of the block
+ * @return false on error, true if ok.
+ */
+
+ public boolean writeContent( ContentIndex ce, byte[] block, int offset,
int length );
+
+ /**
+ * Get the number of entries in the database.
+ * Get the number of entries in the database.
+ *
+ * @return -1 on error, otherwise the number of entries
+ */
+
+ public int countContentEntries();
+
+ /**
+ * Get the lowest priority of content in the DB.
+ * Get the lowest priority of content in the store.
+ * Get the lowest priority value of all content in the store.
+ *
+ * @return the lowest priority
+ */
+
+ public int getMinimumPriority();
+
+ /**
+ * Call a method for each key in the database and
+ * call the callback method on it.
+ * Call a method for each key in the database and
+ * call the callback method on it.
+ *
+ * @param callback the callback method
+ * @param data second argument to all callback calls
+ * @return the number of items stored in the content database
+ */
+
+ public int forEachEntry( EntryCallback callback, Object data );
+
+ /**
+ * Return a random key from the database (just the key, not the
+ * content!).
+ * Get a random content block from MySQL database.
+ * Tries to use indexes efficiently.
+ *
+ * Code supplied by H. Pagenhardt
+ *
+ * @param ce the meta-data of the random content (set)/output
information about the key
+ * @return true on success, false on error
+ */
+
+ public boolean getRandomContent( ContentIndex ce );
+
+ /**
+ * Delete low-priority content from the database
+ * Deletes some least important content
+ *
+ * @param count the number of entries to delete/the number of 1kb
blocks to free
+ * @param callback method to call on each deleted entry/method to call
on each deleted item
+ * @param closure extra argument to callback
+ * @return true on success, false on error
+ */
+
+ public boolean deleteContent( int count, EntryCallback callback, Object
closure );
+
+ /**
+ * Estimate how many blocks can be stored in the DB
+ * before the quota is reached.
+ * Estimate how many blocks can be stored in the DB
+ * before the quota is reached.
+ *
+ * NOTE: this function can not be performed relying on
+ * Data_length+Index_length from "SHOW TABLE STATUS" because
+ * those values seem not to be decreasing in real time.
+ * On mysql 4.0.16, Avg_row_len seems to be updating in real
+ * time w.r.t. insertions and deletions.
+ *
+ * @param quota the number of kb available for the DB
+ * @return number of blocks left
+ */
+
+ public int estimateAvailableBlocks( int quota );
+
+ /**
+ * Free space in the database by removing an entry.
+ *
+ * @param name the key of the entry to remove/the query of the entry to
remove/hashcode for the block to be deleted
+ * @return false on error, true if ok.
+ * Free space in the database by removing one block
+ */
+
+ public boolean unlink( HashCode512 name );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/EntryCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/EntryCallback.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/EntryCallback.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,33 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public interface EntryCallback
+{
+ /**
+ * Callback function type used by the iterator. Contains the key,
+ * index information, the block (null if there is no block in the
+ * database), the length of the block and the closure.
+ *
+ * The callback is responsible for freeing data if data is not null.
+ *
+ * Note that the callback function may not perform additional
+ * read, write or delete operations on the database!
+ * @param key
+ * @param ce
+ * @param data
+ * @param dataLen
+ * @param closure
+ */
+
+ public void onEntry( HashCode512 key, ContentIndex ce, Object data, int
dataLen, Object closure );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/FileIndex.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/FileIndex.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/FileIndex.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,376 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * This module is responsible for storing the names
+ * of indexed files.
+ * access to the list of indexed files
+ *
+ * This module is responsible for storing the names
+ * of indexed files. The index for a file is always
+ * >0, since 0 is reserved for "not indexed".
+ */
+
+public class FileIndex extends LoggedObject
+{
+ public static final String DATABASELIST = "database.list";
+
+ /** names of indexed files */
+ private String[] indexed_files;
+
+ /** Size of the indexed_files list. */
+ private int
indexed_files_size;
+
+ /** number of files that are indexed */
+ private int
indexed_files_count;
+
+ /** Mutex for synced access to indexed_files */
+ private Sync lock;
+
+ /** stat handle for indexed_files_count */
+ private Stat stat_indexed_files_count;
+
+ /** stat handle for total size of indexed files */
+ private Stat stat_indexed_files_size;
+
+ /** */
+ private String shared_file_list;
+
+ /** */
+ private Statistics stats;
+
+ /** */
+ private Prefs prefs;
+
+
+ public FileIndex()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "File index";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the fileindex module.
+ * @param capi
+ */
+
+ public void init( CoreForProtocol capi )
+ {
+ stats=capi.getApplication().getStatistics();
+ prefs=capi.getApplication().getPreferences();
+
+ shared_file_list = getSharedFileList();
+ stat_indexed_files_count=stats.getHandle("# indexed files");
+ stat_indexed_files_size= stats.getHandle("# size of indexed
files");
+ lock=new Mutex();
+ if (!scanDatabaseList()) {
+ trace("Could not initialize file index module !");
+ }
+ }
+
+ /**
+ * Shutdown the fileindex module.
+ */
+
+ public void done()
+ {
+ indexed_files=null;
+ shared_file_list=null;
+ lock=null;
+ }
+
+ /**
+ * Get the name of the file where we store the
+ * list of indexed files.
+ * @return
+ */
+
+ protected String getSharedFileList()
+ {
+ String str;
+
+ str=prefs.getString("AFS","AFSDIR",null);
+ if (str==null) {
+ log(Level.SEVERE,"Configuration file must specify
filename for storing AFS data in section AFS under AFSDIR.");
+ return null;
+ }
+ new DirLocation(str).create(); // important, the directory may
not exist yet !
+ return new FileLocation(str+"/"+DATABASELIST).getPath();
+ }
+
+ /**
+ * Scan the list of on-demand shared files to initialize indexed_files
+ * @return OK on success, false on error
+ */
+
+ protected boolean scanDatabaseList()
+ {
+ BufferedReader handle;
+ String str;
+ long totalSize;
+ List list;
+ FileLocation f;
+
+ try {
+ lock.acquire();
+ try {
+ indexed_files = null;
+ indexed_files_count = 0;
+ indexed_files_size = 0;
+ stat_indexed_files_count.reset();
+ stat_indexed_files_size.reset();
+ totalSize = 0;
+
+ try {
+ handle=new BufferedReader(new
InputStreamReader(new FileInputStream(shared_file_list)));
+ try {
+ list=new ArrayList();
+ for (str=handle.readLine();
str!=null; str=handle.readLine()) {
+ if (str.length()>0) {
+
indexed_files_count++;
+ list.add(str);
+
+ f=new
FileLocation(str);
+ totalSize +=
f.getSize();
+ }
+ else {
+ list.add(null);
+ }
+ }
+
+ if (indexed_files_count == 0) {
+ return true;
+ }
+
+ indexed_files_size =
list.size();
+ indexed_files = (String[])
list.toArray(new String[list.size()]);
+ }
+ finally {
+ handle.close();
+ }
+ }
+ catch( FileNotFoundException x ) {
+ // file not found : not an error
+ }
+ catch( IOException x ) {
+ err("Could not open
"+shared_file_list+" !",x);
+ return false;
+ }
+
+
stat_indexed_files_count.set(indexed_files_count);
+ stat_indexed_files_size.set(totalSize);
+ }
+ finally {
+ lock.release();
+ }
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the name of an indexed file.
+ *
+ * @param index the index of the file (must be strictly positive)
+ * @return the filename
+ */
+
+ public String getIndexedFileName( int index )
+ {
+ if (index<=0 || index>indexed_files_size) {
+ log(Level.WARNING,"getIndexedFileName called with index
out of bounds ("+index+")");
+ return null;
+ }
+
+ try {
+ lock.acquire();
+ try {
+ return indexed_files[index-1];
+ }
+ finally {
+ lock.release();
+ }
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ return null;
+ }
+ }
+
+ /**
+ * Invoke a method on each of the filenames of the indexed files. If
+ * the method returns false, the file is removed from the list of
+ * indexed files!
+ *
+ * @param method the method to invoke for each indexed file
+ * @param data the last argument to method
+ * @return the number of shared files (after changes caused by this
call)
+ */
+
+ public int forEachIndexedFile( IndexedFileNameCallback method, Object
data )
+ {
+ PrintWriter handle;
+ int i;
+ boolean changed,erase;
+
+ try {
+ lock.acquire();
+ try {
+ changed=false;
+ for (i=0; i<indexed_files_size; i++) {
+ if (indexed_files[i] != null) {
+ lock.release();
+ try {
+
erase=!method.onFile(indexed_files[i],i+1,data);
+ }
+ finally {
+ lock.acquire();
+ }
+
+ if (erase) {
+ indexed_files[i]=null;
+ changed=true;
+ }
+ }
+ }
+
+ if (changed) {
+ /* write changed list to the drive */
+
+ try {
+ handle = new PrintWriter(new
OutputStreamWriter(new FileOutputStream(shared_file_list)),true);
+ try {
+ for (i=0;
i<indexed_files_size; i++) {
+ if
(indexed_files[i]!=null) {
+
handle.println(indexed_files[i]);
+ }
+ else {
+
handle.println();
+ }
+ }
+ }
+ finally {
+ handle.close();
+ }
+ }
+ catch( IOException x ) {
+ err("List "+shared_file_list+"
of directly shared filenames not available !",x);
+ return -1;
+ }
+ }
+ }
+ finally {
+ lock.release();
+ }
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ return -1;
+ }
+ return indexed_files_count;
+ }
+
+ /**
+ * Add a name to the list of filenames.
+ *
+ * @param filename the name of the file to add
+ * @return the index of that file in the list in [1,65536], -1 on
error. <em>NEVER</em> returns 0.
+ */
+
+ public int appendFilename( String filename )
+ {
+ BufferedReader handle;
+ PrintWriter out;
+ String str;
+ int pos;
+
+ assert(filename!=null);
+
+ try {
+ lock.acquire();
+ try {
+ pos=0;
+ filename=new FileLocation(filename).getPath();
+
+ try {
+ handle=new BufferedReader(new
InputStreamReader(new FileInputStream(shared_file_list)));
+ try {
+ for (str=handle.readLine();
str!=null; str=handle.readLine()) {
+ pos++;
+ if
(str.equals(filename)) {
+ debug("File
already at index "+pos+".");
+ return pos;
// already there !
+ }
+ }
+ }
+ finally {
+ handle.close();
+ }
+ }
+ catch( FileNotFoundException x ) {
+ // file not found : not an error
+ }
+ catch( IOException x ) {
+ err("List "+shared_file_list+" of
directly shared filenames not available !",x);
+ return -1;
+ }
+
+ if (pos > 0xFFFF) {
+ log(Level.WARNING,"Too many files
indexed (limit is 65535).");
+ return -1;
+ }
+
+ // not there, append
+ try {
+ out = new PrintWriter(new
OutputStreamWriter(new FileOutputStream(shared_file_list,true)),true);
+ try {
+ out.println(filename);
+ }
+ finally {
+ out.close();
+ }
+ }
+ catch( IOException x ) {
+ err("List "+shared_file_list+" of
directly shared filenames not available !",x);
+ return -1;
+ }
+ }
+ finally {
+ lock.release();
+ }
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ return -1;
+ }
+
+ scanDatabaseList();
+
+ debug("Added file to index at position "+pos+".");
+
+ return pos; /* return index */
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/Handler.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/Handler.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/Handler.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,897 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * Handlers for incoming AFS requests (p2p and CS).
+ * Handlers for AFS related messages (CS and p2p).
+ */
+
+public class Handler extends LoggedObject implements AFSConstants
+{
+ private CoreForProtocol coreAPI;
+
+ private Prefs prefs;
+
+ private Stat stat_p2p_query_count;
+ private Stat stat_p2p_superquery_count;
+ private Stat stat_p2p_chk_replies;
+ private Stat stat_p2p_3hash_replies;
+ private Stat stat_cs_query_count;
+ private Stat stat_cs_insert_chk_count;
+ private Stat stat_cs_insert_3hash_count;
+ private Stat stat_cs_index_block_count;
+ private Stat stat_cs_index_file_count;
+ private Stat stat_cs_index_super_count;
+ private Stat stat_cs_delete_chk_count;
+ private Stat stat_cs_delete_3hash_count;
+ private Stat stat_cs_unindex_block_count;
+ private Stat stat_cs_unindex_file_count;
+ private Stat stat_cs_unindex_super_count;
+ private Stat stat_cs_upload_file_count;
+ private Stat stat_cs_insert_sblock_count;
+ private Stat stat_cs_nsquery_count;
+ private Stat stat_p2p_nsquery_count;
+ private Stat stat_p2p_sblock_replies;
+
+ public Policy2 policy;
+ public BloomFilter singleBloomFilter;
+ public BloomFilter superBloomFilter;
+ public FileIndex fileIndex;
+ public Manager manager;
+ public Routing routing;
+
+
+ public Handler()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the handler module. Registers counters
+ * with the statistics module.
+ * @param capi
+ */
+
+ public void init( CoreForProtocol capi )
+ {
+ Statistics stats;
+
+ coreAPI=capi;
+
+ prefs=capi.getApplication().getPreferences();
+
+ stats=capi.getApplication().getStatistics();
+ stat_p2p_query_count=stats.getHandle("# p2p queries received");
+ stat_p2p_superquery_count=stats.getHandle("# p2p super queries
received");
+ stat_p2p_chk_replies=stats.getHandle("# p2p CHK content
received (kb)");
+ stat_p2p_3hash_replies=stats.getHandle("# p2p search results
received (kb)");
+ stat_cs_query_count=stats.getHandle("# client queries
received",Stat.VERBOSE);
+ stat_cs_insert_chk_count=stats.getHandle("# client CHK content
inserted (kb)",Stat.VERBOSE);
+ stat_cs_insert_3hash_count=stats.getHandle("# client 3HASH
search results inserted (kb)",Stat.VERBOSE);
+ stat_cs_index_block_count=stats.getHandle("# client file index
requests received",Stat.VERBOSE);
+ stat_cs_index_file_count=stats.getHandle("# file index requests
received",Stat.VERBOSE);
+ stat_cs_index_super_count=stats.getHandle("# super query index
requests received",Stat.VERBOSE);
+ stat_cs_delete_chk_count=stats.getHandle("# client CHK content
deleted (kb)",Stat.VERBOSE);
+ stat_cs_delete_3hash_count=stats.getHandle("# client 3HASH
search results deleted (kb)",Stat.VERBOSE);
+ stat_cs_unindex_block_count=stats.getHandle("# client file
unindex requests received",Stat.VERBOSE);
+ stat_cs_unindex_file_count=stats.getHandle("# file unindex
requests received",Stat.VERBOSE);
+ stat_cs_unindex_super_count=stats.getHandle("# super query
unindex requests received",Stat.VERBOSE);
+ stat_cs_insert_sblock_count=stats.getHandle("# client SBlock
insert requests received",Stat.VERBOSE);
+ stat_cs_nsquery_count=stats.getHandle("# client namespace
queries received",Stat.VERBOSE);
+ stat_cs_upload_file_count=stats.getHandle("# client file upload
requests",Stat.VERBOSE);
+ stat_p2p_nsquery_count=stats.getHandle("# p2p namespace queries
received");
+ stat_p2p_sblock_replies=stats.getHandle("# p2p SBlocks
received");
+ }
+
+ /**
+ * Handle query for content. Depending on how we like the sender,
+ * lookup, forward or even indirect.
+ * @param sender
+ * @param msg
+ * @return
+ */
+
+ public boolean handleQUERY( HostIdentity sender, P2PMessage msg )
+ {
+ int qp;
+ P2PQuery qmsg;
+ int queries;
+ int ttl;
+ int prio;
+ double preference;
+
+ queries = (msg.getByteSize() - P2PQuery.SIZE) /
HashCode512.SIZE; //todo: AFS_p2p_QUERY.getQueriesCount()
+ if (queries<=0 || (msg.getByteSize()!=P2PQuery.SIZE + queries *
HashCode512.SIZE) ) {
+ log(Level.WARNING,"Query received was malformed.");
+ return false;
+ }
+ if (queries>1)
+ stat_p2p_superquery_count.inc();
+ stat_p2p_query_count.inc();
+ qmsg = (P2PQuery) msg;
+
+ debug("Received query "+qmsg.getQuery(0).toHex()+"
("+queries+") TTL "+qmsg.getTTL()+" PR "+qmsg.getPriority()+" from
"+sender.getName()+".");
+
+ /* decrement ttl (always) */
+ ttl = qmsg.getTTL();
+ debug("Received query for "+qmsg.getQuery(0).toHex()+" with ttl
"+ttl+".");
+
+ if (ttl < 0) {
+ ttl = (int) (ttl - 2*TTL_DECREMENT -
Crypto.nextLong(TTL_DECREMENT));
+ if (ttl > 0)
+ return true; /* just abort */
+ } else
+ ttl = (int) (ttl - 2*TTL_DECREMENT -
Crypto.nextLong(TTL_DECREMENT));
+ qp = policy.evaluateQuery(sender,qmsg.getPriority());
+ if ((qp & Policy2.QUERY_DROPMASK) == 0)
+ return true; /* straight drop. */
+
+ preference=qp & Policy2.QUERY_PRIORITY_BITMASK;
+ if (preference < Policy2.QUERY_BANDWIDTH_VALUE)
+ preference = Policy2.QUERY_BANDWIDTH_VALUE;
+ coreAPI.preferTrafficFrom(sender,
+ preference);
+
+ /* adjust priority */
+ prio = qmsg.getPriority();
+ if ( (qp & Policy2.QUERY_PRIORITY_BITMASK) < prio) {
+ prio = qp & Policy2.QUERY_PRIORITY_BITMASK;
+ qmsg.setPriorityAndTTL(prio,qmsg.getTTL());
+ }
+ prio = prio / queries; /* effective priority for ttl */
+
+ /* adjust TTL */
+ if ( (ttl > 0) &&
+ (ttl > (prio+3)*TTL_DECREMENT) )
+ ttl = (int) ((prio+3)*TTL_DECREMENT); /* bound! */
+ qmsg.setPriorityAndTTL(qmsg.getPriority(),ttl);
+
+ routing.execQuery(qp, qmsg, null);
+ return true;
+ }
+
+ /**
+ * Receive content, do something with it! There are 3 basic
+ * possiblilities. Either our node did the request and we should send
+ * the result to a client via TCP, or the content was requested by
+ * another node and we forwarded the request (and thus we now have to
+ * fwd the reply) or 3rd somebody just send us some content we did NOT
+ * ask for - and we can choose to store it or just discard it.
+ * @param sender
+ * @param msg
+ * @return
+ */
+
+ public boolean handleCHK_CONTENT( HostIdentity sender, P2PMessage msg )
+ {
+ int prio;
+ HashCode512 queryHash;
+ ContentIndex ce=new ContentIndex();
+ P2PChkResult cmsg;
+ boolean ret;
+ double preference;
+ boolean[] dupe=new boolean[1];
+
+ if (msg.getByteSize()!=P2PChkResult.SIZE) {
+ log(Level.WARNING,"WARNING: CHK content message
received was malformed");
+ return false;
+ }
+ stat_p2p_chk_replies.inc();
+ cmsg = (P2PChkResult) msg;
+
queryHash=HashCode512.create(PersistentHelper.toBytes(cmsg.result));
+ prio = routing.useContent(sender,queryHash,msg);
+ if (sender == null) /* no migration, this is already content
from the local node */
+ return true;
+ preference = prio;
+ prio = policy.evaluateContent(queryHash,prio);
+ if (prio != -1)
+ preference+=prio;
+ if (preference < Policy2.CONTENT_BANDWIDTH_VALUE)
+ preference = Policy2.CONTENT_BANDWIDTH_VALUE;
+ coreAPI.preferTrafficFrom(sender,preference);
+
+ if (prio == -1)
+ return true; /* straight drop */
+ ce.hash=(HashCode512) PersistentHelper.copy(queryHash);
//todo: copie utile ?
+ ce.importance = prio;
+ ce.type = ContentIndex.LOOKUP_TYPE_CHK;
+ ce.fileNameIndex = 0;
+ ce.fileOffset = 0;
+ ret = manager.insertContent(ce,
+ ContentBlock.SIZE,
+ cmsg.result,
+ sender,
+ dupe);
+ if (ret && !dupe[0])
+ singleBloomFilter.add(queryHash);
+ return true;
+ }
+
+ /**
+ * Receive content, do something with it! There are 3 basic
+ * possiblilities. Either our node did the request and we should send
+ * the result to a client via TCP, or the content was requested by
+ * another node and we forwarded the request (and thus we now have to
+ * fwd the reply) or 3rd somebody just send us some content we did NOT
+ * ask for - and we can choose to store it or just discard it.
+ * @param sender
+ * @param msg
+ * @return
+ */
+
+ public boolean handle3HASH_CONTENT( HostIdentity sender, P2PMessage msg
)
+ {
+ int prio;
+ P2P3HashResult cmsg;
+ HashCode512 tripleHash;
+ ContentIndex ce=new ContentIndex();
+ boolean ret;
+ double preference;
+ boolean[] dupe=new boolean[1];
+
+ if (msg.getByteSize() != P2P3HashResult.SIZE) {
+ log(Level.WARNING,"WARNING: content message received
was malformed");
+ return false;
+ }
+ stat_p2p_3hash_replies.inc();
+ cmsg = (P2P3HashResult) msg;
+ tripleHash=cmsg.getTripleHash();
+
+ debug("DEBUG: received 3HASH search result for
"+tripleHash.toHex()+" from peer");
+
+ prio = routing.useContent(sender,tripleHash,msg);
+ if (sender == null) { /* no migration, this is already content
from the local node */
+ //fixme: how is it possible to have sender == null ???
+ debug("Content migration not needed, content is
local.");
+ return true;
+ }
+ preference = prio;
+
+ debug("Content migration with preference "+prio);
+
+ prio = policy.evaluateContent(tripleHash,prio);
+ if (prio != -1)
+ preference += prio;
+ if (preference < Policy2.CONTENT_BANDWIDTH_VALUE)
+ preference = Policy2.CONTENT_BANDWIDTH_VALUE;
+ coreAPI.preferTrafficFrom(sender,preference);
+
+ if (prio == -1) {
+ debug("DEBUG: content not important enough, not
replicated");
+ return true; /* straight drop */
+ }
+ debug("DEBUG: content replicated with total preference "+prio);
+
+ ce.hash=cmsg.getDoubleHash();
+ ce.importance = prio;
+ ce.type = ContentIndex.LOOKUP_TYPE_3HASH;
+ ce.fileNameIndex = 0;
+ ce.fileOffset = 0;
+
+ ret =
manager.insertContent(ce,ContentBlock.SIZE,cmsg.getResult(),sender,dupe);
+ if (ret && !dupe[0])
+ singleBloomFilter.add(tripleHash);
+ return true;
+ }
+
+ /**
+ * Process a query from the client. Forwards to the network.
+ * @param sock
+ * @param queryRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestQuery( CSSession sock, CSQuery
queryRequest )
+ {
+ int qp =
Policy2.QUERY_ANSWER|Policy2.QUERY_FORWARD|Policy2.QUERY_INDIRECT|Policy2.QUERY_PRIORITY_BITMASK;
+ P2PQuery msg;
+ int queries;
+ int ttl;
+ boolean ret;
+
+ queries = (queryRequest.getByteSize() - CSQuery.SIZE) /
HashCode512.SIZE;
+ if ( (queries <= 0) || (queryRequest.getByteSize() !=
CSQuery.SIZE + queries * HashCode512.SIZE) ) {
+ log(Level.WARNING,"Received malformed query from
client.");
+ return false;
+ }
+ stat_cs_query_count.inc();
+
+ debug("Received "+queries+" queries
("+queryRequest.getQuery(0).toHex()+") with ttl "+queryRequest.getTTL()+" and
priority "+queryRequest.getPriority()+".");
+
+ msg = new P2PQuery(coreAPI.getIdentity());
+ msg.setQueries(queryRequest.getQueries());
+ /* adjust TTL */
+ ttl = queryRequest.getTTL();
+ if ( (ttl > 0) && (ttl > (msg.getPriority()+8)*TTL_DECREMENT) )
+ ttl = (int) ((msg.getPriority()+8)*TTL_DECREMENT); /*
bound! */
+
+ msg.setPriorityAndTTL(queryRequest.getPriority(),ttl);
+
+ ret = routing.execQuery(qp, msg, sock);
+ debug("Executed "+queries+" queries with result "+ret+".");
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * Process a request to insert content from the client.
+ * @param sock
+ * @param insertRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestInsertCHK( CSSession sock, CSInsertChk
insertRequest )
+ {
+ ContentIndex entry=new ContentIndex();
+ boolean ret;
+ boolean[] dupe=new boolean[1];
+
+ stat_cs_insert_chk_count.inc();
+
+
entry.hash=HashCode512.create(PersistentHelper.toBytes(insertRequest.getContent()));
+
+ debug("DEBUG: received CHK insert request for block
"+entry.hash.toHex());
+
+ entry.type= ContentIndex.LOOKUP_TYPE_CHK;
+ entry.importance= insertRequest.getImportance();
+ entry.fileNameIndex= 0; /* database */
+ entry.fileOffset = 0; /* data/content */
+
+ ret =
manager.insertContent(entry,ContentBlock.SIZE,insertRequest.getContent(),null,dupe);
+ if (ret && !dupe[0])
+ singleBloomFilter.add(entry.hash);
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * Process a request to insert content from the client.
+ * @param sock
+ * @param insertRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestInsert3HASH( CSSession sock,
CSInsert3Hash insertRequest )
+ {
+ ContentIndex entry=new ContentIndex();
+ HashCode512 tripleHash;
+ boolean ret;
+ boolean[] dupe=new boolean[1];
+
+ stat_cs_insert_3hash_count.inc();
+
+ entry.hash=(HashCode512)
PersistentHelper.copy(insertRequest.getDoubleHash()); //todo: copie utile ?
+
tripleHash=HashCode512.create(PersistentHelper.toBytes(insertRequest.getDoubleHash()));
+
+ debug("Received 3HASH insert request for "+tripleHash.toHex()+"
from client");
+
+ entry.type= ContentIndex.LOOKUP_TYPE_3HASH;
+ entry.importance= insertRequest.getImportance();
+ entry.fileNameIndex= 0; /* database */
+ entry.fileOffset = 0; /* data/content */
+ ret =
manager.insertContent(entry,ContentBlock.SIZE,insertRequest.getContent(),null,dupe);
+ if (ret && !dupe[0]) {
+ singleBloomFilter.add(tripleHash);
+ }
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * Process a request to index content from the client.
+ * @param sock
+ * @param indexingRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestIndexBlock( CSSession sock, CSIndexBlock
indexingRequest )
+ {
+ boolean[] dupe=new boolean[1];
+
+ debug("Indexing content
"+indexingRequest.getContentIndex().hash.toHex()+" at offset
"+indexingRequest.getContentIndex().fileOffset);
+
+ stat_cs_index_block_count.inc();
+ return sock.send(new
CSResult(manager.insertContent(indexingRequest.getContentIndex(),0,(byte[])
null,null,dupe)));
+ }
+
+ /**
+ * Process a query to list a file as on-demand encoded from the client.
+ * @param sock
+ * @param listFileRequest
+ *
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestIndexFile( CSSession sock, CSIndexFile
listFileRequest )
+ {
+ DirLocation dir;
+ String str;
+ long quota,usage;
+ int ret;
+
+ stat_cs_index_file_count.inc();
+
+ str = prefs.getString("AFS","INDEX-DIRECTORY",null);
+ if (str == null) {
+ log(Level.WARNING,"Rejecting content-unindex request,
INDEX-DIRECTORY option not set !");
+ return sock.send(CSResult.ERR);
+ }
+
+ dir=new DirLocation(str);
+ dir.create();
+
+ quota=prefs.getInt("AFS","INDEX-QUOTA",0) * 1024 * 1024;
+ if (quota != 0) {
+ usage=dir.getSizeWithoutLinks();
+ if (usage + listFileRequest.filesize > quota) {
+ log(Level.WARNING,"Rejecting file index
request, quota exeeded: "+(usage / 1024 / 1024)+" of "+(quota / 1024 / 1024)+"
(MB)");
+ return sock.send(CSResult.ERR);
+ }
+ }
+
+ str=dir.getPath()+"/"+listFileRequest.hash.toHex();
+ ret = fileIndex.appendFilename(str);
+ if (ret == 0)
+ ret = -1;
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * Process a client request to upload a file (indexing).
+ * @param sock
+ * @param uploadRequest
+ * @return
+ */
+
+ public boolean csHandleRequestUploadFile( CSSession sock, CSUploadFile
uploadRequest )
+ {
+ DirLocation prefix;
+ MappedFile fd;
+ FileLocation filename;
+ String str;
+
+ stat_cs_upload_file_count.inc();
+
+ str=prefs.getString("AFS","INDEX-DIRECTORY",null);
+ if (str==null) {
+ log(Level.WARNING,"WARNING: rejecting content-upload
request, INDEX-DIRECTORY option not set!");
+ return sock.send(CSResult.ERR);
+ }
+
+ prefix=new DirLocation(str);
+ prefix.create();
+
+ filename=prefix.getFile(uploadRequest.hash.toHex());
+ filename.create();
+
+ fd=filename.open();
+ if (fd==null) {
+ log(Level.SEVERE,"ERROR: OPEN() failed on "+filename);
+ return sock.send(CSResult.ERR);
+ }
+
+ try {
+ fd.seek(uploadRequest.pos);
+ if (!fd.writeBytes(uploadRequest.getChunk())) {
+ return sock.send(CSResult.ERR);
+ }
+ }
+ finally {
+ fd.close();
+ }
+ return sock.send(new CSResult(uploadRequest.getChunk().length));
+ }
+
+ /**
+ * Process a client request to extend our super-query bloom
+ * filter.
+ * @param sock
+ * @param superIndexRequest
+ * @return
+ */
+
+ public boolean csHandleRequestIndexSuper( CSSession sock, CSIndexSuper
superIndexRequest )
+ {
+ ContentIndex entry;
+ boolean[] dupe=new boolean[1];
+
+ stat_cs_index_super_count.inc();
+
+ superBloomFilter.add(superIndexRequest.getSuperHash());
+
+ entry=new ContentIndex();
+ entry.type= ContentIndex.LOOKUP_TYPE_SUPER;
+ entry.importance= superIndexRequest.getImportance();
+ entry.fileNameIndex = 0; /* database */
+ entry.fileOffset = 0; /* data/content */
+ entry.hash=(HashCode512)
PersistentHelper.copy(superIndexRequest.getSuperHash()); //todo: copie
utile ???
+ return sock.send(new
CSResult(manager.insertContent(entry,0,(byte[]) null,null,dupe)));
+ }
+
+ /**
+ * Process a request from the client to delete content.
+ * @param sock
+ * @param insertRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestDeleteCHK( CSSession sock, CSDeleteChk
insertRequest )
+ {
+ HashCode512 hc;
+ boolean ret;
+
+ stat_cs_delete_chk_count.inc();
+
+
hc=HashCode512.create(PersistentHelper.toBytes(insertRequest.content));
+
+ debug("DEBUG: received CHK remove request for block
"+hc.toHex());
+
+ ret = manager.removeContent(hc,-1);
+ if (ret)
+ if (singleBloomFilter.test(hc))
+ singleBloomFilter.delete(hc);
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * Process a request from the client to delete content.
+ * @param sock
+ * @param insertRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestDelete3HASH( CSSession sock,
CSDelete3Hash insertRequest )
+ {
+ HashCode512 tripleHash;
+ boolean ret;
+
+ stat_cs_delete_3hash_count.inc();
+
+
tripleHash=HashCode512.create(PersistentHelper.toBytes(insertRequest.doubleHash));
+
+ debug("DEBUG: received 3HASH delete request for
"+tripleHash.toHex()+" from client");
+
+ ret = manager.removeContent(tripleHash,-1);
+ if (ret)
+ singleBloomFilter.delete(tripleHash);
+
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * Process a request from the client to unindex content.
+ * @param sock
+ * @param indexingRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestUnindexBlock( CSSession sock,
CSUnindexBlock indexingRequest )
+ {
+ stat_cs_unindex_block_count.inc();
+ return sock.send(new
CSResult(manager.removeContent(indexingRequest.contentIndex.hash,-1)));
+ }
+
+ /**
+ * Callback used to select the file in the fileindex
+ * that is to be removed.
+ * @param fn
+ * @param i
+ * @param search
+ * @return
+ */
+
+ protected boolean removeMatch( String fn, int i, String search )
+ {
+ return !fn.equals(search);
+ }
+
+ /**
+ * Process a query from the client to remove an on-demand encoded file.
+ * n.b. This function just zeroes the correct row in the list of
+ * on-demand encoded files, if match (deletion is done by
forEachIndexedFile).
+ * The index of the filename that was removed is returned to the client.
+ *
+ * FIXME: It lookslike if listFileRequest.filename was NOT in
database.list,
+ * it gets appended to it, removed from it, and client gets a false idx.
+ * This unnecessarily bloats the database.list by one empty line.
+ * @param sock
+ * @param listFileRequest
+ *
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestUnindexFile( CSSession sock,
CSUnindexFile listFileRequest )
+ {
+ DirLocation dir;
+ String str;
+ int idx;
+
+ stat_cs_unindex_file_count.inc();
+
+ str=prefs.getString("AFS","INDEX-DIRECTORY",null);
+ if (str==null) {
+ log(Level.WARNING,"WARNING: rejecting content-unindex
request, INDEX-DIRECTORY option not set!");
+ return sock.send(CSResult.ERR);
+ }
+
+ dir=new DirLocation(str);
+ dir.create();
+
+ str=dir.getPath()+"/"+listFileRequest.hash.toHex();
+ idx=fileIndex.appendFilename(str);
+ if (idx==-1) {
+ return sock.send(CSResult.ERR);
+ }
+
+ assert(idx!=0) : "Index can't be null !";
+
+ fileIndex.forEachIndexedFile(new IndexedFileNameCallback() {
+ public boolean onFile( String fn, int idxx, Object data
)
+ {
+ return removeMatch(fn,idxx,(String) data);
+ }
+ },str);
+
+ if (!new FileLocation(str).delete()) {
+ log(Level.WARNING,"Could not remove indexed file");
+ idx = -1; /* remove failed!? */
+ }
+ return sock.send(new CSResult(idx));
+ }
+
+ /**
+ * @param sock
+ * @param linkFileRequest
+ * @return false if the TCP connection should be closed, otherwise true
+ */
+
+ public boolean csHandleRequestLinkFile( CSSession sock, CSLinkFile
linkFileRequest )
+ {
+ String filename,tname;
+ DirLocation prefix;
+ HashCode512 hc;
+ FileLocation loc;
+ LinkLocation f;
+
+ /* stat_cs_link_file_count.inc(); */ //todo: statut ???
+
+ tname=linkFileRequest.getPath();
+ loc=new FileLocation(tname);
+ hc=loc.getHash();
+
+ if (hc==null || !hc.equals(linkFileRequest.hash)) {
+ log(Level.WARNING,"WARNING: file link request "+tname+"
from client pointed to file with the wrong data !");
+ return sock.send(CSResult.ERR);
+ }
+
+ filename=prefs.getString("AFS","INDEX-DIRECTORY",null);
+ if (filename==null) {
+ log(Level.WARNING,"WARNING: rejecting content-unindex
request, INDEX-DIRECTORY option not set!");
+ return sock.send(CSResult.ERR);
+ }
+
+ prefix=new DirLocation(filename);
+ prefix.create();
+
+ filename=prefix.getPath()+"/"+linkFileRequest.hash.toHex();
+
+ f=new LinkLocation(filename);
+ if (f.setTarget(new FileLocation(tname))) {
+ if (f.create()) {
+ return sock.send(CSResult.OKAY);
+ }
+ }
+
+ log(Level.WARNING,"Could not create link from \""+tname+"\" to
\""+f.getLabel()+"\".");
+ return sock.send(CSResult.ERR);
+ }
+
+ /**
+ * Process a client request to limit our super-query bloom
+ * filter.
+ * @param sock
+ * @param superIndexRequest
+ * @return
+ */
+
+ public boolean csHandleRequestUnindexSuper( CSSession sock,
CSUnindexSuper superIndexRequest )
+ {
+ stat_cs_unindex_super_count.inc();
+
+ superBloomFilter.delete(superIndexRequest.superHash);
+ return sock.send(new
CSResult(manager.removeContent(superIndexRequest.superHash,-1)));
+ }
+
+ /**
+ * @param sock
+ * @param insertRequest
+ * @return
+ *
+ */
+
+ public boolean csHandleRequestInsertSBlock( CSSession sock,
CSInsertSBlock insertRequest )
+ {
+ ContentIndex entry;
+ HashCode512 ns;
+ boolean ret;
+ boolean[] dupe=new boolean[1];
+
+ stat_cs_insert_sblock_count.inc();
+
+ ns=insertRequest.getContent().getNameSpace();
+ debug("Received SBlock for namespace "+ns.toHex()+" with
routing ID "+insertRequest.getContent().getIdentifier().toHex()+".");
+
+ entry=new ContentIndex();
+ entry.type= ContentIndex.LOOKUP_TYPE_SBLOCK;
+ entry.importance= insertRequest.getImportance();
+ entry.fileNameIndex= 0; /* database */
+ entry.fileOffset = 0; /* data/content */
+ entry.hash=(HashCode512)
PersistentHelper.copy(insertRequest.getContent().getIdentifier()); //todo:
copie utile ?
+ dupe[0] = false;
+ ret =
manager.insertContent(entry,ContentBlock.SIZE,insertRequest.getContent(),null,dupe);
+
+ debug("Received SBlock insert is dupe: "+dupe+" (insert
"+ret+")");
+
+ if (ret && !dupe[0]) {
+
singleBloomFilter.add(insertRequest.getContent().getIdentifier());
+ }
+ return sock.send(new CSResult(ret));
+ }
+
+ /**
+ * @param sock
+ * @param queryRequest
+ * @return
+ *
+ */
+
+ public boolean csHandleRequestNSQuery( CSSession sock, CSNSQuery
queryRequest )
+ {
+ int qp =
Policy2.QUERY_ANSWER|Policy2.QUERY_FORWARD|Policy2.QUERY_INDIRECT|Policy2.QUERY_PRIORITY_BITMASK;
+ P2PNSQuery msg;
+
+ stat_cs_nsquery_count.inc();
+
+ debug("Received NS query
("+queryRequest.namespace.toHex()+"/"+queryRequest.identifier.toHex()+") with
ttl "+queryRequest.ttl+" and priority "+queryRequest.priority+".");
+
+ msg = new P2PNSQuery(coreAPI.getIdentity());
+ msg.setPriorityAndTTL(queryRequest.priority,queryRequest.ttl);
+ msg.identifier=(HashCode512)
PersistentHelper.copy(queryRequest.identifier); //todo: copie utile ???
+ msg.namespace=(HashCode512)
PersistentHelper.copy(queryRequest.namespace); //todo: copie utile ???
+ routing.execQuery(qp, msg, sock);
+ return true;
+ }
+
+ public boolean handleNSQUERY( HostIdentity sender, P2PMessage msg )
+ {
+ int qp;
+ P2PNSQuery qmsg;
+ int ttl;
+ int prio;
+ double preference;
+
+ if (msg.getByteSize() != P2PNSQuery.SIZEX) {
+ log(Level.WARNING,"WARNING: nsquery received was
malformed");
+ return false;
+ }
+ stat_p2p_nsquery_count.inc();
+ qmsg = (P2PNSQuery) msg;
+ /* decrement ttl */
+ ttl = qmsg.getTTL();
+
+ debug("DEBUG: received NS query for "+qmsg.identifier.toHex()+"
with ttl "+ttl);
+
+ if (ttl < 0) {
+ ttl = (int) (ttl - 2*TTL_DECREMENT -
Crypto.nextLong(TTL_DECREMENT));
+ if (ttl > 0)
+ return true; /* just abort */
+ }
+ else
+ ttl = (int) (ttl - 2*TTL_DECREMENT -
Crypto.nextLong(TTL_DECREMENT));
+
+ qp = policy.evaluateQuery(sender,qmsg.getPriority());
+ if ((qp & Policy2.QUERY_DROPMASK) == 0)
+ return true; /* straight drop. */
+
+ preference = (qp & Policy2.QUERY_PRIORITY_BITMASK);
+ if (preference < Policy2.QUERY_BANDWIDTH_VALUE)
+ preference = Policy2.QUERY_BANDWIDTH_VALUE;
+ coreAPI.preferTrafficFrom(sender,preference);
+
+ /* adjust priority */
+ prio = qmsg.getPriority();
+ if ( (qp & Policy2.QUERY_PRIORITY_BITMASK) < prio) {
+ prio = qp & Policy2.QUERY_PRIORITY_BITMASK;
+ qmsg.setPriorityAndTTL(prio,qmsg.getTTL());
+ }
+
+ /* adjust TTL */
+ if ( (ttl > 0) &&
+ (ttl > (prio+3)*TTL_DECREMENT) )
+ ttl = (int) ((prio+3)*TTL_DECREMENT); /* bound! */
+ qmsg.setPriorityAndTTL(qmsg.getPriority(),ttl);
+
+ routing.execQuery(qp,qmsg, null);
+ return true;
+ }
+
+ /**
+ * @param sender
+ * @param msg
+ * @return
+ *
+ */
+
+ public boolean handleSBLOCK_CONTENT( HostIdentity sender, P2PMessage
msg )
+ {
+ int prio;
+ P2PSBlockResult cmsg;
+ ContentIndex ce=new ContentIndex();
+ boolean ret;
+ double preference;
+ boolean[] dupe=new boolean[1];
+
+ if (msg.getByteSize() != P2PSBlockResult.SIZE) {
+ log(Level.WARNING,"WARNING: signed content message
received was malformed");
+ return false;
+ }
+ stat_p2p_sblock_replies.inc();
+ cmsg = (P2PSBlockResult) msg;
+
+ if (!cmsg.getResult().verify())
+ return false;
+ new
Pseudonym(prefs).addNamespace(cmsg.getResult().getNameSpace());
+
+ debug("DEBUG: received SBLOCK search result for
"+cmsg.getResult().getIdentifier().toHex()+" from peer");
+
+ prio =
routing.useContent(sender,cmsg.getResult().getIdentifier(),msg);
+ if (sender == null) { /* no migration, this is already content
from the local node */
+ debug("DEBUG: content migration not needed, content is
local");
+ return true;
+ }
+
+ debug("DEBUG: content migration with preference "+prio);
+
+ preference = prio;
+ prio =
policy.evaluateContent(cmsg.getResult().getIdentifier(),prio);
+ if (prio == -1) {
+ debug("DEBUG: content not important enough, not
replicated");
+ return true; /* straight drop */
+ }
+
+ debug("DEBUG: content replicated with total preference "+prio);
+
+ if (prio != -1)
+ preference += prio;
+ if (preference < Policy2.CONTENT_BANDWIDTH_VALUE)
+ preference = Policy2.CONTENT_BANDWIDTH_VALUE;
+ coreAPI.preferTrafficFrom(sender,preference);
+ ce.hash=(HashCode512)
PersistentHelper.copy(cmsg.getResult().getIdentifier()); //todo: copie utile
???
+ ce.importance = prio;
+ ce.type = ContentIndex.LOOKUP_TYPE_SBLOCK;
+ ce.fileNameIndex = 0;
+ ce.fileOffset = 0;
+
+ ret =
manager.insertContent(ce,ContentBlock.SIZE,cmsg.getResult(),sender,dupe);
+ if (ret && !dupe[0])
+ singleBloomFilter.add(cmsg.getResult().getIdentifier());
+ return true;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/IndexedFileNameCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/IndexedFileNameCallback.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/IndexedFileNameCallback.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,23 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+/**
+ *
+ */
+
+public interface IndexedFileNameCallback
+{
+ /**
+ * Callback for each indexed file.
+ *
+ * @param fn the name of the file
+ * @param idx the index of the file
+ * @param data opaque context pointer for the callee
+ * @return false if the file should be removed from the list
+ */
+
+ public boolean onFile( String fn, int idx, Object data );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/IndirectionTableEntry.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/IndirectionTableEntry.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/IndirectionTableEntry.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,177 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+
+/**
+ * Indirection table entry. Lists what we're looking for,
+ * where to forward it, and how long to keep looking for it.
+ */
+
+public class IndirectionTableEntry extends Object
+{
+ /** what are we waiting for ? */
+ public HashCode512 hash;
+
+ /** Are we limited to a specific namespace ? Non-null if yes. */
+ public HashCode512 namespace;
+
+ /** when can we forget about this entry ? */
+ public long ttl;
+
+ /** How much is this query worth to us, that is, how much would this
node be willing to "pay" for an answer that matches the
+ hash stored in this ITE? (This is NOT the inbound priority, it is the
trust-adjusted inbound priority <B>divided</B> by the
+ number of queries (for a multi-query)). */
+ public int priority;
+
+ /** which replies have we already seen ? hashcodes of the encrypted (!)
replies that we have forwarded so far */
+ private List seen;
+
+ /** How many hosts are waiting for an answer to this query / who are
these hosts ? */
+ private List waiting;
+
+ /** How many tcpsocks are in use ? / local TCP clients to send
the reply to, null if none */
+ private List tcpsocks;
+
+ /** Do we currently have a response in the delay loop (delays are
introduced to make traffic analysis harder
+ and thus enable anonymity) ? This marker is set to avoid looking up
content again before the first content
+ exits the delay loop. Since this *not* looking up content again is
not externally visible, it is ok to do this
+ optimization to reduce disk accesses (see Mantis bug #407). */
+ public boolean successful_local_lookup_in_delay_loop;
+
+ /** Avoiding concurrent lookups for the same ITE: semaphore grants
access to peers to perform a lookup that matches this ITE entry. */
+ public Object lookup_exclusion;
+
+
+ public IndirectionTableEntry()
+ {
+ super();
+ seen=new ArrayList();
+ waiting=new ArrayList();
+ tcpsocks=new ArrayList();
+ hash=new HashCode512();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public boolean hasSeen()
+ {
+ return seen.size()>0;
+ }
+
+ public int getSeenCount()
+ {
+ return seen.size();
+ }
+
+ public HashCode512 getSeen( int index )
+ {
+ return ((index>=0 && index<seen.size()) ? (HashCode512)
seen.get(index) : null);
+ }
+
+ public void addSeen( HashCode512 h )
+ {
+ seen.add(PersistentHelper.copy(h)); //todo: copie utile ???
+ }
+
+ public void clearSeen()
+ {
+ seen.clear();
+ }
+
+ public boolean hasWaitingHost( HostIdentity hi )
+ {
+ return waiting.contains(hi);
+ }
+
+ public boolean hasWaitingHosts()
+ {
+ return waiting.size()>0;
+ }
+
+ public int getWaitingHostsCount()
+ {
+ return waiting.size();
+ }
+
+ public HostIdentity getWaitingHost( int index )
+ {
+ return ((index>=0 && index<waiting.size()) ? (HostIdentity)
waiting.get(index) : null);
+ }
+
+ public void addWaitingHost( HostIdentity hi )
+ {
+ waiting.add(PersistentHelper.copy(hi)); //todo: copie utile ???
+ }
+
+ public void removeWaitingHost( HostIdentity hi )
+ {
+ while (waiting.remove(hi)) {}
+ }
+
+ public void clearWaitingHosts()
+ {
+ waiting.clear();
+ }
+
+ public boolean hasClients()
+ {
+ return tcpsocks.size()>0;
+ }
+
+ public boolean containsClient( CSSession c )
+ {
+ int i;
+
+ for (i=0; i<tcpsocks.size(); i++) {
+ if (tcpsocks.get(i)==c) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getClientsCount()
+ {
+ return tcpsocks.size();
+ }
+
+ public CSSession getClient( int index )
+ {
+ return ((index>=0 && index<tcpsocks.size()) ? (CSSession)
tcpsocks.get(index) : null);
+ }
+
+ public void addClient( CSSession c )
+ {
+ tcpsocks.add(c);
+ }
+
+ public void removeClient( CSSession c )
+ {
+ int i;
+
+ for (i=0; i<tcpsocks.size(); i++) {
+ if (tcpsocks.get(i)==c) {
+ tcpsocks.remove(i);
+ i--;
+ }
+ }
+ }
+
+ public void clearClients()
+ {
+ tcpsocks.clear();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/IterState.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/IterState.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/IterState.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,28 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+/**
+ *
+ */
+
+public class IterState extends Object
+{
+ public boolean hasNext;
+ public Semaphore wsem;
+ public Semaphore sem;
+ public HashCode512 next;
+ public ContentIndex ce;
+ public int bucket;
+ public Object data;
+ public int len;
+ public Task db_iterator;
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/LFS.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/LFS.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/LFS.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -0,0 +1,325 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * Support for special handling of very large (3HASH) reply sets.
+ *
+ * The databases (gdbm in particular, but also the others) do not
+ * handle very large entries very well. This is no problem for CHK,
+ * but there can be several thousand (!) results for a very popular
+ * keyword, like a mime-type. These 3HASH codes with more than
+ * VERY_LARGE_SIZE (16) results are thus stored in separate files.
+ *
+ * The reason is, that gdbm would grow quadratic when the file is
+ * build and that it would also be very slow: every read or write to
+ * these very large content entries would require reading and writing
+ * the *entire* 2 MB block (2 MB for 2,000 entries). This API allows
+ * a random access to one of the results and the use of "append" to
+ * add a single entry. It also does not suffer from the quadratic
+ * explosion in space consumption that gdbm has. So essentially, this
+ * is a crapload of code that does not add any real functionality but
+ * overcomes problems with the crude database implementations that we
+ * would have to use otherwise (and that would be really bad for
+ * performance without this).
+ *
+ * Support for special handling of very large (3HASH) reply sets.
+ *
+ * The databases (gdbm in particular, but also the others) do not
+ * handle very large entries very well. This is no problem for CHK,
+ * but there can be several thousand (!) results for a very popular
+ * keyword, like a mime-type. These 3HASH codes with more than
+ * VERY_LARGE_SIZE (16) results are thus stored in separate files.
+ *
+ * The reason is, that gdbm would grow quadratic when the file is
+ * build and that it would also be very slow: every read or write to
+ * these very large content entries would require reading and writing
+ * the *entire* 2 MB block (2 MB for 2,000 entries). This API allows
+ * a random access to one of the results and the use of "append" to
+ * add a single entry. It also does not suffer from the quadratic
+ * explosion in space consumption that gdbm has. So essentially, this
+ * is a crapload of code that does not add any real functionality but
+ * overcomes problems with the crude database implementations that we
+ * would have to use otherwise (and that would be really bad for
+ * performance without this).
+ */
+
+public class LFS extends LoggedObject
+{
+ public static final String DIR_EXT = ".lfs";
+
+ private DirLocation dir;
+ private Object lock;
+ private StatusCallsService status;
+
+
+ public LFS()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "LFS";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the storage module.
+ * @param str the name of the directory/file
+ * containing the content database
+ * @param sc
+ */
+
+ public void init( String str, StatusCallsService sc )
+ {
+ status=sc;
+
+ dir=new DirLocation(str+DIR_EXT);
+ if (!dir.create()) {
+ log(Level.SEVERE,"Failed to open directory
"+dir.getLabel()+" !");
+ return;
+ }
+
+ log(Level.INFO,"Database directory is located at
\""+dir.getLabel()+"\".");
+
+ lock=new Object();
+ }
+
+ /**
+ * Remove the lfs database.
+ * Remove the lfs database.
+ */
+
+ public void delete()
+ {
+ if (!dir.delete()) {
+ log(Level.SEVERE,"Could not remove
\""+dir.getName()+"\" !");
+ }
+ dir=null;
+ lock=null;
+ }
+
+ /**
+ * Clean shutdown of the storage module.
+ */
+
+ public void done()
+ {
+ dir=null;
+ lock=null;
+ }
+
+ /**
+ * Read the contents of a bucket to a buffer.
+ * @param query
+ *
+ * @return read blocks on success, null on failure
+ */
+
+ public ContentBlock[] read( HashCode512 query )
+ {
+ ContentBlock[] blocks;
+ FileChannel fd;
+ FileLocation fil;
+ int fsize,i;
+ ByteBuffer buf;
+
+ fil=dir.getFile(query.toHex());
+ if (fil==null) {
+ log(Level.WARNING,"File \""+query.toHex()+"\" does not
exist !");
+ return null;
+ }
+
+ blocks=null;
+ synchronized(lock) {
+ try {
+ fd=new
RandomAccessFile(fil.getPath(),"r").getChannel();
+ try {
+ fsize=(int) fd.size();
+ if ((fsize % ContentBlock.SIZE)!=0) {
+ log(Level.WARNING,"LFS database
corrupted (file \""+query.toHex()+"\" has bad length), trying to fix.");
+ fsize = (fsize /
ContentBlock.SIZE) * ContentBlock.SIZE;
+ fd.truncate(fsize);
+ }
+
+ buf=ByteBuffer.allocateDirect(fsize);
+ if (fd.read(buf)!=fsize) {
+ return null;
+ }
+ buf.flip();
+
+ blocks= new
ContentBlock[fsize/ContentBlock.SIZE];
+ for (i=0; i<blocks.length; i++) {
+ blocks[i]=new ContentBlock();
+ blocks[i].readBytes(buf,new
ErrorReporter());
+ }
+ }
+ finally {
+ fd.close();
+ }
+ }
+ catch( IOException x ) {
+ err("Failed to read on \""+query.toHex()+"\"
!",x);
+ return null;
+ }
+ }
+ return blocks;
+ }
+
+ /**
+ * Read one random block from an entry
+ * Read the contents of a bucket to a buffer.
+ *
+ * @param query the hashcode representing the entry
+ * @param prio
+ * @return read blocks on success, null on failure
+ */
+
+ public ContentBlock[] randomRead( HashCode512 query, int prio )
+ {
+ ContentBlock[] blocks;
+ int[] perm;
+ FileChannel fd;
+ FileLocation fil;
+ int size,fsize,max,i;
+
+ max = (50-status.getNetworkLoadUp())*(prio+1);
+ if (max <= 0)
+ max = 1;
+
+ fil=dir.getFile(query.toHex());
+ if (fil==null) {
+ log(Level.WARNING,"File \""+query.toHex()+"\" does not
exist !");
+ return null;
+ }
+
+ blocks=null;
+ synchronized(lock) {
+ try {
+ fd=new
RandomAccessFile(fil.getPath(),"r").getChannel();
+ try {
+ fsize=(int) fd.size();
+ if ((fsize % ContentBlock.SIZE)!=0) {
+ log(Level.WARNING,"LFS database
corrupted (file \""+query.toHex()+"\" has bad length), trying to fix.");
+ fsize = (fsize /
ContentBlock.SIZE) * ContentBlock.SIZE;
+ fd.truncate(fsize);
+ }
+
+ fsize = fsize / ContentBlock.SIZE;
+ if (fsize == 0) {
+ return null;
+ }
+ if (max > fsize) {
+ max = fsize;
+ }
+ log(Level.FINEST,"Received query, have
"+fsize+" results, adding "+max+" to queue.");
+
+ blocks=new ContentBlock[max];
+
+ perm = Crypto.permute(fsize);
+ for (i=0; i<max; i++) {
+ blocks[i]=new ContentBlock();
+
+
fd.position(perm[i]*ContentBlock.SIZE);
+
+
size=fd.read(ByteBuffer.wrap(blocks[i].content));
+ if (size!=ContentBlock.SIZE) {
+ return null;
+ }
+ }
+ }
+ finally {
+ fd.close();
+ }
+ }
+ catch( IOException x ) {
+ err("Failed to read on \""+query.toHex()+"\"
!",x);
+ return null;
+ }
+ }
+ return blocks;
+ }
+
+ /**
+ * Append content to file.
+ *
+ * @param query the key for the entry
+ * @param data what to append/the data to store
+ * @return false on error, true if ok.
+ */
+
+ public boolean append( HashCode512 query, ContentBlock data )
+ {
+ FileChannel fd;
+ FileLocation fil;
+ int offset;
+
+ fil=dir.getFile(query.toHex());
+ fil.create();
+
+ synchronized(lock) {
+ try {
+ fd=new
RandomAccessFile(fil.getPath(),"rw").getChannel();
+ try {
+ fd.position(fd.size());
+ offset=(int) fd.position();
+ if ((offset % ContentBlock.SIZE)!=0) {
+ log(Level.WARNING,"LFS database
corrupted (file \""+query.toHex()+"\" has bad length), trying to fix.");
+
offset=(offset/ContentBlock.SIZE)*ContentBlock.SIZE;
+ fd.position(offset);
+ fd.truncate(offset);
+ }
+
+ fd.write(ByteBuffer.wrap(data.content));
+ }
+ finally {
+ fd.close();
+ }
+ }
+ catch( IOException x ) {
+ err("Failed to append on \""+query.toHex()+"\"
!",x);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Remove an entry.
+ * Free space in the database by removing one file
+ *
+ * @param query the key for the entry/the hashcode representing the
entry
+ * @return false on error, true if ok.
+ */
+
+ public boolean remove( HashCode512 query )
+ {
+ FileLocation f;
+
+ f=dir.getFile(query.toHex());
+ if (f==null) {
+ log(Level.WARNING,"File \""+query.toHex()+"\" does not
exist !");
+ return false;
+ }
+
+ synchronized(lock) {
+ return f.delete();
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/Manager.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/Manager.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/Manager.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,1017 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * This module is responsible to manage content, in particular it needs to
decide what content to keep.
+ * This module is responsible for content management (what to
+ * keep, what to discard, content ageing, content migration).
+ *
+ * The manager.h header defines the external interface to the
+ * GNUnet databases. The manager code is responsible for
+ * space management and on-demand encoding of blocks. The
+ * high-level database abstraction defined in high_backend.h
+ * is responsible for lookup (3HASH and CHK) and block retrieval
+ * (ContentEntries and inserted blocks).
+ */
+
+public class Manager extends LoggedObject
+{
+ public static final String VLS_DIR = "large";
+ public static final String AGEFILE = "database.age";
+
+ public static final int DB_DIRTY_AVAILABLE =
Integer.MIN_VALUE;
+
+
+ public static final boolean TRACK_INDEXED_FILES = true;
+ public static final String TRACKFILE =
"indexed_requests.txt";
+
+
+ /** Entry length that indicates that the entry was too large for the
usual DB and has been stored in a separate file instead. */
+ public static final int VERY_LARGE_FILE = 42;
+
+ /** How large is very large? (number of ContentEntries) Mysql seems to
have some limit at 16k, so let's pick 15 to be on the good side for sure. */
+ public static final int VERY_LARGE_SIZE = 15;
+
+ /** The current base value for fresh content (used to time-out old
content). */
+ private int MANAGER_age;
+
+ /** Is active migration allowed ? This is about us receiving data from
the network,
+ actively pushing content out is always ok. */
+ private boolean useActiveMigration;
+
+ /** Global database handle */
+// private DatabaseAPI dbAPI;
+ /** Handle of the database as returned by initContentDatabase() */
+ public DBHandle[] dbHandles;
+
+ /** The number of buckets */
+ public int buckets;
+
+ /** cache estimated available blocks for each bucket */
+ public int[] dbAvailableBlocks;
+
+ /** Large file handling. */
+ private LFS lfs;
+
+ /** Statistics handles */
+ private Stat stat_handle_lookup_3hash;
+ private Stat stat_handle_lookup_sblock;
+ private Stat stat_handle_lookup_chk;
+ private Stat stat_handle_lookup_ondemand;
+ private Stat stat_handle_lookup_notfound;
+ private Stat stat_handle_spaceleft;
+
+ private BloomFilter2 filter2; //todo: VA PLANTER
+ private FileIndex fileIndex; //todo: VA PLANTER
+ private ContentEncoding encoding; //todo: VA PLANTER
+
+ private Prefs prefs;
+ private Statistics stats;
+ private Scheduler scheduler;
+ private ScheduledTask reduceTask;
+
+
+ public Manager()
+ {
+ super(true);
+ lfs=new LFS();
+
+ reduceTask=new ScheduledTask("REDUCE-IMPORTANCE",new
EvalAction(this,"cronReduceImportance"),Scheduler.HOURS_12);
+ }
+
+ public String toString()
+ {
+ return "Manager";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the manager module.
+ * @param capi
+ */
+
+ public void init( CoreForProtocol capi )
+ {
+ String dtype,afsdir;
+ int delta,i;
+
+ prefs=capi.getApplication().getPreferences();
+ stats=capi.getApplication().getStatistics();
+
+ dtype = prefs.getString("AFS","DATABASETYPE",null);
+ initializeDatabaseAPI(dtype);
+ stat_handle_lookup_sblock=stats.getHandle("# lookup (SBlock,
search results)");
+ stat_handle_lookup_3hash=stats.getHandle("# lookup (3HASH,
search results)");
+ stat_handle_lookup_chk=stats.getHandle("# lookup (CHK, inserted
or migrated content)");
+ stat_handle_lookup_ondemand=stats.getHandle("# lookup
(ONDEMAND, indexed content)");
+ stat_handle_lookup_notfound=stats.getHandle("# lookup (data not
found)");
+ stat_handle_spaceleft=stats.getHandle("# blocks AFS storage
left (estimate)");
+
+ MANAGER_age = readAge();
+
+ useActiveMigration=
prefs.testString("AFS","ACTIVEMIGRATION","YES");
+
+ scheduler=capi.getApplication().getScheduler();
+ scheduler.addJob(reduceTask,Scheduler.HOURS_6);
+
+ delta = estimateGlobalAvailableBlocks();
+ if (delta < 0) {
+ int[] perm = Crypto.permute(buckets);
+ /* we permute to delete content in random order since
+ users may interrupt the process (in particular at
+ the beginning) and we want to make sure that the
+ chances are distributed reasonably at random) */
+ for (i=0; i<buckets; i++) {
+
dbHandles[perm[i]].deleteContent(16-delta/buckets,new EntryCallback() {
+ public void onEntry( HashCode512 key,
ContentIndex ce, Object data, int dataLen, Object closure )
+ {
+
filter2.bf_deleteEntryCallback(key,ce,data,dataLen,closure);
+ }
+ },null);
+ dbAvailableBlocks[perm[i]]=DB_DIRTY_AVAILABLE;
+ }
+ delta = (16-delta/buckets)*buckets;
+ }
+ stat_handle_spaceleft.set(delta);
+
+ afsdir = prefs.getString("AFS","AFSDIR",null);
+ if (afsdir==null) {
+ trace("Configuration file must specify directory for
storing AFS data in section AFS under AFSDIR !");
+ return;
+ }
+
+ lfs.init(afsdir+"/"+VLS_DIR,(StatusCallsService)
capi.service(StatusCallsService.class));
+ }
+
+ /**
+ * Shutdown the manager module.
+ */
+
+ public void done()
+ {
+ int i;
+
+ scheduler.deleteJob(reduceTask);
+
+ for (i=0;i<buckets;i++)
+ dbHandles[i].close();
+ dbHandles=null;
+ dbAvailableBlocks=null;
+ lfs.done();
+ }
+
+ /**
+ * calculates the global available space using
+ * cached bucket availability estimates
+ * @return
+ */
+
+ protected int estimateGlobalAvailableBlocks()
+ {
+ int i;
+ int ret = 0;
+ int perBucketQuota = prefs.getInt("AFS","DISKQUOTA",0) * 1024 /
buckets;
+
+ for (i = 0; i < buckets; ++i) {
+ if (dbAvailableBlocks[i] == DB_DIRTY_AVAILABLE) {
+ dbAvailableBlocks[i] =
dbHandles[i].estimateAvailableBlocks(perBucketQuota);
+ }
+ ret += dbAvailableBlocks[i];
+ }
+ return ret;
+ }
+
+ /**
+ * Load the high-level database as specified by
+ * the given dtype.
+ * @param dtype
+ */
+
+ public void initializeDatabaseAPI( String dtype )
+ {
+ DBHandle dbh;
+ String odtype;
+ int i;
+
+ if (dtype == null) {
+ log(Level.SEVERE,"AFS/DATABASETYPE not specified in
config");
+ return;
+ }
+
+ odtype=prefs.getContentAsString("AFS-DATABASETYPE");
+ if (odtype==null) {
+ prefs.putContent("AFS-DATABASETYPE",dtype);
+ }
+ else if (!odtype.equals(dtype)) {
+ log(Level.SEVERE,"FATAL: AFS database type was changed,
run gnunet-convert");
+ return;
+ }
+
+ buckets= 4 *prefs.getInt("AFS","DISKQUOTA",0) / 1024; // one
bucket per 256 MB
+ if (buckets == 0)
+ buckets = 1; // at least 1 bucket!
+ dbHandles= new DBHandle[buckets];
+ dbAvailableBlocks=new int[buckets];
+
+ for (i=0;i<buckets;i++) {
+ dbh=(DBHandle) new
DynamicLibrary("protocol-afs-"+dtype+".jar").load(DBHandle.class,getClass().getClassLoader());
+ if (dbh==null) {
+ log(Level.SEVERE,"Failed to initialize AFS
database "+i+" !");
+ return;
+ }
+ if
(!dbh.open(prefs,String.valueOf(i),String.valueOf(prefs.getInt("AFS","DISKQUOTA",0))))
{
+ dbh.close();
+ trace("Should not happen !");
+ return;
+ }
+ dbHandles[i]=dbh;
+
+ if (dbHandles[i] == null) {
+ log(Level.SEVERE,"Failed to initialize AFS
database "+i+" !");
+ return;
+ }
+ dbAvailableBlocks[i]=DB_DIRTY_AVAILABLE; /* not yet
initialized */
+ }
+ }
+
+ /**
+ * Store content (if the priority is high enough), potentially
+ * discarding less important content. If this method is called
+ * for indexed content, * data should be null and len==0 and
+ * fields of ce filled properly. For 3HASH inserts, 2HASH must
+ * be provided in ce.hash.
+ *
+ * @param ce the content entry describing the content
+ * @param len the length of the data in bytes/the length of the data in
bytes, either 0 for no data (e.g. on-demand encoding
+ * of indexed content or CONTENT_Block.SIZE for normal data.
+ * @param data the block itself
+ * @param sender from where does the content come? null for
+ * from local client./from where does the content come? null for
+ * from local client.
+ * @param duplicate output param, will be true if content was already
there
+ * @return true if the block was stored, false if not
+ */
+
+ public boolean insertContent( ContentIndex ce, int len, byte[] data,
HostIdentity sender, boolean[] duplicate )
+ {
+ byte[] old;
+ ContentIndex oldce;
+ HashCode512 query;
+ int avail,importance;
+
+ if (ce.fileNameIndex>0) {
+ log(Level.FINEST,"Using fileNameIndex
"+ce.fileNameIndex);
+ }
+
+ if (len!=0 && len!=ContentBlock.SIZE) {
+ trace("Unexpected length "+len+" for insertContent !");
+ return false;
+ }
+
+ duplicate[0] = false;
+ if (sender!=null && !useActiveMigration) {
+ return false; // forbidden!
+ }
+ importance = ce.importance;
+ if (sender!=null && Crypto.nextInt(2 + importance)==0) {
+ return false; // don't bother...
+ }
+
+ ce.importance= importance + MANAGER_age;
+ switch (ce.type) {
+ case ContentIndex.LOOKUP_TYPE_3HASH:
+
query=HashCode512.create(PersistentHelper.toBytes(ce.hash));
+ break;
+ case ContentIndex.LOOKUP_TYPE_CHK:
+ case ContentIndex.LOOKUP_TYPE_CHKS:
+ case ContentIndex.LOOKUP_TYPE_SUPER:
+ case ContentIndex.LOOKUP_TYPE_SBLOCK:
+ query=(HashCode512)
PersistentHelper.copy(ce.hash);
+ break;
+ default:
+ log(Level.WARNING,"Unexpected content type :
"+ce.type);
+ return false;
+ }
+
+ oldce=(ContentIndex) PersistentHelper.copy(ce);
+
+ avail = estimateGlobalAvailableBlocks();
+ if (avail <= 0) {
+ if (importance + MANAGER_age <=
computeHighDB(query).getMinimumPriority())
+ return false; // new content has such a low
priority that we should not even bother!
+ computeHighDB(query).deleteContent(16-avail,new
EntryCallback() {
+ public void onEntry( HashCode512 key,
ContentIndex ce2, Object data2, int dataLen, Object closure )
+ {
+
filter2.bf_deleteEntryCallback(key,ce2,data2,dataLen,closure);
+ }
+ },null);
+ stat_handle_spaceleft.set(16-avail);
+
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+ }
+ else {
+ stat_handle_spaceleft.set(avail);
+ }
+
+ // try to read existing
+ old = computeHighDB(query).readContent(query,oldce,0);
+
+ // add the content
+ switch (ce.type) {
+ case ContentIndex.LOOKUP_TYPE_3HASH:
+ if (len != ContentBlock.SIZE ) {
+ trace("Unexpected length "+len+" for
insertContent !");
+ return false;
+ }
+ return
handle3HSBInsert(query,ce,data,(old!=null ? old.length :
-1),duplicate,len,old,oldce.importance);
+ case ContentIndex.LOOKUP_TYPE_SBLOCK:
+ if (len != ContentBlock.SIZE) {
+ trace("Unexpected length "+len+" for
insertContent !");
+ return false;
+ }
+ return
handle3HSBInsert(query,ce,data,(old!=null ? old.length :
-1),duplicate,len,old,oldce.importance);
+ case ContentIndex.LOOKUP_TYPE_CHK:
+ case ContentIndex.LOOKUP_TYPE_CHKS:
+ case ContentIndex.LOOKUP_TYPE_SUPER:
+ {
+ boolean replace = false;
+ /*
+ * This is a bit messy. The intended idea is
that missing blocks
+ * are always replaced. Indexed blocks are
replaced only if the
+ * new one is indexed AND has a higher priority
than the old one.
+ * Nonindexed, existing blocks are replaced if
the size differs OR if new
+ * is more important OR if new is an indexed
block. This scheme
+ * should never replace an indexed block with a
nonindexed block.
+ * We are not setting *duplicate=YES in the
true replace case because
+ * we don't want the bloomfilters to be
unnecessarily incremented
+ * outside insertContent().
+ */
+ duplicate[0] = true;
+ if (old == null) {
+ replace = true;
+ duplicate[0] = false;
+ }
+ else if (oldce.fileNameIndex > 0) {
+ if (ce.fileNameIndex>0 &&
ce.importance>oldce.importance) {
+ replace = true;
+ }
+ else {
+ replace = false;
+ }
+ }
+ else {
+ if ((old!=null ? old.length : -1) !=
len || ce.importance>oldce.importance || ce.fileNameIndex>0) {
+ replace = true;
+ }
+ else {
+ replace = false;
+ }
+ }
+
+ if (!replace) {
+ return true;
+ }
+
+ dbAvailableBlocks[computeBucketGlobal(query)]=
DB_DIRTY_AVAILABLE;
+ return
computeHighDB(query).writeContent(ce,data,0,len);
+ }
+// break;
+ default:
+ log(Level.WARNING,"Unexpected content type :
"+ce.type);
+ return false;
+ }
+// log(Level.SEVERE,"ERROR: insertContent code ended up where it
never should");
+// return false;
+ }
+
+ public boolean insertContent( ContentIndex ce, int len, Persistent
data, HostIdentity sender, boolean[] duplicate )
+ {
+ return
insertContent(ce,len,PersistentHelper.toBytes(data),sender,duplicate);
+ }
+
+ /**
+ * Locate content. This method locates the data matching the
+ * query. The data is on-demand encrypted if it is
+ * indexed content or retrieved from the contentdatabase
+ * if it was inserted content. The ContentIndex entry is
+ * filled with the appropriate values.
+ * Locate content. This method locates the data matching the content
+ * entry. The data is on-demand encrypted if it is indexed content or
+ * retrieved from the contentdatabase if it was inserted content.
+ *
+ * @param query the CHK or the tripleHash key of the conten/the
query for the content (CHK or 3HASH)
+ * @param ce the content entry describing what to look
for/the meta-data for the content
+ * @param prio the amount to modify the priority of the
entry/by how much should the priority of the content be changed (if it is
found)?
+ * @param isLocal is the request a local request? (true/false)
+ * @return the resulting content, null on error
+ */
+
+ public ContentBlock[] retrieveContent( HashCode512 query, ContentIndex
ce, int prio, boolean isLocal )
+ {
+ ContentBlock[] result;
+ byte[] tmp;
+ int len,i;
+
+ tmp=computeHighDB(query).readContent(query,ce,prio);
+ if (tmp==null) {
+ stat_handle_lookup_notfound.inc();
+ return null;
+ }
+
+ if (tmp.length==VERY_LARGE_FILE) {
+ result=(isLocal ? lfs.read(query) :
lfs.randomRead(query,prio));
+ if (result==null) {
+ return null;
+ }
+ }
+ else {
+ if ((tmp.length % ContentBlock.SIZE)!=0) {
+ log(Level.SEVERE,"ERROR: retrieved content but
size is not multiple of 1k!");
+ return null;
+ }
+
+ result=new ContentBlock[tmp.length/ContentBlock.SIZE];
+ for (i=0; i<result.length; i++) {
+ result[i]=(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(tmp,i*ContentBlock.SIZE,ContentBlock.SIZE));
+ }
+ }
+
+ if (ce.fileNameIndex == 0) {
+ switch (ce.type) {
+ case ContentIndex.LOOKUP_TYPE_CHK:
+ case ContentIndex.LOOKUP_TYPE_CHKS:
+ stat_handle_lookup_chk.inc();
+ break;
+ case ContentIndex.LOOKUP_TYPE_3HASH:
+ stat_handle_lookup_3hash.inc();
+ break;
+ case ContentIndex.LOOKUP_TYPE_SBLOCK:
+ stat_handle_lookup_sblock.inc();
+ break;
+ case ContentIndex.LOOKUP_TYPE_SUPER:
+ // only gnunet-check will be doing
this, don't bother to keep up stats
+ break;
+ default:
+ log(Level.SEVERE,"Manager got
unexpected content type : "+ce.type);
+ break;
+ }
+ return result;
+ }
+
+ log(Level.SEVERE,"Retrieved content but index says on-demand
encoded !");
+
+
+ stat_handle_lookup_ondemand.inc();
+
+ result=new ContentBlock[1];
+
+ len=encodeOnDemand(ce,result);
+ return (len>=0 ? result : null);
+ }
+
+ /**
+ * Explicitly delete content. This method is not currently used
+ * (the manager discards data internally if we run out of space)
+ * but it could be used by a "gnunet-remove" application in the
+ * near future.
+ * <p>
+ *
+ * Explicitly remove some content from the database.
+ * Note that if multiple keywords correspond to the query, all are
+ * removed. To selectively remove one keyword, use retrieveContent and
+ * then insertContent if there are multiple results.
+ *
+ * @param query the query that corresponds to the block to remove
+ * @param bucket where to delete, <0 == autocompute/where to delete (<0
== autocompute)
+ * >=0 is used by gnunet-check.
+ * @return
+ */
+
+ public boolean removeContent( HashCode512 query, int bucket )
+ {
+ byte[] data;
+ ContentIndex ce;
+ DBHandle db;
+ boolean ok;
+
+ if (bucket < 0)
+ db = computeHighDB(query);
+ else
+ db = dbHandles[bucket];
+
+ ce=new ContentIndex();
+ data = db.readContent(query,ce,0);
+ if (data==null) {
+ log(Level.WARNING,"removeContent ("+query.toHex()+")
failed, readContent did not find content !");
+ return false; /* not found! */
+ }
+ if (data.length == VERY_LARGE_FILE) {
+ // need to remove from VLS index, too -- this is
unusual, but we can do it (should currently never happen in practice)
+ ok = lfs.remove(query);
+ if (!ok)
+ log(Level.WARNING,"WARNING: removeContent
failed on LFS content?");
+ }
+
+ ok = db.unlink(query);
+ if (ok) {
+ int delta;
+
+ dbAvailableBlocks[computeBucketGlobal(query)] =
DB_DIRTY_AVAILABLE;
+ delta = estimateGlobalAvailableBlocks();
+ if (delta < 0) /* should not happen */
+ delta = 0;
+ stat_handle_spaceleft.set(delta);
+ }
+ return ok;
+ }
+
+ /**
+ * Get some random contet.
+ * Return a random key from the database.
+ * @param ce output information about the key
+ * @return false on error, true if ok.
+ */
+
+ public boolean retrieveRandomContent( ContentIndex ce )
+ {
+ int bucket;
+
+ bucket=Crypto.nextInt(buckets);
+ if (dbHandles[bucket] == null) {
+ log(Level.SEVERE,"dbHandle at bucket "+bucket+" is
null");
+ return false;
+ }
+ return dbHandles[bucket].getRandomContent(ce);
+ }
+
+ /**
+ * Iterator over all the queries in the database
+ * as needed by resizeBloomfilter.
+ *
+ * The idea is to use this code in the startup
+ * of the AFS module when the quota/memory limitations
+ * have changed and the bloomfilter needs to be
+ * resized. Note that the iterator is quite costly,
+ * but we can assume that the user is not going to
+ * change the configuration all the time :-).
+ * Iterator over all the queries in the database as needed by
+ * resizeBloomfilter (and gnunet-check). Typical use:
+ * <code>
+ * state = makeDatabaseIteratorState();
+ * while (databaseIterator(state, x, y, z, t))
+ * ...do something...
+ * </code>
+ *
+ * @param sss the iterator state as created by
+ * makeDatabaseIteratorState
+ * @param hc next hash code (set)
+ * @param ce next content index (set)
+ * @param bucket where the data actually was (set)
+ * @param data corresponding data (set)
+ * @param datalen length of data (set)
+ * @return true if the iterator has filled in another element
+ * from the database, false if there are no more elements
+ */
+
+ public boolean databaseIterator( IterState sss, HashCode512 hc,
ContentIndex ce, int[] bucket, byte[][] data, int[] datalen )
+ {
+ IterState st = sss;
+
+ try {
+ st.sem.acquire();
+ if (!st.hasNext) {
+ st.sem=null;
+ st.wsem=null;
+ st.db_iterator.join();
+ return false;
+ }
+ hc=(HashCode512) PersistentHelper.copy(st.next);
+ ce=(ContentIndex) PersistentHelper.copy(st.ce);
+ bucket[0] = st.bucket;
+ data[0] = (byte[]) st.data;
+ datalen[0] = st.len;
+ st.wsem.release();
+ return true;
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ return false;
+ }
+ }
+
+ /**
+ * Create the state required for a database iterator.
+ * Create the state required for a database iterator. Calling this
+ * method requires to call databaseIterator with the state returned
+ * until "false" is returned.
+ * @return
+ */
+
+ public Object makeDatabaseIteratorState()
+ {
+ IterState ret;
+
+ ret = new IterState();
+ ret.sem = new Semaphore(0);
+ ret.wsem = new Semaphore(1);
+
+ final IterState _ret = ret;
+
+ ret.db_iterator=new Task("DB-ITER",new AbstractAction() {
+ public void perform()
+ {
+ iterator_helper(_ret);
+ }
+ });
+ ret.db_iterator.launch();
+ return ret;
+ }
+
+ /**
+ * Compute the database bucket id (for gnunet-check)
+ * This function is as crazy as it is since RIPE160 hashes do not seem
+ * to be quite random as we may want the to be... So to get evenly
+ * distributed indices, we have to be a bit tricky. And no, there is
+ * high science but just a bit playing with the formula here.
+ * @param query
+ * @param maxBuckets
+ * @return
+ */
+
+ public int computeBucket( HashCode512 query, int maxBuckets )
+ {
+ HashCode512 qt;
+ long r = 0;
+
+ qt=HashCode512.create(PersistentHelper.toBytes(query));
+
+ for(int i = 0; i < HashCode512.SIZE / 4; i++)
+ r ^= query.getInt(i)-qt.getInt(i);
+ r = r >> 4;
+ r = r & 0x00000000FFFFFFFFL;
+ return (int) (r % maxBuckets);
+ }
+
+ /**
+ * Use this, if initManager() has been executed and
+ * the global dbAPI has the correct bucket count
+ * @param query
+ * @return
+ */
+
+ public int computeBucketGlobal( HashCode512 query )
+ {
+ return computeBucket(query,
+ buckets);
+ }
+
+ /**
+ * @param query
+ * @return
+ *
+ */
+
+ protected DBHandle computeHighDB( HashCode512 query )
+ {
+ return dbHandles[computeBucket(query,buckets)];
+ }
+
+ /**
+ * Open the AGE file and return the handle.
+ * @return
+ */
+
+ protected FileLocation getAgeFileHandle()
+ {
+ DirLocation dir;
+ FileLocation f;
+
+ dir=prefs.getDirLocation("AFS","AFSDIR");
+ if (dir==null) {
+ log(Level.SEVERE,"Configuration file must specify
directory for storage of AFS data in section AFS under AFSDIR.");
+ return null;
+ }
+ f=dir.getFile(AGEFILE);
+ f.create();
+ return f;
+ }
+
+ /**
+ * Cron-job that decreases the importance-level of all
+ * files by 1. Runs 'not very often'.
+ */
+
+ public void cronReduceImportance()
+ {
+ FileLocation f;
+ MappedFile m;
+
+ log(Level.INFO,"Enter cronReduceImportance");
+
+ MANAGER_age++;
+
+ f=getAgeFileHandle();
+ if (f==null) {
+ log(Level.WARNING,"Could not open agefile !");
+ return;
+ }
+
+ m=f.openNew();
+ m.writeInt(MANAGER_age);
+ m.close();
+
+ log(Level.INFO,"Exit cronReduceImportance");
+ }
+
+ protected int readAge()
+ {
+ FileLocation f;
+ MappedFile m;
+ int age;
+
+ f=getAgeFileHandle();
+ if (f==null) {
+ log(Level.WARNING,"Could not open agefile !");
+ return 0;
+ }
+
+ m=f.open();
+ age=m.asInt(0);
+ m.close();
+ return age;
+ }
+
+ /**
+ * Encode a block from a file on the drive, put the
+ * result in the result buffer (allocate) and return
+ * the size of the block.
+ * @param ce
+ * @param result
+ * @return
+ */
+
+ protected int encodeOnDemand( ContentIndex ce, ContentBlock[] result )
+ {
+ String fn;
+ int blen;
+ HashCode512 hc;
+ ContentBlock iobuf;
+ FileChannel fc;
+
+ // on-demand encoding mechanism
+ fn = fileIndex.getIndexedFileName(ce.fileNameIndex);
+ if (fn == null) {
+ log(Level.SEVERE,"Database inconsistent! (index points
to invalid offset ("+ce.fileNameIndex+")");
+ return -1;
+ }
+
+ iobuf = new ContentBlock();
+
+ try {
+ fc=new RandomAccessFile(fn,"r").getChannel();
+
+ if (TRACK_INDEXED_FILES) {
+ PrintWriter fp;
+ FileLocation scratch;
+ DirLocation afsDir;
+
+ afsDir=prefs.getDirLocation("AFS","AFSDIR");
+ if (afsDir!=null) {
+ scratch=afsDir.getFile(TRACKFILE);
+
+ fp=new PrintWriter(new
FileOutputStream(scratch.getPath(),true),true);
+ fp.println(ce.fileNameIndex+"
"+Scheduler.toSeconds(Scheduler.now()));
+ fp.close();
+ }
+ else {
+ log(Level.SEVERE,"Configuration file
must specify directory for storage of AFS data in section AFS under AFSDIR.");
+ }
+ }
+
+ try {
+ fc.position(ce.fileOffset);
+ if (fc.position()!=ce.fileOffset) {
+ log(Level.WARNING,"WARNING: unable to
seek to "+ce.fileOffset+" in "+fn);
+ return -1;
+ }
+ Arrays.fill(iobuf.content,(byte) 0);
+ blen=fc.read(ByteBuffer.wrap(iobuf.content));
+ if (blen <= 0) {
+ if(blen == 0)
+ log(Level.WARNING,"WARNING:
read 0 bytes from "+fn);
+ else
+ log(Level.SEVERE,"ERROR: could
not read file");
+ return -1;
+ }
+
+ debug("Read "+blen+" bytes from "+fn+" for
on-demand encoding at "+ce.fileOffset);
+ }
+ finally {
+ fc.close();
+ }
+ }
+ catch( IOException x ) {
+ err("I/O exception !",x);
+ return -1;
+ }
+
+ hc=HashCode512.create(PersistentHelper.toBytes(iobuf),0,blen);
+
+ result[0] = new ContentBlock();
+ if (!encoding.encryptContent(iobuf,hc,result[0])) {
+ log(Level.SEVERE,"ERROR: encryption of content failed");
+ return -1;
+ }
+
+ hc=HashCode512.create(PersistentHelper.toBytes(result[0]));
+ debug("DEBUG: on-demand encoded content has query "+hc.toHex());
+ return ContentBlock.SIZE;
+ }
+
+ protected void iterator_helper_callback( HashCode512 key, ContentIndex
ce, Object data, int dataLen, IterState sss )
+ {
+ try {
+ sss.wsem.acquire();
+ sss.next=(HashCode512) PersistentHelper.copy(key);
+ sss.ce=(ContentIndex) PersistentHelper.copy(ce);
+ sss.data = data;
+ sss.len = dataLen;
+ sss.sem.release();
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ }
+ }
+
+ /**
+ * Thread that fetches the next entry from the database.
+ * The thread is created by makeDatabaseIteratorState
+ * and exits once we're through the database.
+ * @param sss
+ */
+
+ protected void iterator_helper( IterState sss )
+ {
+ int i;
+
+ try {
+ sss.hasNext = true;
+ for (i=0;i<buckets;i++) {
+ sss.wsem.acquire();
+ sss.bucket = i;
+ sss.wsem.release();
+ dbHandles[i].forEachEntry(new EntryCallback() {
+ public void onEntry( HashCode512 key,
ContentIndex ce, Object data, int dataLen, Object closure )
+ {
+
iterator_helper_callback(key,ce,data,dataLen,(IterState) closure);
+ }
+ },sss);
+ }
+ sss.wsem.acquire();
+ sss.hasNext = false;
+ sss.sem.release();
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ }
+ }
+
+ protected boolean handleVLSResultSet( HashCode512 query, byte[] data,
boolean[] duplicate )
+ {
+ ContentBlock[] blocks;
+ int i,j;
+ int ret;
+
+ blocks=lfs.read(query);
+ if (blocks==null) {
+ log(Level.WARNING,"lfs database inconsistent, trying to
fix");
+ if (computeHighDB(query).unlink(query)) {
+
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+ }
+ else {
+ log(Level.WARNING,"Failed to fix lfs database
inconsistency.");
+ }
+ return false;
+ }
+
+ // check if the content is already present
+ ret=blocks.length;
+ for (i=0; i<ret; i++) {
+ for (j=0; j<ContentBlock.SIZE &&
data[j]==blocks[i].content[j]; j++) {}
+
+ if (j==ContentBlock.SIZE) {
+ duplicate[0] = true;
+ return true;
+ }
+ }
+ return lfs.append(query,(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(data)));
+ }
+
+ protected boolean migrateToVLS( byte[] old, int oldLen, HashCode512
query, byte[] data, ContentIndex ce )
+ {
+ int i;
+ boolean ret;
+ ContentBlock block;
+
+ ret = true;
+
+ i = 0;
+ while ( (i < oldLen / ContentBlock.SIZE ) && ret) {
+ block=(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(old,i*ContentBlock.SIZE,ContentBlock.SIZE));
+ ret = lfs.append(query,block);
+ i++;
+ }
+
+ if (ret) {
+ block=(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(data));
+ ret = lfs.append(query,block);
+ }
+
+ if (!ret) {
+ lfs.remove(query);
+ return ret;
+ }
+ // put forwarding content (marked by size)
+
ret=computeHighDB(query).writeContent(ce,data,0,VERY_LARGE_FILE); // data:
random bits
+
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+ return ret;
+ }
+
+ /**
+ * 3HASH and SBlock results require special treatment
+ * since multiple results are possible.
+ * @param query
+ * @param ce
+ * @param data
+ * @param oldLen
+ * @param duplicate
+ * @param len
+ * @param old
+ * @param oldImportance
+ * @return
+ */
+
+ protected boolean handle3HSBInsert( HashCode512 query, ContentIndex ce,
byte[] data, int oldLen, boolean[] duplicate, int len, byte[] old, int
oldImportance )
+ {
+ boolean ret;
+ byte[] tmp;
+ int n,i;
+
+ if (oldLen == -1) {
+ // no old content, just write
+
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+ return computeHighDB(query).writeContent(ce,data,0,len);
+ }
+
+ // check if content is ALREADY in VLS store
+ if (oldLen == VERY_LARGE_FILE) {
+ return handleVLSResultSet(query,data,duplicate);
+ }
+
+ // Not VLS, check if the content is already present
+ for (n=0; n<oldLen/len; n++) {
+ for (i=0; i<len && old[len*n+i]==data[i]; i++) {}
+ if (i==len) {
+ // content already there, abort
+ duplicate[0] = true;
+ return true;
+ }
+ }
+
+ // check if we need to *move* the content to VLS store
+ if (oldLen / ContentBlock.SIZE >= VERY_LARGE_SIZE) {
+ return migrateToVLS(old, oldLen, query, data, ce);
+ }
+
+ // else: default behavior: append
+
+ tmp = new byte[oldLen + len];
+ System.arraycopy(old,0,tmp,0,oldLen);
+ System.arraycopy(data,0,tmp,oldLen,len);
+ // Discussion: perhaps we should use eg max(a,b)? Otherwise n
local inserts throws this through the ceiling...
+ // OTOH, if the same block was part of two files, it is really
twice as important, so adding makes sense in some cases.
+ ce.importance = oldImportance + ce.importance;
+ ret = computeHighDB(query).writeContent(ce,tmp,0,oldLen+len);
+
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+ return ret;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/Migration.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/Migration.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/Migration.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,273 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * This module is responsible for pushing content out
+ * into the network.
+ */
+
+public class Migration extends LoggedObject
+{
+ /** use a 64-entry RCB buffer */
+ public static final int RCB_SIZE = 128;
+
+ /** */
+ private CoreForProtocol coreAPI;
+
+ /** */
+ private StatusCallsService status;
+
+ /** */
+ private Stat stat_handle_content_pushed;
+
+ /** Semaphore on which the RCB aquire thread waits if the RCB buffer is
full. */
+ private Semaphore aquireMoreSignal;
+
+ /** */
+ private Semaphore doneSignal;
+
+ /** Lock for the RCB buffer. */
+ private Object lock;
+
+ /** Buffer with pre-fetched random content for migration. */
+ private ContentIndex[] randomContentBuffer;
+
+ /** Highest index in RCB that is valid. */
+ private int rCBPos;
+
+ /** */
+ private Task gather_thread;
+
+ /** */
+ private Manager manager;
+
+
+ public Migration( Manager mgr )
+ {
+ super(true);
+ randomContentBuffer=new ContentIndex[RCB_SIZE];
+ manager=mgr;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the migration module.
+ * @param capi
+ */
+
+ public void init( CoreForProtocol capi )
+ {
+ Statistics stats;
+
+ coreAPI=capi;
+
+ stats=capi.getApplication().getStatistics();
+ status=(StatusCallsService)
capi.service(StatusCallsService.class);
+ stat_handle_content_pushed=stats.getHandle("# kb content pushed
out as padding",Stat.VERBOSE);
+
+ Arrays.fill(randomContentBuffer,null);
+
+ aquireMoreSignal = new Semaphore(RCB_SIZE);
+ doneSignal = null;
+ lock=new Object();
+
+ gather_thread=new Task("MIGRATION-GATHER",new AbstractAction() {
+ public void perform()
+ {
+ rcbAquire();
+ }
+ });
+ gather_thread.launch();
+
+ coreAPI.registerSendCallback(P2PChkResult.SIZE,new
BufferFillCallback() {
+ public boolean fillBuffer( HostIdentity receiver,
ByteBuffer buf )
+ {
+ return activeMigrationCallback(receiver,buf);
+ }
+ });
+ }
+
+ /**
+ *
+ */
+
+ public void done()
+ {
+ coreAPI.unregisterSendCallback(P2PChkResult.SIZE,new
BufferFillCallback() {
+ public boolean fillBuffer( HostIdentity receiver,
ByteBuffer buf )
+ {
+ return activeMigrationCallback(receiver,buf);
+ }
+ });
+
+ try {
+ doneSignal = new Semaphore(0);
+ aquireMoreSignal.release();
+ doneSignal.acquire();
+ aquireMoreSignal=null;
+ doneSignal=null;
+ lock=null;
+ gather_thread.join();
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ }
+ }
+
+ protected void rcbAquire()
+ {
+ ContentIndex ce,cp;
+ boolean ok;
+
+ try {
+ while (true) {
+ aquireMoreSignal.acquire();
+ if (doneSignal != null)
+ break;
+
+ ce=new ContentIndex();
+ ok = manager.retrieveRandomContent(ce);
+ if (ok)
+ if
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH ||
ce.type==ContentIndex.LOOKUP_TYPE_SUPER)
+ ok = false; // can not
migrate these
+ if (ok) {
+ cp=(ContentIndex)
PersistentHelper.copy(ce);
+ synchronized(lock) {
+ randomContentBuffer[rCBPos++] =
cp;
+ }
+ }
+ else {
+ int load = status.getCPULoad();
+ if (load < 10)
+ load = 10;
+
Scheduler.sleep(Scheduler.seconds(load/5)); // the higher the load, the
longer the sleep, but at least 2 seconds
+ aquireMoreSignal.release(); // send
myself signal to go again !
+ }
+ }
+ doneSignal.release();
+ }
+ catch( InterruptedException x ) {
+ err("",x);
+ }
+ }
+
+ /**
+ * Select content for active migration. Takes the best match from the
+ * randomContentBuffer (if the RCB is non-empty) and returns it.
+ * @param receiver
+ * @param ce
+ *
+ * @return false if the RCB is empty
+ */
+
+ protected boolean selectMigrationContent( HostIdentity receiver,
ContentIndex ce )
+ {
+ int dist;
+ int minDist;
+ int minIdx;
+ int i;
+
+ minIdx = -1;
+ minDist = -1; // max
+
+ synchronized(lock) {
+ for (i=0;i<rCBPos;i++) {
+ dist =
receiver.distance(randomContentBuffer[i].hash);
+ if (dist < minDist) {
+ minIdx = i;
+ minDist = dist;
+ }
+ }
+ if (minIdx == -1) {
+ return false;
+ }
+ ce=(ContentIndex)
PersistentHelper.copy(randomContentBuffer[minIdx]);
+ randomContentBuffer[minIdx] =
randomContentBuffer[--rCBPos];
+ randomContentBuffer[rCBPos] = null;
+ }
+ aquireMoreSignal.release();
+ return true;
+ }
+
+ /**
+ * Callback method for pushing content into the network.
+ * The method chooses either a "recently" deleted block
+ * or content that has a hash close to the receiver ID
+ * (randomized to guarantee diversity, unpredictability
+ * etc.).<p>
+ *
+ * @param receiver the receiver of the message
+ * @param buf
+ * @return the number of bytes written to
+ * that buffer (must be a positive number).
+ */
+
+ protected boolean activeMigrationCallback( HostIdentity receiver,
ByteBuffer buf )
+ {
+ ContentIndex ce;
+
+ ce=new ContentIndex();
+ while (buf.remaining()>P2PChkResult.SIZE) {
+ if (!selectMigrationContent(receiver,ce)) {
+ return true; // nothing selected, that's the
end
+ }
+
+ // append it !
+ if (!buildCHKReply(ce,buf)) {
+ return false; // abort early after any error
+ }
+
+ stat_handle_content_pushed.inc();
+ }
+ return true;
+ }
+
+ /**
+ * Build a CHK reply message for some content
+ * selected for migration.
+ * @param ce
+ * @param buf
+ * @return OK on success, false on error
+ */
+
+ protected boolean buildCHKReply( ContentIndex ce, ByteBuffer buf )
+ {
+ ContentBlock[] data;
+
+ if (ce.type==ContentIndex.LOOKUP_TYPE_3HASH ||
ce.type==ContentIndex.LOOKUP_TYPE_SUPER) {
+ return false;
+ }
+
+ data=manager.retrieveContent(ce.hash,ce,0,false); // low
prio! & should not matter for CHK anyway
+ if (data==null) { // can happen if we're concurrently
inserting, _should be_ rare but is OK !
+ return false;
+ }
+ if (data.length != ContentBlock.SIZE) {
+ log(Level.WARNING,"BuildCHKReply got unsuitable block
from db (len="+data.length+",type="+ce.type+")");
+ return false;
+ }
+ return PersistentHelper.write(data[0],buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/MySQLHandle.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/MySQLHandle.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/MySQLHandle.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,1030 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.sql.*;
+import java.util.logging.*;
+
+/**
+ * Handle for a high-level database (mysql, simple)
+ * mysql wrapper
+ *
+ * API for the "high-level" database libraries.
+ * Equivalent to what is specified in high_backend.h.
+ * A header specifying the interfaces that each (high-level) database backend
(gdbm, tdb, mysql, etc.) must provide.
+ * Database: MySQL
+ *
+ * HIGHLIGHTS
+ *
+ * Pros
+ * + On up-to-date hardware where mysql can be used comfortably, this
+ * module will have better performance than the other db choices
+ * (according to our tests).
+ * + Its often possible to recover the mysql database from internal
+ * inconsistencies. The other db choices do not support repair
+ * (gnunet-check cannot fix problems internal to the dbmgr!).
+ * For example, we have seen several cases where power failure
+ * has ruined a gdbm database beyond repair.
+ * Cons
+ * - Memory usage (Comment: "I have 1G and it never caused me trouble")
+ * - Manual setup
+ *
+ * MANUAL SETUP INSTRUCTIONS
+ *
+ * 1) in /etc/gnunet.conf, set
+ * <pre>
+ *
+ * DATABASETYPE = "mysql"
+ *
+ * </pre>
+ * 2) Then access mysql as root,
+ * <pre>
+ *
+ * # mysql -u root -p
+ *
+ * </pre>
+ * and do the following. [You should replace $USER with the username
+ * that will be running the gnunetd process].
+ * <pre>
+ *
+ CREATE DATABASE gnunet;
+ GRANT select,insert,update,delete,create,alter,drop
+ ON gnunet.* TO address@hidden;
+ SET PASSWORD FOR address@hidden('$the_password_you_like');
+ FLUSH PRIVILEGES;
+ *
+ * </pre>
+ * 3) In the $HOME directory of $USER, create a ".my.cnf" file
+ * with the following lines
+ * <pre>
+
+ [client]
+ user=$USER
+ password=$the_password_you_like
+
+ * </pre>
+ *
+ * Thats it. Note that .my.cnf file is a security risk unless its on
+ * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
+ * link. Even greater security risk can be achieved by setting no
+ * password for $USER. Luckily $USER has only priviledges to mess
+ * up GNUnet's tables, nothing else (unless you give him more,
+ * of course).<p>
+ *
+ * 4) Still, perhaps you should briefly try if the DB connection
+ * works. First, login as $USER. Then use,
+ *
+ * <pre>
+ * # mysql -u $USER
+ * mysql> use gnunet
+ * </pre>
+ *
+ * If you get the message "Database changed" it probably works.
+ *
+ * [If you get "ERROR 2002: Can't connect to local MySQL server
+ * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
+ * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
+ * so there may be some additional trouble depending on your mysql setup.]
+ *
+ * REPAIRING TABLES
+ *
+ * - Its probably healthy to check your tables for inconsistencies
+ * every now and then.
+ * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
+ * databases have been corrupted.
+ * - The tables can be verified/fixed in two ways;
+ * 1) by shutting down mysqld (mandatory!) and running
+ * # myisamchk -r *.MYI
+ * in /var/lib/mysql/gnunet/ (or wherever the tables are stored).
+ * Another repair command is "mysqlcheck". The usable command
+ * may depend on your mysql build/version. Or,
+ * 2) by executing
+ * mysql> REPAIR TABLE data1024of
+ * for each table in the gnunet database (USE gnunet; SHOW TABLES;)
+ *
+ * If you have problems related to the mysql module, your best
+ * friend is probably the mysql manual. The first thing to check
+ * is that mysql is basically operational, that you can connect
+ * to it, create tables, issue queries etc.
+ *
+ * Database: MySQL
+ *
+ * NOTE: This db module does NOT work with mysql v3.23.49 due to a bug
+ * in mysql. All later versions should be fine, including the 4.0.x
+ * series. Current devel version is 4.0.16-log on debian/unstable.
+ *
+ * HIGHLIGHTS
+ *
+ * Pros
+ * + On up-to-date hardware where mysql can be used comfortably, this
+ * module will have better performance than the other db choices
+ * (according to our tests).
+ * + Its often possible to recover the mysql database from internal
+ * inconsistencies. The other db choices do not support repair
+ * (gnunet-check cannot fix problems internal to the dbmgr!).
+ * For example, we have seen several cases where power failure
+ * has ruined a gdbm database beyond repair.
+ * Cons
+ * - Memory usage (Comment: "I have 1G and it never caused me trouble")
+ * - Manual setup
+ *
+ * MANUAL SETUP INSTRUCTIONS
+ *
+ * 1) in /etc/gnunet.conf, set
+ * <pre>
+ *
+ * DATABASETYPE = "mysql"
+ *
+ * </pre>
+ * 2) Then access mysql as root,
+ * <pre>
+ *
+ * # mysql -u root -p
+ *
+ * </pre>
+ * and do the following. [You should replace $USER with the username
+ * that will be running the gnunetd process].
+ * <pre>
+ *
+ CREATE DATABASE gnunet;
+ GRANT select,insert,update,delete,create,alter,drop
+ ON gnunet.* TO address@hidden;
+ SET PASSWORD FOR address@hidden('$the_password_you_like');
+ FLUSH PRIVILEGES;
+ *
+ * </pre>
+ * 3) In the $HOME directory of $USER, create a ".my.cnf" file
+ * with the following lines
+ * <pre>
+
+ [client]
+ user=$USER
+ password=$the_password_you_like
+
+ * </pre>
+ *
+ * Thats it. Note that .my.cnf file is a security risk unless its on
+ * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
+ * link. Even greater security risk can be achieved by setting no
+ * password for $USER. Luckily $USER has only priviledges to mess
+ * up GNUnet's tables, nothing else (unless you give him more,
+ * of course).<p>
+ *
+ * 4) Still, perhaps you should briefly try if the DB connection
+ * works. First, login as $USER. Then use,
+ *
+ * <pre>
+ * # mysql -u $USER
+ * mysql> use gnunet
+ * </pre>
+ *
+ * If you get the message "Database changed" it probably works.
+ *
+ * [If you get "ERROR 2002: Can't connect to local MySQL server
+ * through socket '/tmp/mysql.sock' (2)" it may be resolvable by
+ * "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
+ * so there may be some additional trouble depending on your mysql setup.]
+ *
+ * REPAIRING TABLES
+ *
+ * - Its probably healthy to check your tables for inconsistencies
+ * every now and then.
+ * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
+ * databases have been corrupted.
+ * - The tables can be verified/fixed in two ways;
+ * 1) by shutting down mysqld (mandatory!) and running
+ * # myisamchk -r *.MYI
+ * in /var/lib/mysql/gnunet/ (or wherever the tables are stored).
+ * Another repair command is "mysqlcheck". The usable command
+ * may depend on your mysql build/version. Or,
+ * 2) by executing
+ * mysql> REPAIR TABLE data1024of
+ * for each table in the gnunet database (USE gnunet; SHOW TABLES;)
+ *
+ * EFFICIENCY ISSUES
+ *
+ * If you suffer from too slow index/insert speeds,
+ * you might try to define /etc/gnunet.conf option
+ *
+ * [AFS]
+ * MYSQL_DELAYED = YES
+ *
+ * for small efficiency boost. The option will let MySQL bundle multiple
+ * inserts before actually writing them to disk. You shouldn't use this
+ * option unless you're an (my)sql expert and really know what you're doing.
+ * Especially, if you run into any trouble due to this, you're on your own.
+ *
+ * PROBLEMS?
+ *
+ * If you have problems related to the mysql module, your best
+ * friend is probably the mysql manual. The first thing to check
+ * is that mysql is basically operational, that you can connect
+ * to it, create tables, issue queries etc.
+ */
+
+public class MySQLHandle extends LoggedObject implements DBHandle
+{
+ private static long ramdomSpentTime;
+ private static int randomCalls;
+
+ /** */
+ private Connection dbf;
+
+ private String tableName;
+ /** database index */
+// private int;
+
+ /** total number of databases */
+// private int;
+
+ /** */
+ private Object DATABASE_Lock_;
+
+ /** which column contains the Avg_row_length in SHOW TABLE STATUS
resultset */
+ private int avgLength_ID;
+
+ /** use potentially unsafe delayed inserts? */
+ private boolean useDelayed;
+
+
+ /**
+ * Popularity of _known_ 3hash keywords can be tracked by creating
+ * the following table and enabling TRACK_3HASH_QUERIES. In addition,
+ * you'll have to fill the table with <name,hash> pairs yourself
+ * (not provided). Tracking is not generally recommended as
+ * it may harm your deniability.
+ *
+ USE gnunet;
+ CREATE TABLE `dictionary` (
+ `name` tinyblob NOT NULL,
+ `hash` varchar(40) binary NOT NULL default '',
+ `hits` smallint(5) unsigned NOT NULL default '0',
+ PRIMARY KEY (`hash`),
+ UNIQUE KEY `unique_name` (`name`(32))
+ ) TYPE=MyISAM
+ *
+ */
+
+ public static final boolean TRACK_3HASH_QUERIES = false;
+
+
+ public MySQLHandle()
+ {
+ super(true);
+ avgLength_ID=-1;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public boolean open( Prefs prefs, String backend, String name )
+ {
+ Connection conn;
+ Statement stmt;
+ ResultSet rset;
+ ResultSetMetaData meta;
+ StringBuffer sql;
+ boolean found;
+ int i;
+
+ tableName="data"+name+"of"+backend;
+
+ try {
+
Class.forName(prefs.getString("AFS","MYSQL_DRIVER",null));
+ conn=DriverManager.getConnection(
+
"jdbc:mysql://"+prefs.getString("AFS","MYSQL_HOST",null)+"/gnunet",
+ prefs.getString("AFS","MYSQL_USER",null),
+ prefs.getString("AFS","MYSQL_PASSWORD",null)
+ );
+ conn.setAutoCommit(false);
+ }
+ catch( SQLException x ) {
+ err("Unable to init MySQL !",x);
+ return false;
+ }
+ catch( ClassNotFoundException x ) {
+ err("Unable to init MySQL !",x);
+ return false;
+ }
+
+ dbf=conn;
+
+ useDelayed=prefs.testString("AFS","MYSQL_DELAYED","YES");
+ DATABASE_Lock_=new Object();
+
+ found=false;
+ try {
+ stmt=dbf.createStatement();
+ try {
+ sql=new StringBuffer();
+ sql.append("CREATE TABLE IF NOT EXISTS
"+tableName+" (");
+ sql.append(" hash tinyblob NOT NULL
default'',");
+ sql.append(" priority int(11) NOT NULL default
0,");
+ sql.append(" type tinyint NOT NULL default
0,");
+ sql.append(" fileIndex smallint NOT NULL
default 0,");
+ sql.append(" fileOffset int(11) NOT NULL
default 0,");
+ sql.append(" doubleHash tinyblob NOT NULL
default '',");
+ sql.append(" content mediumblob NOT NULL
default '',");
+ sql.append(" PRIMARY KEY (hash(20)),");
+ sql.append(" KEY priority (priority)");
+ sql.append(") TYPE=MyISAM");
+ stmt.executeUpdate(sql.toString());
+
+ // find out which column contains the avg row
length field and assume
+ // that mysqld always gives it in the same
order across calls :)
+ sql.setLength(0);
+ sql.append("SHOW TABLE STATUS ");
+ sql.append("FROM gnunet ");
+ sql.append("LIKE '"+tableName+"'");
+
+ rset=stmt.executeQuery(sql.toString());
+ if (rset.next()) {
+ meta=rset.getMetaData();
+
+ for (i=0; i<meta.getColumnCount() &&
!found; i++) {
+ if
(meta.getColumnName(i+1).equals("Avg_row_length")) {
+ avgLength_ID=i;
+ found=true;
+ }
+ }
+ if (!found) {
+ log(Level.SEVERE,"ERROR: mysql
Avg_row_length not found in SHOW TABLE STATUS");
+ return false;
+ }
+ }
+ rset.close();
+
+ if (avgLength_ID == -1) {
+ log(Level.SEVERE,"FATAL: could not find
row avg_row_length");
+ return false;
+ }
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to init !",x);
+ return false;
+ }
+ return true;
+ }
+
+ public void close()
+ {
+ DATABASE_Lock_=null;
+ try {
+ dbf.close();
+ dbf=null;
+ }
+ catch( SQLException x ) {
+ err("Failed to close connection !",x);
+ }
+ }
+
+ public void drop()
+ {
+ Statement stmt;
+ StringBuffer sql;
+
+ try {
+ sql=new StringBuffer();
+ sql.append("DROP TABLE "+tableName);
+
+ stmt=dbf.createStatement();
+ try {
+ stmt.executeUpdate(sql.toString());
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to close connection !",x);
+ }
+
+ close();
+ }
+
+ public byte[] readContent( HashCode512 query, ContentIndex ce, int prio
)
+ {
+ byte[] tmp,result;
+ ResultSet rset;
+ Statement stmt;
+ StringBuffer sql;
+ int _prio=-1;
+
+ result=null;
+ synchronized(DATABASE_Lock_) {
+ sql=new StringBuffer();
+ sql.append("SELECT content, type, priority, doubleHash,
fileOffset, fileIndex ");
+ sql.append("FROM "+tableName+" ");
+ sql.append("WHERE hash='"+escape(query)+"'");
+
+ try {
+
stmt=dbf.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);
+ try {
+ rset=stmt.executeQuery(sql.toString());
+
+ if (rset.next()) {
+ result=rset.getBytes(1);
+
+ ce.type = rset.getInt(2);
+ ce.importance = rset.getInt(3);
+ if
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH) {
+ tmp=rset.getBytes(4);
+ if (tmp!=null &&
tmp.length==HashCode512.SIZE) {
+
ce.hash=(HashCode512)
PersistentHelper.readFully(HashCode512.class,ByteBuffer.wrap(tmp));
+ }
+ }
+ else {
+ ce.hash=(HashCode512)
PersistentHelper.copy(query);
+ }
+ ce.fileOffset=rset.getInt(5);
+ ce.fileNameIndex=rset.getInt(6);
+
+ if (prio != 0) {
+ //todo: debugguer le
UPDATE qui devrait se faire ICI
+//
rset.updateInt(3,rset.getInt(3)+prio);
+// rset.updateRow();
+
+
_prio=rset.getInt(3)+prio;
+ }
+ }
+
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+
+ if (TRACK_3HASH_QUERIES) {
+ if
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH) {
+ stmt=dbf.createStatement();
+ try {
+
stmt.executeUpdate("UPDATE dictionary SET hits=hits+1 WHERE
hash='"+escape(query)+"'");
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ }
+
+ //tempo (a supprimer apres debug)
+ if (_prio!=0) {
+ sql=new StringBuffer();
+ sql.append("UPDATE "+tableName+" ");
+ sql.append("SET priority="+_prio+" ");
+ sql.append("WHERE
hash='"+escape(query)+"'");
+
+ stmt=dbf.createStatement();
+ try {
+
stmt.executeUpdate(sql.toString());
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to read content !",x);
+ result=null;
+ }
+ }
+ return result;
+ }
+
+ public boolean writeContent( ContentIndex ce, byte[] block, int offset,
int length )
+ {
+ Statement stmt;
+ HashCode512 tripleHash;
+ StringBuffer sql;
+ String doubleHash,escapedBlock,escapedHash;
+
+ synchronized(DATABASE_Lock_) {
+ if (ce.type== ContentIndex.LOOKUP_TYPE_3HASH) {
+
tripleHash=HashCode512.create(PersistentHelper.toBytes(ce.hash));
+ escapedHash=escape(tripleHash);
+ doubleHash = escape(ce.hash);
+ }
+ else {
+ doubleHash = null;
+ escapedHash=escape(ce.hash);
+ }
+
+ escapedBlock=escape(block,offset,length);
+
+ sql=new StringBuffer();
+ sql.append("REPLACE "+(useDelayed ? "DELAYED" : "")+"
INTO "+tableName+" ");
+
sql.append("(content,hash,priority,fileOffset,fileIndex,doubleHash,type)");
+ sql.append(" VALUES (");
+ sql.append("'"+(length > 0 ? escapedBlock : "")+"',");
+ sql.append("'"+escapedHash+"',");
+ sql.append("'"+ce.importance+"',");
+ sql.append("'"+ce.fileOffset+"',");
+ sql.append("'"+ce.fileNameIndex+"',");
+ sql.append("'"+(doubleHash!=null ? doubleHash :
"")+"',");
+ sql.append(ce.type);
+ sql.append(")");
+
+ try {
+ stmt=dbf.createStatement();
+ try {
+ stmt.executeUpdate(sql.toString());
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to write content !",x);
+
+ //todo: a supprimer apres debug
+
log(Level.SEVERE,Utils.toString(block,offset,offset+length,"SEQ."));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the number of entries in the database.
+ * Get the number of entries in the database.
+ *
+ * @return -1 on error, otherwise the number of entries
+ */
+
+ public int countContentEntries()
+ {
+ Statement stmt;
+ ResultSet rset;
+ StringBuffer sql;
+ int count;
+
+ count=0;
+ synchronized(DATABASE_Lock_) {
+ sql=new StringBuffer();
+ sql.append("SELECT count(*) ");
+ sql.append("FROM "+tableName);
+
+ try {
+ stmt=dbf.createStatement();
+ try {
+ rset=stmt.executeQuery(sql.toString());
+ if (rset.next()) {
+ count=rset.getInt(1);
+ }
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to get minimum priority !",x);
+ count=-1;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the lowest priority of content in the DB.
+ * Get the lowest priority of content in the store.
+ * Get the lowest priority value of all content in the store.
+ *
+ * @return the lowest priority
+ */
+
+ public int getMinimumPriority()
+ {
+ Statement stmt;
+ ResultSet rset;
+ StringBuffer sql;
+ int minPrio;
+
+ minPrio=0;
+ synchronized(DATABASE_Lock_) {
+ sql=new StringBuffer();
+ sql.append("SELECT MIN(priority) ");
+ sql.append("FROM "+tableName);
+
+ try {
+ stmt=dbf.createStatement();
+ try {
+ rset=stmt.executeQuery(sql.toString());
+ if (rset.next()) {
+ minPrio=rset.getInt(1);
+ }
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to get minimum priority !",x);
+ }
+ }
+ return minPrio;
+ }
+
+ /**
+ * Call a method for each key in the database and
+ * call the callback method on it.
+ * Call a method for each key in the database and
+ * call the callback method on it.
+ *
+ * @param callback the callback method
+ * @param data second argument to all callback calls
+ * @return the number of items stored in the content database
+ */
+
+ public int forEachEntry( EntryCallback callback, Object data )
+ {
+ byte[] result,tmp;
+ Statement stmt;
+ ResultSet rset;
+ ContentIndex ce;
+ HashCode512 h;
+ StringBuffer sql;
+ int count;
+
+ count=0;
+ synchronized(DATABASE_Lock_) {
+ sql=new StringBuffer();
+ sql.append("SELECT content, type, priority, doubleHash,
fileOffset, fileIndex, hash ");
+ sql.append("FROM "+tableName);
+
+ try {
+ stmt=dbf.createStatement();
+ try {
+ rset=stmt.executeQuery(sql.toString());
+ while (rset.next()) {
+ ce=new ContentIndex();
+
+ result=rset.getBytes(1);
+ ce.type=rset.getInt(2);
+ ce.importance=rset.getInt(3);
+ if
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH) {
+ tmp=rset.getBytes(4);
+ if (tmp!=null &&
tmp.length==HashCode512.SIZE) {
+
ce.hash=(HashCode512)
PersistentHelper.readFully(HashCode512.class,ByteBuffer.wrap(tmp));
+ }
+ }
+ else {
+ tmp=rset.getBytes(7);
+ ce.hash=(HashCode512)
PersistentHelper.readFully(HashCode512.class,ByteBuffer.wrap(tmp));
+ }
+ ce.fileOffset=rset.getInt(5);
+ ce.fileNameIndex=rset.getInt(6);
+
+ tmp=rset.getBytes(7);
+ h=(HashCode512)
PersistentHelper.readFully(HashCode512.class,ByteBuffer.wrap(tmp));
+
+
callback.onEntry(h,ce,result,(result!=null ? result.length : 0),data);
+ count++;
+ }
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to iterate over entries !",x);
+ count=-1;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Return a random key from the database (just the key, not the
+ * content!).
+ * Get a random content block from MySQL database.
+ * Tries to use indexes efficiently.
+ *
+ * Code supplied by H. Pagenhardt
+ *
+ * @param ce the meta-data of the random content (set)/output
information about the key
+ * @return true on success, false on error
+ */
+
+ public boolean getRandomContent( ContentIndex ce )
+ {
+ byte[] tmp;
+ Statement stmt;
+ ResultSet rset;
+ HashCode512 hash;
+ StringBuffer sql;
+ long startTime,endTime;
+ boolean found;
+
+ startTime=Scheduler.now();
+
+ synchronized(DATABASE_Lock_) {
+ found=false;
+
+ hash=HashCode512.random();
+
+ sql=new StringBuffer();
+ sql.append("SELECT hash, type, priority, fileOffset,
fileIndex ");
+ sql.append("FROM "+tableName+" ");
+ sql.append("WHERE hash >= '"+escape(hash)+"' ");
+ sql.append("AND (type =
"+ContentIndex.LOOKUP_TYPE_CHK+" OR type = "+ContentIndex.LOOKUP_TYPE_CHKS+")
");
+ sql.append("LIMIT 1");
+
+ try {
+ stmt=dbf.createStatement();
+ try {
+ rset=stmt.executeQuery(sql.toString());
+
+ found=rset.next();
+ if (!found) {
+ rset.close();
+
+ sql.setLength(0);
+ sql.append("SELECT
hash,type,priority,fileOffset,fileIndex ");
+ sql.append("FROM "+tableName+"
");
+ sql.append("WHERE hash >= '' ");
+ sql.append("AND (type =
"+ContentIndex.LOOKUP_TYPE_CHK+" OR type = "+ContentIndex.LOOKUP_TYPE_CHKS+")
");
+ sql.append("LIMIT 1");
+
+
rset=stmt.executeQuery(sql.toString());
+ found=rset.next();
+ }
+
+ if (found) {
+ tmp=rset.getBytes(1);
+ ce.hash=(HashCode512)
PersistentHelper.readFully(HashCode512.class,ByteBuffer.wrap(tmp));
+ ce.type=rset.getInt(2);
+ ce.importance=rset.getInt(3);
+ ce.fileOffset=rset.getInt(4);
+ ce.fileNameIndex=rset.getInt(5);
+ }
+
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to get random content !",x);
+ found=false;
+ }
+ }
+
+ endTime=Scheduler.now();
+ randomCalls++;
+ ramdomSpentTime+=(endTime-startTime);
+ debug("getRandomContent spent total
"+Scheduler.toMillis(ramdomSpentTime)+"ms / "+randomCalls+" calls.");
+
+ if (!found) {
+ log(Level.FINEST,"MySQL random didn't find anything !");
+ }
+ return found;
+ }
+
+ /**
+ * Delete low-priority content from the database
+ * Deletes some least important content
+ *
+ * @param count the number of entries to delete/the number of 1kb
blocks to free
+ * @param callback method to call on each deleted entry/method to call
on each deleted item
+ * @param closure extra argument to callback
+ * @return true on success, false on error
+ */
+
+ public boolean deleteContent( int count, EntryCallback callback, Object
closure )
+ {
+ HashCode512[] deleteThese;
+ byte[] data,tmp;
+ ContentIndex ce;
+ Statement stmt;
+ ResultSet rset;
+ StringBuffer sql;
+ int k;
+
+ synchronized(DATABASE_Lock_) {
+ try {
+ stmt=dbf.createStatement();
+ try {
+ // collect hashes to delete
+ sql=new StringBuffer();
+ sql.append("SELECT hash FROM
"+tableName+" ");
+ sql.append("ORDER BY priority ASC LIMIT
"+count);
+
+ deleteThese=new HashCode512[count];
+
+ k=0;
+ rset=stmt.executeQuery(sql.toString());
+ while (rset.next()) {
+ tmp=rset.getBytes(1);
+ deleteThese[k++]=(HashCode512)
PersistentHelper.readFully(HashCode512.class,ByteBuffer.wrap(tmp));
+ }
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+
+ // delete collected hashes
+ count=k;
+ for(k=0; k<count; k++) {
+ ce=new ContentIndex();
+
+ data=readContent(deleteThese[k],ce,0);
+ if (data!=null && callback!=null) {
+
callback.onEntry(deleteThese[k],ce,data,data.length,closure);
+ }
+
+ sql.setLength(0);
+ sql.append("DELETE FROM "+tableName+"
");
+ sql.append("WHERE
hash='"+escape(deleteThese[k])+"'");
+
+ stmt=dbf.createStatement();
+ try {
+
stmt.executeUpdate(sql.toString());
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to delete content !",x);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Estimate how many blocks can be stored in the DB
+ * before the quota is reached.
+ * Estimate how many blocks can be stored in the DB
+ * before the quota is reached.
+ *
+ * NOTE: this function can not be performed relying on
+ * Data_length+Index_length from "SHOW TABLE STATUS" because
+ * those values seem not to be decreasing in real time.
+ * On mysql 4.0.16, Avg_row_len seems to be updating in real
+ * time w.r.t. insertions and deletions.
+ *
+ * @param quota the number of kb available for the DB
+ * @return number of blocks left
+ */
+
+ public int estimateAvailableBlocks( int quota )
+ {
+ Statement stmt;
+ ResultSet rset;
+ StringBuffer sql;
+ long avgRowLen,rowsInTable;
+ int kbUsed;
+
+ avgRowLen=-1;
+ rowsInTable=0;
+ synchronized(DATABASE_Lock_) {
+ try {
+ stmt=dbf.createStatement();
+ try {
+ // find out average row length in bytes
+ // FIXME: probably unnecessary to check
avg row length every time
+ sql=new StringBuffer();
+ sql.append("SHOW TABLE STATUS ");
+ sql.append("FROM gnunet ");
+ sql.append("LIKE '"+tableName+"'");
+
+ rset=stmt.executeQuery(sql.toString());
+ if (!rset.next()) {
+ log(Level.SEVERE,"Unable to
estimate available blocks (query \""+sql+"\" had no results).");
+ return 0;
+ }
+
+ if
(avgLength_ID>=rset.getMetaData().getColumnCount() || avgLength_ID<0) {
+ log(Level.SEVERE,"Unable to
estimate available blocks (query \""+sql+"\" had
"+rset.getMetaData().getColumnCount()+" rows, expected > "+avgLength_ID+").");
+ return 0;
+ }
+
+ avgRowLen=rset.getLong(avgLength_ID+1);
+ if (rset.wasNull()) {
+ avgRowLen=-1;
+ }
+
debug("******************************************* avgRowLen = "+avgRowLen);
+ rset.close();
+
+ if (avgRowLen<0) {
+ log(Level.SEVERE,"FATAL: mysql
claimed avgRowLen<0 (or failed)");
+ return 0;
+ }
+
+ // find number of entries (rows)
+ sql.setLength(0);
+ sql.append("SELECT count(*) ");
+ sql.append("FROM "+tableName);
+
+ rset=stmt.executeQuery(sql.toString());
+
+ //todo: if (!rset.next()) {}
+ if (rset.next()) {
+ rowsInTable=rset.getLong(1);
+ }
+ rset.close();
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to estimate available blocks !",x);
+ return 0;
+ }
+ }
+
+ kbUsed=(int) ((rowsInTable * avgRowLen) / 1024);
+ debug("Estimate content available
(q="+quota+",u="+kbUsed+",rem="+(quota-kbUsed)+")");
+ return quota - kbUsed;
+ }
+
+ /**
+ * Free space in the database by removing an entry.
+ *
+ * @param name the key of the entry to remove/the query of the entry to
remove/hashcode for the block to be deleted
+ * @return false on error, true if ok.
+ * Free space in the database by removing one block
+ */
+
+ public boolean unlink( HashCode512 name )
+ {
+ Statement stmt;
+ StringBuffer sql;
+
+ synchronized(DATABASE_Lock_) {
+ sql=new StringBuffer();
+ sql.append("DELETE FROM "+tableName+" ");
+ sql.append("WHERE hash='"+escape(name)+"'");
+
+ try {
+ stmt=dbf.createStatement();
+ try {
+ stmt.executeUpdate(sql.toString());
+ }
+ finally {
+ stmt.close();
+ }
+ }
+ catch( SQLException x ) {
+ err("Failed to unlink "+name.toHex()+" !",x);
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static String escape( HashCode512 h )
+ {
+ return escape(PersistentHelper.toBytes(h));
+ }
+
+ public static String escape( byte[] b )
+ {
+ return escape(b,0,b.length);
+ }
+
+ public static String escape( byte[] b, int offset, int length )
+ {
+ StringBuffer buf;
+ int i;
+ char c;
+
+ buf=new StringBuffer();
+ for (i=0; i<length; i++) {
+ c=(char) (b[offset+i] & 0x000000ff);
+ switch (c) {
+ case '\'': buf.append("''"); break;
+ case '\\': buf.append("\\\\"); break;
+ default: buf.append(c); break;
+ }
+ }
+ return buf.toString();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/NativeFSProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/NativeFSProtocol.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/NativeFSProtocol.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,68 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.AbstractProtocol;
+import org.gnu.freeway.protocol.Protocol;
+import org.gnu.freeway.server.CPluginLoader;
+import org.gnu.freeway.server.CoreAPI;
+import org.gnu.freeway.server.CoreForProtocol;
+
+/**
+ * @file NativeFSProtocol.java
+ * @brief
+ * @author mdonoughe
+ */
+public class NativeFSProtocol extends AbstractProtocol implements Protocol {
+ private CPluginLoader loader;
+
+ public NativeFSProtocol() {
+ super();
+
+ loader = CPluginLoader._;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.gnu.freeway.protocol.Protocol#init(org.gnu.freeway.server.CoreForProtocol)
+ */
+ public boolean init(CoreForProtocol api) {
+ boolean ok = super.init(api);
+ CoreAPI._.setProtocolCore(api);
+ ok &= CoreAPI.OK.getValue() ==
loader.loadApplicationModule(CPluginLoader.classToLibraryName(getName()));
+ return ok;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gnu.freeway.protocol.Protocol#done()
+ */
+ public void done() {
+
loader.unloadApplicationModule(CPluginLoader.classToLibraryName(getName()));
+ super.done();
+ }
+
+ public String toString() {
+ String ret = this.getClass().getSimpleName();
+ if(ret.startsWith("Native"))
+ ret = ret.substring(6);
+ if(ret.endsWith("Protocol"))
+ ret = ret.substring(0, ret.length() - 8);
+ return ret.toLowerCase();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/Policy2.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/Policy2.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/Policy2.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,117 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Policy interface. This is the interface to the C part of the policy.
+ * resource allocation (storage space, routing) implementation
+ */
+
+public class Policy2 extends Object
+{
+ /** Drop the query if & with this bitmask is 0 */
+ public static final int QUERY_DROPMASK = 0x00FF0000;
+
+ /** Send answer if local files match */
+ public static final int QUERY_ANSWER = 0x00020000;
+
+ /** Forward the query, priority is encoded in QUERY_PRIORITY_BITMASK */
+ public static final int QUERY_FORWARD = 0x00040000;
+
+ /** Indirect the query (use this as the originating node) */
+ public static final int QUERY_INDIRECT = 0x00080000;
+
+ /** Maximum priority to use (apply this bitmask to the QueryPolicy) */
+ public static final int QUERY_PRIORITY_BITMASK = 0x0000FFFF;
+
+ /** Bandwidth value of an (effectively) 0-priority query. */
+ public static final double QUERY_BANDWIDTH_VALUE = 0.01;
+
+ /** Bandwidth value of a 0-priority content (must be fairly high
compared to query since content is
+ typically significantly larger -- and more valueable since it can take
many queries to get one piece of content). */
+ public static final double CONTENT_BANDWIDTH_VALUE = 0.8;
+
+ private CoreForProtocol coreAPI;
+
+
+ public Policy2( CoreForProtocol capi )
+ {
+ super();
+ coreAPI=capi;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * A query has been received. The question is, if it should be forwarded
+ * and if with which priority. Routing decisions(to whom) are to be
taken elsewhere.
+ * <p>
+ *
+ * @param sender the host sending us the query
+ * @param priority the priority the query had when it came in, may be
an arbitrary number if the
+ * sender is malicious! Cap by trustlevel first!
+ * @return binary encoding: QUERY_XXXX constants
+ */
+
+ public int evaluateQuery( HostIdentity sender, int priority )
+ {
+ int netLoad = ((StatusCallsService)
coreAPI.service(StatusCallsService.class)).getNetworkLoadUp();
+
+ if (netLoad < 50)
+ return 0 /* minimum priority */ |
+ QUERY_ANSWER | QUERY_FORWARD | QUERY_INDIRECT;
+ /* charge! */
+ priority = - coreAPI.changeTrust(sender, -priority);
+ if (netLoad < 100 + priority)
+ return ((priority) & QUERY_DROPMASK) |
+ QUERY_ANSWER | QUERY_FORWARD | QUERY_INDIRECT;
+ else if (netLoad < 100 + 10 * priority)
+ return ((priority) & QUERY_DROPMASK) |
+ QUERY_ANSWER | QUERY_FORWARD;
+ else if (netLoad < 100)
+ return ((priority) & QUERY_DROPMASK) | QUERY_ANSWER;
+ else
+ return 0; /* drop entirely */
+ }
+
+ /**
+ * Some content dropped by. We may want to store it locally, or not.
+ * The policy adjusts the priority and returns the effective
+ * importance for the content.
+ *
+ * @param hc the query
+ * @param priority of the original query
+ * @return -1 if the content should be dropped, the
+ * priority for keeping it otherwise/-1 if the content should not be
replicated,
+ * otherwise the new priority for the lookup database
+ */
+
+ public int evaluateContent( HashCode512 hc, int priority )
+ {
+ int distance;
+ int j;
+
+ distance = coreAPI.getIdentity().distance(hc);
+ /* compute 'log' of distance */
+ j = 16;
+ while (distance > 0) {
+ distance = distance>>1;
+ j--;
+ }
+ if (j < 0)
+ return -1;
+ return priority * j;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/QueryManager.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/QueryManager.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/QueryManager.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,694 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * The query manager is responsible for queueing queries
+ * forwarding of queries
+ *
+ * The query manager is responsible for queueing queries. Queued
+ * queries are used to fill buffers (instead of using noise). The QM
+ * is also responsible for selecting the initial set of nodes that
+ * will receive the query. For a good choice, it keeps track of which
+ * nodes were recently hot in answering queries. Some randomness is
+ * preserved to ensure that we potentially find a better path.<p>
+ *
+ * Routing is an incredibly hard problem, so please consider
+ * consulting with other gnunet-developers before making any
+ * significant changes here, even if you have CVS write access.
+ */
+
+public class QueryManager extends LoggedObject implements AFSConstants
+{
+ /** Set to 'YES' to play with 0.6.2b (and earlier) behavior. */
+ private static final boolean TRADITIONAL_SELECTION = false;
+
+ /** of how many outbound queries do we simultaneously keep track ? */
+ public static final int QUERY_RECORD_COUNT = 512;
+
+ public static final double FINAL_GOAL = 3.0;
+
+ /** how many peers should be selected on average? */
+ public static final double SELECT_GOAL = 2.0;
+
+ /** How much is a query worth 'in general' (even if there is no trust
relationship between the peers !).
+ Multiplied by the number of queries in the request. 20 is for '20
bytes / hash', so this is kind of the base unit. */
+ public static final int BASE_QUERY_PRIORITY = 20;
+
+ /** Array of the queries we are currently sending out. */
+ private QueryRecord[] queries;
+ private int zePos;
+
+ /** Mutex for all query manager structures. */
+ private Object queryManagerLock;
+
+ private int Xaverage = 0;
+ private double Xweight = 4.0;
+ private double Yweight = 4.0;
+ private Scheduler scheduler;
+ private ScheduledTask ageTask;
+ private CoreForProtocol coreAPI;
+
+ /** Linked list tracking reply statistics. Synchronize access using
the queryManagerLock! */
+ private ReplyTrackData rtdList;
+
+
+ public QueryManager()
+ {
+ super(true);
+ queries=new QueryRecord[QUERY_RECORD_COUNT];
+ ageTask=new ScheduledTask("AGE-RTD",new
EvalAction(this,"ageRTD"),Scheduler.MINUTES_2);
+ }
+
+ public String toString()
+ {
+ return "Query manager";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize the query management.
+ * @param capi
+ */
+
+ public void init( CoreForProtocol capi )
+ {
+ int i;
+
+ coreAPI=capi;
+ scheduler=capi.getApplication().getScheduler();
+
+ for (i=0;i<QUERY_RECORD_COUNT;i++) {
+ queries[i]=new QueryRecord();
+ queries[i].expires = 0; /* all expired */
+ queries[i].msg = null;
+ }
+ queryManagerLock = coreAPI.getConnectionModuleLock();
+ coreAPI.registerSendCallback(P2PQuery.SIZE+HashCode512.SIZE,new
BufferFillCallback() {
+ public boolean fillBuffer( HostIdentity receiver,
ByteBuffer buf )
+ {
+ return fillInQuery(receiver,buf);
+ }
+ });
+
+ scheduler.addJob(ageTask,Scheduler.MINUTES_2);
+ }
+
+ /**
+ * Shutdown query management.
+ */
+
+ public void done()
+ {
+ int i;
+
+ scheduler.deleteJob(ageTask);
+
+ for (i=0; i<QUERY_RECORD_COUNT; i++) {
+ queries[i]=null;
+ }
+
+
coreAPI.unregisterSendCallback(P2PQuery.SIZE+HashCode512.SIZE,new
BufferFillCallback() {
+ public boolean fillBuffer( HostIdentity receiver,
ByteBuffer buf )
+ {
+ return fillInQuery(receiver,buf);
+ }
+ });
+ }
+
+ /**
+ * Take a query and forward it to the appropriate number of nodes
(depending on load, queue, etc).
+ *
+ * @param msg the query
+ * @param origin where did the query come from?
+ * @param client where did the query come from? (if it was a
client)
+ */
+
+ public void forwardQuery( P2PQuery msg, HostIdentity origin, CSSession
client )
+ {
+ long now;
+ QueryRecord qr;
+ QueryRecord dummy=new QueryRecord();
+ long oldestTime;
+ long expirationTime;
+ int oldestIndex;
+ int i;
+ boolean noclear = false;
+
+ debug("DEBUG: forwarding query for "+msg.getQuery(0).toHex()+"
with ttl "+msg.getTTL());
+
+ now=Scheduler.now();
+
+ synchronized(queryManagerLock) {
+ oldestIndex = -1;
+ expirationTime = now + msg.getTTL();
+ oldestTime = expirationTime;
+
+ for (i=0; i<QUERY_RECORD_COUNT; i++) {
+ if (queries[i].expires<oldestTime) {
+ oldestTime=queries[i].expires;
+ oldestIndex=i;
+ }
+
+ if (queries[i].msg==null) {
+ continue;
+ }
+
+ if ( queries[i].msg.sameQueries(msg)) {
+ /* We have exactly this query pending
already. Replace existing query! */
+
+ oldestIndex = i;
+ if ( (queries[i].expires > now - 4 *
TTL_DECREMENT) && /* not long expired */ (Crypto.nextInt(4) != 0) ) {
+ /* do not clear the bitmap
describing which peers we have
+ forwarded the query to
already; but do this only with high
+ probability since we may want
to try again if the query is
+ retransmitted lots (this can
happen if this is the only
+ query; we may forward it to
all connected peers and get no
+ reply. If the initiator keeps
retrying, we want to
+ eventually forward it again.
+
+ Note that the initial
probability here (0.6.0/0.6.1) was
+ very low (1:64), which is far
too low considering that the
+ clients do an exponential
back-off. The rule is a pure
+ optimization, and as such the
probability that we
+ eventually forward must be
significant. 25% seems to work
+ better... (extra-note: in
small testbeds, the problem
+ is bigger than in a larger
network where the case that
+ a query stays in the QM
indefinitely might be much more
+ rare; so don't just trust a
micro-scale benchmark when
+ trying to figure out an
'optimal' threshold). */
+ noclear = true;
+
+ debug("DEBUG: QM noclear rule
applied!\n");
+ }
+ break; /* this is it, do not scan for
other 'oldest' entries */
+ }
+ }
+
+ if (oldestIndex == -1) {
+ debug("DEBUG: keeping track of
"+QUERY_RECORD_COUNT+" queries already, will not manage this one");
+ qr = dummy;
+ }
+ else {
+ qr = queries[oldestIndex];
+ qr.msg = null;
+ }
+ qr.expires = expirationTime;
+ qr.transmissionCount = 0;
+ qr.msg=(P2PQuery) PersistentHelper.copy(msg);
+
+ if (!noclear) {
+ qr.clearBits();
+ qr.noTarget=(origin != null ? origin :
coreAPI.getIdentity());
+ qr.localClient=client;
+ qr.totalDistance=0;
+
+ if (TRADITIONAL_SELECTION) {
+
qr.activeConnections=coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+ public void callback(
HostIdentity identity, Object data )
+ {
+
selectActiveNodes(identity,(QueryRecord) data);
+ }
+ },qr);
+
+ if (qr.activeConnections>0) {
+ selectActiveNodes(null, qr); /*
give SAN chance to adjust weight at the end of the iteration! */
+
+ for
(i=BITMAP_SIZE*4/qr.activeConnections;i>=0;i--) {
+
qr.setBit(Crypto.nextInt(BITMAP_SIZE)*8); /* select 4 random nodes */
+ }
+
+
coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+ public void callback(
HostIdentity identity, Object data )
+ {
+
selectRandomNodes(identity,(QueryRecord) data);
+ }
+ },qr);
+ }
+ }
+ else {
+ qr.rankings = new int[8*BITMAP_SIZE];
+
qr.activeConnections=coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+ public void callback(
HostIdentity identity, Object data )
+ {
+
newSelectCode(identity,(QueryRecord) data);
+ }
+ },qr);
+
+ /* actual selection, proportional to
rankings assigned by newSelectCode ... */
+ {
+ int j;
+ long rankingSum = 0;
+ for (i=0;i<8*BITMAP_SIZE;i++)
+ rankingSum +=
qr.rankings[i];
+
+ if ( (rankingSum != 0) && /*
doppelt haelt besser */ (qr.activeConnections > 0) ) {
+ /* select 4 peers for
forwarding */
+ for (i=0;i<4;i++) {
+ long sel;
+ long pos;
+ sel =
Crypto.nextLong(rankingSum);
+ pos = 0;
+ for
(j=0;j<8*BITMAP_SIZE;j++) {
+ pos +=
qr.rankings[j];
+ if (pos
> sel) {
+
qr.setBit(j);
+
break;
+
}
+ }
+ }
+ }
+ else {
+ /* no bias available,
go random! */
+ if
(qr.activeConnections > 0) {
+ for
(i=4*BITMAP_SIZE*8/qr.activeConnections-1;i>=0;i--) {
+
qr.setBit(Crypto.nextInt(BITMAP_SIZE)*8); /* select 4 random nodes */
+ }
+ }
+ }
+ }
+
+ qr.rankings = null;
+ }
+ /* now forward to a couple of selected nodes */
+ coreAPI.forAllConnectedNodes(new
PerNodeCallback() {
+ public void callback( HostIdentity
identity, Object data )
+ {
+
sendToSelected(identity,(QueryRecord) data);
+ }
+ },qr);
+ }
+ }
+ }
+
+ /**
+ * Stop transmitting a certain query (we don't route it anymore or we
have learned the answer).
+ * @param query
+ */
+
+ public void dequeueQuery( HashCode512 query )
+ {
+ int i,j;
+ QueryRecord qr;
+
+ synchronized(queryManagerLock) {
+ for (i=0;i<QUERY_RECORD_COUNT;i++) {
+ qr = queries[i];
+ if( qr.msg != null ) {
+ for
(j=NUMBER_OF_QUERIES(qr.msg)-1;j>=0;j--) {
+ if
(query.equals(qr.msg.getQuery(j))) {
+ qr.expires = 0; /*
expire NOW! */
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * How many queries are in the given query (header given).
+ * @param p
+ * @return
+ */
+
+ protected int NUMBER_OF_QUERIES( Persistent p )
+ {
+ return ((p.getByteSize()-P2PQuery.SIZE)/HashCode512.SIZE);
+ }
+
+ /**
+ * Map the id to an index into the bitmap array.
+ * @param id
+ * @return
+ */
+
+ protected int getIndex( HostIdentity id )
+ {
+ int index;
+
+ index = coreAPI.computeIndex(id);
+ if (index > 8*BITMAP_SIZE)
+ index = index & (8*BITMAP_SIZE-1);
+ return index;
+ }
+
+ /**
+ * Callback method for filling buffers. This method is invoked by the
+ * core if a message is about to be send and there is space left for a
+ * 3QUERY. We then search the pending queries and fill one (or more)
+ * in if possible.
+ *
+ * Note that the same query is not transmitted twice to a peer and that
+ * queries are not queued more frequently than 2 TTL_DECREMENT.
+ *
+ * @param receiver the receiver of the message
+ * @param buf
+ * @return the number of bytes written to
+ * that buffer (must be a positive number).
+ */
+
+ protected boolean fillInQuery( HostIdentity receiver, ByteBuffer buf )
+ {
+ long now;
+ int start,delta;
+
+ now=Scheduler.now();
+ synchronized(queryManagerLock) {
+ start = zePos;
+ delta = 0;
+ while (buf.remaining()>P2PQuery.SIZE+HashCode512.SIZE) {
+ if (queries[zePos].expires>now &&
+
!queries[zePos].getBit(getIndex(receiver)) &&
+ buf.remaining()>=
queries[zePos].msg.getByteSize()
+ ) {
+
+ debug("Adding
"+NUMBER_OF_QUERIES(queries[zePos].msg)+" queries
("+queries[zePos].msg.getQuery(0).toHex()+") to outbound buffer of
"+receiver.getName());
+
+
queries[zePos].setBit(getIndex(receiver));
+ if
(!PersistentHelper.write(queries[zePos].msg,buf)) {
+ return false;
+ }
+ queries[zePos].sendCount++;
+ delta +=
queries[zePos].msg.getByteSize();
+ }
+ zePos++;
+ if (zePos >= QUERY_RECORD_COUNT)
+ zePos = 0;
+ if (zePos == start)
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A "PerNodeCallback" method that selects the most
+ * active nodes for forwarding (with some randomness).
+ *
+ * Also computes the sum of the distances of the
+ * other node IDs as a side-effect.
+ * @param id
+ * @param qr
+ */
+
+ protected void selectActiveNodes( HostIdentity id, QueryRecord qr )
+ {
+ int trf;
+
+ if (id==null) {
+ // special call for weight adjustment
+
Xweight=Xweight/Math.sqrt((qr.transmissionCount+1.0)/(SELECT_GOAL+1.0));
+ return;
+ }
+
+ trf = coreAPI.queryBPMfromPeer(id);
+ Xaverage = (Xaverage * 15 + trf) / 16; /* approximate average
over time...*/
+
+ debug("Selecting from active nodes "+id.getName()+" as
rand("+(trf+1)+") > rand("+(Xaverage+1)+")*"+Xweight+".");
+
+ /* Forward the query to peers that have on average lots of
bandwidth
+ assigned to them (and are thus recently very productive...) */
+ if (Crypto.nextInt(trf+1)>Crypto.nextInt(Xaverage+1)*Xweight) {
+ qr.setBit(getIndex(id));
+ qr.transmissionCount++;
+
+ debug("Node selected for forwarding due to activity:
"+id.getName()+".");
+ }
+ else {
+ qr.totalDistance+=id.distance(qr.msg.getQuery(0));
+ }
+ }
+
+ /**
+ * A "PerNodeCallback" method that selects some
+ * random nodes (biased according to proximity).
+ * @param id
+ * @param qr
+ */
+
+ protected void selectRandomNodes( HostIdentity id, QueryRecord qr )
+ {
+ int avgDist;
+ int peerDist;
+
+ if (id == null) {
+ Yweight = Yweight / Math.sqrt(
(qr.transmissionCount+1.0) / (FINAL_GOAL+1.0) );
+ return;
+ }
+
+ if (qr.totalDistance==0 || qr.activeConnections==0) {
+ return; /* activeConnections should never be 0, if
totalDistance is 0, this is caused by us selectAcitveNodes selecting all nodes
already */
+ }
+
+ if (qr.getBit(getIndex(id))) {
+ return;
+ }
+
+ avgDist = (int) (qr.totalDistance / qr.activeConnections);
+ peerDist = id.distance(qr.msg.getQuery(0));
+
+ debug("Selecting at random from active nodes "+id.getName()+"
using rand("+(peerDist+1)+")*"+qr.transmissionCount+"*"+Yweight+" <
rand("+(avgDist+1)+").");
+
+ /* Select 2 random nodes for forwarding. Give preference
+ to nodes that are close. Division by 4 to ensure
+ that we are in the range of a signed int. */
+ if (Crypto.nextInt(1+peerDist) * qr.transmissionCount * Yweight
< Crypto.nextInt(1+avgDist) ) {
+ debug("Node selected for forwarding from random set:
"+id.getName()+".");
+
+ qr.setBit(getIndex(id));
+ qr.transmissionCount++;
+ }
+ }
+
+ protected void newSelectCode( HostIdentity id, QueryRecord qr )
+ {
+ ReplyTrackData pos;
+ ResponseList rp;
+ int ranking = 0;
+ int distance;
+
+ pos = rtdList;
+ while (pos != null) {
+ if ((qr.localClient==null &&
pos.queryOrigin.equals(qr.noTarget)) || qr.localClient == pos.localQueryOrigin)
{
+ break;
+ }
+ pos=pos.next;
+ }
+
+ if (pos != null) {
+ rp = pos.responseList;
+ while (rp != null) {
+ if (rp.responder.equals(id)) {
+ break;
+ }
+ rp = rp.next;
+ }
+
+ if (rp != null) {
+ if (rp.responseCount < 0xFFFF)
+ ranking = 0x7FFF * rp.responseCount;
+ else
+ ranking = 0x7FFFFFF;
+ }
+ }
+
+ distance=id.distance(qr.msg.getQuery(0));
+ if (distance <= 0)
+ distance = 1;
+
+ ranking += 0xFFFF / (1 + Crypto.nextInt(distance));
+ ranking += Crypto.nextInt(0xFF); /* small random chance for
everyone */
+ qr.rankings[getIndex(id)]=ranking;
+ }
+
+ /**
+ * A "PerNodeCallback" method that forwards
+ * the query to the selected nodes.
+ * @param id
+ * @param qr
+ */
+
+ protected void sendToSelected( HostIdentity id, QueryRecord qr )
+ {
+ if (id.equals(qr.noTarget)) {
+ return;
+ }
+
+ if (qr.getBit(getIndex(id))) {
+ debug("Queueing query "+qr.msg.getQuery(0).toHex()+" in
buffer of selected node "+id.getName()+".");
+
+ coreAPI.sendToNode(id,
+ qr.msg,
+ BASE_QUERY_PRIORITY
*(qr.msg.getPriority()*2+NUMBER_OF_QUERIES(qr.msg)),
+ (int) TTL_DECREMENT);
+ }
+ }
+
+ /**
+ * Cron job that ages the RTD data and that frees memory for entries
that reach 0.
+ */
+
+ public void ageRTD()
+ {
+ ReplyTrackData pos;
+ ReplyTrackData prev;
+ ResponseList rpos;
+ ResponseList rprev;
+
+ synchronized(queryManagerLock) {
+ prev = null;
+ pos = rtdList;
+ while (pos != null) {
+ /* after 10 minutes, always discard everything
*/
+ if (pos.lastReplyReceived <
Scheduler.toSeconds(Scheduler.now()) - 600) {
+ while (pos.responseList != null) {
+ rpos = pos.responseList;
+ pos.responseList = rpos.next;
+ }
+ }
+ /* otherwise, age reply counts */
+ rprev = null;
+ rpos = pos.responseList;
+ while (rpos != null) {
+ rpos.responseCount = rpos.responseCount
/ 2;
+ if (rpos.responseCount == 0) {
+ if (rprev == null)
+ pos.responseList =
rpos.next;
+ else
+ rprev.next = rpos.next;
+ if (rprev == null)
+ rpos = pos.responseList;
+ else
+ rpos = rprev.next;
+ continue;
+ }
+ }
+ /* if we have no counts for a peer anymore,
+ free pos entry */
+ if (pos.responseList == null) {
+ if (prev == null)
+ rtdList = pos.next;
+ else
+ prev.next = pos.next;
+ if (prev == null)
+ pos = rtdList;
+ else
+ pos = prev.next;
+ continue;
+ }
+ prev = pos;
+ pos = pos.next;
+ }
+ }
+ }
+
+ /**
+ * We received a reply from 'responder' to a query received from
+ * 'origin' (or 'localOrigin'). Update reply track data!
+ *
+ * @param origin only valid if localOrigin == null
+ * @param localOrigin origin if query was initiated by local client
+ * @param responder peer that send the reply
+ */
+
+ public void updateResponseData( HostIdentity origin, CSSession
localOrigin, HostIdentity responder )
+ {
+ ReplyTrackData pos;
+ ReplyTrackData prev;
+ ResponseList rpos;
+ ResponseList rprev;
+
+ if (responder == null)
+ return; /* we don't track local responses */
+
+ synchronized(queryManagerLock) {
+ pos = rtdList;
+ prev = null;
+ while (pos != null) {
+ if (pos.localQueryOrigin==localOrigin &&
(localOrigin!=null || origin.equals(pos.queryOrigin))) {
+ break; /* found */
+ }
+ prev = pos;
+ pos = pos.next;
+ }
+ if (pos == null) {
+ pos = new ReplyTrackData();
+ pos.next = null;
+ pos.localQueryOrigin = localOrigin;
+ if (localOrigin == null)
+ pos.queryOrigin = origin;
+ pos.responseList = null;
+ if (prev == null)
+ rtdList = pos;
+ else
+ prev.next = pos;
+ }
+ pos.lastReplyReceived=(int)
Scheduler.toSeconds(Scheduler.now());
+ rpos = pos.responseList;
+ rprev = null;
+ while (rpos != null) {
+ if (responder.equals(rpos.responder)) {
+ rpos.responseCount++;
+ return;
+ }
+ rprev = rpos;
+ rpos = rpos.next;
+ }
+
+ rpos = new ResponseList();
+ rpos.responseCount = 1;
+ rpos.responder = responder;
+ rpos.next = null;
+ if (rprev == null)
+ pos.responseList = rpos;
+ else
+ rprev.next = rpos;
+ }
+ }
+}
+
+/**
+ * Linked list of peer ids with number of replies received.
+ */
+
+class ResponseList extends Object
+{
+ public HostIdentity responder;
+ public int responseCount;
+ public ResponseList next;
+}
+
+/**
+ * Structure for tracking from which peer we got valueable replies for which
clients / other peers.
+ */
+
+class ReplyTrackData extends Object
+{
+ /** For which client does this entry track replies ? Only valid if
localQueryOrigin == null! */
+ public HostIdentity queryOrigin;
+
+ /** For which client does this entry track replies ? */
+ public CSSession localQueryOrigin;
+
+ /** Time at which we received the last reply for this client (in
seconds). Used to discard old entries eventually. */
+ public int lastReplyReceived;
+
+ /** Linked list of peers that responded, with number of responses. */
+ public ResponseList responseList;
+
+ /** Linked list. */
+ public ReplyTrackData next;
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/QueryRecord.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/QueryRecord.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/QueryRecord.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,91 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.CSSession;
+
+import java.util.*;
+
+/**
+ * In this struct, we store information about a
+ * query that is being send from the local node to
+ * optimize the sending strategy.
+ */
+
+public class QueryRecord extends Object implements AFSConstants
+{
+ /** How often did we send this query so far ? */
+ public int sendCount;
+
+ /** The message that we are sending. */
+ public P2PQuery msg;
+
+ /** Bit-map marking the hostIndices (computeIndex) of nodes that have
received this query already.
+ Note that the bit-map has a maximum size, if the index is
out-of-bounds, it is hashed into
+ the smaller size of the bitmap. There may thus be nodes with identical
indices, in that case, only one of
+ the nodes will receive the query. */
+ public byte[] bitmap;
+
+ /** When do we stop forwarding (!) this query ? */
+ public long expires;
+
+ /** How many nodes were connected when we initated sending this query ?
*/
+ public int activeConnections;
+
+ /** What is the total distance of the query to the connected nodes ? */
+ public long totalDistance;
+
+ /** To how many peers has / will this query be transmitted ? */
+ public int transmissionCount;
+
+ /** To which peer will we never send this message ? */
+ public HostIdentity noTarget;
+
+ /** Sender identity, for a local client. */
+ public CSSession localClient;
+
+ /** How important would it be to send the message to all peers in this
bucket ? */
+ public int[] rankings;
+
+
+ public QueryRecord()
+ {
+ super();
+ bitmap=new byte[BITMAP_SIZE];
+ msg=new P2PQuery();
+ }
+
+ public String toString()
+ {
+ return "Query record";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public boolean getBit( int bit )
+ {
+ byte theBit;
+
+ theBit=(byte) (1 << (bit & 7));
+ return (bitmap[bit>>3] & theBit)!=0;
+ }
+
+ public void setBit( int bit )
+ {
+ byte theBit;
+
+ theBit=(byte) (1 << (bit & 7));
+ bitmap[bit>>3] |= theBit;
+ }
+
+ public void clearBits()
+ {
+ Arrays.fill(bitmap,(byte) 0);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/Routing.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/Routing.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/Routing.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,1328 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * Routing interface. This is the interface that does the routing.
+ * routing of AFS queries and replies
+ *
+ * The routing code is responsible for deciding which replies
+ * need to be forwarded to which peers. While the querymanager
+ * decides where to forward queries, it needs to negotiate with
+ * the routing code which queries can be forwarded since we may
+ * not be able to keep track of all queries.
+ */
+
+public class Routing extends LoggedObject implements AFSConstants
+{
+ private static final boolean DEBUG_WRITE_INDTABLE = true;
+
+ /** How much is a response worth 'in general'. Since replies are
roughly 1k and should be much (factor of 4)
+ preferred over queries (which have a base priority of 20, which yields
a base unit of roughly 1 per byte).
+ Thus if we set this value to 4092 we'd rather send a reply instead of
a query unless the queries have
+ (on average) a priority that is more than double the reply priority
(note that querymanager multiplies the
+ query priority with 2 to compute the scheduling priority). */
+
+ public static final int BASE_REPLY_PRIORITY = 4092;
+
+ /**
+ * minimum indirection table size, defaults to 8192 entries, reduce if
+ * you have very little memory, enlarge if you start to overflow often
+ * and have memory available.<p>
+ *
+ * If the average query lives for say 1 minute (10 hops), and you have
+ * a 56k connection (= 420 kb/minute, or approximately 8000
+ * queries/minute) the maximum reasonable routing table size would
+ * thus be 8192 entries. Every entry takes about 68 bytes.<p>
+ *
+ * The larger the value is that you pick here, the greater your
+ * anonymity can become. It also can improve your download speed.<p>
+ *
+ * Memory consumption:
+ * <ul>
+ * <li>8192 => 560k indirection table => approx. 6 MB gnunetd</li>
+ * <li>65536 => 4456k indirection table => approx. 10 MB gnuentd</li>
+ * </ul>
+ * <p>
+ * THE VALUE YOU PICK MUST BE A POWER OF 2, for example:
+ * 128, 256, 512, 1024, 2048, 4092, 8192, 16384, 32768, 65536
+ */
+
+ public static final int MIN_INDIRECTION_TABLE_SIZE = 8192;
+
+ /**
+ * Under certain cirumstances, two peers can interlock in their
+ * routing such that both have a slot that is blocked exactly until
+ * the other peer will make that slot available. This is the
+ * probability that one will give in. And yes, it's a hack. It
+ * may not be needed anymore once we add collision-resistance to
+ * the routing hash table.
+ */
+
+ public static final int TIE_BREAKER_CHANCE = 4;
+
+ /** ITE modes for addToSlot. */
+ public static final int ITE_REPLACE = 0;
+ public static final int ITE_GROW = 1;
+
+ /** Size of the indirection table specified in gnunet.conf */
+ private int indirectionTableSize;
+
+ /** The routing table. This table has entries for all queries that we
have recently send out.
+ It helps GNUnet to route the replies back to the respective sender. */
+
+ private IndirectionTableEntry[] ROUTING_indTable_;
+
+ /** Stats handle for how much content replies we have send back to
clients. */
+ private Stat stat_cs_reply_content_out;
+
+ /** Stats handles about incoming blocks */
+ private Stat stat_content_in_ok;
+ private Stat stat_content_in_dupe;
+ private Stat stat_content_in_orphan;
+ private Stat stat_routingFull;
+ private Stat stat_routingReplaced;
+ private Stat stat_routingPresent;
+ private Stat stat_p2p_query_out;
+ private Stat stat_concurrent_route_replacement;
+ private Stat stat_delaytime_route_replacement;
+
+ private int random_qsel;
+
+ private CoreForProtocol coreAPI;
+ private Prefs prefs;
+ private Statistics stats;
+ private Scheduler scheduler;
+ private int round = 0;
+
+ public Policy policy;
+ public Manager manager;
+ public QueryManager queryManager;
+ public BloomFilter superBloomFilter;
+ public BloomFilter singleBloomFilter;
+
+ private ScheduledTask writeTask;
+
+
+ public Routing()
+ {
+ super(true);
+ coreAPI=null;
+
+ writeTask=new ScheduledTask("WRITE-ID-TABLE",new
EvalAction(this,"writeIDtable"),Scheduler.MINUTES_1);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * This function will write numeric entries of the indirectiontable
+ * to a textfile. With idtablesize of (8192*8), it will generate
+ * >1200 kb per run. The output will not be an accurate snapshot
+ * of the idtable at any moment as the locking is done on per-entry
+ * basis. Naturally this function might also twist the routing
+ * itself a little by locking the entries. The hope is that analyzing
+ * this data in eg octave or matlab might help understand the
+ * routing behaviour better. Or not.
+ */
+
+ public void writeIDtable()
+ {
+ IndirectionTableEntry ite;
+ MappedFile fp;
+ FileLocation f;
+ DirLocation dir;
+ long now;
+ int i;
+
+ now=Scheduler.now();
+
+ dir=prefs.getDirLocation("","GNUNETD_HOME");
+ if (dir==null) {
+ dir=new DirLocation("~");
+ }
+
+ f=dir.getFile("gn_idstats.txt");
+ f.create();
+
+ fp=f.open();
+ fp.seekEnd();
+
+ for(i=0; i<indirectionTableSize; i++) {
+ ite=ROUTING_indTable_[i];
+ synchronized(ite.lookup_exclusion) {
+ fp.writeString(round+" "+i+"
"+(ite.namespace!=null ? 1 : 0)+" "+
+ (ite.ttl == 0 ? 0 :
Scheduler.toSeconds(ite.ttl-now))+" "+ite.priority+" "+
+ ite.getSeenCount()+"
"+ite.getWaitingHostsCount()+" "+ite.getClientsCount()+"\n");
+ }
+ }
+
+ fp.close();
+ round++;
+ }
+
+ /**
+ * Initialize routing module.
+ * Initialize routing module (initializes indirection table)
+ * @param capi
+ */
+
+ public void init( CoreForProtocol capi )
+ {
+ int i;
+
+ coreAPI=capi;
+ prefs=capi.getApplication().getPreferences();
+ stats=capi.getApplication().getStatistics();
+ scheduler=capi.getApplication().getScheduler();
+
+ random_qsel = Crypto.nextInt(HashCode512.SIZE/4);
+ stat_cs_reply_content_out=stats.getHandle("# kb downloaded by
clients",Stat.VERBOSE);
+ stat_delaytime_route_replacement=stats.getHandle("# routing
entry replaced during delaytime",Stat.VERBOSE);
+ stat_concurrent_route_replacement=stats.getHandle("# routing
entry replaced during lookup",Stat.VERBOSE);
+
+ stat_content_in_ok=stats.getHandle("# kb ok content in");
+ stat_content_in_dupe=stats.getHandle("# kb dupe content in");
+ stat_content_in_orphan=stats.getHandle("# kb orphan or pushed
content in");
+ stat_routingFull=stats.getHandle("# routing table full");
+ stat_routingReplaced=stats.getHandle("# routing table entry
replaced");
+ stat_routingPresent=stats.getHandle("# routing table entry
already in place");
+ stat_p2p_query_out=stats.getHandle("# p2p queries sent");
+ indirectionTableSize
=prefs.getInt("AFS","INDIRECTIONTABLESIZE",0);
+ if (indirectionTableSize < MIN_INDIRECTION_TABLE_SIZE)
+ indirectionTableSize = MIN_INDIRECTION_TABLE_SIZE;
+ i = 1;
+ while (i < indirectionTableSize)
+ i*=2;
+ indirectionTableSize = i; // make sure it's a power of 2
+
+ debug("Set indirection table size to
"+indirectionTableSize+".");
+
+ ROUTING_indTable_= new
IndirectionTableEntry[indirectionTableSize];
+ for (i=0;i<indirectionTableSize;i++) {
+ ROUTING_indTable_[i]=new IndirectionTableEntry();
+ ROUTING_indTable_[i].namespace = null;
+ ROUTING_indTable_[i].ttl = 0; // expired / free
+
ROUTING_indTable_[i].successful_local_lookup_in_delay_loop = false;
+ ROUTING_indTable_[i].lookup_exclusion=new Object();
+ }
+ ((Server) coreAPI.getApplication()).registerCSExitHandler(new
ClientExitHandler() {
+ public void handle( CSSession client )
+ {
+ cancelTCP_routing(client);
+ }
+ });
+
+ if (DEBUG_WRITE_INDTABLE) {
+ scheduler.addJob(writeTask,0);
+ }
+ }
+
+ /**
+ * Shutdown the routing module.
+ */
+
+ public void done()
+ {
+ ((Server) coreAPI.getApplication()).unregisterCSExitHandler(new
ClientExitHandler() {
+ public void handle( CSSession client )
+ {
+ cancelTCP_routing(client);
+ }
+ });
+ ROUTING_indTable_=null;
+
+ if (DEBUG_WRITE_INDTABLE) {
+ scheduler.deleteJob(writeTask);
+ }
+ }
+
+ /**
+ * Print the current routing table.
+ * Print the routing table.
+ */
+
+ public void printRoutingTable()
+ {
+ int i;
+ IndirectionTableEntry ite;
+ long now;
+
+ now=Scheduler.now();
+ log(Level.INFO,"Routing TABLE:");
+ for (i=0;i<indirectionTableSize;i++) {
+ ite = ROUTING_indTable_[i];
+ synchronized(ite.lookup_exclusion) {
+ // if (ite.ttl >= now)
+ log(Level.FINEST,i+": hash "+ite.hash.toHex()+"
ttl "+Scheduler.toSeconds(ite.ttl - now)+"s hostsWaiting
"+ite.getWaitingHostsCount()+" prio "+ite.priority+" seenIndex:
"+ite.getSeenCount());
+ }
+ }
+ }
+
+ /**
+ * Execute the query. <p>
+ *
+ * Execute means to test if we can route the query (or, in the case
+ * of a multi-query, any of the sub-queries). If yes, we lookup the
+ * content locally and potentially route it deferred. Regardless if
+ * the content was found or not, the queries that we can route are
+ * forwarded to other peers (by the querymanager code).<p>
+ *
+ * The decision if we can route is made by "needsForwarding". Note that
+ * queries that we are already routing do not "need forwarding". If
+ * we do route the query, execQuery decides if we are going to do source
+ * rewriting or not.<p>
+ *
+ * If we route a query, execSingleQuery will use the bloom filters and
+ * the databases to locate the content and queue a cron job that will
+ * pass the response to "useContent" as if it came from another peer.
+ * Note that if the query originated from a local client, the response
+ * is instant (no cron job scheduled).
+ *
+ * @param qp the polciy (priority) for the query
+ * @param msg the query message (with host identity for the reply)
+ * @param sock the TCP socket to send the answer to if it is
+ * a query from the local host, otherwise null.
+ * @return true if the query was routed (at least in part), false if it
was dropped
+ */
+
+ public boolean execQuery( int qp, P2PQuery msg, CSSession sock )
+ {
+ HostIdentity sender;
+ HostIdentity senderCpy;
+ int prio;
+ int count;
+ int routeCount;
+
+ count = (msg.getByteSize()-P2PQuery.SIZE) / HashCode512.SIZE;
+ prio = msg.getPriority() / count; // per-query priority
+
+ // source rewriting (or not...)
+ if (sock == null) {
+ if (msg.getReturnTo().equals(coreAPI.getIdentity()))
+ return false; // A to B, B sends back to A
without (!) source rewriting, in this case, A must just drop
+ senderCpy=(HostIdentity)
PersistentHelper.copy(msg.getReturnTo()); //todo: copie utile ???
+ sender = senderCpy;
+ } else {
+ sender = null;
+ senderCpy=(HostIdentity)
PersistentHelper.copy(coreAPI.getIdentity()); //todo: copie utile ???
+ }
+ if ((qp & Policy2.QUERY_INDIRECT) != 0) {
+ msg.setReturnTo((HostIdentity)
PersistentHelper.copy(coreAPI.getIdentity())); //todo: copie utile ???
+ }
+ else {
+ msg.setPriorityAndTTL(0,msg.getTTL());
+ }
+
+ debug("Received "+count+"-query "+msg.getQuery(0).toHex()+"
with ttl "+msg.getTTL()+" and priority "+msg.getPriority()+".");
+
+ if (msg instanceof P2PNSQuery) {
+ if
(execNSQuery(sender,sock,prio,msg.getTTL(),((P2PNSQuery)
msg).identifier,((P2PNSQuery) msg).namespace))
+ routeCount = 2; // NAMESPACE + IDENTIFIER!
+ else
+ routeCount = 0;
+ }
+ else {
+ if (count > 1) { // MULTI-QUERY, take apart for
individual routing, but reassemble for forwarding
+ int i;
+ boolean superBF;
+
+ superBF =
superBloomFilter.test(msg.getQuery(0));
+ routeCount = 1;
+ for (i=1;i<count;i++) {
+ if
(execSingleQuery(sender,sock,prio,msg.getTTL(),msg.getQuery(i),superBF)) {
+ // route this query !
+
msg.setQuery(routeCount,msg.getQuery(i));
+ routeCount++;
+ }
+ }
+ if (routeCount == 1)
+ routeCount = 0; // nothing to forward
+ }
+ else { // single query or 3hash search
+ if
(execSingleQuery(sender,sock,prio,msg.getTTL(),msg.getQuery(0),false))
+ routeCount = 1;
+ else
+ routeCount = 0;
+ }
+ }
+
+ if (routeCount >= 1) {
+ stat_p2p_query_out.add(routeCount);
+ queryManager.forwardQuery(msg,((sock == null) ? sender
: null),sock);
+
+ debug("Slots free in routing table for "+((sock==null)
? "remote" : "LOCAL")+
+ " query "+msg.getQuery(0).toHex()+"; forwarded
"+routeCount+" out of "+count+" queries.");
+ return true;
+ }
+
+ debug("0 slots free in routing table for "+((sock==null) ?
"remote" : "LOCAL")+
+ " query "+msg.getQuery(0).toHex()+" with "+count+" hash
codes, none forwarded.");
+ return false;
+ }
+
+ /**
+ * Content has arrived. We must decide if we want to
+ * a) forward it to our clients
+ * b) indirect it to other nodes.
+ * The routing module should know what to do.
+ *
+ * This method checks the routing table if we have a matching route and
if yes queues the reply.
+ * It also makes sure that we do not send the same reply back on the
same route more than once.
+ *
+ * @param hostId who sent the message
+ * @param queryHash the hashcode from the query
+ * @param msg the p2p message we received, good for
indirecting, must potentially be turned into the adequate CS message.
+ * @return how good this content was (priority of
the original request)
+ */
+
+ public int useContent( HostIdentity hostId, HashCode512 queryHash,
P2PMessage msg )
+ {
+ int i;
+ ContentBlock content;
+ HashCode512 contentHC;
+ IndirectionTableEntry ite;
+ int prio = -1;
+
+ //log(Level.FINEST,"DEBUG: useContent - prints routing table");
+ //printRoutingTable();
+
+ debug("Received content "+queryHash.toHex()+" from peer
"+((hostId == null) ? "self" : hostId.getName())+".");
+
+ ite = ROUTING_indTable_[computeRoutingIndex(queryHash)];
+
+ synchronized(ite.lookup_exclusion) {
+ if (!ite.hash.equals(queryHash) ) {
+ stat_content_in_orphan.inc();
+ debug("No matching query pending for content
"+queryHash.toHex()+" (not indirected).");
+ return 0; // no indirection pending: was useless
+ }
+
+ if (msg instanceof P2P3HashResult) {
+ content=new ContentBlock(((P2P3HashResult)
msg).getResult());
+ if (ite.namespace != null) {
+ return 0;
+ }
+ }
+ else if (msg instanceof P2PChkResult) {
+ queryManager.dequeueQuery(ite.hash);
+ content = ((P2PChkResult)msg).result;
+ if (ite.namespace != null) {
+ return 0;
+ }
+
+ // remove the sender from the waiting list (if
the sender was waiting for a response)
+ if (hostId != null) {
+ ite.removeWaitingHost(hostId);
+ }
+ }
+ else if (msg instanceof P2PSBlockResult) {
+ content=(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,PersistentHelper.toBuffer(((P2PSBlockResult)msg).getResult()));
+ if (ite.namespace == null) {
+ return 0;
+ }
+ HashCode512 hc;
+
hc=((P2PSBlockResult)msg).getResult().getNameSpace();
+ if (! ite.namespace.equals(hc) ) {
+ return 0;
+ }
+ }
+ else {
+ log(Level.WARNING,"Unexpected p2p result :
"+msg);
+ return 0;
+ }
+
+
contentHC=HashCode512.create(PersistentHelper.toBytes(content));
+
+ for (i=0;i<ite.getSeenCount();i++) {
+ if (contentHC.equals(ite.getSeen(i))) {
+ stat_content_in_dupe.inc();
+ debug("Content is not new (slot:
"+computeRoutingIndex(queryHash)+").");
+ return 0; // seen before, useless
+ }
+ }
+ // new reply, adjust credits !
+ if (hostId != null) // if we are the sender, hostId
will be null
+ coreAPI.changeTrust(hostId, ite.priority);
+ prio = ite.priority;
+ ite.priority = 0; // no priority for further replies,
because we don't get paid for those...
+
+ debug("Indirecting new content matching query
"+ite.hash.toHex()+".");
+
+ for (i=0; i<ite.getClientsCount(); i++) {
+
queryManager.updateResponseData(null,ite.getClient(i),hostId);
+ }
+
+ for (i=0; i<ite.getWaitingHostsCount(); i++) {
+
queryManager.updateResponseData(ite.getWaitingHost(i),null,hostId);
+ }
+
+ sendReply(ite,msg);
+ ite.addSeen(contentHC);
+ stat_content_in_ok.inc();
+ }
+ return prio;
+ }
+
+ /**
+ * Compute the hashtable index of a host id.
+ * @param query
+ * @return
+ */
+
+ protected int computeRoutingIndex( HashCode512 query )
+ {
+ int res;
+
+ res = query.getInt(random_qsel) & (indirectionTableSize - 1);
+ if (res>=indirectionTableSize) {
+ trace("Indirection table size not power of 2 ?
("+indirectionTableSize+")");
+ }
+ return res;
+ }
+
+ /**
+ * Queue a CHK reply with cron to simulate
+ * another peer returning the response with
+ * some latency (and then route as usual).
+ *
+ * @param sender the next hop
+ * @param result the content that was found
+ */
+
+ protected void queueCHKReply( HostIdentity sender, ContentBlock result )
+ {
+ final P2PChkResult _msg;
+ IndirectionTableEntry ite;
+ HashCode512 hc;
+
+ hc=HashCode512.create(PersistentHelper.toBytes(result));
+
+ ite=ROUTING_indTable_[computeRoutingIndex(hc)];
+ if (!ite.hash.equals(hc)) {
+ debug("Concurrent route replacement : "+hc.toHex()+".");
+
+ stat_concurrent_route_replacement.inc();
+ return;
+ }
+ if (ite.successful_local_lookup_in_delay_loop) {
+ debug("Unexpected concurrent CHK lookup of
"+hc.toHex()+".");
+ return; // wow, really bad concurrent DB lookup and
processing for the same query. Well, at least we should not also queue the
delayed reply twice...
+ }
+ ite.successful_local_lookup_in_delay_loop = true;
+
+ _msg=new P2PChkResult();
+ _msg.result=(ContentBlock) PersistentHelper.copy(result);
//todo: copie utile ???
+
+ // delay reply, delay longer if we are busy (makes it harder to
predict / analyze, too).
+ scheduler.addJob(new ScheduledTask("DELAYED-REPLY",new
AbstractAction() {
+ public void perform()
+ {
+ HashCode512 hc2;
+ IndirectionTableEntry ite2;
+
+
hc2=HashCode512.create(PersistentHelper.toBytes(_msg.result));
+
+ ite2 =
ROUTING_indTable_[computeRoutingIndex(hc2)];
+ synchronized(ite2.lookup_exclusion) {
+ if (hc2.equals(ite2.hash)) {
+
ite2.successful_local_lookup_in_delay_loop = false;
+ }
+ else {
+
stat_delaytime_route_replacement.inc();
+ }
+ }
+ useContent(null,hc2,_msg);
+ }
+ }),Crypto.nextLong(TTL_DECREMENT));
+ }
+
+ /**
+ * Queue an SBLOCK reply with cron to simulate
+ * another peer returning the response with
+ * some latency (and then route as usual).
+ *
+ * @param sender the next hop
+ * @param result the content that was found
+ */
+
+ protected void queueSBLOCKReply( HostIdentity sender, EncryptedSBlock
result )
+ {
+ final P2PSBlockResult _msg;
+
+ _msg=new P2PSBlockResult(result);
+
+ // delay reply, delay longer if we are busy (makes it harder to
predict / analyze, too).
+ scheduler.addJob(new ScheduledTask("SBLOCK-REPLY",new
AbstractAction() {
+ public void perform()
+ {
+
useContent(null,_msg.getResult().getIdentifier(),_msg);
+ }
+ }),Crypto.nextLong(TTL_DECREMENT));
+ }
+
+ /**
+ * Queue a 3Hash reply with cron to simulate
+ * another peer returning the response with
+ * some latency (and then route as usual).
+ *
+ * @param sender the next hop
+ * @param hc the double (!) hash
+ * @param result the content that was found
+ */
+
+ protected void queue3HashReply( HostIdentity sender, HashCode512 hc,
ContentBlock result )
+ {
+ final P2P3HashResult _msg;
+
+ _msg=new P2P3HashResult(hc,result);
+
+ // delay reply, delay longer if we are busy (makes it harder to
predict / analyze, too).
+ scheduler.addJob(new ScheduledTask("3HASH-REPLY",new
AbstractAction() {
+ public void perform()
+ {
+ useContent(null,_msg.getTripleHash(),_msg);
+ }
+ }),Crypto.nextLong(TTL_DECREMENT));
+ }
+
+ /**
+ * Hand a CHK reply to the client.
+ * @param sock the client socket
+ * @param result the response
+ */
+
+ protected void tellClientCHKReply( CSSession sock, ContentBlock result )
+ {
+ CSResultChk reply;
+ HashCode512 hc;
+
+ hc=HashCode512.create(PersistentHelper.toBytes(result));
+
+ debug("Sending client response to CHK query "+hc.toHex()+".");
+
+ reply = new CSResultChk();
+ reply.result=(ContentBlock) PersistentHelper.copy(result);
//todo: copie utile ???
+
+ stat_cs_reply_content_out.inc();
+
+ sock.send(reply);
+ }
+
+ /**
+ * Hand an SBLOCK reply to the client.
+ * @param sock the client socket
+ * @param result the response
+ */
+
+ protected void tellClientSBLOCKReply( CSSession sock, EncryptedSBlock
result )
+ {
+ CSResultSBlock reply;
+
+ reply=new CSResultSBlock(result);
+
+ stat_cs_reply_content_out.inc();
+
+ sock.send(reply);
+ }
+
+ /**
+ * Hand a 3Hash reply to the client.
+ * @param sock the client socket
+ * @param hc the double hash
+ * @param result the response
+ */
+
+ protected void tellClient3HashReply( CSSession sock, HashCode512 hc,
ContentBlock result )
+ {
+ CSResult3Hash reply;
+
+ reply = new CSResult3Hash(hc,result);
+
+ stat_cs_reply_content_out.inc();
+
+ sock.send(reply);
+ }
+
+ /**
+ * Add an entry to the routing table. The lock on the ite
+ * must be held and is being released.
+ *
+ * @param mode replace or extend an existing entry?
+ * @param ite slot in the routing table that is manipulated
+ * @param query the query to look for
+ * @param namespace the namespace to look in (null for global namespace)
+ * @param ttl how long to keep the new entry, relative ttl
+ * @param priority how important is the new entry
+ * @param sender for which node is the entry (null for local client)
+ * @param sock for which local client is the entry (null for peer)
+ * @return true if sock or sender was added, false if both are null or
existed already
+ * in the queue
+ */
+
+ protected boolean addToSlot( int mode, IndirectionTableEntry ite,
HashCode512 query, HashCode512 namespace, int ttl, int priority, HostIdentity
sender, CSSession sock )
+ {
+ long now;
+ boolean ret = false;
+
+ // namespace handling: always override with the new value
(query collisions are supposed to be 'impossible',
+ // so this should always be correct. Either we replace the
existing slot with something new, or it
+ // should not make a difference since the old and the new
namespace will be the same.
+ if (ite.namespace != null) {
+ if (namespace == null) {
+ ite.namespace = null;
+ } else {
+ ite.namespace=(HashCode512)
PersistentHelper.copy(namespace); //todo: copie utile ???
+ }
+ } else {
+ if (namespace != null) {
+ ite.namespace=(HashCode512)
PersistentHelper.copy(namespace); //todo: copie utile ???
+ }
+ }
+ now=Scheduler.now();
+ if (mode == ITE_REPLACE) {
+ ite.clearSeen();
+ if (query.equals(ite.hash)) {
+ stat_routingPresent.inc();
+ ite.ttl = now + ttl;
+ ite.priority = priority;
+ }
+ else {
+ if (ite.hasClients() && sender==null &&
!ite.hasSeen()) {
+ debug("Replacing local query
"+query.toHex()+" without results with foreign query !");
+ }
+
+ ite.successful_local_lookup_in_delay_loop =
false;
+ // different request, flush pending queues
+ stat_routingReplaced.inc();
+ queryManager.dequeueQuery(ite.hash);
+ ite.hash=(HashCode512)
PersistentHelper.copy(query); //todo: copie utile ???
+ ite.clearWaitingHosts();
+ ite.clearClients();
+ ite.ttl = now + ttl;
+ ite.priority = priority;
+ }
+ } else { // GROW mode
+ if (!query.equals(ite.hash)) {
+ trace("Assert failed !");
+ return false;
+ }
+ if (sender != null) {
+ if (ite.hasWaitingHost(sender)) {
+ sender=null;
+ }
+ }
+ stat_routingPresent.inc();
+
+ if (sock!=null && ite.containsClient(sock)) {
+ sock=null;
+ }
+
+ if ( (sock == null) &&
+ (sender == null) ) {
+ return ret; // already there!
+ }
+ // extend lifetime
+ if (ite.ttl < now + ttl)
+ ite.ttl = now + ttl;
+ ite.priority += priority;
+ }
+
+ if (sock!=null) {
+ if (ite.containsClient(sock)) {
+ sock=null;
+ }
+
+ if (sock != null) {
+ ite.addClient(sock);
+ ite.clearSeen(); // new listener, flush "seen"
list
+ ret = true;
+ }
+ }
+
+ if (sender!=null && ite.hasWaitingHost(sender)) {
+ sender=null;
+ }
+
+ if (sender!=null) {
+ ite.addWaitingHost(sender);
+ ret = true;
+ // again: new listener, flush seen list
+ ite.clearSeen();
+ }
+ return ret;
+ }
+
+ /**
+ * Find out, if this query is already pending. If the ttl of
+ * the new query is higher than the ttl of an existing query,
+ * false is returned since we should re-send the query.<p>
+ *
+ * If true is returned, the slot is also marked as used by
+ * the query and the sender (HostId or socket) is added.<p>
+ *
+ * This method contains a heuristic that attempts to do its best to
+ * route queries without getting too many cycles, send a query and
+ * then drop it from the routing table without sending a response,
+ * etc. Before touching this code, definitely consult Christian
+ * (address@hidden) who has put more bugs in these five lines
+ * of code than anyone on this planet would think is possible.
+ *
+ *
+ * @param query the hash to look for
+ * @param namespace the namespace to look in, null for the global
namespace
+ * @param ttl how long would the new query last
+ * @param priority the priority of the query
+ * @param sender which peer transmitted the query? (null for this peer)
+ * @param sock which client transmitted the query? (null for other peer)
+ * @param isRouted set to true if we can route this query, false if we
can not
+ * @param doForward is set to true if we should forward the query,
false if not
+ * @return a case ID for debugging
+ */
+
+ protected int needsForwarding( HashCode512 query, HashCode512
namespace, int ttl, int priority, HostIdentity sender, CSSession sock,
boolean[] isRouted, boolean[] doForward )
+ {
+ IndirectionTableEntry ite;
+ long now;
+
+ now=Scheduler.now();
+ ite = ROUTING_indTable_[computeRoutingIndex(query)];
+ // released either in here or by addToSlot!
+
+ if ( ( ite.ttl < now - TTL_DECREMENT * 10) &&
+ ( ttl > - TTL_DECREMENT * 5) ) {
+ addToSlot(ITE_REPLACE, ite, query, namespace, ttl,
priority, sender, sock);
+ isRouted[0] = true;
+ doForward[0] = true;
+ return 21;
+ }
+ if (ttl<0 && query.equals(ite.hash)) {
+ // if ttl is "expired" and we have the exact query
pending, route replies but do NOT forward _again_!
+
+ debug("GROW: ttl < 0 and existing query is equal
("+ttl+", "+(ite.ttl-now)+").");
+
+ addToSlot(ITE_GROW, ite, query, namespace, ttl,
priority, sender, sock);
+ // don't go again, we are not even going to reset the
seen list, so why bother looking locally again, if we would find
+ //something, the seen list would block sending the
reply anyway since we're not resetting that (ttl too small!)!
+ isRouted[0] = false;
+ doForward[0] = false;
+ return 0;
+ }
+
+ if ( (ite.ttl + (TTL_DECREMENT * coreAPI.estimateNetworkSize())
<
+ (now + ttl)) &&
+ (ite.ttl < now) ) {
+ // expired AND is significantly (!) longer expired than
new query
+
+ debug("REPLACE and reset SEEN: existing query expired
and older than new query ("+ttl+", "+(ite.ttl-now)+").");
+
+ // previous entry relatively expired, start using the
slot -- and kill the old seen list!
+ ite.clearSeen();
+ if ( query.equals(ite.hash) &&
+ (ite.
successful_local_lookup_in_delay_loop) ) {
+ isRouted[0] = false;
+ doForward[0] = false;
+ addToSlot(ITE_GROW, ite, query, namespace, ttl,
priority, sender, sock);
+ return 1;
+ }
+ isRouted[0] = true;
+ doForward[0] = true;
+ addToSlot(ITE_REPLACE, ite, query, namespace, ttl,
priority, sender, sock);
+ return 2;
+ }
+
+ if (query.equals(ite.hash) ) {
+ if (!ite.hasSeen()) {
+ // can not tell if CHK/3HASH/NSQUERY
+ if (ite.ttl + TTL_DECREMENT < (now + ttl)) { //
ttl of new is SIGNIFICANTLY longer?
+ // query again
+
+ debug("REPLACE (seen was empty):
existing query and TTL higher ("+(ite.ttl-now)+", "+ttl+").");
+
+ addToSlot(ITE_REPLACE, ite, query,
namespace, ttl, priority, sender, sock);
+ if
(ite.successful_local_lookup_in_delay_loop) {
+ isRouted[0] = false; // don't
go again, we are already processing a local lookup!
+ doForward[0] = false;
+ return 3;
+ }
+ isRouted[0] = true;
+ doForward[0] = true;
+ return 4;
+ }
+
+ // new TTL is lower than the old one, thus just
wait for the reply that may come back
+
+ debug("GROW - equal existing query exists
without replies ("+(ite.ttl-now)+", "+ttl+").");
+
+ if (addToSlot(ITE_GROW, ite, query, namespace,
ttl, priority, sender, sock)) {
+ if
(ite.successful_local_lookup_in_delay_loop) {
+ isRouted[0] = false; // don't
go again, we are already processing a local lookup!
+ doForward[0] = false;
+ return 5;
+ }
+ isRouted[0] = true;
+ doForward[0] = false;
+ return 6;
+ }
+
+ // same query with _higher_ TTL has already
been processed FOR THE SAME recipient! Do NOT do
+ // the lookup *again*.
+ isRouted[0] = false;
+ doForward[0] = false;
+ return 7;
+ }
+ // ok, seen reply before, can judge type of query!
+
+ // pending == new!
+ if ( ite.hash.equals(ite.getSeen(0)) &&
+ (ite.namespace == null) ) { // CHK
+ if (ite.ttl < (now + ttl)) { // ttl of new is
longer?
+ // go again
+ ite.clearSeen();
+
+ debug("REPLACE and reset SEEN: existing
query equal but we've seen the response already ("+(ite.ttl-now)+", "+ttl+").");
+
+ addToSlot(ITE_REPLACE, ite, query,
namespace, ttl, priority, sender, sock);
+ if
(ite.successful_local_lookup_in_delay_loop) {
+ isRouted[0] = false; // don't
go again, we are already processing a local lookup!
+ doForward[0] = false;
+ return 8;
+ }
+ isRouted[0] = true;
+ // only forward if new TTL is
significantly higher
+ if (ite.ttl + TTL_DECREMENT < (now +
ttl))
+ doForward[0] = true;
+ else
+ doForward[0] = false;
+ return 9;
+ }
+
+ // new TTL is lower than the old one, thus just
wait for the reply that may come back
+ debug("GROW - equal existing query exists
without replies ("+(ite.ttl-now)+", "+ttl+").");
+
+ if (addToSlot(ITE_GROW, ite, query, namespace,
ttl, priority, sender, sock)) {
+ if
(ite.successful_local_lookup_in_delay_loop) {
+ isRouted[0] = false;
+ doForward[0] = false;
+ return 10;
+ }
+ isRouted[0] = true;
+ doForward[0] = false;
+ return 11;
+ }
+ isRouted[0] = false;
+ doForward[0] = false;
+ return 12;
+ }
+
+ // 3HASH or SQUERY, multiple results possible!
+ // It's a pending 3HASH or SQUERY that can have
multiple replies. Do not re-send, just forward the
+ // answers that we get from now on to this additional
receiver
+ boolean isttlHigher;
+
+ debug("GROW - equal existing query exists without
replies ("+(ite.ttl-now)+", "+ttl+").");
+
+ if (ite.ttl < now+ttl)
+ isttlHigher = false;
+ else
+ isttlHigher = true;
+ if (addToSlot(ITE_GROW, ite, query, namespace, ttl,
priority, sender, sock)) {
+ isRouted[0] = true;
+ doForward[0] = false;
+ return 13;
+ }
+ // receiver is the same as the one that already got the
answer, do not bother to do this again,
+ // IF the TTL is not higher!
+ isRouted[0] = isttlHigher;
+ doForward[0] = false;
+ return 14;
+ }
+ // a different query that is expired a bit longer is using the
slot; but if it is a CHK query that has received
+ // a response already, we can eagerly throw it out anyway,
since the request has been satisfied completely
+ if ( (ite.ttl + TTL_DECREMENT < (now + ttl) ) &&
+ (ite.ttl < now) &&
+ (ite.getSeenCount() == 1) &&
+ (ite.namespace == null) &&
+ (ite.hash.equals(ite.getSeen(0))) ) {
+ // is CHK and we have seen the answer, get rid of it
early
+
+ debug("CHK "+ite.hash.toHex()+" with reply already
seen, replacing eagerly ("+(ite.ttl-now)+", "+ttl+").");
+
+ addToSlot(ITE_REPLACE, ite, query, namespace, ttl,
priority, sender, sock);
+ isRouted[0] = true;
+ doForward[0] = true;
+ return 15;
+ }
+ // Another still valid query is using the slot. Now we need a
_really_ good reason to discard it...
+ if (ttl < 0) {
+ isRouted[0] = false;
+ doForward[0] = false;
+ return 16; // if new ttl is "expired", don't bother
with priorities
+ }
+
+ // Finally try to find a _strong_ reason looking at
priority/ttl relationships to replace the existing query. A low ttl with high
+ // priority should be preferred, so we do a
cross-multiplication (!). Also, we want a _strong_ reason, so we add a "magic"
factor
+ // of 10 for the additional work that the replacement would
make (the network needs a certain amount of resilience to changes in
+ // the routing table, otherwise it might happen that query A
replaces query B which replaces query A which could happen so
+ // quickly that no response to either query ever makes it
through...
+
+ if ( ((ite.ttl - now) * priority) >
+ 10 * (ttl * ite.priority) ) {
+
+ debug("Priority of new query is much higher, overriding
("+(ite.ttl-now)+", "+ttl+").");
+
+ addToSlot(ITE_REPLACE, ite, query, namespace, ttl,
priority, sender, sock);
+ isRouted[0] = true;
+ doForward[0] = true;
+ return 17;
+ }
+
+ if (Crypto.nextInt(TIE_BREAKER_CHANCE) == 0) {
+ debug("TIE-BREAKER. Overriding ("+(ite.ttl-now)+",
"+ttl+").");
+
+ addToSlot(ITE_REPLACE, ite, query, namespace, ttl,
priority, sender, sock);
+ isRouted[0] = true;
+ doForward[0] = true;
+ return 20;
+ }
+
+ // sadly, the slot is busy with something else; we can not even
add ourselves to the reply set
+ stat_routingFull.inc();
+ isRouted[0] = false;
+ doForward[0] = false;
+
+ debug("Existing "+(!ite.hasClients() ? "remote" : "LOCAL")+
+ " query "+ite.hash.toHex()+"
("+computeRoutingIndex(ite.hash)+") is more important (EP: "+
+ ite.priority+", ET: "+(ite.ttl-now)+"; NP:
"+priority+", NT: "+ttl+")");
+ return 18;
+ }
+
+ /**
+ * Send a reply to a host. Distinguishes between local and remote
+ * delivery, converts the reply into the appropriate format and sends
+ * it out.
+ *
+ * @param ite the matching slot in the indirection table
+ * @param msg the message to route
+ */
+
+ protected void sendReply( IndirectionTableEntry ite, P2PMessage msg )
+ {
+ int j;
+ int maxDelay;
+ long now;
+
+ now=Scheduler.now();
+ if (now < ite.ttl)
+ maxDelay = (int) (ite.ttl - now);
+ else
+ maxDelay = (int) TTL_DECREMENT; // for expired queries
+
+ // send to peers
+ for (j=0; j<ite.getWaitingHostsCount(); j++) {
+
coreAPI.sendToNode(ite.getWaitingHost(j),msg,BASE_REPLY_PRIORITY
*(ite.priority+1),maxDelay);// weigh priority
+ }
+
+ for (j=0; j<ite.getClientsCount(); j++) {
+ if (msg instanceof P2P3HashResult) {
+ tellClient3HashReply(ite.getClient(j),
+ ((P2P3HashResult)
msg).getDoubleHash(),
+ new
ContentBlock(((P2P3HashResult) msg).getResult())
+ );
+ }
+ else if (msg instanceof P2PChkResult) {
+
tellClientCHKReply(ite.getClient(j),((P2PChkResult)msg).result);
+ }
+ else if (msg instanceof P2PSBlockResult) {
+
tellClientSBLOCKReply(ite.getClient(j),((P2PSBlockResult) msg).getResult());
+ }
+ else {
+ log(Level.WARNING,"Unexpected p2p result :
"+msg);
+ }
+ }
+ }
+
+ /**
+ * TCP connection is shut down, cancel all replies to that client.
+ * @param sock
+ */
+
+ protected void cancelTCP_routing( CSSession sock )
+ {
+ IndirectionTableEntry ite;
+ int i;
+
+ for (i=0; i<indirectionTableSize; i++) {
+ ite=ROUTING_indTable_[i];
+ synchronized(ite.lookup_exclusion) {
+ ite.removeClient(sock);
+ }
+ }
+ }
+
+ /**
+ * Execute a single query. Tests if the query can be routed. If yes,
+ * the query is added to the routing table and the content is looked
+ * for locally. If the content is available locally, a deferred
+ * response is simulated with a cron job and the local content is
+ * marked as valueable. The method returns true if the query should
+ * subsequently be routed to other peers.
+ *
+ * @param sender next hop in routing of the reply
+ * @param sock client socket if we are ultimate receiver
+ * @param prio the effective priority of the query
+ * @param ttl the relative ttl of the query
+ * @param query the query itself
+ * @param superHash true if the super-hash test has indicated that we#
+ * have a reply locally available
+ * @return true if the query should be routed further, false if not.
+ */
+
+ protected boolean execSingleQuery( HostIdentity sender, CSSession sock,
int prio, int ttl, HashCode512 query, boolean superHash )
+ {
+ ContentIndex ce=new ContentIndex();
+ ContentBlock[] result;
+ int len;
+ boolean[] isRouted=new boolean[1];
+ boolean[] doForward=new
boolean[1];
+ IndirectionTableEntry ite;
+ int nfCase,i,rcount;
+ HashCode512 hc;
+
+ ite = ROUTING_indTable_[computeRoutingIndex(query)];
+ synchronized(ite.lookup_exclusion) {
+ nfCase = needsForwarding(query,
+ null,
+ ttl,
+ prio,
+ sender,
+ sock,
+ isRouted,
+ doForward);
+
+ debug("Needs forwarding decided for "+((sock == null) ?
"remote" : "LOCAL")+
+ " query "+query.toHex()+"
("+computeRoutingIndex(query)+", ttl "+ttl+
+ ", pri "+prio+"): case "+nfCase+"
("+(doForward[0] ? "FWD" : "")+", "+(isRouted[0] ? "ROUTE" : "")+")");
+
+ if ( (sender != null) && !isRouted[0]) {
+ return false; // if we can't route, forwarding
never makes any sense
+ }
+
+ if (!superHash && !singleBloomFilter.test(query)) {
+ return doForward[0]; // content not available
locally, just route
+ }
+
+ result = manager.retrieveContent(query,ce,prio,sender
== null);
+ if (result==null) {
+ return doForward[0]; // bloomfilter was wrong,
content not there
+ }
+
+ len=result.length;
+ if (len == ContentBlock.SIZE) {
+
hc=HashCode512.create(PersistentHelper.toBytes(result[0]));
+ if (ite.hasSeen()) {
+ if (hc.equals(ite.getSeen(0))) {
+ log(Level.SEVERE,"Lookup
produced result already seen. Case: "+nfCase+".");
+ }
+ }
+ }
+
+ if (sender != null) {
+ if (ce.type == ContentIndex.LOOKUP_TYPE_3HASH) {
+ if
(!policy.checkAnonymityPolicy(CSMessage.IS_RESULT_3HASH,P2P3HashResult.SIZE)) {
+ return doForward[0]; // policy
says: no direct response, but routing is ok!
+ }
+ }
+ else {
+ if
(!policy.checkAnonymityPolicy(CSMessage.IS_RESULT_CHK,P2PChkResult.SIZE)) {
+ return doForward[0]; // policy
says: no direct response, but routing is ok
+ }
+ }
+ }
+
+ switch (ce.type) {
+ case ContentIndex.LOOKUP_TYPE_CHK:
+ case ContentIndex.LOOKUP_TYPE_CHKS:
+ if (len != ContentBlock.SIZE) {
+ log(Level.WARNING,"Local CHK
content had bad size : "+len+".");
+ break;
+ }
+ if (sock != null) {
+
tellClientCHKReply(sock,result[0]);
+ doForward[0]=false; // purely
local handling!
+ }
+ if (sender!=null) {
+ queueCHKReply(sender,result[0]);
+ }
+ doForward[0]=false; // we have the one
and only answer
+ break;
+
+ case ContentIndex.LOOKUP_TYPE_3HASH:
+ byte[]
tmp=PersistentHelper.toBytes(result[0]);
+ ContentBlock res;
+
+ rcount = len / ContentBlock.SIZE;
+ if (rcount * ContentBlock.SIZE != len) {
+ log(Level.WARNING,"Database
returned a 3HASH result that is not multiple of 1k ("+len+") ! Database
corrupted ?");
+ break;
+ }
+
+ if (sock!=null) {
+ for (i=0; i<rcount; i++) {
+ res=(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(tmp,i*ContentBlock.SIZE,ContentBlock.SIZE));
+
tellClient3HashReply(sock,ce.hash,res);
+ }
+ }
+
+ if (sender!=null) {
+ for (i=0; i<rcount; i++) {
+ res=(ContentBlock)
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(tmp,i*ContentBlock.SIZE,ContentBlock.SIZE));
+
queue3HashReply(sender,ce.hash,res);
+ }
+ }
+ break;
+
+ default:
+ log(Level.FINEST,"Query lookup produced
unexpected type "+ce.type+" !");
+ break;
+ }
+ }
+ return doForward[0];
+ }
+
+ /**
+ * Execute a namespace query. Tests if the query can be routed. If yes,
+ * the query is added to the routing table and the content is looked
+ * for locally. If the content is available locally, a deferred
+ * response is simulated with a cron job and the local content is
+ * marked as valueable. The method returns true if the query should
+ * subsequently be routed to other peers.
+ *
+ * @param sender next hop in routing of the reply
+ * @param sock client socket if we are ultimate receiver
+ * @param prio the effective priority of the query
+ * @param ttl the relative ttl of the query
+ * @param query the query itself
+ * @param namespace for which namespace
+ * @return true if the query should be routed further, false if not.
+ */
+
+ protected boolean execNSQuery( HostIdentity sender, CSSession sock, int
prio, int ttl, HashCode512 query, HashCode512 namespace )
+ {
+ ContentIndex ce=new ContentIndex();
+ ContentBlock[] result;
+ int len;
+ HashCode512 hc;
+ boolean[] isRouted=new boolean[1];
+ boolean[] doForwarding=new
boolean[1];
+ int k;
+ IndirectionTableEntry ite;
+
+ debug("Received NS query for
"+namespace.toHex()+"/"+query.toHex()+".");
+
+ ite = ROUTING_indTable_[computeRoutingIndex(query)];
+ synchronized(ite.lookup_exclusion) {
+
needsForwarding(query,namespace,ttl,prio,sender,sock,isRouted,doForwarding);
+ }
+ if (!isRouted[0]) {
+ return false;
+ }
+ if (!singleBloomFilter.test(query)) {
+ debug("Bloomfilter test says content not available
locally.");
+ return doForwarding[0]; // content not available
locally, just route
+ }
+
+ result=manager.retrieveContent(query,ce,prio,sender == null);
+ if (result==null) {
+ debug("Bloomfilter test wrong, DB lookup failed.");
+ return doForwarding[0]; // bloomfilter was wrong,
content not there
+ }
+
+ len=result.length;
+ if (ce.type != ContentIndex.LOOKUP_TYPE_SBLOCK) {
+ return doForwarding[0];
+ }
+
+ if (sender != null) {
+ if
(!policy.checkAnonymityPolicy(CSMessage.IS_RESULT_SBLOCK,P2PSBlockResult.SIZE))
{
+ debug("Anonymity policy denies reply at this
time.");
+ return doForwarding[0]; // policy says: no
direct response, but routing is ok
+ }
+ }
+
+ if (0!=(len % ContentBlock.SIZE)) {
+ log(Level.WARNING,"Local SBLOCK content had bad size
"+len+".");
+ return doForwarding[0];
+ }
+
+ EncryptedSBlock sblock;
+ byte[] tmp;
+
+ for (k=(len/ContentBlock.SIZE)-1; k>=0; k--) {
+ tmp=PersistentHelper.toBytes(result[k]);
+ sblock=(EncryptedSBlock)
PersistentHelper.readFully(EncryptedSBlock.class,ByteBuffer.wrap(tmp,k*ContentBlock.SIZE,ContentBlock.SIZE));
+
+ hc=sblock.getNameSpace();
+ if (! namespace.equals(hc)) {
+ log(Level.WARNING,"WARNING: namespace mismatch
(should be rare but can theoretically happen).");
+ return doForwarding[0];
+ }
+ if (sender != null) {
+ queueSBLOCKReply(sender,sblock);
+ }
+ if (sock != null) {
+ tellClientSBLOCKReply(sock,sblock);
+ doForwarding[0] = false;
+ }
+ }
+ return doForwarding[0];
+ }
+
+ /**
+ * Handle query for current average routing priority.
+ *
+ * @param sock
+ * @param msg
+ * @return
+ */
+
+ public boolean csHandleRequestAvgPriority( CSSession sock,
CSGetAvgPriority msg )
+ {
+ int i;
+ IndirectionTableEntry ite;
+ int j = 0;
+ long priSum = 0;
+
+ for (i=0; i<MIN_INDIRECTION_TABLE_SIZE; i++) {
+ ite = ROUTING_indTable_[i];
+ synchronized(ite.lookup_exclusion) {
+ if (ite.ttl!=0 && ite.hasWaitingHosts() &&
!ite.hasClients() ) {
+ /* only count entries that do NOT
correspond to local requests in any way... */
+ priSum += ite.priority;
+ j++;
+ }
+ }
+ }
+ if (j > 0)
+ priSum = priSum / j;
+ return sock.send(new CSResult((int) priSum));
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSConstants.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSConstants.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSConstants.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,87 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+
+/**
+ *
+ */
+
+public interface AFSConstants
+{
+ public static final String GNUNET_URI_PREFIX_AFS =
"gnunet://afs/";
+
+ /* major/minor format versions (current) */
+ public static final int ROOT_MINOR_VERSION = 0;
+ public static final int ROOT_MAJOR_VERSION = 1;
+
+ /* major/minor format versions (current) */
+ public static final int SBLOCK_MINOR_VERSION = 0;
+ public static final int SBLOCK_MAJOR_VERSION = 2;
+
+ /** Fixed SBlock updateInterval codes. Positive values are interpreted
as durations (in seconds) for periodical updates. */
+ public static final int SBLOCK_UPDATE_SPORADIC = -1;
+ public static final int SBLOCK_UPDATE_NONE = 0;
+
+
+ /** Size of the Blocks we slice file data into (DBlocks and IBlocks).
Never change this ! */
+ public static final int CONTENT_SIZE = 1024;
+
+
+ /** Block is freshly created, nothing has been done. */
+ public static final int BLOCK_CREATED =
0;
+
+ /** We know the correct block data and is is on the drive (and in
memory if data != null) */
+ public static final int BLOCK_PRESENT =
1;
+
+ /** We do not know the correct data, but we have not done a request yet.
+ It may be that we can construct the data from the children (if they
are present). */
+ public static final int BLOCK_NOT_PRESENT =
2;
+
+ /** We have a request pending for this block (either with the parent if
parent != null)
+ or a direct request if parent == null. */
+ public static final int BLOCK_PENDING =
3;
+
+ /** This block is present and all children (transitively) are also
present. */
+ public static final int BLOCK_CHILDREN_PRESENT = 4;
+
+ /** This iblock has a super-query pending. */
+ public static final int BLOCK_SUPERQUERY_PENDING = 5;
+
+ /** This block is done (about to be freed). */
+ public static final int BLOCK_DONE
= 6;
+
+ /** This block shall not be freed, even if all children are dead. */
+ public static final int BLOCK_PERSISTENT =
7;
+
+
+ /** by which amount do we decrement the TTL for simple forwarding /
indirection of the query; in milli-seconds.
+ Set somewhat in accordance to your network latency (above the time
it'll take you to send a packet and get a reply). */
+ public static final long TTL_DECREMENT =
Scheduler.SECS_5;
+
+ /** Highest TTL allowed ? (equivalent of 25-50 HOPS distance !) */
+ public static final long MAX_TTL = (100 *
TTL_DECREMENT);
+
+ /** After how many retries do we print a warning ? */
+ public static final int MAX_TRIES = 50;
+
+ /* what is the context in which a root-node was discovered? */
+ public static final int DIR_CONTEXT_SEARCH = 1;
+ public static final int DIR_CONTEXT_INSERT = 2;
+ public static final int DIR_CONTEXT_DIRECTORY = 4;
+ public static final int DIR_CONTEXT_INSERT_SB = 8;
+
+ public static final int DIR_CONTEXT_ALL =
(DIR_CONTEXT_SEARCH|DIR_CONTEXT_INSERT|DIR_CONTEXT_DIRECTORY|DIR_CONTEXT_INSERT_SB);
+
+ /* see also: http://www.w3.org/TR/PNG#R.PNG-file-signature */
+ public static final String GNUNET_DIRECTORY_MAGIC =
"\211GND\r\n\032\n";
+ public static final String GNUNET_DIRECTORY_EXT = ".gnd";
+ public static final String GNUNET_DIRECTORY_MIME =
"application/gnunet-directory";
+
+ /** default size of the query record bitmap: 16 byte = 128 bit */
+ public static final int BITMAP_SIZE = 16;
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSUtils.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSUtils.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/AFSUtils.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,121 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class AFSUtils extends Object
+{
+ private static Logger logger;
+
+ static {
+ logger=Logger.getLogger(AFSUtils.class.getName());
+ }
+
+
+ /**
+ * Parse the keywords (join at spaces, separate at AND).
+ * @param keywords the list of keywords
+ * @return the hashcodes of the keywords
+ */
+
+ public static HashCode512[] parseKeywords( String[] keywords )
+ {
+ HashCode512[] h;
+ List list;
+ StringBuffer buf;
+ String str;
+ int i;
+
+ list=new ArrayList();
+
+ buf=new StringBuffer();
+ for (i=0; i<keywords.length; i++) {
+ buf.setLength(0);
+ while (i<keywords.length && (!keywords[i].equals("AND")
|| i==keywords.length-1)) {
+ buf.append(keywords[i++]);
+ buf.append(" ");
+ }
+
+ if (buf.length()>0) {
+ buf.setLength(buf.length()-1);
+ list.add(buf.toString());
+ }
+ }
+
+ h=new HashCode512[list.size()];
+ for (i=0; i<h.length; i++) {
+ str=(String) list.get(i);
+
+ logger.log(Level.FINEST,"Keyword : \""+str+"\"");
+ h[i]=HashCode512.create(str);
+ }
+ return h;
+ }
+
+ /**
+ * Build an initial set of query messages from the list of keywords.
+ *
+ * @param keywords the keywords (or keys)
+ * @return the resulting query messages
+ */
+
+ public static CSQuery[] buildMessages( HashCode512[] keywords )
+ {
+ HashCode512 doubleHash;
+ CSQuery[] messages;
+ int i;
+
+ messages=new CSQuery[keywords.length];
+ for (i=0; i<messages.length; i++) {
+ messages[i]=new CSQuery();
+ messages[i].setPriorityAndTTL(
+ 5+Crypto.nextInt(20),
+ (int)
(AFSConstants.TTL_DECREMENT*4+Crypto.nextInt((int)
Scheduler.seconds(messages.length*5)))
+ );
+
+
doubleHash=HashCode512.create(PersistentHelper.toBytes(keywords[i]));
+
messages[i].addQuery(HashCode512.create(PersistentHelper.toBytes(doubleHash)));
+ }
+ return messages;
+ }
+
+ public static String get( ByteBuffer buf, int len )
+ {
+ byte[] b;
+ int n;
+
+ b=new byte[len];
+ buf.get(b);
+
+ for (n=0; n<b.length && b[n]!=0; n++) {}
+ return new String(b,0,n);
+ }
+
+ public static void put( String str, ByteBuffer buf, int len )
+ {
+ byte[] b;
+ int n;
+
+ b=str.getBytes();
+ n=Math.min(b.length,len-1);
+
+ buf.put(b,0,n);
+ while (n<len) {
+ buf.put((byte) 0);
+ n++;
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Block.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Block.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Block.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,552 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Shared structure used in the internal objectish representation
+ * of all blocks (DBlocks and IBlocks) in the merkle-tree.
+ * Merkle-tree-CHK file encoding for anonymous file sharing
+ *
+ * Note that the current implementation no longer uses the exact
+ * scheme from the ESED paper. Extensive documentation is forthcoming,
+ * for now see http://www.ovmj.org/GNUnet/encoding.php3
+ */
+
+public abstract class Block extends LoggedObject implements AFSConstants
+{
+ /** The parent node in the file-tree, null for the node on top of the
file-tree. */
+ private IBlock parent;
+
+ /** The total size of the file. */
+ private int filesize;
+
+ /** Position of the block relative to the beginning of the file. */
+ private int pos;
+
+ /** How many bytes in data are actual data (not padding)? Set to 0 to
indicate that the download of this block is complete. */
+ private int len;
+
+ /** Pointer to the data of this block, null if the data is not yet
available. */
+ private Persistent data;
+
+ /** Hashes of the plaintext block (key) and the encrypted block
(query). */
+ private ChkHashes chk;
+
+ /** See BLOCK_XXX constants. */
+ private int status;
+
+
+ protected Block( int size, int length )
+ {
+ super(true);
+ parent=null;
+ filesize=size;
+ pos=0;
+ len=length;
+ data=null;
+ chk=null;
+ status=BLOCK_CREATED;
+
+ assert(length>0 && length<=ContentBlock.SIZE) : "Invalid block
size ("+length+").";
+ }
+
+ protected Block( Block b, int offset, int length )
+ {
+ this(b.filesize,length);
+ parent=(IBlock) b;
+ pos=offset;
+ }
+
+ protected Block( Block b )
+ {
+ super(true);
+ filesize=b.filesize;
+ pos=b.pos;
+ chk=(ChkHashes) PersistentHelper.copy(b.chk);
+ len=b.len;
+ data=PersistentHelper.copy(b.data);
+ parent=b.parent;
+ status=b.status;
+ }
+
+ public String toString()
+ {
+ return "Block";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getDataLength()
+ {
+ return len;
+ }
+
+ public boolean hasData()
+ {
+ return data!=null;
+ }
+
+ public byte[] getRawData()
+ {
+ //todo: bufferiser
+ return (data!=null ? PersistentHelper.toBytes(data) : new
byte[] {});
+ }
+
+ public int getDataCRC()
+ {
+ return Crypto.crc32(getRawData(),0,len);
+ }
+
+ public Persistent getData()
+ {
+ return data;
+ }
+
+ public Persistent getData( Class c )
+ {
+ //todo: optimiser
+ return
PersistentHelper.readFully(c,ByteBuffer.wrap(getRawData()));
+ }
+
+ public void setData( Persistent p )
+ {
+ data=p;
+ //todo: len par rapport a p.getByteSize() ???
+ }
+
+ public void clearData()
+ {
+ data=null;
+ }
+
+ protected boolean loadData( NodeContext nc, boolean warning )
+ {
+ int n;
+ ContentBlock tmp;
+
+ tmp=new ContentBlock();
+
+ n=nc.ioc.read(getDepth(),pos,tmp.content,len);
+//debug("... load data (pos="+pos+",len="+len+") = "+n);
+ if (n!=len) {
+ if (warning) {
+ log(Level.WARNING,"Read from file did not
return expected size "+len+", but "+n+".");
+ }
+ return false;
+ }
+ data=tmp;
+ return true;
+ }
+
+ public boolean writeData( NodeContext nc )
+ {
+ int n;
+
+ n=nc.ioc.write(getDepth(),pos,getRawData(),len);
+ if (n!=len) {
+ log(Level.SEVERE,"Writing to file failed
("+len+","+n+") !");
+ return false;
+ }
+ return true;
+ }
+
+ public IBlock getParent()
+ {
+ return parent;
+ }
+
+ public void detach()
+ {
+ parent=null;
+ }
+
+ public boolean checkTopCRC( int crc )
+ {
+ return Crypto.crc32(getRawData(),0,len)==crc;
+ }
+
+ public HashCode512 getKey()
+ {
+ return chk.key;
+ }
+
+ public HashCode512 getQuery()
+ {
+ return chk.query;
+ }
+
+ public void setKeyAndQuery( ChkHashes c )
+ {
+ chk=(ChkHashes) PersistentHelper.copy(c);
+ }
+
+ public void setKeyAndQuery( FileIdentifier fid )
+ {
+ chk=new ChkHashes();
+ chk.query=fid.getFileQuery();
+ chk.key=fid.getFileKey();
+ }
+
+ public ChkHashes getKeyAndQuery()
+ {
+ return chk;
+ }
+
+ public boolean isStatus( int s )
+ {
+ return status==s;
+ }
+
+ public int getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus( int s )
+ {
+ status=s;
+ }
+
+ public abstract int getDepth();
+
+ public int getFilePosition()
+ {
+ return pos;
+ }
+
+ public int getFileSize()
+ {
+ return filesize;
+ }
+
+ /**
+ * Encrypt this block and initialize
+ * this.chk and return the encrpyted data (edata)
+ * @return
+ */
+
+ protected ContentBlock encrypt()
+ {
+ ContentBlock edata;
+ byte[] bdata;
+
+ bdata=getRawData();
+//System.out.println("CHK="+chk+" len = "+len);
+ chk=new ChkHashes();
+ chk.key=HashCode512.create(bdata,0,len);
+
+ Arrays.fill(bdata,len,ContentBlock.SIZE,(byte) 0);
+ edata = new ContentBlock();
+ if (!new ContentEncoding().encryptContent(bdata,chk.key,edata))
{
+ trace("Encryption failed !?");
+ return null;
+ }
+
chk.query=HashCode512.create(PersistentHelper.toBytes(edata),0,ContentBlock.SIZE);
+ return edata;
+ }
+
+ /**
+ * This block has received a CHK reply for a block. Decrypt.
+ *
+ * @param query the query for which reply is the answer
+ * @param reply the reply
+ * @return OK if the reply was valid, false on error
+ */
+
+ protected boolean receivedChk( HashCode512 query, CSResultChk reply )
+ {
+ byte[] tmp;
+ HashCode512 hc;
+
+ assert(chk.query.equals(query)) : "invoked with reply for a
different block. This should not be.";
+
+ tmp=reply.decrypt(chk.key);
+ if (tmp==null) {
+ trace("Decryption failed !?");
+ return false;
+ }
+ setData(new ContentBlock(tmp));
+
+ hc=HashCode512.create(getRawData(),0,len);
+ if (!chk.key.equals(hc)) {
+ data = null;
+ log(Level.SEVERE,"Decrypted content does not match key.
This is either a bug or a maliciously inserted file. Download aborted.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Delete a block (send appropriate message to gnunetd).
+ *
+ * @param nc the context
+ * @param sock the socket to talk to gnunetd
+ * @return OK on success, false on error
+ */
+
+ public boolean delete( NodeContext nc, CSSession sock )
+ {
+ ContentBlock edata;
+ CSUnindexBlock request;
+ CSResult rv;
+
+ edata = encrypt();
+ if (sock == null) {
+ return true; // fake insert only
+ }
+
+ if (nc.index != 0) {
+ request=new CSUnindexBlock();
+ request.contentIndex.importance= nc.priority;
+ request.contentIndex.type=
ContentIndex.LOOKUP_TYPE_CHKS;
+ request.contentIndex.fileNameIndex= nc.index;
+ request.contentIndex.fileOffset= pos;
+ request.contentIndex.hash=(HashCode512)
PersistentHelper.copy(chk.query);
+ if (!sock.send(request)) {
+ log(Level.WARNING,"Could not send unindex
information to gnunetd. Is gnunetd running ?");
+ return false;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send
confirmation of deletion.");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"Server could not perform
unindexing (content already removed ?).");
+ return false;
+ }
+ return true;
+ }
+ return deleteCHKBlock(sock,edata,nc.priority);
+ }
+
+ /**
+ * Insert a CHK block (insert, not index!)
+ *
+ * @param eblock the block to insert
+ * @param priority the priority to use
+ * @param sock the socket to talk to gnunetd
+ * @return OK on success, false on error
+ */
+
+ protected boolean insertCHKBlock( CSSession sock, ContentBlock eblock,
int priority )
+ {
+ CSInsertChk request;
+ CSResult rv;
+
+ if (sock == null)
+ return true; // fake insert
+
+ request = new CSInsertChk(priority,eblock.content);
+
+ if (!sock.send(request)) {
+ log(Level.WARNING,"Could not send index information to
gnunetd. Is gnunetd running ?");
+ return false;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send confirmation of
insertion.");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"Server could not perform
insertion.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Delete a CHK block.
+ *
+ * @param eblock the block to insert
+ * @param priority the priority to use
+ * @param sock the socket to talk to gnunetd
+ * @return OK on success, false on error
+ */
+
+ protected boolean deleteCHKBlock( CSSession sock, ContentBlock eblock,
int priority )
+ {
+ CSDeleteChk request;
+ CSResult rv;
+
+ if (sock == null)
+ return true; // fake insert
+
+ request =new CSDeleteChk();
+ request.importance = priority;
+
System.arraycopy(eblock.content,0,request.content,0,ContentBlock.SIZE);
+
+ if (!sock.send(request)) {
+ log(Level.WARNING,"Could not send delete information to
gnunetd. Is gnunetd running ?");
+ return false;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send confirmation of
deletion.");
+ return false;
+ }
+
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"Server could not perform deletion.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Send a single query via the RequestManager to gnunetd.
+ *
+ * @param rm the rm used to issue the query
+ * @param receiver the receiver to call on the reply
+ * @param nc the context
+ * @param query the query to perform
+ */
+
+ protected void issueQuery( RequestManager rm, Listener receiver,
NodeContext nc, HashCode512 query )
+ {
+ CSQuery msg;
+
+ msg = new CSQuery();
+ msg.setPriorityAndTTL(1,1);
+ msg.addQuery(query);
+
+debug("issueQuery +++ CSQuery (2): "+msg);
+
+ rm.request(this,receiver,nc,msg);
+ }
+
+ /**
+ * Print the block summary (for debugging)
+ * @param indent
+ */
+
+ public abstract void print( int indent );
+
+ /**
+ * Free the associated resources of this Block. DOES ALSO free the
+ * memory occupied by the Block struct itself!
+ *
+ * @param rm reference to the RequestManager for requests
+ */
+
+ public void destroy( RequestManager rm )
+ {
+ /* better make sure that we have no request pending... */
+ if (rm != null) { /* rm == null for gnunet-insert! */
+ rm.assertDead(this);
+ if (rm.top == this) {
+ rm.top=null;
+ }
+ }
+
+ if (parent != null) {
+ if (parent.childrenDestroyed(this)) {
+ parent.destroy(rm);
+ }
+ }
+ data=null;
+ }
+
+ /**
+ * Check if this block is already present, if yes, loads it.
+ * @param nc the context
+ * @return YES if the block is present, NO if not
+ */
+
+ public abstract boolean check( NodeContext nc );
+
+ /**
+ * Download this node (and the children below). Note that the
+ * processing is asynchronous until the pmodel is called with position
+ * == total (and thus no more requests are pending) or the request
+ * manager is aborted by the user.
+ * @param nc the context
+ * @param rm the request manager
+ */
+
+ public abstract void download( NodeContext nc, RequestManager rm );
+
+ /**
+ * Insert the current block into the network. Implementations
+ * are also responsible for updating the corresponding fields
+ * of the parent node (of course, except if the parent is
+ * null in the case of the top-node in the tree).<p>
+ * Inner nodes first call the respective inserter methods for
+ * their children.<p>
+ *
+ * Insert a block (send appropriate message to gnunetd).
+ * This method encrypts the block and then sends an
+ * index or insertion request to gnunetd, depending on
+ * the configuration.
+ *
+ * @param nc the node context/the context (gives us the priority)
+ * @param sock the socket to use to talk to the core/the socket to talk
to gnunetd, null if
+ * we just do a "fake" insert to compute the tree in memory
+ * @return OK on success, false on error
+ */
+
+ public boolean insert( NodeContext nc, CSSession sock )
+ {
+ ContentBlock edata;
+ CSIndexBlock request;
+ CSResult rv;
+ ContentIndex index;
+
+ edata = encrypt();
+ if (sock == null) {
+ return true; // fake insert only
+ }
+
+ if (nc.index != 0) {
+ index=new ContentIndex();
+ index.importance=nc.priority;
+ index.type=ContentIndex.LOOKUP_TYPE_CHKS;
+ index.fileNameIndex=nc.index;
+ index.fileOffset=pos;
+ index.hash=(HashCode512)
PersistentHelper.copy(chk.query);
+
+ request=new CSIndexBlock(index);
+ if (!sock.send(request)) {
+ log(Level.WARNING,"Could not send index
information to gnunetd. Is gnunetd running ?");
+ return false;
+ }
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send
confirmation of insertion.");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"Server could not perform
indexing.");
+ return false;
+ }
+ return true;
+ }
+ return insertCHKBlock(sock,edata,nc.priority);
+ }
+
+ public abstract boolean receive( HashCode512 query, CSResultChk reply,
RequestManager rm, NodeContext nc );
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static Block create( int size )
+ {
+ assert(size>0) : "size ("+size+") not allowed to be <= 0";
+
+ return (size>ContentBlock.SIZE ? (Block) new IBlock(size) :
(Block) new DBlock(size));
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDelete3Hash.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDelete3Hash.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDelete3Hash.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,75 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSDelete3Hash extends CSMessage
+{
+ public static final int SIZE =
8+HashCode512.SIZE+ContentBlock.SIZE;
+
+ /** The (initial) priority of the data. */
+ public int importance;
+
+ /** The doubleHash of the plaintext. */
+ public HashCode512 doubleHash;
+
+ /** The data to insert */
+ public ContentBlock content;
+
+
+ public CSDelete3Hash()
+ {
+ super(IS_DELETE_3HASH);
+ importance=0;
+ doubleHash=new HashCode512();
+ content=new ContentBlock();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_DELETE_3HASH,"bad type !");
+
+ importance=buf.getInt();
+ doubleHash.readBytes(buf,err);
+ content.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_DELETE_3HASH);
+ buf.putInt(importance);
+ doubleHash.writeBytes(buf);
+ content.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDeleteChk.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDeleteChk.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSDeleteChk.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,68 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSDeleteChk extends CSMessage
+{
+ public static final int SIZE = 8+ContentBlock.SIZE;
+
+ /** The (initial) priority of the data */
+ public int importance;
+
+ /** The data to insert */
+ public ContentBlock content;
+
+
+ public CSDeleteChk()
+ {
+ super(IS_DELETE_CHK);
+ importance=0;
+ content=new ContentBlock();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_DELETE_CHK,"bad type !");
+
+ importance=buf.getInt();
+ content.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_DELETE_CHK);
+ buf.putInt(importance);
+ content.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSGetAvgPriority.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSGetAvgPriority.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSGetAvgPriority.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ */
+
+public class CSGetAvgPriority extends CSMessage
+{
+ public static final int SIZE = 4;
+
+
+ public CSGetAvgPriority()
+ {
+ super(IS_GET_AVG_PRIORITY);
+ }
+
+ public String toString()
+ {
+ return "Get average priority message";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"invalid size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_GET_AVG_PRIORITY,"invalid type !");
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_GET_AVG_PRIORITY);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexBlock.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexBlock.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,73 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the INDEX of the node.
+ */
+
+public class CSIndexBlock extends CSMessage
+{
+ public static final int SIZE = 4+ContentIndex.SIZE;
+
+ /** indexing information */
+ private ContentIndex contentIndex;
+
+
+ public CSIndexBlock()
+ {
+ super(IS_INDEX_BLOCK);
+ contentIndex=new ContentIndex();
+ }
+
+ public CSIndexBlock( ContentIndex idx )
+ {
+ this();
+ contentIndex=idx;
+ }
+
+ public String toString()
+ {
+ return "Client/server index block
[contentIndex="+contentIndex+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public ContentIndex getContentIndex()
+ {
+ return contentIndex;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_INDEX_BLOCK,"bad type !");
+
+ contentIndex.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_INDEX_BLOCK);
+ contentIndex.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexFile.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexFile.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,79 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a filename to the list of directly shared files
+ */
+
+public class CSIndexFile extends CSMessage
+{
+ public static final int SIZE = 8+HashCode512.SIZE;
+
+ /** Size of the file in bytes. */
+ public long filesize;
+
+ /** RIPE160 hash of the entire file (to avoid duplicates !). */
+ public HashCode512 hash;
+
+
+ public CSIndexFile()
+ {
+ super(IS_INDEX_FILE);
+ filesize=0;
+ hash=new HashCode512();
+ }
+
+ public CSIndexFile( FileLocation f )
+ {
+ this();
+ filesize=f.getSize();
+ hash=f.getHash();
+ }
+
+ public String toString()
+ {
+ return "Client/server index file [filesize="+filesize+",
hash="+hash+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_INDEX_FILE,"bad type !");
+
+ filesize=buf.getInt(); //fixme: convert to a long value
+
+ hash=new HashCode512();
+ hash.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_INDEX_FILE);
+ buf.putInt((int) filesize); //fixme: convert to a long value
+ hash.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexSuper.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexSuper.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSIndexSuper.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,87 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a super-query to the bloom filter.
+ */
+
+public class CSIndexSuper extends CSMessage
+{
+ public static final int SIZE = HashCode512.SIZE+8;
+
+ /** The super-hash for the bloom-filter. */
+ private HashCode512 superHash;
+
+ /** The (initial) priority of the data. */
+ private int importance;
+
+
+ public CSIndexSuper()
+ {
+ super(IS_INDEX_SUPER);
+ superHash=new HashCode512();
+ importance=0;
+ }
+
+ public CSIndexSuper( HashCode512 h, int pri )
+ {
+ this();
+ superHash=(HashCode512) PersistentHelper.copy(h);
+ importance=pri;
+ }
+
+ public String toString()
+ {
+ return "Client/server index super [superHash="+superHash+",
importance="+importance+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public HashCode512 getSuperHash()
+ {
+ return superHash;
+ }
+
+ public int getImportance()
+ {
+ return importance;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_INDEX_SUPER,"bad type !");
+
+ superHash.readBytes(buf,err);
+
+ importance=buf.getInt();
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_INDEX_SUPER);
+ superHash.writeBytes(buf);
+ buf.putInt(importance);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsert3Hash.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsert3Hash.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsert3Hash.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,98 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSInsert3Hash extends CSMessage
+{
+ public static final int SIZE =
HashCode512.SIZE+ContentBlock.SIZE+8;
+
+ /** The (initial) priority of the data (network byte order) */
+ private int importance;
+
+ /** The doubleHash of the plaintext. */
+ private HashCode512 doubleHash;
+
+ /** The data to insert */
+ private ContentBlock content;
+
+
+ public CSInsert3Hash()
+ {
+ super(IS_INSERT_3HASH);
+ importance=0;
+ doubleHash=new HashCode512();
+ content=new ContentBlock();
+ }
+
+ public CSInsert3Hash( int prio, HashCode512 h, ContentBlock c )
+ {
+ this();
+ importance=prio;
+ doubleHash=h;
+ content=c;
+ }
+
+ public String toString()
+ {
+ return "Insert 3-hash (c/s) [importance="+importance+",
doubleHash="+doubleHash+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getImportance()
+ {
+ return importance;
+ }
+
+ public HashCode512 getDoubleHash()
+ {
+ return doubleHash;
+ }
+
+ public ContentBlock getContent()
+ {
+ return content;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_INSERT_3HASH,"bad type !");
+
+ importance=buf.getInt();
+ doubleHash.readBytes(buf,err);
+ content.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_INSERT_3HASH);
+ buf.putInt(importance);
+ doubleHash.writeBytes(buf);
+ content.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertChk.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertChk.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertChk.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,87 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSInsertChk extends CSMessage
+{
+ private static final int SIZE = ContentBlock.SIZE+8;
+
+ /** The (initial) priority of the data. */
+ private int importance;
+
+ /** The data to insert */
+ private ContentBlock content;
+
+
+ public CSInsertChk()
+ {
+ super(IS_INSERT_CHK);
+ importance=0;
+ content=new ContentBlock();
+ }
+
+ public CSInsertChk( int prio, byte[] b )
+ {
+ this();
+ importance=prio;
+ assert(b.length==ContentBlock.SIZE);
+ System.arraycopy(b,0,content.content,0,b.length);
+ }
+
+ public String toString()
+ {
+ return "Client/server insert chk [importance="+importance+",
content="+content+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getImportance()
+ {
+ return importance;
+ }
+
+ public ContentBlock getContent()
+ {
+ return content;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_INSERT_CHK,"bad type !");
+
+ importance=buf.getInt();
+
+ content.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_INSERT_CHK);
+ buf.putInt(importance);
+ content.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertSBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertSBlock.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSInsertSBlock.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,85 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSInsertSBlock extends CSMessage
+{
+ public static final int SIZE = SBlock.SIZE+8;
+
+ /** The (initial) priority of the data. */
+ private int importance;
+
+ /** The data to insert. */
+ private EncryptedSBlock content;
+
+
+ public CSInsertSBlock()
+ {
+ super(IS_INSERT_SBLOCK);
+ importance=0;
+ content=null;
+ }
+
+ public CSInsertSBlock( int i, EncryptedSBlock eb )
+ {
+ this();
+ importance=i;
+ content=eb;
+ }
+
+ public String toString()
+ {
+ return "C/S insert SBlock [importance="+importance+",
content="+content+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getImportance()
+ {
+ return importance;
+ }
+
+ public EncryptedSBlock getContent()
+ {
+ return content;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_INSERT_SBLOCK,"bad type !");
+
+ importance=buf.getInt();
+ content.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_INSERT_SBLOCK);
+ buf.putInt(importance);
+ content.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSLinkFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSLinkFile.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSLinkFile.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,89 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * AFS message sent for linking to a file.
+ */
+
+public class CSLinkFile extends CSMessage
+{
+ public static final int SIZE = 4+HashCode512.SIZE;
+
+ /** RIPE160 hash of the entire file (to avoid duplicates !) */
+ public HashCode512 hash;
+
+ /** Path of the file. */
+ public String data;
+
+
+ public CSLinkFile()
+ {
+ super(IS_LINK_FILE);
+ hash=new HashCode512();
+ data="";
+ }
+
+ public CSLinkFile( FileLocation f )
+ {
+ this();
+ hash=f.getHash();
+ data=f.getPath();
+ }
+
+ public String toString()
+ {
+ return "Client/server link file [hash="+hash+", data="+data+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getPath() //todo: use FileLocation
+ {
+ return data;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE+data.length();
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ byte[] b;
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size<SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_LINK_FILE,"bad type !");
+
+ hash=new HashCode512();
+ hash.readBytes(buf,err);
+
+ //fixme: character set encoding
+ b=new byte[size-SIZE];
+ buf.get(b);
+ data=new String(b);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) getByteSize());
+ buf.putShort((short) IS_LINK_FILE);
+ hash.writeBytes(buf);
+
+ //fixme: character set encoding
+ buf.put(data.getBytes());
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSNSQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSNSQuery.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSNSQuery.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,60 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * peer-to-peer message containing a namespace-query
+ */
+
+public class CSNSQuery extends CSMessage
+{
+ public static final int SIZE = HashCode512.SIZE*2+12;
+
+ /** how important is this request (network byte order) */
+ public int priority;
+
+ /** time to live in cronMILLIS (network byte order) */
+ public int ttl;
+
+ /** ID of the Namespace that we are searching in */
+ public HashCode512 namespace;
+
+ /** ID (in the namespace) that we're looking for */
+ public HashCode512 identifier;
+
+
+ public CSNSQuery()
+ {
+ super(IS_NSQUERY);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ throw new IllegalStateException();
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ throw new IllegalStateException();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSQuery.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSQuery.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,151 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * peer-to-peer message containing a set of queries.
+ */
+
+public class CSQuery extends CSMessage
+{
+ public static final int SIZE = 12;
+
+ /** */
+ private int size;
+
+ /** How important is this request ? */
+ private int priority;
+
+ /** Time to live in milliseconds. */
+ private int ttl;
+
+ /** Hashcodes of the files we're looking for.
+ If multiple queries are given, the first query is the super-query for
the bloom filter. */
+ private List queries;
+
+
+ public CSQuery()
+ {
+ super(IS_QUERY);
+ size=SIZE;
+ priority=0;
+ ttl=0;
+ queries=new ArrayList();
+ }
+
+ public String toString()
+ {
+ return "Client/server query [priority="+priority+",
ttl="+ttl+", queries.count="+queries.size()+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getPriority()
+ {
+ return priority;
+ }
+
+ public void setPriority( int pri )
+ {
+ priority=pri;
+ }
+
+ public int getTTL()
+ {
+ return ttl;
+ }
+
+ public void setTTL( int time )
+ {
+ ttl=time;
+ }
+
+ public void setPriorityAndTTL( int pri, int time )
+ {
+ priority=pri;
+ ttl=time;
+ }
+
+ public int getQueriesCount()
+ {
+ return queries.size();
+ }
+
+ public HashCode512 getQuery( int index )
+ {
+ return ((index>=0 && index<queries.size()) ? (HashCode512)
queries.get(index) : null);
+ }
+
+ public void addQuery( HashCode512 h )
+ {
+ queries.add(PersistentHelper.copy(h));
+ size+=HashCode512.SIZE;
+ }
+
+ public HashCode512[] getQueries()
+ {
+ return (HashCode512[]) queries.toArray(new
HashCode512[queries.size()]);
+ }
+
+ public void setQueries( HashCode512[] q )
+ {
+ queries.clear();
+ queries.addAll(Arrays.asList(q));
+ size=SIZE+q.length*HashCode512.SIZE;
+ }
+
+ public void clearQueries()
+ {
+ queries.clear();
+ size=SIZE;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE+queries.size()*HashCode512.SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ HashCode512 h;
+ int type,i;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf((size<SIZE || ((size-SIZE) %
HashCode512.SIZE)!=0),"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_QUERY,"bad type !");
+
+ priority=buf.getInt();
+ ttl=buf.getInt();
+
+ queries.clear();
+ for (i=(size-SIZE)/HashCode512.SIZE; i>0; i--) {
+ h=new HashCode512();
+ h.readBytes(buf,err);
+ queries.add(h);
+ }
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.putShort((short) size);
+ buf.putShort((short) IS_QUERY);
+ buf.putInt(priority);
+ buf.putInt(ttl);
+ for (i=0; i<queries.size(); i++) {
+ ((HashCode512) queries.get(i)).writeBytes(buf);
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResult3Hash.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResult3Hash.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResult3Hash.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,104 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: search result content send back by gnunetd
+ */
+
+public class CSResult3Hash extends CSMessage implements AFSConstants
+{
+ public static final int SIZE = HashCode512.SIZE+CONTENT_SIZE+4;
+
+ /** The double-hash. */
+ private HashCode512 hash;
+
+ /** The search result. */
+ private byte[] result;
+
+
+ public CSResult3Hash()
+ {
+ super(IS_RESULT_3HASH);
+ hash=new HashCode512();
+ result=new byte[CONTENT_SIZE];
+ }
+
+ public CSResult3Hash( HashCode512 h, ContentBlock block )
+ {
+ this();
+ hash=(HashCode512) PersistentHelper.copy(h); //todo: copie
utile ???
+ System.arraycopy(block.content,0,result,0,CONTENT_SIZE);
+ }
+
+ public String toString()
+ {
+ return "C/S 3-hash result [hash="+hash+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public HashCode512 getDoubleHash()
+ {
+ return hash;
+ }
+
+ public HashCode512 getTripleHash()
+ {
+ return HashCode512.create(PersistentHelper.toBytes(hash));
+ }
+
+ /**
+ * Returns the content of the node in bytes.
+ * @param keyword
+ * @return
+ */
+
+ public byte[] decrypt( HashCode512 keyword )
+ {
+ byte[] iv,buf;
+ SessionKey skey;
+
+ // get key and init value from the hash code
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+ skey=keyword.extractKey(iv);
+
+ buf=skey.decrypt(result,iv);
+ return buf;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_RESULT_3HASH,"bad type !");
+
+ hash.readBytes(buf,err);
+ buf.get(result);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_RESULT_3HASH);
+ hash.writeBytes(buf);
+ buf.put(result);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultChk.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultChk.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultChk.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,95 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * client-server message for search results
+ *
+ * Used in the CS-TCP communication: search result content send back
+ * by gnunetd
+ */
+
+public class CSResultChk extends CSMessage
+{
+ public static final int SIZE = ContentBlock.SIZE+4;
+
+ /** The search result. */
+ public ContentBlock result;
+
+
+ public CSResultChk()
+ {
+ super(IS_RESULT_CHK);
+ result=null;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Decrypts inner data block.
+ *
+ * @param keyword represents the key concatenated with the
initial value used in the alg
+ * @return decrypted block on success, null on
error
+ */
+
+ public byte[] decrypt( HashCode512 keyword )
+ {
+ byte[] iv;
+ SessionKey skey;
+
+ // get key and init value from the hash code
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+ skey=keyword.extractKey(iv);
+
+ return skey.decrypt(result.content,iv);
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_RESULT_CHK,"bad type !");
+
+ result=new ContentBlock();
+ result.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_RESULT_CHK);
+
+ if (result!=null) {
+ result.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<ContentBlock.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultSBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultSBlock.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSResultSBlock.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,75 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * client-server message for SBlock results
+ *
+ * Used in the CS-TCP communication: SBlock result content send back
+ * by gnunetd
+ */
+
+public class CSResultSBlock extends CSMessage
+{
+ public static final int SIZE = 4+EncryptedSBlock.SIZE;
+
+ /** The search result. */
+ private EncryptedSBlock result;
+
+
+ public CSResultSBlock()
+ {
+ super(IS_RESULT_SBLOCK);
+ result=new EncryptedSBlock();
+ }
+
+ public CSResultSBlock( EncryptedSBlock esb )
+ {
+ this();
+ result=(EncryptedSBlock) PersistentHelper.copy(esb); //todo:
copie utile ???
+ }
+
+ public String toString()
+ {
+ return "C/S result SBlock [result="+result+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public EncryptedSBlock getResult()
+ {
+ return result;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_RESULT_SBLOCK,"bad type !");
+
+ result.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_RESULT_SBLOCK);
+ result.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexBlock.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexBlock.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the INDEX of the node.
+ */
+
+public class CSUnindexBlock extends CSMessage
+{
+ public static final int SIZE = ContentIndex.SIZE+4;
+
+ /** indexing information */
+ public ContentIndex contentIndex;
+
+
+ public CSUnindexBlock()
+ {
+ super(IS_UNINDEX_BLOCK);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ throw new IllegalStateException();
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ throw new IllegalStateException();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexFile.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexFile.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,79 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a filename to the list of directly shared files
+ */
+
+public class CSUnindexFile extends CSMessage
+{
+ public static final int SIZE = 8+HashCode512.SIZE;
+
+ /** Size of the file. */
+ public long filesize;
+
+ /** RIPE160 hash of the entire file (to avoid duplicates !). */
+ public HashCode512 hash;
+
+
+ public CSUnindexFile()
+ {
+ super(IS_UNINDEX_FILE);
+ filesize=0;
+ hash=new HashCode512();
+ }
+
+ public CSUnindexFile( FileLocation f )
+ {
+ this();
+ filesize=f.getSize();
+ hash=f.getHash();
+ }
+
+ public String toString()
+ {
+ return "Client/server unindex file [filesize="+filesize+",
hash="+hash+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_UNINDEX_FILE,"bad type !");
+
+ filesize=buf.getInt(); //fixme: convert to a long value
+
+ hash=new HashCode512();
+ hash.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_UNINDEX_FILE);
+ buf.putInt((int) filesize); //fixme: convert to a long value
+ hash.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexSuper.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexSuper.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUnindexSuper.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,55 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a super-query to the bloom filter.
+ */
+
+public class CSUnindexSuper extends CSMessage
+{
+ public static final int SIZE = HashCode512.SIZE+8;
+
+ /** The super-hash for the bloom-filter. */
+ public HashCode512 superHash;
+
+ /** The (initial) priority of the data (network byte order) */
+ public int importance;
+
+
+ public CSUnindexSuper()
+ {
+ super(IS_UNINDEX_SUPER);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ throw new IllegalStateException();
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ throw new IllegalStateException();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUploadFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUploadFile.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/CSUploadFile.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,125 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * AFS message sent for uploading a file.
+ */
+
+public class CSUploadFile extends CSMessage
+{
+ public static final int SIZE = 8+HashCode512.SIZE;
+
+ /** Position in the file. */
+ public long pos;
+
+ /** RIPE160MD hash of the entire file (to avoid duplicates !). */
+ public HashCode512 hash;
+
+ /** Chunk of binary content. */
+ public byte[] data;
+
+
+ public CSUploadFile()
+ {
+ super(IS_UPLOAD_FILE);
+ pos=0;
+ hash=new HashCode512();
+ data=new byte[0];
+ }
+
+ public CSUploadFile( FileLocation f )
+ {
+ this();
+ hash=f.getHash();
+ }
+
+ public String toString()
+ {
+ return "Client/server upload file [pos="+pos+", hash="+hash+",
data="+data+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public byte[] getChunk()
+ {
+ return data;
+ }
+
+ public int copyChunk( FileChannel fc ) throws IOException //todo:
with MappedFile
+ {
+ Logger logger;
+ int copied,read;
+
+ pos=(int) fc.position();
+
+ copied=Math.min(65532-SIZE,(int) (fc.size()-fc.position()));
+
+ data=new byte[copied];
+ read=fc.read(ByteBuffer.wrap(data));
+ if (read!=copied) {
+ logger=Logger.getLogger(getClass().getName());
+ logger.log(Level.SEVERE,"Unable to read "+copied+"
bytes from "+fc+" (read "+read+") !");
+ return -1;
+ }
+ return read;
+ }
+
+ public int copyChunk( byte[] b, int offset )
+ {
+ int copied;
+
+ pos=offset;
+
+ copied=Math.min(65532-SIZE,b.length-offset);
+
+ data=new byte[copied];
+ System.arraycopy(b,offset,data,0,copied);
+ return copied;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE+data.length;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size<SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_UPLOAD_FILE,"bad type !");
+
+ pos=buf.getInt(); //fixme: convert to a long value
+
+ hash=new HashCode512();
+ hash.readBytes(buf,err);
+
+ data=new byte[size-SIZE];
+ buf.get(data);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) getByteSize());
+ buf.putShort((short) IS_UPLOAD_FILE);
+ buf.putInt((int) pos); //fixme: convert to a long value
+ hash.writeBytes(buf);
+ buf.put(data);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/ChkHashes.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/ChkHashes.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/ChkHashes.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,114 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Pair of Hashcodes for CHK encoded blocks.
+ *
+ * Every DBlock and IBlock is represented by two
+ * hashcodes, one is the key used to encrypt or
+ * decrypt the block; the other one is used to
+ * search for the block without reveiling the key.
+ * See also Freenet's CHK keys.<p>
+ *
+ * Note that GNUnet uses a different encoding for
+ * the RBlocks (root-nodes) in order to make searches
+ * possible.
+ */
+
+public class ChkHashes extends Object implements Persistent
+{
+ public static final int SIZE = HashCode512.SIZE*2;
+
+ /** The hash of the plaintext is the key to decrypt. */
+ public HashCode512 key;
+
+ /** The hash of the encrypted block is the query. */
+ public HashCode512 query;
+
+
+ public ChkHashes()
+ {
+ super();
+ key=new HashCode512();
+ query=new HashCode512();
+ }
+
+ public String toString()
+ {
+ return "Check hashes [key="+key+", query="+query+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public HashCode512 getKeyHash()
+ {
+ return key;
+ }
+
+ public HashCode512 getQueryHash()
+ {
+ return query;
+ }
+
+ public int hashCode()
+ {
+ return (key.hashCode()<<1)^query.hashCode();
+ }
+
+ public boolean equals( Object obj )
+ {
+ ChkHashes c;
+
+ if (!(obj instanceof ChkHashes)) {
+ return false;
+ }
+ c=(ChkHashes) obj;
+ return (c.key.equals(key) && c.query.equals(query));
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ key=new HashCode512();
+ key.readBytes(buf,err);
+
+ query=new HashCode512();
+ query.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ if (key!=null) {
+ key.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ if (query!=null) {
+ query.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentBlock.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentBlock.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,63 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * basic transmission unit for content in GNUnet
+ *
+ * A CONTENT_Block, representative of the structure
+ * of the leaf nodes (a simple chunk of 1 kb of data)
+ */
+
+public class ContentBlock extends Object implements Persistent, AFSConstants
+{
+ public static final int SIZE = 1024;
+
+
+ public byte[] content;
+
+//TODO: SUPPRIMER, NE SERT A RIEN !!!!!!!!!!!!
+ public ContentBlock()
+ {
+ super();
+ content=new byte[CONTENT_SIZE];
+ Arrays.fill(content,(byte) 0); // not necessary, but...
+ }
+
+ public ContentBlock( byte[] b )
+ {
+ this();
+ assert(b.length==CONTENT_SIZE);
+ System.arraycopy(b,0,content,0,b.length);
+ }
+
+ public String toString()
+ {
+ return "Content block";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ buf.get(content);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.put(content);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentEncoding.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentEncoding.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentEncoding.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,65 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Encryption and decryption of blocks for deniability.
+ */
+
+public class ContentEncoding extends LoggedObject
+{
+ public ContentEncoding()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Encrypts a given data block
+ *
+ * @param data represents the data block
+ * @param hashcode represents the key concatenated with the initial
+ * value used in the alg
+ * @param result where to store the result (encrypted block)
+ * @return OK on success, false on error
+ */
+
+ public boolean encryptContent( ContentBlock data, HashCode512 hashcode,
ContentBlock result )
+ {
+ return encryptContent(data.content,hashcode,result);
+ }
+
+ public boolean encryptContent( byte[] data, HashCode512 hashcode,
ContentBlock result )
+ {
+ byte[] iv,res;
+ SessionKey skey;
+
+ if (data==null || hashcode==null || result==null) {
+ trace("Aborting encryptContent: null in arguments.");
+ return false;
+ }
+
+ // get key and init value from the hash code
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+ skey=hashcode.extractKey(iv);
+
+ res=skey.encrypt(data,0,ContentBlock.SIZE,iv);
+ if (res==null) {
+ return false;
+ }
+ System.arraycopy(res,0,result.content,0,res.length);
+ return true;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentIndex.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentIndex.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/ContentIndex.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,136 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Type of the content index file entries. The size of this
+ * struct dominates the database size, so keep it as small
+ * as possible. 32 byte should be enough!
+ *
+ * This structure is also used as a convenience struct to
+ * pass arguments around the db. Perhaps not a good idea.
+ */
+
+public class ContentIndex extends Object implements Persistent
+{
+ public static final int SIZE = HashCode512.SIZE+12;
+
+ /** Free entry. Historical. */
+ public static final int LOOKUP_TYPE_FREE = 0;
+
+ /** Historical. */
+ public static final int LOOKUP_TYPE_DELETED = 1;
+
+ /** (migrated) CHK content. */
+ public static final int LOOKUP_TYPE_CHK = 2;
+
+ /** Search result, never indexed (always inserted). */
+ public static final int LOOKUP_TYPE_3HASH = 3;
+
+ /** Super-query. Add to superBloomFilter, does not refer to any content
in particular. */
+ public static final int LOOKUP_TYPE_SUPER = 4;
+
+ /** CHK content covered by super-query (treat like CHK except do not
add to singleBloomFilter). */
+ public static final int LOOKUP_TYPE_CHKS = 5;
+
+ /** SBlock content. */
+ public static final int LOOKUP_TYPE_SBLOCK = 6;
+
+ /** The double-hash (hash of the hash of the plaintext) of this entry
for 3HASH entries, or the CHK query hash
+ (hash of the encrypted content) for CHK entries. Which is the case can
be determined by looking at fileNameIndex and fileOffset. */
+ public HashCode512 hash;
+
+ /** The current rating of this content (in network byte order). */
+ public int importance;
+
+ /** The type of the entry. See LOOKUP_TYPE_XXX. The field is always in
network byte order. */
+ public int type;
+
+ /** This field gives the index of the file into the file-index module
if the value is >0. If the
+ value is 0, the file is in the contentdatabase. The field is always in
network byte order. */
+ public int fileNameIndex;
+
+ /** The offset in the file for on-demand-encoded files where
fileNameIndex is >0. */
+ public long fileOffset;
+
+
+ public ContentIndex()
+ {
+ super();
+ hash=null;
+ importance=0;
+ type=0;
+ fileNameIndex=0;
+ fileOffset=0;
+ }
+
+ public String toString()
+ {
+ return "Content index";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int hashCode()
+ {
+ return type;
+ }
+
+ public boolean equals( Object obj )
+ {
+ ContentIndex ce;
+
+ if (!(obj instanceof ContentIndex)) {
+ return false;
+ }
+
+ ce=(ContentIndex) obj;
+ return ((ce.hash!=null ? ce.hash.equals(hash) : hash==null) &&
+ ce.importance==importance &&
+ ce.type==type &&
+ ce.fileNameIndex==fileNameIndex &&
+ ce.fileOffset==fileOffset);
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ hash=new HashCode512();
+ hash.readBytes(buf,err);
+
+ importance=buf.getInt();
+ type=buf.getShort() & 0x0000ffff;
+ fileNameIndex=buf.getShort() & 0x0000ffff;
+ fileOffset=buf.getInt(); //fixme: long value
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ if (hash!=null) {
+ hash.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ buf.putInt(importance);
+ buf.putShort((short) type);
+ buf.putShort((short) fileNameIndex);
+ buf.putInt((int) fileOffset); //fixme: long value
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/DBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/DBlock.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/DBlock.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,284 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * Leaf (level-zero node) in the merkle-tree.
+ */
+
+public class DBlock extends Block
+{
+ /**
+ * Create a top-DBlock for files <= 1k where there is no parent IBlock.
+ *
+ * @param size the size of the file
+ */
+
+ public DBlock( int size )
+ {
+ super(size,size);
+ }
+
+ /**
+ * Create a DBlock. Note that this method can NOT be used for files <=
+ * 1k since parent may not be null (which it would be for the
+ * top-block). Use createTopDBlock for files <= 1k.
+ *
+ * @param parent the parent block
+ * @param pos2 the offset of b block in the file
+ */
+
+ public DBlock( IBlock parent, int pos2 )
+ {
+
super(parent,pos2,Math.min(ContentBlock.SIZE,parent.getFileSize()-pos2));
+ }
+
+ public String toString()
+ {
+ return "Data block [offset="+getFilePosition()+",
size="+getFileSize()+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getDepth()
+ {
+ return 0;
+ }
+
+ /**
+ * Insert the current block into the network. Implementations
+ * are also responsible for updating the corresponding fields
+ * of the parent node (of course, except if the parent is
+ * null in the case of the top-node in the tree).<p>
+ *
+ * Inner nodes first call the respective inserter methods for
+ * their children.<p>
+ *
+ * @param nc the context (gives us the priority)
+ * @param sock the socket to use to talk to the core
+ * @return OK on success, false on error
+ */
+
+ public boolean insert( NodeContext nc, CSSession sock )
+ {
+ boolean res;
+
+//debug("DI1 "+hasData());
+ if (hasData()) {
+ return true;
+ }
+
+//debug("will load");
+ if (!loadData(nc,sock!=null)) {
+ return false;
+ }
+
+ debug("Read "+getDataLength()+" bytes from IOC for insertion
(2).");
+
+ nc.stats.progress += getDataLength();
+ if (nc.pmodel != null) {
+ nc.pmodel.progress(nc.stats,nc.data);
+ }
+
+ res = super.insert(nc,sock);
+ debug("Inserting dblock "+getFilePosition()+" of len
"+getDataLength()+" under query "+getQuery().toHex()+".");
+ return res;
+ }
+
+ /**
+ * Delete the current block from the local peer. Works just
+ * like insert.
+ *
+ * @param nc the context (gives us the priority)
+ * @param sock the socket to use to talk to the core
+ * @return OK on success, false on error
+ */
+
+ public boolean delete( NodeContext nc, CSSession sock )
+ {
+ boolean res;
+
+ if (hasData()) {
+ return true;
+ }
+
+ if (!loadData(nc,sock!=null)) {
+ return false;
+ }
+ debug("Loaded "+getDataLength()+" bytes for deletion.");
+
+ nc.stats.progress += getDataLength();
+ if (nc.pmodel != null) {
+ nc.pmodel.progress(nc.stats,nc.data);
+ }
+ res = super.delete(nc,sock);
+ debug("Deleting dblock "+getFilePosition()+" of len
"+getDataLength()+" under query "+getQuery().toHex());
+ return res;
+ }
+
+ /**
+ * Check if this dblock is already present on the drive.
+ * If the block is present, the parent and the
+ * ProgressModel are notified.
+ *
+ * @param nc the context (gives us the priority)
+ * @return YES if present, NO if not.
+ */
+
+ public boolean check( NodeContext nc )
+ {
+ HashCode512 hc;
+
+ debug("check: "+this);
+
+ /* first check if its already present */
+
+ if (loadData(nc,false)) {
+ hc=HashCode512.create(getRawData(),0,getDataLength());
+ if (hc.equals(getKey())) {
+ setStatus(BLOCK_PRESENT);
+ nc.stats.filesize = getFileSize();
+ nc.stats.progress += getDataLength();
+ nc.pmodel.progress(nc.stats,nc.data);
+ return true;
+ }
+ }
+ clearData();
+ return false;
+ }
+
+ /**
+ * Download this node (and the children below). Note that the
+ * processing is asynchronous until the pmodel is called with position
+ * == total (and thus no more requests are pending) or the request
+ * manager is aborted by the user.
+ *
+ * @param nc the context (gives us the priority)
+ * @param rm the request manager
+ */
+
+ public void download( NodeContext nc, RequestManager rm )
+ {
+ debug("download");
+
+ if (check(nc)) {
+ if (getParent() != null) {
+ getParent().childDownloadCompleted(this,nc,rm);
+ }
+ /* leaf node, we're done when present */
+ setStatus(BLOCK_DONE);
+ destroy(rm);
+ return;
+ }
+ // not present, either request ourselves or let the parent do
it automagically when we return...
+ setStatus(BLOCK_PENDING);
+ if (getParent() == null) {
+ issueQuery(rm,new Listener() {
+ public boolean listen( HashCode512 query,
CSResultChk reply, RequestManager rmg, NodeContext data )
+ {
+ return receive(query,reply,rmg,data);
+ }
+ },nc,getQuery());
+ }
+ }
+
+ /**
+ * Function that is called when a message matching a
+ * request for a DBlock is received. Decrypts the received
+ * block and writes it to the file. Notifies the parent
+ * and the ProgressModel.
+ *
+ * @param query the query that was sent out
+ * @param reply the reply that was received
+ * @param rm the handle for the request manager
+ * @param nc the context (gives us the priority)
+ * @return false the request manager should abort the download,
+ * OK if everything is fine
+ */
+
+ public boolean receive( HashCode512 query, CSResultChk reply,
RequestManager rm, NodeContext nc )
+ {
+ ProgressStats pstats;
+ int i;
+
+ debug("dblxock_download_receive : "+this);
+
+ if (!isStatus(BLOCK_PENDING)) {
+ trace("dblxock_download_receive called, but no request
was pending !");
+ return false;
+ }
+ if (!receivedChk(query,reply)) {
+ pstats=new ProgressStats();
+ nc.pmodel.progress(pstats,nc.data);
+ return false;
+ }
+ if (!writeData(nc)) {
+ pstats=new ProgressStats();
+ nc.pmodel.progress(pstats,nc.data);
+ log(Level.SEVERE,"Writing to file failed !");
+ return false;
+ }
+
+ for (i=0; i<10; i++) {
+ if (nc.stats.progress * 10000L > nc.stats.filesize *
(10000L-(1024>>i)) &&
+ (nc.stats.progress-getDataLength()) * 10000L <=
nc.stats.filesize * (10000L - (1024 >> i))
+ ) {
+ // end-game boundary crossed, slaughter TTLs
+ rm.endGame();
+ }
+ }
+
+ setStatus(BLOCK_PRESENT);
+ /* request satisfied, remove from RM */
+
+ nc.stats.progress += getDataLength();
+ if (getParent() != null) {
+ /* child, must tell parent to adjust requests */
+ getParent().childDownloadCompleted(this,nc,rm);
+ getParent().doSuperRequest(nc,rm);
+ }
+ else {
+ /* top block, must cancel my own request */
+ rm.update(this,null);
+ }
+ // leaf, done when download complete
+ setStatus(BLOCK_DONE);
+ destroy(rm);
+ nc.pmodel.progress(nc.stats,nc.data);
+ return true;
+ }
+
+ /**
+ * Print a block to log.
+ * @param indent
+ */
+
+ public void print( int indent )
+ {
+ debug("print: "+this);
+
+ log(Level.FINEST,Utils.repeat('*',indent)+" DBLOCK (0)
"+getFilePosition()+" "+getQuery().toHex());
+ }
+
+ /**
+ * Free the associated resources of this Block. DOES ALSO free the
+ * memory occupied by the Block struct itself!
+ *
+ * @param rm reference to the RequestManager for requests
+ */
+
+ public void destroy( RequestManager rm )
+ {
+ debug("destroy");
+ super.destroy(rm);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteURI.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteURI.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,29 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ */
+
+public class DeleteURI extends GeneralURI
+{
+// public int action;
+ public String filename;
+
+
+ public DeleteURI()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteUtil.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteUtil.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/DeleteUtil.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,116 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * deleteutil, helper methods for file deletion.
+ * Break file that is deleted into blocks and encrypts
+ * them according to the CHK-triple-hash-tree scheme.
+ * Then sends delete-requests to gnunetd.
+ */
+
+public class DeleteUtil extends LoggedObject
+{
+ public DeleteUtil()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Ask gnunetd for an index that matches the filename
+ * @param sock
+ * @param filename
+ * @return the index, -1 on error
+ */
+
+ protected int askDeleteFilename( CSSession sock, String filename )
+ {
+ CSUnindexFile request;
+ CSResult rv;
+ FileLocation f;
+
+ f=new FileLocation(filename);
+ if (!f.exists()) {
+ return -1;
+ }
+
+ request=new CSUnindexFile(f);
+
+ if (!sock.send(request)) {
+ log(Level.WARNING,"Could not send data to gnunetd. Is
gnunetd running ?");
+ return -1;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Could not receive data from gnunetd.
Is gnunetd running ?");
+ return -1;
+ }
+ return rv.getResult();
+ }
+
+ /**
+ * Deletes a file under the given name from the local GNUnet node.
+ *
+ * @param sock the socket to use to talk to gnunetd
+ * @param filename the name of the file to delete
+ * @param model the delete model used to
+ * update status information; points to null if
+ * no status updates shall be given, otherwise
+ * to a method that takes two size_t arguments
+ * (retrieved so far, total).
+ * @param model_data pointer that is passed to the model method
+ * @return OK on success, false on error
+ */
+
+ public boolean deleteFile( CSSession sock, String filename,
ProgressModel model, Object model_data )
+ {
+ NodeContext nc;
+ Block top;
+ int filesize,ret;
+
+ filename = new FileLocation(filename).getPath();
+ filesize = (int) new FileLocation(filename).getSize();
+
+ nc=new NodeContext();
+ nc.pmodel = model;
+ nc.data = model_data;
+ nc.stats.filesize = filesize;
+ nc.priority = 0;
+
+ ret=askDeleteFilename(sock, filename);
+ if (ret <= 0) {
+ return false;
+ }
+ nc.index = ret;
+ if (!nc.ioc.init(filesize,filename,true)) {
+ return false;
+ }
+
+ top = Block.create(filesize);
+ if (!top.delete(nc,sock)) {
+ top.destroy(null);
+ nc.ioc.free(false);
+ return false;
+ }
+ nc.ioc.free(false);
+ top.destroy(null);
+ return true;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadURI.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadURI.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,31 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ */
+
+public class DownloadURI extends GeneralURI
+{
+// public int action;
+ public FileIdentifier fid;
+ public String filename;
+
+
+ public DownloadURI()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadUtil.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadUtil.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/DownloadUtil.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,111 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.*;
+
+/**
+ * Helper functions for downloading.
+ * Download helper methods (which do the real work).
+ */
+
+public class DownloadUtil extends Object
+{
+
+ public DownloadUtil()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void pModelWrap( ProgressStats stats, PMWrap wrap )
+ {
+ if (wrap.userModel!=null) {
+ wrap.userModel.progress(stats,wrap.userData);
+ }
+ if (stats.progress == stats.filesize) {
+ wrap.nc.ioc.free(stats.progress!=0);
+ }
+ }
+
+ /**
+ * Download a file.
+ *
+ * @param app
+ * @param p
+ * @param pppp
+ * @param fi the file identification (CHK, crc32, size) of the file
+ * @param fileName the name of the file
+ * @param model the download model used to
+ * update status information; points to null if
+ * no status updates shall be given, otherwise
+ * to a method that takes two size_t arguments
+ * (retrieved so far, total).
+ * @param data pointer that is passed to the model method
+ * @return a request manager that can be used to abort on success, null
on error
+ */
+
+ public RequestManager downloadFile( AbstractClient app, Policy p,
Priority pppp, FileIdentifier fi, String fileName, ProgressModel model, Object
data )
+ {
+ NodeContext nc;
+ Block top;
+ RequestManager rm;
+ PMWrap wrap;
+
+ nc=new NodeContext();
+
+ rm=new RequestManager(app,p,pppp);
+ if (rm==null) {
+ return null;
+ }
+
+ if (!nc.ioc.init((int) fi.getFileLength(),fileName,false)) {
//fixme: long value
+ rm.destroy();
+ return null;
+ }
+
+ wrap=new PMWrap();
+ wrap.userModel = model;
+ wrap.userData = data;
+ wrap.nc = nc;
+
+ final PMWrap _wrap = wrap;
+
+ nc.priority = 0; /* unused */
+ nc.index = 0; /* unused */
+ nc.pmodel = new ProgressModel() {
+ public void progress( ProgressStats stats, Object o )
+ {
+ pModelWrap(stats,_wrap);
+ }
+ };
+ nc.data = wrap;
+ nc.stats=new ProgressStats();
+ nc.stats.filesize=(int) fi.getFileLength(); //fixme: long
value
+
+ top=Block.create((int) fi.getFileLength()); //fixme: long
value
+ top.setKeyAndQuery(fi);
+
+ rm.topCrc32=fi.getFileCRC();
+ rm.top=top;
+
+ top.download(nc,rm);
+ return rm;
+ }
+}
+
+class PMWrap extends Object
+{
+ protected ProgressModel userModel;
+ protected Object userData;
+ protected NodeContext nc;
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/EncryptedSBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/EncryptedSBlock.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/EncryptedSBlock.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,130 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class EncryptedSBlock extends LoggedObject implements Persistent
+{
+ public static final int SIZE = 1024;
+ public static final int ENCRYPTED_SIZE = 484;
+ public static final int SIGNED_SIZE = 504;
+
+ /** */
+ private byte[] encryptedData;
+
+ /** */
+ private HashCode512 identifier;
+
+ /** */
+ private Signature signature;
+
+ /** */
+ private PublicKey subspace;
+
+
+ public EncryptedSBlock()
+ {
+ super(true);
+ encryptedData=new byte[ENCRYPTED_SIZE];
+ identifier=new HashCode512();
+ signature=new Signature();
+ subspace=new PublicKey();
+ }
+
+ public EncryptedSBlock( byte[] b, HashCode512 id, Signature sig,
PublicKey pub )
+ {
+ this();
+
+ assert(b.length==ENCRYPTED_SIZE);
+ System.arraycopy(b,0,encryptedData,0,ENCRYPTED_SIZE);
+ identifier=(HashCode512) PersistentHelper.copy(id); //todo:
copie utile ???
+ signature=(Signature) PersistentHelper.copy(sig);
//todo: copie utile ???
+ subspace=(PublicKey) PersistentHelper.copy(pub);
//todo: copie utile ???
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Verify that a given encryted SBlock is well-formed.
+ * @return
+ */
+
+ public boolean verify()
+ {
+ byte[] raw;
+
+ raw=PersistentHelper.toBytes(this);
+ return subspace.verify(signature,raw,0,SIGNED_SIZE);
+ }
+
+ /**
+ * @param key
+ * @return
+ */
+
+ public SBlock decrypt( HashCode512 key )
+ {
+ byte[] raw,iv,tmp;
+ SessionKey skey;
+
+ raw=PersistentHelper.toBytes(this);
+
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+ skey=key.extractKey(iv);
+
+ tmp=skey.decrypt(raw,0,ENCRYPTED_SIZE,iv);
+ if (tmp==null || tmp.length!=ENCRYPTED_SIZE) {
+ return null;
+ }
+ System.arraycopy(tmp,0,raw,0,ENCRYPTED_SIZE);
+ return (SBlock)
PersistentHelper.readFully(SBlock.class,ByteBuffer.wrap(raw));
+ }
+
+ public HashCode512 getIdentifier()
+ {
+ return identifier;
+ }
+
+ public HashCode512 getNameSpace()
+ {
+ return HashCode512.create(PersistentHelper.toBytes(subspace));
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.put(encryptedData);
+ identifier.writeBytes(buf);
+ signature.writeBytes(buf);
+ subspace.writeBytes(buf);
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ buf.get(encryptedData);
+ identifier.readBytes(buf,err);
+ signature.readBytes(buf,err);
+ subspace.readBytes(buf,err);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Extractor.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Extractor.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Extractor.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,26 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ *
+ */
+
+public class Extractor extends Object
+{
+ public Extractor()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/FileIdentifier.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/FileIdentifier.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/FileIdentifier.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,187 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ * information required to download a file from GNUnet
+ *
+ * A FileIdentifier groups the information
+ * required to download (and check) a file.
+ */
+
+public class FileIdentifier extends Object implements Persistent, AFSConstants
+{
+ public static final int SIZE = 48;
+
+ /** Total size of the file in bytes. */
+ private long fileLength;
+
+ /** Top CRC of the tree-encoding. */
+ private int fileCRC;
+
+ /** Query and key of the top IBlock. */
+ private ChkHashes chk;
+
+
+ public FileIdentifier()
+ {
+ super();
+ fileLength=0;
+ fileCRC=0;
+ chk=new ChkHashes();
+ }
+
+ public FileIdentifier( Block b )
+ {
+ this();
+ chk=(ChkHashes) PersistentHelper.copy(b.getKeyAndQuery());
//todo: copie utile ???
+ fileCRC=b.getDataCRC();
+ fileLength=b.getFileSize();
+ }
+
+ public FileIdentifier( long length, int crc, ChkHashes h )
+ {
+ this();
+ fileLength=length;
+ fileCRC=crc;
+ chk=(ChkHashes) PersistentHelper.copy(h); //todo: copie
utile ???
+ }
+
+ public String toString()
+ {
+ return "File identifier";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public long getFileLength()
+ {
+ return fileLength;
+ }
+
+ public int getFileCRC()
+ {
+ return fileCRC;
+ }
+
+ public HashCode512 getFileQuery()
+ {
+ return chk.query;
+ }
+
+ public HashCode512 getFileKey()
+ {
+ return chk.key;
+ }
+
+ /**
+ * Convert a fileIdentifier to an URI string
+ * (to display it to the user).
+ *
+ * @return string containing the url (must be freed by caller)
+ */
+
+ public String toURI()
+ {
+ StringBuffer buf;
+ String str;
+
+ buf=new StringBuffer();
+ buf.append(GNUNET_URI_PREFIX_AFS);
+ buf.append(chk.key.toHex());
+ buf.append(".");
+ buf.append(chk.query.toHex());
+ buf.append(".");
+
+ str="00000000000000000000000"+Utils.toHex(fileCRC);//todo:
mieux faire !!!
+ buf.append(str.substring(str.length()-8));
+
+ buf.append(".");
+ buf.append(fileLength);
+ return buf.toString();
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.putInt((int) fileLength); //fixme: long value
+ buf.putInt(fileCRC);
+ if (chk!=null) {
+ chk.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<ChkHashes.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ fileLength=buf.getInt();
+ fileCRC=buf.getInt();
+ chk=new ChkHashes();
+ chk.readBytes(buf,err);
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Converts an AFS uri to a FileIdentifier
+ * FIXME: Not much error handling. Doesn't recognize
+ * a namespace URL. We'd need something more general,
+ * or a mechanism that recognizes uri type and routes
+ * it to the right function.
+ *
+ * @param str the URI of a fileIdentifier
+ * @return the fileidentifier (caller must free)
+ */
+
+ public static FileIdentifier parseURI( String str )
+ {
+ FileIdentifier fid;
+ Logger logger;
+ Matcher m;
+
+ assert(str!=null) : "null string";
+
+ if (!str.startsWith(GNUNET_URI_PREFIX_AFS)) {
+ logger=Logger.getLogger(FileIdentifier.class.getName());
+ logger.log(Level.SEVERE,"Malformed URI :"+str);
+ return null;
+ }
+
+
m=Pattern.compile("([0-9A-Za-z]{40})\\.([0-9A-Za-z]{40})\\.([0-9A-Fa-f]+)\\.([0-9]+)").matcher("");
+ m.reset(str.substring(GNUNET_URI_PREFIX_AFS.length()));
+ if (!m.matches()) {
+ logger=Logger.getLogger(FileIdentifier.class.getName());
+ logger.log(Level.SEVERE,"Malformed URI :"+str);
+ return null;
+ }
+
+ fid=new FileIdentifier();
+ fid.chk.key=HashCode512.parse(m.group(1));
+ fid.chk.query=HashCode512.parse(m.group(2));
+ fid.fileCRC=(int) Long.parseLong(m.group(3),16); //car
parseInt ne prend pas en compte si >2^31 en non signe
+ fid.fileLength=Long.parseLong(m.group(4));
+ return fid;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectory.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectory.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectory.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,172 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Format of a GNUnet directory (both in memory and on the drive).
+ * functions for building directories
+ *
+ * Helper functions for building directories.
+ *
+ * Directories are an add-on mechanism on top of the ESED II. As
+ * such, gnunetd has no notion of directories. Thus, this code is
+ * NEVER run inside of gnunetd but always by the various AFS tools.
+ * Since multiple AFS tools may concurrently access the directories
+ * from different processes, IPC is required to synchronize the
+ * access.
+ *
+ */
+
+public class GNDirectory extends Object implements Persistent, AFSConstants
+{
+ public static final int SIZE = RootNode.SIZE; // 1024
+
+ /** */
+ public byte[] MAGIC;
+
+ /** in network byte order */
+ public int version;
+
+ /** number of files in the directory */
+ public int number_of_files;
+
+ /** description/filename of the directory */
+ public String description;
+
+ /** must be zero for now */
+ public byte[] reserved;
+
+ /** number_of_files root-nodes */
+ public RootNode[] contents;
+
+
+ public GNDirectory()
+ {
+ super();
+ MAGIC=new byte[8];
+ Arrays.fill(MAGIC,(byte) 0);
+ version=0;
+ number_of_files=0;
+ description="";
+ reserved=new byte[RootNode.SIZE - 256 - 16];
+ Arrays.fill(reserved,(byte) 0);
+ contents=new RootNode[] {};
+ }
+
+ /**
+ * Build a GNUnet directory in memory.
+ *
+ * @param numberOfEntries how many files are in the directory
+ * @param name what is the name of the directory
+ * @param entries the entries in the directory
+ */
+
+ public GNDirectory( int numberOfEntries, String name, RootNode[]
entries )
+ {
+ this();
+
+ int i;
+ System.arraycopy(GNUNET_DIRECTORY_MAGIC.getBytes(),0,MAGIC,0,8);
+ number_of_files = numberOfEntries;
+ if (!name.endsWith(File.separator)) {
+ name+="/";
+ }
+ description=name;
+
+ contents=new RootNode[numberOfEntries];
+ for (i=0; i<contents.length; i++) {
+ contents[i]=entries[i];
+ }
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE+number_of_files*RootNode.SIZE;
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.put(MAGIC);
+ buf.putInt(version);
+ buf.putInt(number_of_files);
+ AFSUtils.put(description,buf,256);
+ buf.put(reserved);
+
+ for (i=0; i<number_of_files; i++) {
+ contents[i].writeBytes(buf);
+ }
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int i;
+
+ buf.get(MAGIC);
+ version=buf.getInt();
+ number_of_files=buf.getInt();
+ description=AFSUtils.get(buf,256);
+ buf.get(reserved);
+
+ err.reportIf(version!=0,"bad version");
+
err.reportIf(!Arrays.equals(MAGIC,GNUNET_DIRECTORY_MAGIC.getBytes()),"bad
magic");
+
+ contents=new RootNode[number_of_files];
+ for (i=0; i<number_of_files; i++) {
+ contents[i]=new RootNode();
+ contents[i].readBytes(buf,err);
+ }
+
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Appends a suffix ".gnd" to a given string if the suffix
+ * doesn't exist already. Existing suffix '/' is replaced if
+ * encountered.
+ *
+ * @param dn the directory name (string)
+ * @return the converted name on success
+ */
+
+ public static String expandDirectoryName( String dn )
+ {
+ if (dn==null) {
+
Logger.getLogger("").log(Level.SEVERE,"expandDirectoryName called with null
argument");
+ return null;
+ }
+
+ if (dn.endsWith(File.separator)) {
+ dn=dn.substring(0,dn.length()-File.separator.length());
+ }
+ if (!dn.endsWith(GNUNET_DIRECTORY_EXT)) {
+ dn+=GNUNET_DIRECTORY_EXT;
+ }
+ return dn;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectoryDatabase.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectoryDatabase.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/GNDirectoryDatabase.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,229 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * The "state" database (see include/util/state.h) is used
+ * to store the data. Note that state does not do any locking,
+ * and that it in particular can not do any locking for us since
+ * it is IPC!
+ */
+
+public class GNDirectoryDatabase extends LoggedObject implements AFSConstants
+{
+ /** */
+ private Prefs prefs;
+
+
+ public GNDirectoryDatabase( Prefs p )
+ {
+ super(true);
+ prefs=p;
+ }
+
+ public String toString()
+ {
+ return "GN directory database";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Makes a root-node available for directory building.
+ *
+ * This function is called whenever a root-node is encountered. This
+ * can either be because the user inserted a file locally; because we
+ * received a search result or because the user retrieved a directory
+ * with root-nodes. From which context the root node was encountered
+ * is specified in the context parameters.<p>
+ *
+ * makeRootNodeAvailable adds the node to the list of files that
+ * we can build a directory from later. The context is used to allow
+ * the user to filter on root-node sources.
+ *
+ * @param root the file identifier that was encountered
+ * @param context the context in which the identifier was encountered
(may not be a bitmask)
+ */
+
+ public void makeRootNodeAvailable( Node root, int context )
+ {
+ byte[] b;
+ RootNode nd;
+ MappedFile sem;
+ ByteBuffer buf;
+ int ret;
+
+ if (!prefs.testString("AFS","COLLECT-FILE-IDENTIFIERS","YES") )
{
+ log(Level.FINEST,"Collecting file identifiers
disabled.");
+ return;
+ }
+
+ sem=createLock();
+ if (sem==null) {
+ return;
+ }
+
+ try {
+ b=PersistentHelper.toBytes(root);
+
+ buf=prefs.getContent("dir"+context);
+ if (buf!=null) {
+ // if size is not a multiple of the RootNode
size, try to fix DB by truncating !
+ ret=buf.limit();
+ if (ret % RootNode.SIZE != 0) {
+ ret -= ret % RootNode.SIZE;
+
+ buf.position(ret);
+ buf.limit(ret);
+ prefs.putContent("dir"+context,buf);
+ buf.position(0);
+ }
+
+ ret = ret / RootNode.SIZE;
+ while (ret > 0) {
+ ret--;
+
+ nd=(RootNode)
PersistentHelper.read(RootNode.class,buf);
+ if
(Arrays.equals(b,PersistentHelper.toBytes(nd))) {//todo: 'equals' sur RootNode,
NNode, SBlock
+ return; // already present
+ }
+ }
+ }
+ prefs.appendContent("dir"+context,b);
+ }
+ finally {
+ releaseLock(sem);
+ }
+ }
+
+ /**
+ * Remove all of the root-nodes of a particular type
+ * from the directory database.
+ *
+ * @param contexts bitmask of the databases that should be emptied.
+ */
+
+ public void emptyDirectoryDatabase( int contexts )
+ {
+ MappedFile sem;
+ int i;
+
+ sem=createLock();
+ if (sem==null) {
+ return;
+ }
+
+ try {
+ i=1;
+ while (contexts > 0) {
+ if ((contexts & i) > 0) {
+ contexts -= i;
+ prefs.unlink("dir"+i);
+ }
+ i*=2;
+ }
+ }
+ finally {
+ releaseLock(sem);
+ }
+ }
+
+ /**
+ * Iterate over all entries that match the given context
+ * mask.
+ *
+ * @param contexts context bitmask for the entries to iterate over
+ * @param callback function to call on each entry, may be null
+ * @param closure extra argument to the callback
+ * @return number of entries found
+ */
+
+ public int iterateDirectoryDatabase( int contexts, RootNodeCallback
callback, Object closure )
+ {
+ RootNode nd;
+ MappedFile sem;
+ ByteBuffer buf;
+ int ret,rval,i;
+
+ sem=createLock();
+ if (sem==null) {
+ return 0;
+ }
+
+ rval=0;
+ try {
+ i = 1;
+ while (contexts > 0) {
+ if ((contexts & i) > 0) {
+ contexts -= i;
+
+ buf=prefs.getContent("dir"+i);
+ if (buf!=null) {
+ // if size is not a multiple of
the RootNode size, try to fix DB by truncating !
+ ret=buf.limit();
+ if (ret % RootNode.SIZE != 0) {
+ ret -= ret %
RootNode.SIZE;
+
+ buf.position(ret);
+ buf.limit(ret);
+
prefs.putContent("dir"+i,buf);
+ buf.position(0);
+ }
+
+ ret = ret / RootNode.SIZE;
+ while (ret > 0) {
+ ret--;
+ nd=(RootNode)
PersistentHelper.read(RootNode.class,buf);
+ if (callback != null) {
+
callback.rootNode(nd,closure);
+ }
+ rval++;
+ }
+ }
+ }
+ i*=2;
+ }
+ }
+ finally {
+ releaseLock(sem);
+ }
+ return rval;
+ }
+
+ protected MappedFile createLock()
+ {
+ FileLocation loc;
+ MappedFile f;
+
+
loc=prefs.getDirLocation("","GNUNET_HOME").getFile("directory_ipc_lock");
+
+ f=loc.openNew();
+ if (f==null) {
+ return null;
+ }
+ if (f.lock()) {
+ return f;
+ }
+ f.close();
+ return null;
+ }
+
+ protected void releaseLock( MappedFile f )
+ {
+ f.unlock();
+ f.close();
+
+ f.getLocation().delete();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/GeneralURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/GeneralURI.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/GeneralURI.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,31 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ * Header prefix for whatever is in an AFS URI.
+ * "action" is the identifier of the URI purpose/context.
+ */
+
+public class GeneralURI extends Object
+{
+ public int action;
+// public Object data;
+
+
+ public GeneralURI()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlock.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlock.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,757 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * An inner node in the merkle-tree.
+ */
+
+public class IBlock extends Block
+{
+ /** Number of CHK_Hashes per IBlock. The value must be 25 since
25*40+20+4 is 1024.
+ The other values are 40=CHK_Hashes.SIZE, 20=HashCode512.SIZE for the
super-hash
+ and 4=int.SIZE for the CRC32. */
+ public static final int CHK_PER_INODE = 25;
+
+
+ /** The depth of this node in the file tree. At depth 0 we have the
leaves, since this is an IBlock, depth is always > 0. */
+ private int depth;
+
+ /** CRC (if (data != null): ((IBlockData)data).crc32). */
+ private int crc32;
+
+ /** Number of children [1-CHK_PER_INODE] of this node. */
+ private int childcount;
+
+ /** References to the children (IBlocks or DBlocks, depending on if
depth > 1 or not). */
+ private Block[] children;
+
+ /** CRC of each of the children. */
+ private int[] crcs;
+
+
+ /**
+ * Create a top-IBlock for the root of the file tree.
+ * Note that you must set the chk field before calling
+ * download.
+ * @param filesize2 the size of the file
+ */
+
+ public IBlock( int filesize2 )
+ {
+
super(filesize2,computeDataLength(filesize2,0,IOContext.computeDepth(filesize2)));
+ depth=IOContext.computeDepth(filesize2);
+ childcount=0;
+ children=null;//new Block[CHK_PER_INODE];
+ crcs=new int[CHK_PER_INODE];
+
+ init();
+ }
+
+ /**
+ * Create an IBlock. Use createTopIBlxock for the
+ * node on top of the file-tree.
+ *
+ * @param pos2 the position of the IBlock in the file
+ * @param parent2 the parent block
+ */
+
+ public IBlock( IBlock parent2, int pos2 )
+ {
+
super(parent2,pos2,computeDataLength(parent2.getFileSize(),pos2,parent2.depth -
1));
+ depth=parent2.depth-1;
+ childcount=0;
+ children=null;//new Block[CHK_PER_INODE];
+ crcs=new int[CHK_PER_INODE];
+
+ init();
+ }
+
+ public IBlock( IBlock b )
+ {
+ super(b);
+ depth=b.depth;
+ childcount=b.childcount;
+ children=null;//new Block[CHK_PER_INODE];
+ System.arraycopy(b.children,0,children,0,b.children.length);
+ crcs=new int[CHK_PER_INODE];
+ System.arraycopy(b.crcs,0,crcs,0,b.crcs.length);
+
+ init();
+ }
+
+ public String toString()
+ {
+ return "Inner block [offset="+getFilePosition()+",
size="+getFileSize()+", childcount="+childcount+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Initialize IBlock fields
+ */
+
+ protected void init()
+ {
+
childcount=computeChildCount(getFileSize(),getFilePosition(),depth);
+ children=null;
+ }
+
+ /**
+ * Allocate space for children.
+ */
+
+ protected void allocateChildren()
+ {
+ int cover,i;
+
+ if (children!=null)
+ return;
+
+ children=new Block[childcount];
+
+ // create child nodes
+ cover=computeChildCover(depth);
+ for (i=0; i<childcount; i++) {
+ if (depth>1) {
+ children[i]=new
IBlock(this,getFilePosition()+i*cover);
+ }
+ else {
+ children[i]=new
DBlock(this,getFilePosition()+i*cover);
+ }
+ }
+ }
+
+ public int getDepth()
+ {
+ return depth;
+ }
+
+ public boolean childrenDestroyed( Block b )
+ {
+ int live,i;
+
+ live=0;
+ if (children!=null) {
+ for (i=0; i<childcount;i++) {
+ if (children[i] == b)
+ children[i] = null;
+ if (children[i] != null)
+ live++;
+ }
+ }
+ return (live==0 && !isStatus(BLOCK_PERSISTENT));
+ }
+
+ /**
+ * A child has been completely downloaded. Perform the
+ * appropriate CRC checks in the parent node and free associated
+ * resources if possible. Since the only errors are either
+ * bugs or hash-crc-collisions (probability 1:2^160), we
+ * always die on errors (return values do not work well for
+ * async calls anyway).<p>
+ *
+ * Note that the leaves update the ProgressModel, so we do
+ * not have to worry about that.
+ * Note that the leaves update the ProgressModel, so we do
+ * not have to worry about that. If all children of a node
+ * are complete, this method calls itself recursively to
+ * notify the parent of the parent.
+ *
+ * @param child the completed child block
+ * @param nc the context (IO, priority, etc.)
+ * @param rm request manager to schedule queries
+ */
+
+ public void childDownloadCompleted( Block child, NodeContext nc,
RequestManager rm )
+ {
+ int i,pendingChildren;
+
+ debug("childDownloadxComplete "+this+" "+child);
+
+ assert(children!=null);
+
+ for (i=0;i<childcount;i++)
+ if (children[i] == child)
+ break;
+
+ assert (i!=childcount) : "childxDownloadCompleted called on
node that does not know that child ! ("+child+", "+this+")";
+
+ crcs[i] = Crypto.crc32(child.getRawData(),
0,child.getDataLength());
+
+ pendingChildren = 0;
+ for (i=0;i<childcount;i++)
+ if ( (children[i] != null) &&
!children[i].isStatus(BLOCK_PRESENT))
+ pendingChildren++;
+
+ // check if this IBlock is complete, if yes, go to our parent
and notify that we are done !
+ if (getParent() != null) {
+ if (pendingChildren == 0) {
+ if (fuckedCRC32(crcs,childcount)!=crc32) {
+ log(Level.SEVERE,"File corrupted (or
bug), crc mismatch in block "+depth+
+ " "+getFilePosition()+
+ ":
"+fuckedCRC32(crcs,childcount)+
+ " != "+crc32);
+ }
+ getParent().childDownloadCompleted(this,nc,rm);
+ }
+ }
+ else { // parent == null
+ if (pendingChildren == 0) {
+ if (fuckedCRC32(crcs,childcount)!=crc32 ||
+ !checkTopCRC(rm.topCrc32)) {
+ log(Level.SEVERE,"File corrupted (or
bug), top CRC mismatch in block "+depth+
+ " "+getFilePosition()+
+ ":
"+Utils.toHex(fuckedCRC32(crcs,childcount))+
+ " != "+Utils.toHex(crc32)+
+ " or
"+checkTopCRC(rm.topCrc32));
+
+ return;
+ }
+ }
+ }
+
+ // free memory as early as possible !
+ if (pendingChildren == 0) {
+ clearData();
+ }
+ }
+
+ public static int fuckedCRC32( int[] p, int count )
+ {
+ byte[] b;
+ ByteBuffer buf;
+ int i,crc;
+
+ b=new byte[count*4];
+ buf=ByteBuffer.wrap(b);
+ buf.order(ByteOrder.LITTLE_ENDIAN);//hum, hum...
+ for (i=0; i<count; i++) {
+ buf.putInt(p[i]);
+ }
+ crc=Crypto.crc32(b);
+ return ((crc & 0x000000ff)<<24) |
+ ((crc & 0x0000ff00)<<8) |
+ ((crc>>8) & 0x0000ff00) |
+ ((crc>>24) & 0x000000ff)
+ ;
+ }
+
+ /**
+ * Insert the current block into the network. Implementations
+ * are also responsible for updating the corresponding fields
+ * of the parent node (of course, except if the parent is
+ * null in the case of the top-node in the tree).<p>
+ *
+ * Inner nodes first call the respective inserter methods for
+ * their children.<p>
+ *
+ * @param nc the context (gives us the priority)
+ * @param sock the socket to use to talk to the core
+ * @return OK on success, false on error
+ */
+
+ public boolean insert( NodeContext nc, CSSession sock )
+ {
+ Block child;
+ IBlockData ibd;
+ CSIndexSuper req;
+ ContentBlock edata;
+ CSResult rv;
+ int ui,childCover;
+
+ debug("insert");
+
+ setStatus(BLOCK_PERSISTENT);
+ ibd = new IBlockData();
+ setData(ibd);
+
+ childCover = ContentBlock.SIZE;
+ for (ui=0;ui<depth-1;ui++)
+ childCover *= CHK_PER_INODE;
+
+ allocateChildren();
+
+ for (ui=0; ui<childcount; ui++) {
+ child = children[ui];
+ if (!child.insert(nc,sock)) {
+ if (sock != null)
+ log(Level.WARNING,"Child insertion
failed on level "+depth+", pos "+child.getFilePosition()+", aborting !");
+ return false; /* abort! */
+ }
+
+ crcs[ui] =
Crypto.crc32(child.getRawData(),0,child.getDataLength());
+ ibd.chks[ui]=(ChkHashes)
PersistentHelper.copy(child.getKeyAndQuery());
+ child.destroy(null);
+ children[ui] = null;
+ }
+
+
ibd.superHash=HashCode512.create(PersistentHelper.toBytes(ibd.chks),0,ChkHashes.SIZE
* childcount);
+
+ if (nc.index!=0 && sock!=null) {
+ req=new CSIndexSuper(ibd.superHash,nc.priority);
+
+ if (sock.send(req)) {
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send
confirmation of insertion.");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"Server could not
perform insertion.");
+ return false;
+ }
+ }
+ else {
+ log(Level.WARNING,"Could not send super-index
information to gnunetd. Is gnunetd running ?");
+ return false;
+ }
+ }
+
+ ibd.crc32 = fuckedCRC32(crcs,childcount);
+ crc32 = ibd.crc32;
+ edata = encrypt();
+ return insertCHKBlock(sock,edata,nc.priority);
+ }
+
+ /**
+ * Remove the current block from the local AFS storage.
+ *
+ * @param nc the context (gives us the priority)
+ * @param sock the socket to use to talk to the core
+ * @return OK on success, false on error
+ */
+
+ public boolean delete( NodeContext nc, CSSession sock )
+ {
+ int ui,childCover;
+ Block child;
+ IBlockData ibd;
+ CSUnindexSuper req;
+ ContentBlock edata;
+ CSResult rv;
+
+ setStatus(BLOCK_PERSISTENT);
+ ibd = new IBlockData();
+ setData(ibd);
+ childCover = ContentBlock.SIZE;
+ for (ui=0;ui<depth-1;ui++)
+ childCover *= CHK_PER_INODE;
+
+ allocateChildren();
+
+ for (ui=0;ui<childcount;ui++) {
+ child = children[ui];
+ if (!child.delete(nc,sock)) {
+ if (sock != null) {
+ log(Level.WARNING,"Failed to delete
child on level "+depth+", pos "+child.getFilePosition()+". Will continue.");
+ }
+ }
+ crcs[ui] =
Crypto.crc32(child.getRawData(),0,child.getDataLength());
+ ibd.chks[ui]=(ChkHashes)
PersistentHelper.copy(child.getKeyAndQuery());
+ child.destroy(null);
+ children[ui] = null;
+ }
+
+
ibd.superHash=HashCode512.create(PersistentHelper.toBytes(ibd.chks),0,ChkHashes.SIZE
* childcount);
+ if (sock != null) {
+ req=new CSUnindexSuper();
+ req.importance = nc.priority;
+ req.superHash=(HashCode512)
PersistentHelper.copy(ibd.superHash);
+
+ if (sock.send(req)) {
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send
confirmation of deletion.");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ // super blocks don't matter !
+ }
+ }
+ else {
+ log(Level.WARNING,"Could not send super-unindex
information to gnunetd. Is gnunetd running ?");
+ return false;
+ }
+ }
+
+ ibd.crc32 = fuckedCRC32(crcs,childcount);
+ edata = encrypt();
+ return deleteCHKBlock(sock,edata,nc.priority);
+ }
+
+ /**
+ * The request manager got a reply for one of the childs
+ * we were looking after. Update the RM query, call
+ * receive on the appropriate child, etc.
+ *
+ * @param query the query that was sent out
+ * @param reply the reply that was received
+ * @param rm the handle for the request manager
+ * @param nc the context (gives us the priority)
+ * @return false the request manager should abort the download
+ */
+
+ protected boolean receiveChild( HashCode512 query, CSResultChk reply,
RequestManager rm, NodeContext nc )
+ {
+ int i;
+ IBlockData ibd;
+
+ debug("iblock_download_receivec_child "+this);
+
+ if (!isStatus(BLOCK_SUPERQUERY_PENDING)) {
+ trace("No superquery is pending !");
+ return false;
+ }
+
+ debug("iblock "+this+" receives message for child");
+
+ ibd=(IBlockData) getData(IBlockData.class);
+
+ allocateChildren();
+
+ for (i=0; i<childcount; i++) {
+ if (query.equals(ibd.chks[i].query)) {
+ if (children[i]!=null &&
children[i].isStatus(BLOCK_PENDING)) {
+ return
children[i].receive(query,reply,rm,nc);
+ }
+ }
+ }
+ return true; // we may receive replies twice, just ignore
those
+ }
+
+ /**
+ * Call download on the children to test if they are present.
+ *
+ * @param nc the context (gives us the priority)
+ * @param rm the request manager
+ */
+
+ protected void downloadChildren( NodeContext nc, RequestManager rm )
+ {
+ int i;
+ IBlockData ibd;
+ Block child;
+
+ debug("iblock_download_childcren "+this);
+
+ assert(childcount<=CHK_PER_INODE) : "iblock "+this+" has
"+childcount+" children !";
+
+ ibd=(IBlockData) getData(IBlockData.class);
+
+ allocateChildren();
+
+ for (i=0; i<childcount; i++) {
+ child=children[i];
+ if (child!=null) {
+ child.setKeyAndQuery(ibd.chks[i]);
+ child.download(nc,rm);
+ }
+ }
+ }
+
+ /**
+ * Send the super-request that groups the queries for all
+ * child-nodes in one large query. Note that recursion and
+ * updates are checked by the "superState" field of IBlock.
+ *
+ * @param rm reference to the RequestManager for requests
+ * @param nc the context (gives us the priority)
+ */
+
+ protected void doSuperRequest( NodeContext nc, RequestManager rm )
+ {
+ IBlockData ibd;
+ CSQuery msg;
+ int liveChildren,i;
+
+ debug("ibclock_do_superrequest "+this);
+
+ liveChildren = 0;
+ allocateChildren();
+ for (i=0;i<childcount;i++)
+ if (children[i] != null)
+ if (children[i].isStatus(BLOCK_PENDING))
+ liveChildren++;
+
+ if (liveChildren == 0) {
+ debug("iblock "+this+" cancels request, all children
done ("+getStatus()+")");
+
+ /* finally drop remaining requests, all satisfied! */
+ if (isStatus(BLOCK_SUPERQUERY_PENDING)) {
+ rm.update(this,null);
+ }
+ setStatus(BLOCK_CHILDREN_PRESENT);
+ return; /* we are done here! */
+ }
+
+ ibd=(IBlockData) getData(IBlockData.class);
+
+ msg=new CSQuery();
+ msg.setPriorityAndTTL(1,1);
+ msg.addQuery(ibd.superHash);
+//trace("CSQuery (1): "+msg);
+
+ liveChildren = 0;
+ allocateChildren();
+ for (i=0;i<childcount;i++) {
+ if (children[i] != null) {
+ if (children[i].isStatus(BLOCK_PENDING)) {
+ msg.addQuery(ibd.chks[i].query);
+ liveChildren++;
+ }
+ }
+ }
+
+ if (isStatus(BLOCK_SUPERQUERY_PENDING)) {
+ debug("iblock "+this+" updates request,
"+liveChildren+" children pending");
+
+ rm.update(this,msg);
+ }
+ else {
+ debug("iblock "+this+" starts request, "+liveChildren+"
children pending");
+
+ setStatus(BLOCK_SUPERQUERY_PENDING);
+ rm.request(this,new Listener() {
+ public boolean listen( HashCode512 query,
CSResultChk reply, RequestManager rmg, NodeContext data )
+ {
+ return
receiveChild(query,reply,rmg,data);
+ }
+ },nc,msg);
+ }
+ }
+
+ /**
+ * Type of a method that is called by the RequestManager
+ * whenever a reply to a query has been received.
+ *
+ * @param query the query that was sent out
+ * @param reply the reply that was received
+ * @param rm the handle for the request manager
+ * @param nc the context (gives us the priority)
+ * @return false the request manager should abort the download
+ */
+
+ public boolean receive( HashCode512 query, CSResultChk reply,
RequestManager rm, NodeContext nc )
+ {
+ ProgressStats pstats;
+
+ debug("receive");
+
+ if (!isStatus(BLOCK_PENDING)) {
+ /* As far as I can tell, this should never happen */
+ log(Level.WARNING,"IBlock "+this+" receives reply, but
we are already done !");
+ return true;
+ }
+
+ debug("iblock "+this+" receives reply");
+
+ if (!receivedChk(query,reply)) {
+ pstats=new ProgressStats();
+ nc.pmodel.progress(pstats,nc.data);
+ return false;
+ }
+
+ if (!writeData(nc)) {
+ pstats=new ProgressStats();
+ nc.pmodel.progress(pstats,nc.data);
+ log(Level.SEVERE,"Write to temporary IBlock file
failed, aborting.");
+ return false;
+ }
+
+ crc32=((IBlockData) getData()).crc32;
+
+ setStatus(BLOCK_PRESENT);
+ if (getParent() == null) {
+ /* our request, stop doing it */
+ rm.update(this,null);
+ }
+ else {
+ getParent().childDownloadCompleted(this,nc,rm);
+ getParent().doSuperRequest(nc,rm);
+ }
+ setStatus(BLOCK_PERSISTENT);
+ downloadChildren(nc,rm);
+ doSuperRequest(nc,rm);
+ return true;
+ }
+
+ /**
+ * Download this node (and the children below). Note that the
+ * processing is asynchronous until the pmodel is called with position
+ * == total (and thus no more requests are pending) or the request
+ * manager is aborted by the user.
+ *
+ * @param rm the request manager
+ * @param nc the context (gives us the priority)
+ */
+
+ public void download( NodeContext nc, RequestManager rm )
+ {
+ NodeContext fakeContext;
+ IBlock fakeThis;
+ boolean isPresent;
+
+ debug("download");
+debug("S1");
+ isPresent=check(nc);
+debug("S11 "+isPresent);
+ if (!isPresent) {
+ fakeContext=new NodeContext();
+ fakeContext.ioc=new IOContext(nc.ioc);
+ fakeContext.priority=0;
+ fakeContext.index=-1;
+ fakeContext.pmodel=ProgressModel.NO_MODEL;
+ fakeContext.data = null;
+ fakeContext.stats.progress = 0;
+
+ fakeThis=new IBlock(this);
+
+ fakeThis.detach();
+ fakeThis.setStatus(BLOCK_PERSISTENT);
+
+ if (fakeThis.insert(fakeContext,null)) {
+ if
(fakeThis.getKeyAndQuery().equals(getKeyAndQuery())) {
+ setStatus(BLOCK_PRESENT);
+ setData(fakeThis.getData());
+ crc32=fakeThis.crc32;
+ fakeThis.clearData();
+
+ isPresent = true;
+ }
+ }
+ fakeThis.destroy(null);
+ }
+
+ if (isPresent) {
+ if (getParent() != null) {
+ getParent().childDownloadCompleted(this,nc,rm);
+ getParent().doSuperRequest(nc,rm);
+ }
+ setStatus(BLOCK_PERSISTENT);
+
+ downloadChildren(nc,rm);
+ doSuperRequest(nc,rm);
+ return;
+ }
+ // not present, either request ourselves or let the parent do
it automagically when we return...
+ setStatus(BLOCK_PENDING);
+ if (getParent() == null) {
+ issueQuery(rm,new Listener() {
+ public boolean listen( HashCode512 query,
CSResultChk reply, RequestManager rmg, NodeContext data )
+ {
+ return receive(query,reply,rmg,data);
+ }
+ },nc,getQuery());
+ }
+ }
+
+ /**
+ * Check if an IBlock is already present.
+ *
+ * @param nc the context (gives us the priority)
+ * @return YES if it is present, NO if not.
+ */
+
+ public boolean check( NodeContext nc )
+ {
+ HashCode512 hc;
+
+ debug("check");
+
+ /* first check if its already present */
+ if (loadData(nc,false)) {
+debug("check 2");
+ hc=HashCode512.create(getRawData(),0,getDataLength());
+ if (hc.equals(getKey())) {
+ crc32=((IBlockData) getData()).crc32;
+ return true;
+ }
+ }
+debug("check 3");
+ clearData();
+ return false;
+ }
+
+ public void print( int indent )
+ {
+ int i;
+
+ log(Level.FINEST,Utils.repeat('*',indent)+" IBLOCK ("+depth+")
"+getFilePosition()+" "+getQuery().toHex()+" ("+childcount+" children)");
+
+ if (children!=null) {
+ for (i=0; i<childcount; i++) {
+ if (children[i]!=null) {
+ children[i].print(indent+2);
+ }
+ }
+ }
+ }
+
+ /**
+ * Free the associated resources of this Block. DOES ALSO free the
+ * memory occupied by the Block struct itself!
+ *
+ * @param rm reference to the RequestManager for requests
+ */
+
+ public void destroy( RequestManager rm )
+ {
+ int i;
+
+ debug("destroy");
+ setStatus(BLOCK_PERSISTENT); /* last child would otherwise call
destroy on us! */
+ if (children!=null) {
+ for (i=0; i<childcount; i++) {
+ if (children[i]!=null) {
+ children[i].destroy(rm);
+ }
+ }
+ children=null;
+ }
+ super.destroy(rm);
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static int computeChildCover( int depth )
+ {
+ int cover,i;
+
+ cover=ContentBlock.SIZE;
+ for (i=0; i<depth-1; i++) {
+ cover*=CHK_PER_INODE;
+ }
+ return cover;
+ }
+
+ public static int computeChildCount( int size, int pos, int depth )
+ {
+ int myCover,cover,count;
+
+ cover=computeChildCover(depth);
+
+ myCover=Math.min(size-pos,CHK_PER_INODE*cover);
+ for (count=0; myCover>0; count++) {
+ myCover-=cover;
+ }
+ return count;
+ }
+
+ public static int computeDataLength( int size, int pos, int depth )
+ {
+ // superhash + crc + chkhashes * children #
+ return
computeChildCount(size,pos,depth)*ChkHashes.SIZE+HashCode512.SIZE+4;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlockData.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlockData.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/IBlockData.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,88 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * format of an IBlock.
+ */
+
+public class IBlockData extends Object implements Persistent
+{
+ public static final int SIZE =
HashCode512.SIZE+4+ChkHashes.SIZE*IBlock.CHK_PER_INODE;
+
+ /** The super-Hashcode for retrieving all CHK_PER_INODE sub-nodes in
one big lookup.
+ This hash is the hash of the concatenation of all encrypted
CHK_PER_INODE children of this node. */
+ public HashCode512 superHash;
+
+ /** The CRC32 checksum of the sub-blocks (crc32N of the concatenation
of the individual crc32N's over
+ the plaintext-data (without padding) of each block). */
+ public int crc32;
+
+ /** The keys and queries for the nodes one level below. This entry must
be at the end since it is variable size ! */
+ public ChkHashes[] chks;
+
+
+ public IBlockData()
+ {
+ super();
+ chks=new ChkHashes[IBlock.CHK_PER_INODE];
+ for (int i=0; i<chks.length; i++) {
+ chks[i]=new ChkHashes();
+ }
+ }
+
+ public String toString()
+ {
+ return "Inner block data";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int i;
+
+ superHash=new HashCode512();
+ superHash.readBytes(buf,err);
+
+ crc32=buf.getInt();
+
+ for (i=0; i<chks.length; i++) {
+ chks[i]=new ChkHashes();
+ chks[i].readBytes(buf,err);
+ }
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ if (superHash!=null) {
+ superHash.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ buf.putInt(crc32);
+
+ for (i=0; i<chks.length; i++) {
+ chks[i].writeBytes(buf);
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/IOContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/IOContext.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/IOContext.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,320 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * IO context for reading-writing AFS file blocks.
+ *
+ * encapsulation of IO
+ *
+ * In GNUnet, files are stored in the form of a balanced tree, not
+ * unlike INodes in unix filesystems. When we download files, the
+ * inner nodes of the tree are stored under FILENAME.X (where X
+ * characterizes the level of the node in the tree). If the download
+ * is aborted and resumed later, these .X files can be used to avoid
+ * downloading the inner blocks again. The successfully received leaf
+ * nodes in FILENAME (the target file) are of course also not
+ * downloaded again.<p>
+ *
+ * The IOContext struct presents an easy api to access the various
+ * dot-files. It uses function pointers to allow implementors to
+ * provide a different mechanism (other than files on the drive) to
+ * cache the IBlocks.
+ */
+
+public class IOContext extends LoggedObject implements AFSConstants
+{
+ /** The base-filename */
+ private String filename;
+
+ /** The depth of the file-tree. */
+ private int treeDepth;
+
+ /** A lock for each file-handle for synchronizing access. */
+ private Mutex[] locks;
+
+ /** The file handles for each level in the tree. */
+ private FileChannel[] handles;
+
+
+ public IOContext()
+ {
+ super(true);
+ filename="";
+ treeDepth=0;
+ locks=null;
+ handles=null;
+ }
+
+ public IOContext( IOContext ioc )
+ {
+ this();
+ init(ioc);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void init( IOContext ioc )
+ {
+ filename=ioc.filename;
+ treeDepth=ioc.treeDepth;
+
+ locks=new Mutex[ioc.locks.length];
+ System.arraycopy(ioc.locks,0,locks,0,ioc.locks.length);
+
+ handles=new FileChannel[ioc.handles.length];
+ System.arraycopy(ioc.handles,0,handles,0,ioc.handles.length);
+ }
+
+ public String getFilename()
+ {
+ return filename;
+ }
+
+ /**
+ * Initialize an IOContext.
+ *
+ * @param filesize the size of the file
+ * @param str the name of the level-0 file
+ * @param rdOnly use YES for read-only IOC
+ * @return OK on success, false on failure
+ */
+
+ public boolean init( int filesize, String str, boolean rdOnly )
+ {
+ FileChannel fc;
+ String fn;
+ int i;
+
+ log(Level.FINEST,"Init on '"+str+"' ("+filesize+" bytes)
\""+(rdOnly ? "r" : "r/w")+"\" mode.");
+
+ treeDepth = computeDepth(filesize);
+ locks = new Mutex[treeDepth+1];
+ handles = new FileChannel[treeDepth+1];
+ Arrays.fill(handles,null);
+ filename = str;
+
+ if (!rdOnly) {
+ try {
+ // if exists and oversized, truncate
+ fc=new
RandomAccessFile(filename,"rw").getChannel();
+ try {
+ if (fc.size()>filesize) {
+ fc.truncate(filesize);
+ }
+ }
+ catch( IOException x ) {
+ err("Unable to truncate "+filename+"
!",x);
+ return false;
+ }
+ }
+ catch( FileNotFoundException x ) {
+ }
+
+ }
+
+ for (i=0; i<=treeDepth; i++) {
+ locks[i]=new Mutex();
+
+ fn=filename+(i>0 ? "."+(char) ('A'+i) : "");
+ try {
+ handles[i]=new RandomAccessFile(fn,(rdOnly ?
"r" : "rw")).getChannel();
+ }
+ catch( IOException x ) {
+ handles[i]=null;
+
+ if (!rdOnly || i==0) {
+ err("Could not open file "+fn+" !",x);
+ free(false);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Read method.
+ *
+ * @param level level in the tree to read/write at
+ * @param pos position where to read or write
+ * @param buf where to read from or write to
+ * @param len how many bytes to read or write
+ * @return number of bytes read or written, -1 on error
+ */
+
+ public int read( int level, int pos, byte[] buf, int len )
+ {
+ return read(level,pos,ByteBuffer.wrap(buf,0,len));
+ }
+
+ public int read( int level, int pos, ByteBuffer buf )
+ {
+ int ret;
+ int lpos;
+
+
+//log(Level.FINEST,">>> read level="+level+", pos="+pos);
+
+ lpos=pos;
+ for (ret=0; ret<level; ret++) {
+ lpos/=IBlock.CHK_PER_INODE;
+ }
+
+ try {
+ locks[level].acquire();
+ try {
+ handles[level].position(lpos);
+ ret=handles[level].read(buf);
+ }
+ finally {
+ locks[level].release();
+ }
+ }
+ catch( IOException x ) {
+ err("Failed to read "+buf.remaining()+" bytes on
"+handles[level]+" !",x);
+ ret=-1;
+ }
+ catch( InterruptedException x ) {
+ err("Failed to read "+buf.remaining()+" bytes on
"+handles[level]+" !",x);
+ ret=-1;
+ }
+ return ret;
+ }
+
+ /**
+ * Write method.
+ *
+ * @param level level in the tree to read/write at
+ * @param pos position where to read or write
+ * @param buf where to read from or write to
+ * @param len how many bytes to read or write
+ * @return number of bytes read or written, -1 on error
+ */
+
+ public int write( int level, int pos, byte[] buf, int len )
+ {
+ return write(level,pos,ByteBuffer.wrap(buf,0,len));
+ }
+
+ public int write( int level, int pos, ByteBuffer buf )
+ {
+ int ret,lpos,len;
+
+//log(Level.FINEST,">>> write level="+level+", pos="+pos);
+
+ lpos = pos;
+ for (ret=0; ret<level; ret++) {
+ lpos /= IBlock.CHK_PER_INODE;
+ }
+
+ try {
+ locks[level].acquire();
+ try {
+ handles[level].position(lpos);
+ len=buf.remaining();
+ ret = handles[level].write(buf);
+ if (ret != len) {
+
log(Level.FINEST,"Write("+handles[level]+", "+buf+", "+len+") failed !");
+ }
+ }
+ finally {
+ locks[level].release();
+ }
+ }
+ catch( IOException x ) {
+ err("Failed to write "+buf.remaining()+" bytes on
"+handles[level]+" !",x);
+ ret=-1;
+ }
+ catch( InterruptedException x ) {
+ err("Interrupted !",x);
+ ret=-1;
+ }
+ return ret;
+ }
+
+ /**
+ * Close the files in the IOContext and free
+ * the associated resources. Does NOT free
+ * the memory occupied by the IOContext struct
+ * itself.
+ *
+ * @param unlinkTreeFiles if YES, the non-level 0 files
+ * are unlinked (removed), set to NO if the download
+ * is not complete and may be resumed later.
+ */
+
+ public void free( boolean unlinkTreeFiles )
+ {
+ int i;
+ String fn;
+
+ log(Level.FINEST,"Free (unlink: "+unlinkTreeFiles+").");
+ for (i=0;i<=treeDepth;i++) {
+ if (handles[i] != null) {
+ try {
+ handles[i].close();
+ }
+ catch( IOException x ) {
+ err("Failed to close "+handles[i]+"
!",x);
+ }
+ handles[i] = null;
+ }
+ locks[i]=null;
+ }
+
+ if (unlinkTreeFiles) {
+ for (i=1; i<=treeDepth; i++) {
+ fn=filename+"."+(char) ('A'+i);
+ if (!new File(fn).delete()) {
+ log(Level.WARNING,"Could not unlink
temporary file "+fn+".");
+ }
+ }
+ }
+
+ filename=null;
+ handles=null;
+ locks=null;
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Compute the depth of the tree.
+ * @param size file length for which to compute the depth
+ * @return depth of the tree
+ */
+
+ public static int computeDepth( int size )
+ {
+ int depth,n;
+
+ depth=0;
+
+ n=CONTENT_SIZE;
+ while (n<size) {
+ depth++;
+ n*=IBlock.CHK_PER_INODE;
+ }
+ return depth;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertURI.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertURI.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,33 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ */
+
+public class InsertURI extends GeneralURI
+{
+// public int action;
+ public String filename;
+ public String[] keywords;
+ public String pseudonym;
+ public String password;
+ public int keycount;
+
+
+ public InsertURI()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertUtil.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertUtil.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertUtil.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,660 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Insertutil, helper methods for file insertion.
+ * Break file that is inserted into blocks and encrypts them according to the
CHK-triple-hash-tree scheme (ESED II).
+ *
+ * @see "http://www.ovmj.org/GNUnet/encoding.php3"
+ */
+
+public class InsertUtil extends LoggedObject implements AFSConstants
+{
+ /** Default priority for locally indexed content ("infty") */
+ public static final int LOCAL_INDEXED_CONTENT_PRIO = 0xFFFF;
+
+ private Prefs prefs;
+
+
+ public InsertUtil( Prefs p )
+ {
+ super(true);
+ prefs=p;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Insert the SBlock
+ *
+ * @param esb
+ * @param sock
+ * @return OK on success, false on error
+ */
+
+ public boolean insert( EncryptedSBlock esb, CSSession sock )
+ {
+ CSInsertSBlock msg;
+ CSResult rv;
+
+ msg=new
CSInsertSBlock(prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0),esb);
+
+ if (!sock.send(msg)) {
+ log(Level.WARNING,"Server did not seem to be alive.");
+ return false;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Server did not send confirmation of
insertion.");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"Server could not perform
insertion.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Ask gnunetd to receive and store a file in
+ * on the server side.
+ *
+ * @param sock connection to gnunetd
+ * @param filename the name to add to fileindex.c
+ * @return the index, -1 on error
+ */
+
+ protected int transferFile( CSSession sock, String filename )
+ {
+ CSIndexFile request;
+ CSUploadFile upload;
+ CSResult rv;
+ int index,pos,fsize,delta;
+ FileChannel handle;
+ FileLocation f;
+
+ f=new FileLocation(filename);
+ if (!f.exists()) {
+ return -1;
+ }
+
+ /* first: request index */
+ request=new CSIndexFile(f);
+
+ if (!sock.send(request)) {
+ log(Level.WARNING,"Could not request or receive data
from gnunetd. Is gnunetd running ?");
+ return -1;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Could not request or receive data
from gnunetd. Is gnunetd running ?");
+ return -1;
+ }
+
+ index=rv.getResult();
+ if (index == -1) {
+ log(Level.WARNING,"gnunetd refused to index file (no
space left ?)");
+ return -1;
+ }
+
+ assert(index!=0) : "gnunetd violated protocol (returned 0)";
+
+ if (prefs.testString("GNUNET-INSERT","LINK","YES")) {
+ CSLinkFile req;
+
+ req=new CSLinkFile(f);
+
+ if (sock.send(req)) {
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv!=null && rv.isOkay()) {
+ /* link successful */
+ return index;
+ }
+ }
+
+ log(Level.WARNING,"WARNING: link request to gnunetd
failed. Trying to, make copy instead.");
+ }
+
+ // do not create link: transfer the file !
+ try {
+ fsize=(int) f.getSize();
+
+ upload=new CSUploadFile(f);
+
+ handle=new
RandomAccessFile(f.getPath(),"r").getChannel();
+ try {
+ pos = 0;
+ while (pos < fsize) {
+ delta=upload.copyChunk(handle);
+ if (delta<0) {
+ log(Level.SEVERE,"could not
read file");
+ index=-1;
+ break;
+ }
+
+ if (!sock.send(upload)) {
+ log(Level.WARNING,"Could not
send data to gnunetd. Is gnunetd running ?");
+ index=-1;
+ break;
+ }
+
+ rv=(CSResult)
sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"Could not
receive data from gnunetd. Is gnunetd running ?");
+ index=-1;
+ break;
+ }
+ if (!rv.isOkay()) {
+ index=-1;
+ break;
+ }
+
+ pos+=delta;
+ }
+ }
+ finally {
+ handle.close();
+ }
+ }
+ catch( IOException x ) {
+ err("Could not upload file \""+f.getLabel()+"\" !",x);
+ return -1;
+ }
+ return index;
+ }
+
+ /**
+ * Inserts a file under the given name into the local GNUnet node.
+ * Insert (or index) a file under the given name into the local GNUnet
+ * node.
+ *
+ * @param sock the socket to use to talk to gnunetd/connection to
gnunetd
+ * @param filename the name of the (incoming/source) file/the name of
the file to insert
+ * @param model the insert model used to
+ * update status information; points to null if
+ * no status updates shall be given, otherwise
+ * to a method that takes two integer arguments
+ * (retrieved so far, total).
+ * @param model_data pointer that is passed to the model method
+ * @return top IBlock on success, null on error/null on error,
otherwise the top block
+ */
+
+ public Block insertFile( CSSession sock, String filename, ProgressModel
model, Object model_data )
+ {
+ NodeContext nc;
+ int filesize;
+ Block top;
+ FileLocation f;
+ String restore;
+ int ret;
+
+ f=new FileLocation(filename);
+ if (!f.exists()) {
+ log(Level.WARNING,"Could not insert, file
'"+f.getPath()+"' does not exist !");
+ return null;
+ }
+
+ filename=f.getPath();
+ filesize = (int) f.getSize();
+
+ restore = prefs.getString("GNUNET-INSERT","INDEX-CONTENT",null);
+ if (filesize <= ContentBlock.SIZE) {
+ prefs.setString("GNUNET-INSERT","INDEX-CONTENT","NO");
+ }
+
+ if (filesize==0) {
+ log(Level.INFO,"File '"+filename+"' is empty, can't
insert it.");
+
prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);
+ return null;
+ }
+
+ nc=new NodeContext();
+ nc.pmodel = model;
+ nc.data = model_data;
+ nc.stats=new ProgressStats();
+ nc.stats.filesize = filesize;
+ nc.priority =
prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0);
+ if (nc.priority == 0) {
+ nc.priority = LOCAL_INDEXED_CONTENT_PRIO;
+ }
+
+ if (prefs.testString("GNUNET-INSERT","INDEX-CONTENT","YES")) {
+ ret = transferFile(sock, filename);
+ assert(ret!=0);
+
+ if (ret == -1) {
+ log(Level.WARNING,"Adding to index list failed,
trying insertion !");
+ nc.index = 0;
+ }
+ else {
+ nc.index = ret;
+ }
+ }
+ else {
+ nc.index = 0; /* 0: no indexing */
+ }
+
+ if (!nc.ioc.init(filesize,filename,true)) {
+
prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);
+ return null;
+ }
+
+ top = Block.create(filesize);
+ if (!top.insert(nc,sock)) {
+ top.destroy(null);
+ nc.ioc.free(false);
+
prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);//todo: le mettre en
finally
+ return null;
+ }
+ nc.ioc.free(false);
+ prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);
+ return top;
+ }
+
+ /**
+ * Insert a root-block into GNUnet.
+ *
+ * @param sock connection to gnunetd
+ * @param top the top block of the file
+ * @param description description to use
+ * @param filenameRoot filename to use
+ * @param mimetype mimetype to use
+ * @param keywords the keywords that shall be used to retrieve the
file/to be associated with the file
+ * @return the root node on success, null on error
+ */
+
+ public RootNode insertRoot( CSSession sock, Block top, String
description, String filenameRoot, String mimetype, String[] keywords )
+ {
+ int i,priority;
+ RootNode rn;
+ boolean res;
+ FileIdentifier fid;
+
+ priority = prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0);
+
+ fid=new FileIdentifier(top);
+
+ rn=new RootNode(fid);
+ rn.setDescription(description);
+ rn.setFileName(filenameRoot);
+ rn.setMimeType(mimetype);
+
+ res = true;
+ for (i=0; i<keywords.length; i++) {
+ if
(!insertRootWithKeyword(sock,rn,keywords[i],priority)) {
+ res = false;
+ }
+ }
+
+ // directory support...
+ new
GNDirectoryDatabase(prefs).makeRootNodeAvailable(rn,DIR_CONTEXT_INSERT);
+ return (res ? (RootNode) PersistentHelper.copy(rn) : null);
//todo: copie utile ???
+ }
+
+ /**
+ * Creates root node for the tree and writes the top-level tree node.
+ *
+ * @param sock connection to gnunetd
+ * @param rn the RootNode to insert
+ * @param keyword the keyword under which the rn is inserted
+ * @param contentPriority priority of the inserted content
+ * @return
+ */
+
+ public boolean insertRootWithKeyword( CSSession sock, RootNode rn,
String keyword, int contentPriority )
+ {
+ HashCode512 hc;
+ CSInsert3Hash msg;
+ CSResult rv;
+ ContentBlock block;
+
+ hc=HashCode512.create(keyword);
+
+ block=new ContentBlock();
+ if (!new
ContentEncoding().encryptContent(PersistentHelper.toBytes(rn),hc,block)) {
+ log(Level.SEVERE,"Encryption failed.");
+ return false;
+ }
+
+ msg = new CSInsert3Hash(contentPriority,hc,block);
+ if (!sock.send(msg)) {
+ log(Level.WARNING,"WARNING: could not send data to
gnunetd. Is gnunetd running ?");
+ return false;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ log(Level.WARNING,"WARNING: server did not send
confirmation of insertion");
+ return false;
+ }
+ if (!rv.isOkay()) {
+ log(Level.WARNING,"WARNING: server could not perform
insertion");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Inserts a directory. Sets the file-identifier that can afterwards be
used to retrieve the directory.
+ * Does <em>not</em> insert any RBlocks or SBlocks.
+ *
+ * @param sock
+ * @param nodeCount how many rootNodes in the directory
+ * @param rootNodes the actual nodes
+ * @param dirName name of this directory
+ * @param model
+ * @param modelArg
+ * @return null on failure, resulting file identifier for the directory
on success
+ */
+
+ public FileIdentifier insertDirectory( CSSession sock, int nodeCount,
RootNode[] rootNodes, String dirName, ProgressModel model, Object modelArg )
+ {
+ GNDirectory dir;
+ FileLocation temp;
+ Block top;
+ String oldval;
+ FileIdentifier fid;
+ MappedFile f;
+ boolean res;
+
+ dir=new GNDirectory(nodeCount,dirName,rootNodes);
+
+ temp=FileLocation.temporary();
+
+ f=temp.openNew();
+ res=f.writePersistent(dir);
+ f.close();
+
+ if (!res) {
+ log(Level.WARNING,"Could not write directory to
temporary file \""+temp.getLabel()+"\".");
+ temp.delete();
+ return null;
+ }
+
+ /* ok, insert the directory */
+ oldval = prefs.getString("GNUNET-INSERT",
"INDEX-CONTENT",null);
+ prefs.setString("GNUNET-INSERT", "INDEX-CONTENT","NO");
+ top = insertFile(sock,temp.getPath(),model,modelArg);
+
+ temp.delete();
+
+ prefs.setString("GNUNET-INSERT","INDEX-CONTENT", oldval);
+ if (top == null) {
+ log(Level.SEVERE,"Error inserting directory
"+temp.getPath()+".\nYou may want to check whether or not you are out of
space.\nRun gnunet-stats | grep \"AFS storage left\" to check.\n");
+ return null;
+ }
+
+ fid=new FileIdentifier(top);
+ top.destroy(null);
+ return fid;
+ }
+
+ /**
+ * Build an RBlock for the given file and insert
+ * it into GNUnet under all applicable keywords.
+ *
+ * @param sock
+ * @param fid the identifier for the file
+ * @param filename the full filename (complete path)
+ * @param gloKeywords
+ * @param gloKeywordCnt
+ * @param extractors_
+ * @return the RootNode
+ */
+
+ protected RootNode buildFileRBlock( CSSession sock, FileIdentifier fid,
String filename, String[] gloKeywords, int gloKeywordCnt, List extractors_ )
+ {
+ RootNode result;
+ int index,i;
+ String description;
+ String mimetype;
+ String shortFN;
+ boolean nodirectindex;
+ String[] keywords;
+ Extractor[] extractors = (Extractor[])
extractors_.toArray(new Extractor[extractors_.size()]);
+
+ mimetype = prefs.getString("GNUNET-INSERT","MIMETYPE",null);
+ description =
prefs.getString("GNUNET-INSERT","DESCRIPTION",null);
+ nodirectindex =
prefs.testString("GNUNET-INSERT","ADDITIONAL-RBLOCKS","NO");
+
+ shortFN = prefs.getString("GNUNET-INSERT","FILENAME",null);
+ if (shortFN == null) {
+ index=filename.replace('\\','/').lastIndexOf('/');
+ shortFN=filename.substring(index+1);
+ }
+
+ keywords=new String[] {};
+ if (!prefs.testString("GNUNET-INSERT","EXTRACT-KEYWORDS","NO"))
{
+ keywords=new
KeyWords().extractKeywordsMulti(filename,description,mimetype,extractors);
+ }
+
+ if (mimetype == null)
+ mimetype = "unknown";
+ if (description == null)
+ description = shortFN;
+
+ result=new RootNode(fid);
+ result.setDescription(description);
+ result.setFileName(shortFN);
+ result.setMimeType(mimetype);
+
+ for (i=0;i<gloKeywordCnt;i++)
+ if (!insertRootWithKeyword(sock,
+ result,
+ gloKeywords[i],
+
prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0))) {
+ log(Level.SEVERE,"Failed to insert RBlock. Is
gnunetd running and space available ?");
+ break;
+ }
+
+ for (i=0; i<keywords.length; i++) {
+ if (!nodirectindex) {
+ if
(!insertRootWithKeyword(sock,result,keywords[i],prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0)))
{
+ log(Level.SEVERE,"Failed to insert
RBlock. Is gnunetd running and space available ?");
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Build an RBlock for a directory (and insert the RBlock
+ * into GNUnet under all applicable keywords).
+ * @param sock
+ *
+ * @param fid the identifier for the file
+ * @param dirName the name of the last component of the path to the
directory
+ * @param description the description for the file
+ * @param gloKeywords
+ * @param gloKeywordCnt
+ * @return the RBlock
+ */
+
+ public RootNode buildDirectoryRBlock( CSSession sock, FileIdentifier
fid, String dirName, String description, String[] gloKeywords, int
gloKeywordCnt )
+ {
+ RootNode result;
+ int i;
+ String dn;
+
+ dn=dirName;
+ if (!dn.endsWith(GNUNET_DIRECTORY_EXT)) {
+ dn+=GNUNET_DIRECTORY_EXT;
+ }
+
+ result=new RootNode(fid);
+ result.setDescription(description);
+ result.setFileName(dn);
+ result.setMimeType(GNUNET_DIRECTORY_MIME);
+
+ for (i=0;i<gloKeywordCnt;i++) {
+ if
(!insertRootWithKeyword(sock,result,gloKeywords[i],prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0)))
{
+ log(Level.SEVERE,"Failed to insert RBlock. Is
gnunetd running and space available ?");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Index or insert a file or directory. Creates and inserts RootNodes
+ * for the file if applicable. Recursively processes directory if
+ * applicable. If directories are build or if filename refers to a
+ * single file, a plaintext RootNode that identifies the inserted
+ * object is returned and the FileIdentifier fid is set. If we do not
+ * create directories and a directory is given or if there was an
+ * error, null is returned. Every file encountered is inserted with
+ * all specified global keywords and (if applicable) additional keywords
+ * are extracted with the extractors.
+ *
+ * @param sock
+ * @param filename the name of the file or directory
+ * @param gloKeywords
+ * @param gloKeywordCnt
+ * @param extractors_
+ * @param model
+ * @param modelArg
+ * @param insert callback used to insert individual (leaf) files
+ * @param insertArg
+ * @return RootNode that identifies the single file or directory or
+ * null on error or null if filename is a directory and we don't
+ * create directories.
+ */
+
+ public RootNode insertRecursively( CSSession sock, String filename,
String[] gloKeywords, int gloKeywordCnt, List extractors_, ProgressModel model,
Object modelArg, InsertWrapper insert, Object insertArg )
+ {
+ DECData dec;
+ DirLocation dir;
+ FileIdentifier fid;
+ String dirName;
+ int index;
+ boolean builddir,processRecursive;
+
+ dir=new DirLocation(filename);
+ if (!dir.exists()) {
+ fid=insert.insert(sock,filename,insertArg);
+ if (fid==null) {
+ return null;
+ }
+ return
buildFileRBlock(sock,fid,filename,gloKeywords,gloKeywordCnt,extractors_);
+ }
+
+ processRecursive =
prefs.testString("GNUNET-INSERT","RECURSIVE","YES");
+ if (processRecursive) {
+ builddir =
prefs.testString("GNUNET-INSERT","BUILDDIR","YES");
+
+ dec=new DECData();
+ dec.identifiers.clear();
+ dec.nodes.clear();
+ dec.sock = sock;
+ dec.gloKeywords = gloKeywords;
+ dec.gloKeywordCnt = gloKeywordCnt;
+ dec.extractors_ = extractors_;
+ dec.model = model;
+ dec.model_arg = modelArg;
+ dec.insert = insert;
+ dec.insert_arg = insertArg;
+
+ final DECData _dec = dec;
+
+ dir.traverse(new Traverser() {
+ public boolean examine( Location node, int
depth )
+ {
+ dirEntryCallback(node,_dec);
+ return true;
+ }
+ });
+
+ assert(dec.nodes.size() == dec.identifiers.size()) :
"ERROR: assertion violated";
+
+ if (builddir) {
+
index=filename.replace('\\','/').lastIndexOf('/');
+ dirName=filename.substring(index+1);
+
+
fid=insertDirectory(sock,dec.nodes.size(),dec.getNodes(),dirName,model,modelArg);
+ dec.identifiers.clear();
+ dec.nodes.clear();
+ return
buildDirectoryRBlock(sock,fid,dirName,dirName,gloKeywords,gloKeywordCnt);
+ }
+ dec.identifiers.clear();
+ dec.nodes.clear();
+ return null;
+ }
+ return null;
+ }
+
+ protected void dirEntryCallback( Location node, DECData data )
+ {
+ String fn;
+ RootNode rb;
+
+ data.identifiers.add(null);
+ data.nodes.add(null);
+
+ fn = node.getName();
+ rb = insertRecursively(data.sock,
+ fn,
+ data.gloKeywords,
+ data.gloKeywordCnt,
+ data.extractors_,
+ data.model,
+ data.model_arg,
+ data.insert,
+ data.insert_arg);
+
+ if (rb != null) {
+
data.identifiers.set(data.identifiers.size()-1,rb.getFileIdentifier());
+ data.nodes.set(data.nodes.size()-1,rb);
+ }
+ else {
+ data.identifiers.remove(data.identifiers.size()-1);
+ data.nodes.remove(data.nodes.size()-1);
+ }
+ }
+}
+
+class DECData extends Object
+{
+ public List identifiers;
+ public List nodes;
+ public CSSession sock;
+ public String[] gloKeywords;
+ public int gloKeywordCnt;
+ public List extractors_;
+ public ProgressModel model;
+ public Object model_arg;
+ public InsertWrapper insert;
+ public Object insert_arg;
+
+
+ public DECData()
+ {
+ super();
+ identifiers=new ArrayList();
+ nodes=new ArrayList();
+ }
+
+ public RootNode[] getNodes()
+ {
+ return (RootNode[]) nodes.toArray(new RootNode[nodes.size()]);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertWrapper.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertWrapper.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/InsertWrapper.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,27 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public interface InsertWrapper
+{
+ /**
+ * Wrapper around insertFile that gives the user the appropriate
+ * feedback. The insertWrapper is expected to return fid at the
+ * end of the insertion. See "gnunet-insert.c::doFile()" for
+ * a possible implementation.
+ * @param sock
+ * @param filename
+ * @param closure
+ * @return
+ */
+
+ public FileIdentifier insert( CSSession sock, String filename, Object
closure );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/KeyWords.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/KeyWords.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/KeyWords.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,90 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import java.io.*;
+
+/**
+ * Layer to encapsulate the keyword extraction API and
+ * make it accessible to gnunet-insert.
+ * Layer to encapsulate the keyword extraction API and make it
+ * accessible to gnunet-insert.
+ *
+ * TODO: extractors à implémenter
+ */
+
+public class KeyWords extends Object
+{
+ public KeyWords()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Extract keywords, mime-type and description from a file
+ *
+ * @param filename the name of the file
+ * @param description the description (the user may have
+ * supplied a description already (*description != null),
+ * in that case, append, mind the maximum size!
+ * @param mimetype the mimetype, again, the user may
+ * have supplied one
+ * @return the list of keywords
+ */
+
+ public String[] extractKeywords( File filename, StringBuffer
description, StringBuffer mimetype )
+ {
+ //todo: à implémenter
+ return new String[] {};
+ }
+
+ public String[] extractKeywords( File filename, StringBuffer
description, StringBuffer mimetype, Extractor[] xxx )
+ {
+ //todo: à implémenter
+ return new String[] {};
+ }
+
+ /**
+ * Load the extractors as specified by the configuration.
+ *
+ * @return linked list of extractrs
+ */
+
+ public Extractor[] getExtractors()
+ {
+ //todo: à implémenter
+ return new Extractor[] {};
+ }
+
+ /**
+ * Extract keywords, mime-type and description from a file
+ *
+ * @param filename the name of the file
+ * @param description the description (the user may have
+ * supplied a description already (*description != null),
+ * in that case, append, mind the maximum size!
+ * @param mimetype the mimetype, again, the user may
+ * have supplied one
+ * @param exList the list of extractors/list of libextractor plugins,
null if
+ * libextractor is not used. Of type EXTRACTOR_ExtractorList*
+ * @return keywords the list of keywords, allocate space at
+ * another location if required, copy existing keywords
+ * over to that space! Do NEVER free *keywords!
+ */
+
+ public String[] extractKeywordsMulti( String filename, String
description, String mimetype, Extractor[] exList )
+ {
+ //todo: à implémenter
+ return new String[] {};
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Listener.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Listener.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Listener.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,29 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * type of callback used by nodes in the merkle tree to receive
+ * content-arrived notifications from the RequestManager
+ */
+
+public interface Listener
+{
+ /**
+ * Type of a method that is called by the RequestManager
+ * whenever a reply to a query has been received.
+ *
+ * @param query the query that was sent out
+ * @param reply the reply that was received
+ * @param rm the handle for the request manager
+ * @param data an opaque handle that is passed along,
+ * typically used to pass the NodeContext
+ * @return false the request manager should abort the download
+ */
+
+ public boolean listen( HashCode512 query, CSResultChk reply,
RequestManager rm, NodeContext data );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/NSSearchResultCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/NSSearchResultCallback.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/NSSearchResultCallback.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,21 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ */
+
+public interface NSSearchResultCallback
+{
+ /**
+ * Type of a callback method for results that have
+ * been received.
+ *
+ * @param sb the plaintext of the SBlock that has been received
+ * @param data the opaque handle (context for the callee)
+ */
+
+ public void searchResult( SBlock sb, Object data );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Node.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Node.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Node.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,116 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public abstract class Node extends LoggedObject implements Persistent,
AFSConstants
+{
+ /** Major format version. */
+ protected int majorVersion;
+
+ /** Minor format version. */
+ protected int minorVersion;
+
+ /** Information required for the download. */
+ protected FileIdentifier fileIdentifier;
+
+ /** Description of the content. */
+ protected String description;
+
+ /** Suggested filename. */
+ protected String fileName;
+
+ /** Mime-type (as claimed by insertion !). */
+ protected String mimeType;
+
+
+ protected Node( int major, int minor )
+ {
+ super(true);
+ majorVersion=major;
+ minorVersion=minor;
+ fileIdentifier=new FileIdentifier();
+ description="";
+ fileName="";
+ mimeType="";
+ }
+
+ public String toString()
+ {
+ return "Node [majorVersion="+majorVersion+",
minorVersion="+minorVersion+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getMajorVersion()
+ {
+ return majorVersion;
+ }
+
+ public int getMinorVersion()
+ {
+ return minorVersion;
+ }
+
+ public FileIdentifier getFileIdentifier()
+ {
+ return fileIdentifier;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription( String str )
+ {
+ description=str;
+ }
+
+ public String getFileName()
+ {
+ return fileName;
+ }
+
+ public void setFileName( String str )
+ {
+ fileName=str;
+ }
+
+ public String getMimeType()
+ {
+ return mimeType;
+ }
+
+ public void setMimeType( String str )
+ {
+ mimeType=str;
+ }
+
+ public abstract String toLabel();
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static int extractMajorVersion( byte[] b )
+ {
+ return (b.length>=2 ? ((b[0] & 0x000000ff)<<8) | (b[1] &
0x000000ff) : -1);
+ }
+
+ public static int extractMinorVersion( byte[] b )
+ {
+ if (b.length<4) {
+ return -1;
+ }
+ return (b.length>=4 ? ((b[2] & 0x000000ff)<<8) | (b[3] &
0x000000ff) : -1);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/NodeContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/NodeContext.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/NodeContext.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,55 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ * Context information for the merkle-tree objects
+ *
+ * The NodeContext groups the IOC and the progress model
+ * into a single struct.
+ */
+
+public class NodeContext extends Object
+{
+ /** The IO context for IO operations. */
+ public IOContext ioc;
+
+ /** Priority */
+ public int priority;
+
+ /** Index of the file that we are indexing, 0 for insertion. */
+ public int index;
+
+ /** The ProgressModel to communicate status updates. */
+ public ProgressModel pmodel;
+
+ /** Data argument to the ProgressModel. */
+ public Object data;
+
+ /** Current progress so far. */
+ public ProgressStats stats;
+
+
+ public NodeContext()
+ {
+ super();
+ ioc=new IOContext();
+ priority=0;
+ index=0;
+ pmodel=null;
+ data=null;
+ stats=new ProgressStats();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/P2P3HashResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/P2P3HashResult.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/P2P3HashResult.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,91 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * A return message for search query with double-hash proof.
+ */
+
+public class P2P3HashResult extends P2PMessage implements AFSConstants
+{
+ public static final int SIZE = HashCode512.SIZE+CONTENT_SIZE+4;
+
+ /** The double-hash. */
+ private HashCode512 hash;
+
+ /** The search result. */
+ private byte[] result;
+
+
+ public P2P3HashResult()
+ {
+ super(IS_3HASH_RESULT);
+ hash=new HashCode512();
+ result=new byte[CONTENT_SIZE];
+ }
+
+ public P2P3HashResult( HashCode512 h, ContentBlock block )
+ {
+ this();
+ hash=h;
+ System.arraycopy(block.content,0,result,0,CONTENT_SIZE);
+ }
+
+ public String toString()
+ {
+ return "P2P 3-hash result [hash="+hash+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public HashCode512 getDoubleHash()
+ {
+ return hash;
+ }
+
+ public HashCode512 getTripleHash()
+ {
+ return HashCode512.create(PersistentHelper.toBytes(hash));
+ }
+
+ public byte[] getResult()
+ {
+ return result;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_3HASH_RESULT,"bad type !");
+
+ hash.readBytes(buf,err);
+
+ buf.get(result);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_3HASH_RESULT);
+ hash.writeBytes(buf);
+ buf.put(result);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PChkResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PChkResult.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PChkResult.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,71 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Return message for content download (CHK style)
+ */
+
+public class P2PChkResult extends P2PMessage
+{
+ public static final int SIZE = 4+ContentBlock.SIZE;
+
+ /** The search result. */
+ public ContentBlock result;
+
+
+ public P2PChkResult()
+ {
+ super(IS_CHK_RESULT);
+ result=null;
+ }
+
+ public String toString()
+ {
+ return "P2P chk result [result="+result+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size !");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_CHK_RESULT,"bad type !");
+
+ result=new ContentBlock();
+ result.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_CHK_RESULT);
+ if (result!=null) {
+ result.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<ContentBlock.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PNSQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PNSQuery.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PNSQuery.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,61 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Request for content from a namespace.
+ */
+
+public class P2PNSQuery extends P2PQuery
+{
+ public static final int SIZEX =
P2PQuery.SIZE+HashCode512.SIZE*2;
+
+ /** Namespace that we are restricted to */
+ public HashCode512 namespace;
+
+ /** Identifier that we are looking for. */
+ public HashCode512 identifier;
+
+
+ public P2PNSQuery( HostIdentity hi )
+ {
+ this();
+ //todo: copie de 'hi' utile ???
+ throw new IllegalStateException("A CORRIGER");
+ }
+
+ public P2PNSQuery()
+ {
+ super(IS_NSQUERY);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getByteSize()
+ {
+ return SIZEX;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ throw new IllegalStateException();
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ throw new IllegalStateException();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PQuery.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PQuery.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,180 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * Request for content. The number of queries can
+ * be determined from the header size.
+ */
+
+public class P2PQuery extends P2PMessage
+{
+ public static final int SIZE = 12+HostIdentity.SIZE;
+
+ /** How important is this request ? */
+ private int priority;
+
+ /** Time to live in milliseconds. */
+ private int ttl;
+
+ /** To whom to return results ? */
+ private HostIdentity returnTo;
+
+ /** Hashcodes of the file(s) we're looking for. If multiple queries are
given, the first query
+ is the super-query for the bloom filter. If only one query is given,
the bloom filter should NOT
+ be used since it does not contain summaries for simple 1k blocks. It
is not possible to group
+ multiple queries with this message type if they are not dominated by
the same super-query. */
+ private List queries;
+
+
+ public P2PQuery()
+ {
+ this(IS_QUERY);
+ }
+
+ public P2PQuery( HostIdentity hi )
+ {
+ this();
+ returnTo=(HostIdentity) PersistentHelper.copy(hi); //todo:
copie utile ?
+ }
+
+ protected P2PQuery( int t )
+ {
+ super(t);
+ priority=0;
+ ttl=0;
+ returnTo=null;
+ queries=new ArrayList();
+ }
+
+ public String toString()
+ {
+ return "AFS p2p query";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public int getPriority()
+ {
+ return priority;
+ }
+
+ public int getTTL()
+ {
+ return ttl;
+ }
+
+ public void setPriorityAndTTL( int p, int t )
+ {
+ priority=p;
+ ttl=t;
+ }
+
+ public HostIdentity getReturnTo()
+ {
+ return returnTo;
+ }
+
+ public void setReturnTo( HostIdentity hi )
+ {
+ returnTo=(HostIdentity) PersistentHelper.copy(hi);
+ }
+
+ public boolean sameQueries( P2PQuery q )
+ {
+ return queries.equals(q.queries);
+ }
+
+ public HashCode512 getQuery( int index )
+ {
+ return ((index>=0 && index<queries.size()) ? (HashCode512)
queries.get(index) : null);
+ }
+
+ public void addQuery( HashCode512 h )
+ {
+ queries.add(PersistentHelper.copy(h));
+ }
+
+ public void setQuery( int index, HashCode512 h )
+ {
+ queries.set(index,PersistentHelper.copy(h)); //todo: copie
utile ???
+ }
+
+ public HashCode512[] getQueries()
+ {
+ return (HashCode512[]) queries.toArray(new
HashCode512[queries.size()]);
+ }
+
+ public void setQueries( HashCode512[] q )
+ {
+ queries.clear();
+ queries.addAll(Arrays.asList(q));
+ }
+
+ public void clearQueries()
+ {
+ queries.clear();
+ }
+
+ public int getByteSize()
+ {
+ return SIZE+queries.size()*HashCode512.SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ HashCode512 h;
+ int size,type,i;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size<SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_QUERY,"bad type !");
+
+ priority=buf.getInt();
+ ttl=buf.getInt();
+
+ returnTo=new HostIdentity();
+ returnTo.readBytes(buf,err);
+
+ queries.clear();
+ for (i=(size-SIZE)/HashCode512.SIZE; i>0; i--) {
+ h=new HashCode512();
+ h.readBytes(buf,err);
+ queries.add(h);
+ }
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ HashCode512 h;
+ int i;
+
+ buf.putShort((short) getByteSize());
+ buf.putShort((short) IS_QUERY);
+ buf.putInt(priority);
+ buf.putInt(ttl);
+ if (returnTo!=null) {
+ returnTo.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HostIdentity.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ for (i=0; i<queries.size(); i++) {
+ h=(HashCode512) queries.get(i);
+ h.writeBytes(buf);
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PSBlockResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PSBlockResult.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/P2PSBlockResult.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,72 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Return message for SBlock download
+ */
+
+public class P2PSBlockResult extends P2PMessage
+{
+ public static final int SIZE = 4+EncryptedSBlock.SIZE;
+
+ /** The search result. */
+ private EncryptedSBlock result;
+
+
+ public P2PSBlockResult()
+ {
+ super(IS_SBLOCK_RESULT);
+ result=new EncryptedSBlock();
+ }
+
+ public P2PSBlockResult( EncryptedSBlock esb )
+ {
+ this();
+ result=(EncryptedSBlock) PersistentHelper.copy(esb); //todo:
copie utile ???
+ }
+
+ public String toString()
+ {
+ return "P2P result SBlock [result="+result+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public EncryptedSBlock getResult()
+ {
+ return result;
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int size,type;
+
+ size=buf.getShort() & 0x0000ffff;
+ err.reportIf(size!=SIZE,"bad size");
+
+ type=buf.getShort() & 0x0000ffff;
+ err.reportIf(type!=IS_SBLOCK_RESULT,"bad type !");
+
+ result.readBytes(buf,err);
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ buf.putShort((short) SIZE);
+ buf.putShort((short) IS_SBLOCK_RESULT);
+ result.writeBytes(buf);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Policy.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Policy.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Policy.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,373 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * The code in this module is responsible for enforcing
+ * the anonymity policy set by the user.
+ */
+
+public class Policy extends LoggedObject implements AFSConstants
+{
+ /** Socket to communicate with gnunetd. */
+ private CSSession sock;
+
+ /** CoreAPI, null if we are using the socket ! */
+ private CoreForProtocol coreAPI;
+
+ /** Which value did the user specify for the sendPolicy ? */
+ private int sendPolicy;
+
+ /** Which value did the user specify for the receivePolicy ? */
+ private int receivePolicy;
+
+ /** Last time traffic information was obtained. */
+ private long lastPoll;
+
+ /** Mutex for synchronizing access. */
+ private Object lock;
+
+ /** Number of peers that were active at the last poll (since this is
always type sensitive, not totals). */
+ private int chkPeers;
+
+ /** */
+ private int hashPeers;
+
+ /** */
+ private int queryPeers;
+
+ /** Number of bytes of unmatched bytes of traffic at the last poll
(totals and separate for the 3 message types). */
+ private int totalReceiveBytes;
+
+ /** */
+ private int totalCHKBytes;
+
+ /** */
+ private int total3HASHBytes;
+
+ /** */
+ private int totalQueryBytes;
+
+
+ protected Policy( Prefs prefs )
+ {
+ super(true);
+ sock=null;
+ coreAPI=null;
+ sendPolicy=prefs.getInt("AFS","ANONYMITY-SEND",0);
+ receivePolicy=prefs.getInt("AFS","ANONYMITY-RECEIVE",0);
+ lastPoll=0;
+ lock=new Object();
+ chkPeers=0;
+ hashPeers=0;
+ queryPeers=0;
+ totalReceiveBytes=0;
+ totalCHKBytes=0;
+ total3HASHBytes=0;
+ totalQueryBytes=0;
+ }
+
+ /**
+ * Initialize the module / from server.
+ *
+ * @param prefs
+ * @param capi the GNUnet core API
+ */
+
+ public Policy( Prefs prefs, CoreForProtocol capi )
+ {
+ this(prefs);
+ coreAPI=capi;
+ }
+
+ /**
+ * Initialize the module / we are a client.
+ *
+ * @param prefs
+ * @param session
+ */
+
+ public Policy( Prefs prefs, CSSession session )
+ {
+ this(prefs);
+
+ assert(session!=null);
+ sock=session;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Shutdown the module.
+ */
+
+ public void doneAnonymityPolicy()
+ {
+ if (sock!=null) {
+ sock.disconnect();
+ sock=null;
+ }
+ coreAPI=null;
+ }
+
+ /**
+ * Check if the anonymity policy will be violated if
+ * a message of the given type will be send.
+ * @param type the request type of the message that will be
+ * transmitted
+ * @param size the size of the message that will be
+ * transmitted
+ * @return true if this is ok for the policy, false if not
+ */
+
+ public boolean checkAnonymityPolicy( int type, int size )
+ {
+ if (sock==null && coreAPI==null) {
+ return true;
+ }
+
+ if (sock == null)
+ pollCAPI();
+ else
+ pollSocket();
+
+ switch (type) {
+ case CSMessage.IS_QUERY:
+ return checkPolicy(receivePolicy, type, size);
+ case CSMessage.IS_RESULT_3HASH:
+ case CSMessage.IS_RESULT_CHK:
+ return checkPolicy(sendPolicy, type, size);
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Poll gnunetd via TCP about traffic information.
+ * Note that we only send the request if we would
+ * otherwise potentially have to refuse messages.
+ */
+
+ protected void pollSocket()
+ {
+ long now;
+ CSTrafficInfo info;
+ CSTrafficRequest req;
+ TrafficCounter tc;
+ int i;
+
+ now=Scheduler.now();
+
+ synchronized(lock) {
+ if (now - lastPoll < TTL_DECREMENT) {
+ return; // poll at most every ttl-decrement
time units
+ }
+
+ lastPoll = now;
+
+ req=new CSTrafficRequest();
+ req.timePeriod=(int) TTL_DECREMENT;
+
+ if (!sock.send(req)) {
+ log(Level.WARNING,"WARNING: could not query
gnunetd about traffic conditions");
+ return;
+ }
+
+ info = (CSTrafficInfo)
sock.receive(CSTrafficInfo.class);
+ if (info==null) {
+ log(Level.WARNING,"WARNING: did not receive
reply from gnunetd about traffic conditions");
+ return;
+ }
+
+ for (i=info.count-1; i>=0; i--) {
+ tc=info.counters[i];
+ if ((tc.flags & TrafficCounter.TC_TYPE_MASK) ==
TrafficCounter.TC_RECEIVED) {
+ totalReceiveBytes += tc.count *
tc.avrg_size;
+ switch (tc.type) {
+ case P2PMessage.IS_QUERY:
+ totalQueryBytes +=
tc.count * tc.avrg_size;
+ queryPeers += (tc.flags
& TrafficCounter.TC_DIVERSITY_MASK);
+ break;
+ case P2PMessage.IS_3HASH_RESULT:
+ total3HASHBytes +=
tc.count * tc.avrg_size;
+ hashPeers += (tc.flags
& TrafficCounter.TC_DIVERSITY_MASK);
+ break;
+ case P2PMessage.IS_CHK_RESULT:
+ totalCHKBytes +=
tc.count * tc.avrg_size;
+ chkPeers += (tc.flags &
TrafficCounter.TC_DIVERSITY_MASK);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Poll gnunet core via coreapi about traffic information.
+ */
+
+ protected void pollCAPI()
+ {
+ long now;
+ int
avgMessageSize,messageCount,peerCount,messageType;
+ int[] counts;
+
+ now=Scheduler.now();
+ synchronized(lock) {
+ if (now - lastPoll < TTL_DECREMENT) {
+ return; // don't bother
+ }
+ lastPoll = now;
+
+ for
(messageType=0;messageType<P2PMessage.IS_MAX;messageType++) {
+
counts=coreAPI.getTrafficStats(messageType,TrafficCounter.TC_RECEIVED,(int)
TTL_DECREMENT);
+ avgMessageSize=counts[0];
+ messageCount=counts[1];
+ peerCount=counts[2];
+
+ totalReceiveBytes += messageCount *
avgMessageSize;
+ switch (messageType) {
+ case P2PMessage.IS_QUERY:
+ totalQueryBytes += messageCount
* avgMessageSize;
+ queryPeers += peerCount;
+ break;
+
+ case P2PMessage.IS_3HASH_RESULT:
+ total3HASHBytes += messageCount
* avgMessageSize;
+ hashPeers += peerCount;
+ break;
+
+ case P2PMessage.IS_CHK_RESULT:
+ totalCHKBytes += messageCount *
avgMessageSize;
+ chkPeers += peerCount;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Test if the required number of peers were active.
+ *
+ * @param port which protocol are we interested in
+ * @param peerCount how many peers are required
+ * @return true if we had sufficient amounts of traffic, false if not
+ */
+
+ protected boolean checkPeerPolicy( int port, int peerCount )
+ {
+ switch (port) {
+ case P2PMessage.IS_QUERY:
+ if (queryPeers >= peerCount)
+ return true;
+ return false;
+ case P2PMessage.IS_CHK_RESULT:
+ if (chkPeers >= peerCount)
+ return true;
+ return false;
+ case P2PMessage.IS_3HASH_RESULT:
+ if (hashPeers >= peerCount)
+ return true;
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Test if the required amount of traffic is available.
+ *
+ * @param port what type of traffic are we interested in?
+ * @param size how much traffic do we intend to produce?
+ * @param byteRatio how much cover traffic do we need (byteRatio*size)
+ * @param strictMatch does only traffic of exactly the same
+ * type (port) count?
+ * @return true if enough cover traffic was be found, false if not.
+ */
+
+ protected boolean checkRatioPolicy( int port, int size, int byteRatio,
boolean strictMatch )
+ {
+ int cost;
+
+ cost = byteRatio * size;
+ if (strictMatch) {
+ switch (port) {
+ case P2PMessage.IS_QUERY:
+ if (totalQueryBytes < cost)
+ return false;
+ totalQueryBytes -= cost;
+ return true;
+ case P2PMessage.IS_CHK_RESULT:
+ if (totalCHKBytes < cost)
+ return false;
+ totalCHKBytes -= cost;
+ return true;
+ case P2PMessage.IS_3HASH_RESULT:
+ if (total3HASHBytes < cost)
+ return false;
+ total3HASHBytes -= cost;
+ return true;
+ default:
+ return false;
+ }
+ }
+ if (totalReceiveBytes < cost)
+ return false;
+ totalReceiveBytes -= cost;
+ return true;
+ }
+
+ /**
+ * Test if the policy requirements are fullfilled.
+ *
+ * @param policyValue the anonymity degree required by the user
+ * @param type the message type
+ * @param size the size of the message
+ * @return true if this isok for the policy, false if not.
+ */
+
+ protected boolean checkPolicy( int policyValue, int type, int size )
+ {
+ int peerCount,byteRatio;
+
+ if (policyValue <= 0)
+ return true; /* no policy */
+ if (policyValue >= 1000) {
+ byteRatio = policyValue / 1000;
+ peerCount = policyValue % 1000;
+ } else {
+ byteRatio = policyValue;
+ peerCount = 0;
+ }
+ if (peerCount > 0)
+ if (false == checkPeerPolicy(type,
+ peerCount))
+ return false;
+ if (byteRatio > 0)
+ if (false == checkRatioPolicy(type,
+ size,
+ byteRatio,
+ policyValue >= 1000))
+ return false;
+ return true;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Priority.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Priority.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Priority.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,99 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ * Keep track of the maximum priority that we are currently using.
+ */
+
+public class Priority extends LoggedObject implements AFSConstants
+{
+ private int maxPriority_;
+ private Scheduler scheduler;
+ private AbstractClient client;
+ private ScheduledTask trackJob;
+
+
+ public Priority( Application app )
+ {
+ super(true);
+ maxPriority_=0;
+ client=(AbstractClient) app;
+ scheduler=app.getScheduler();
+ trackJob=new ScheduledTask("TRACK-PRIORITY",new
AbstractAction() {
+ public void perform()
+ {
+ trackPriority();
+ }
+ },TTL_DECREMENT);
+ }
+
+ /**
+ * This method must be called to start the priority
+ * tracker.
+ */
+
+ public void startAFSPriorityTracker()
+ {
+ trackPriority();
+ scheduler.addJob(trackJob,TTL_DECREMENT);
+ }
+
+ /**
+ * This method must be called to stop the priority
+ * tracker. Call after cron has been stopped.
+ */
+
+ public void stopAFSPriorityTracker()
+ {
+ scheduler.deleteJob(trackJob);
+ }
+
+ /**
+ * What is the highest priority that AFS clients should use for
requests at this point in time ?
+ * @return
+ */
+
+ public int getMaxPriority()
+ {
+ return maxPriority_;
+ }
+
+ protected void trackPriority()
+ {
+ CSSession sock;
+ CSGetAvgPriority req;
+ CSResult rv;
+ int res;
+
+ sock=client.connect();
+ if (sock == null) {
+ maxPriority_ = 0;
+ return;
+ }
+
+ req=new CSGetAvgPriority();
+ if (sock.send(req)) {
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv!=null) {
+ res=rv.getResult();
+ maxPriority_ = 2*res+1;
+
+ debug("Current maximum priority :
"+maxPriority_);
+ }
+ else {
+ maxPriority_ = 0;
+ }
+ }
+ else {
+ maxPriority_ = 0;
+ }
+ sock.disconnect();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressModel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressModel.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressModel.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,33 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ * callback for updates on the progress of an operation
+ */
+
+public interface ProgressModel
+{
+ public static final ProgressModel NO_MODEL = new
ProgressModel() {
+ public void progress( ProgressStats stats, Object data )
+ {
+ }
+ };
+
+ /**
+ * Called whenever we make progress. Callback methods
+ * of this type are used during insertion and download to notify the
+ * user interface of the progress we're making. If the model is called
+ * with position == total, the download is complete. If the model
+ * is called with position == total == 0, then there was a fatal error
+ * and the download was aborted.
+ *
+ * @param stats progress statistics
+ * @param data a context passed around for use by the PM
+ * implementation
+ */
+
+ public void progress( ProgressStats stats, Object data );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressStats.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressStats.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/ProgressStats.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,54 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ * statistics about the progress
+ *
+ * Progress of the current operation. Used for passing
+ * data to callbacks. Some of these make sense only for
+ * downloading.
+ */
+
+public class ProgressStats extends Object
+{
+ public int progress; /* bytes processed */
+ public int filesize; /* total file size */
+ public int requestsSent;
+ public int requestsPending;
+ public int currentRetries;
+ public int totalRetries;
+ public int currentTTL;
+ public int duplicationEstimate;
+
+
+ public ProgressStats()
+ {
+ super();
+ }
+
+ public ProgressStats( ProgressStats p )
+ {
+ super();
+ progress=p.progress;
+ filesize=p.filesize;
+ requestsSent=p.requestsSent;
+ requestsPending=p.requestsPending;
+ currentRetries=p.currentRetries;
+ totalRetries=p.totalRetries;
+ currentTTL=p.currentTTL;
+ duplicationEstimate=p.duplicationEstimate;
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/Pseudonym.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/Pseudonym.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/Pseudonym.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,282 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * functions for handling pseudonyms
+ */
+
+public class Pseudonym extends LoggedObject
+{
+ private static final String PSEUDODIR =
"data/pseudonyms";
+ private static final String NS_HANDLE =
"known_namespaces";
+
+ private Prefs prefs;
+ private DirLocation base;
+
+
+ public Pseudonym( Prefs p )
+ {
+ super(true);
+ prefs=p;
+
+ base=prefs.getDirLocation("","GNUNET_HOME");
+ if (base==null) {
+ trace("Configuration file must specify a directory for
GNUnet to store per-peer data under GNUNET_HOME !");
+ throw new RuntimeException();
+ }
+ base=base.getDirectory(PSEUDODIR);
+ base.create();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Create a new pseudonym.
+ *
+ * @param name the name of the pseudonym
+ * @param password passphrase to encrypt the pseudonym on disk (may be
null)
+ * @return null on error (e.g. pseudonym exists), otherwise the secret
key
+ */
+
+ public PrivateKey createPseudonym( String name, String password )
+ {
+ FileLocation fileName;
+ PrivateKey hk;
+ byte[] dst;
+ SessionKey key;
+ HashCode512 hc;
+ byte[] iv;
+ MappedFile f;
+
+ fileName=base.getFile(name);
+ if (fileName.exists()) {
+ log(Level.WARNING,"Can't create pseudonym "+name+",
file \""+fileName.getLabel()+"\" exists.");
+ return null;
+ }
+
+ hk=PrivateKey.create();
+ dst=PersistentHelper.toBytes(hk);
+
+ if (password!=null) {
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+
System.arraycopy(SessionKey.INITVALUE,0,iv,0,SessionKey.BLOWFISH_BLOCK_LENGTH);
+
+ hc=HashCode512.create(password);
+ key=hc.extractKey(null);
+
+ dst=key.encrypt(dst,iv);
+ if (dst==null) {
+ return null;
+ }
+ }
+
+ f=fileName.openNew();
+ if (!f.writeBytes(dst)) {
+ log(Level.WARNING,"Failed to write "+dst.length+"
bytes.");
+ f.close();
+
+ fileName.delete();
+ return null;
+ }
+ f.close();
+ return hk;
+ }
+
+ /**
+ * Delete a pseudonym.
+ *
+ * @param name the name of the pseudonym
+ * @return OK on success, false on error
+ */
+
+ public boolean deletePseudonym( String name )
+ {
+ FileLocation f;
+
+ f=base.getFile(name);
+ if (!f.exists()) {
+ log(Level.WARNING,"File does not exist : "+f+".");
+ return false;
+ }
+ if (!f.delete()) {
+ log(Level.WARNING,"Could not unlink "+f+".");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Read pseudonym.
+ *
+ * @param name the name of the pseudonym
+ * @param password passphrase to encrypt the pseudonym on disk (may be
null)
+ * @return null on error (e.g. password invalid, pseudonym does not
exist), otherwise the secret key
+ */
+
+ public PrivateKey readPseudonym( String name, String password )
+ {
+ PrivateKey hk;
+ ByteBuffer buf;
+ SessionKey key;
+ HashCode512 hc;
+ byte[] iv,tmp;
+ MappedFile f;
+ FileLocation loc;
+
+ loc=base.getFile(name);
+ if (!loc.exists()) {
+ log(Level.WARNING,"File \""+loc.getLabel()+"\" does not
exist.");
+ return null;
+ }
+
+ f=loc.open();
+ buf=f.readBuffer();
+ if (buf==null) {
+ log(Level.WARNING,"Failed to read "+loc.getLabel()+".");
+ f.close();
+ return null;
+ }
+
+ if (password != null) {
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+
System.arraycopy(SessionKey.INITVALUE,0,iv,0,SessionKey.BLOWFISH_BLOCK_LENGTH);
+
+ hc=HashCode512.create(password);
+ key=hc.extractKey(null);
+
+ tmp=new byte[buf.capacity()];
+ buf.get(tmp);
+ tmp=key.decrypt(tmp,iv);
+ if (tmp==null) {
+ log(Level.WARNING,"Decrypting pseudonym
failed.");
+ return null;
+ }
+ buf=ByteBuffer.wrap(tmp);
+ }
+
+ hk=(PrivateKey) PersistentHelper.readFully(PrivateKey.class,
buf);
+ if (hk==null) {
+ // wrong PW happens A LOT, thus don't always print this
warning !
+ log(Level.FINEST,"Pseudonym format for "+name+"
invalid. Wrong password ?");
+ f.close();
+ return null;
+ }
+ f.close();
+ return hk;
+ }
+
+ /**
+ * Test if we have any pseudonyms.
+ *
+ * @return YES if we do have pseudonyms, otherwise NO.
+ */
+
+ public boolean havePseudonyms()
+ {
+ return base.count()>0;
+ }
+
+ /**
+ * Build a list of all available pseudonyms.
+ *
+ * @return null on error, otherwise the list of pseudonyms
+ */
+
+ public String[] listPseudonyms()
+ {
+ TraverserContext ctx;
+ final List _myList=new ArrayList();
+
+ ctx=new TraverserContext();
+ ctx.setFilesOnly(true);
+
+ base.traverse(new Traverser() {
+ public boolean examine( Location node, int depth )
+ {
+ _myList.add(node.getName());
+ return true;
+ }
+ },ctx);
+ return (String[]) _myList.toArray(new String[_myList.size()]);
+ }
+
+ /**
+ * Build a list of all known namespaces.
+ *
+ * @param list where to store the names of the namespaces
+ * @return false on error, otherwise the number of known namespaces
+ */
+
+ public int listNamespaces( HashCode512[][] list )
+ {
+ list[0]=listNamespaces();
+ return (list[0]!=null ? list[0].length : -1);
+ }
+
+ public HashCode512[] listNamespaces()
+ {
+ HashCode512[] list;
+ ByteBuffer buf;
+ int i;
+
+ buf=prefs.getContent(NS_HANDLE);
+ if (buf==null) {
+ return null;
+ }
+ if ((buf.limit() % HashCode512.SIZE)!=0) {
+ log(Level.WARNING,"State database "+NS_HANDLE+"
corrupted, deleting contents.");
+ prefs.unlink(NS_HANDLE);
+ return null;
+ }
+
+ list=new HashCode512[buf.limit()/HashCode512.SIZE];
+ for (i=0; i<list.length; i++) {
+ list[i]=(HashCode512)
PersistentHelper.read(HashCode512.class,buf);
+ if (list[i]==null) {
+ return null;
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Add a namespace to the set of known namespaces.
+ *
+ * @param ns the namespace identifier
+ */
+
+ public void addNamespace( HashCode512 ns )
+ {
+ HashCode512[] list;
+ int i;
+
+ list=listNamespaces();
+ if (list!=null) {
+ for (i=0; i<list.length; i++) {
+ if (ns.equals(list[i])) {
+ return; // seen before
+ }
+ }
+ }
+
+ prefs.appendContent(NS_HANDLE,PersistentHelper.toBytes(ns));
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestContinuation.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestContinuation.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestContinuation.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,37 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ */
+
+public class RequestContinuation extends Object
+{
+ public NodeContext nc;
+ public RequestEntry entry;
+ /* in HOST byte order! */
+ public int ttl;
+ /* in HOST byte order! */
+ public int prevttl;
+ public int prevpri;
+ public long prevlt;
+ public RequestContinuation next;
+
+
+ public RequestContinuation()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestEntry.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestEntry.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestEntry.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,50 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ * Format of a request as tracked by the RequestManager.
+ */
+
+public class RequestEntry extends Object
+{
+ /** The message that is send to gnunetd. */
+ public CSQuery message;
+
+ /** Last time the query was send. */
+ public long lasttime;
+
+ /** Whom to call once we get a reply? */
+ public Listener receiver;
+
+ /** The node to pass to the receiver method. */
+ public Block receiverNode;
+
+ /** Opaque data handle to pass to the Listener. */
+ public NodeContext data;
+
+ /** How long have we been actively trying this one? */
+ public int tries;
+
+ /** How many replies have we received for this entry? (for
super-queries, thus always in [0,25]).
+ [reset for each retransmission; used to NOT increment the TTL if we
got a reply] */
+ public int successful_replies;
+
+
+ public RequestEntry()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestManager.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestManager.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/RequestManager.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,855 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * The RequestManager keeps track of and re-issues queries
+ * The RequestManager keeps track of queries and re-issues
+ * requests if no reply is received.
+ * structure that keeps track of currently pending requests for a download
+ *
+ * Handle to the state of a request manager. Here we keep track of
+ * which queries went out with which priorities and which nodes in
+ * the merkle-tree are waiting for the replies.
+ */
+
+public class RequestManager extends LoggedObject implements AFSConstants
+{
+ private static int lpri;
+
+ static {
+ lpri=0;
+ }
+
+ /** Mutex for synchronizing access to this struct */
+ private Object lock;
+
+ /** Current list of all pending requests */
+ private RequestEntry[] requestList;
+
+ /** Number of pending requests (highest used index) */
+ private int requestListIndex;
+
+ /** Number of entries allocated for requestList */
+ private int requestListSize;
+
+ /** Current "good" TTL (initial) [64s]. In HOST byte order. */
+ private int initialTTL;
+
+ /** Congestion window. How many messages should be pending
concurrently? */
+ private int congestionWindow;
+
+ /** Slow-start threshold (see RFC 2001) */
+ private int ssthresh;
+
+ /** Current estimate of "duplication" rate (amount of duplicate replies
we get). */
+ private int duplicationEstimate;
+
+ /** Socket used to talk to gnunetd. */
+ private CSSession sock;
+
+ /** The thread that receives results from gnunetd. */
+ private Task receiveThread_;
+
+ /** */
+ private int lastDET;
+
+ /** */
+ private RequestContinuation start;
+
+ /** CRC of the top-IBlock, see downloadutil.c and
block.c::childDownloadCompleted. */
+ public int topCrc32;
//fixme: private
+
+ /** The top block. */
+ public Block top; //todo:
remettre en private
+
+ private Scheduler scheduler;
+ private Policy policy;
+
+ private ScheduledTask rrjob = new
ScheduledTask("REQUEST-JOB",new AbstractAction() {
+ public void perform()
+ {
+ requestJob();
+ }
+ });
+ private Priority priority;
+
+
+ private RequestManager()
+ {
+ super(true);
+ }
+
+ /**
+ * Create a request manager. Will create the request manager
+ * datastructures and also connect to gnunetd. Creates thread that
+ * listens to gnunetd replies and another thread that periodically
+ * re-issues the queries. Use destroyRequestManager to abort and/or to
+ * free resources after the download is complete. The callback method
+ * in nc will be invoked to notify the caller of the download progress
+ * such that it is possible to tell when we are done.
+ * @param app
+ * @param p
+ * @param pppp
+ */
+
+ public RequestManager( AbstractClient app, Policy p, Priority pppp )
+ {
+ this();
+ scheduler=app.getScheduler();
+ policy=p;
+ priority=pppp;
+
+ start=null;
+ lastDET=0;
+ lock=new Object();
+ requestListIndex=0;
+ requestListSize=256;
+ requestList=new RequestEntry[256];
+ initialTTL= (int) Scheduler.SECS_5;
+ /* RFC 2001 suggests to use 1 segment size initially;
+ Given 1500 octets per message in GNUnet, we would
+ have 2-3 queries of maximum size (552); but since
+ we are multi-casting to many peers at the same
+ time AND since queries can be much smaller,
+ we do WHAT??? */
+ congestionWindow=1; /* RSS is 1 */
+ ssthresh=65535;
+ duplicationEstimate=0;
+ sock=app.connect();
+ if (sock==null) {
+ log(Level.WARNING,"Could not create socket to connect
to gnunetd.");
+ requestList=null;
+ requestListSize=0;
+ return;
+ }
+
+ receiveThread_=new Task("REQUEST-MANAGER-RECEIVE",new
AbstractAction() {
+ public void perform()
+ {
+ receiveThread();
+ }
+ });
+ receiveThread_.launch();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Destroy the resources associated with a request manager.
+ * Invoke this method to abort the download or to clean up
+ * after the download is complete.
+ *
+ */
+
+ public void destroy()
+ {
+ CSSession sockx;
+ int i;
+
+ scheduler.suspend();
+
+ synchronized(lock) {
+ sockx=sock;
+ sock = null;
+ scheduler.deleteJob(rrjob);
+ }
+
+ if (sockx != null) {
+ sockx.disconnect(); /* unblock RM thread */
+ }
+
+ receiveThread_.join();
+
+ synchronized(lock) {
+ for (i=0; i<requestListIndex; i++) {
+ freeInContinuations(requestList[i]);
+ requestList[i]=null;
+ }
+ requestListIndex = 0;
+ requestList=null;
+ requestListSize=0;
+ }
+
+ if (top != null) {
+ top.destroy(this);
+ }
+ scheduler.resume();
+ }
+
+ /**
+ * Print the contents of the request manager. For debugging.
+ * For debugging.
+ */
+
+ public void print()
+ {
+ int i;
+
+ synchronized(lock) {
+ log(Level.FINEST,"RM TTL "+initialTTL+"
duplicates"+duplicationEstimate);
+
+ for (i=0; i<requestListIndex; i++) {
+ log(Level.FINEST,i+":
"+requestList[i].message.getQuery(0).toHex()+" for node
"+requestList[i].receiverNode+" ("+requestList[i].tries+" tries)");
+ }
+ }
+ }
+
+ /**
+ * Assert that there are no pending requests for this node.
+ * @param node
+ */
+
+ public void assertDead( Block node )
+ {
+ int i;
+
+ synchronized(lock) {
+ for (i=0; i<requestListIndex; i++) {
+ if (requestList[i].receiverNode == node) {
+ assert(false) : "Node "+node+" is being
destroyed while request is pending.";
+ }
+ }
+ }
+ }
+
+ /**
+ * We are approaching the end of the download. Cut all TTLs in half.
+ */
+
+ public void endGame()
+ {
+ int i;
+
+ synchronized(lock) {
+ for (i=0;i<requestListIndex;i++) {
+ RequestEntry entry = requestList[i];
+ int ttl = entry.message.getTTL();
+ entry.message.setTTL(ttl / 2);
+ }
+ }
+ }
+
+ /**
+ * Queue a request for execution.
+ *
+ * @param node the node to call once a reply is received
+ * @param callback the method to invoke
+ * @param data the data argument to the Listener
+ * @param message the query to send to gnunetd, freed by callee!
+ */
+
+ public void request( Block node, Listener callback, NodeContext data,
CSQuery message )
+ {
+ RequestEntry[] old;
+ RequestEntry entry;
+
+ debug("Request request for "+node+" with callback "+callback);
+
+ entry = new RequestEntry();
+ entry.message= message;
+ entry.successful_replies= 0;
+ entry.lasttime = 0; /* never sent */
+ entry.receiver = callback;
+ entry.receiverNode= node;
+ entry.data = data;
+ entry.tries= 0; /* not tried so far */
+
+ synchronized(lock) {
+ /* can we add to current list & issue instantly? */
+ if (requestListSize == requestListIndex) {
+ old=requestList;
+
+ requestListSize*=2;
+ requestList=new RequestEntry[requestListSize];
+
System.arraycopy(old,0,requestList,0,old.length);
+ }
+ requestList[requestListIndex++] = entry;
+
+ debug("Scheduling next run for now !");
+ scheduler.advanceJob(rrjob);
+ }
+ return;
+ }
+
+ /**
+ * Update a request. This method is used to selectively
+ * change a query or drop it entirely.
+ *
+ * @param node the block for which the request is updated
+ * @param msg the new query message for that node, null for
+ * none (then the request is dropped)
+ */
+
+ public void update( Block node, CSQuery msg )
+ {
+ int i;
+
+ debug("DEBUG: updating request for "+node+" to "+msg);
+
+ synchronized(lock) {
+ for (i=0;i<requestListIndex;i++) {
+ if (requestList[i].receiverNode == node) {
+ if (msg != null) {
+ // update
+ // keep priority & ttl
+
msg.setPriorityAndTTL(requestList[i].message.getPriority(),requestList[i].message.getTTL());
+
requestList[i].successful_replies++;
+ requestList[i].message = msg;
+
+ // also wait a bit longer
before re-issueing the request, after all, we got at least one of the replies !
+ // add 2*TTL grace time if we
got a reply to a multi-query; this dramatically reduces the amount of "useless"
(duplicate) replies we get !
+
requestList[i].lasttime=Scheduler.now()+2*TTL_DECREMENT;
+ }
+ else {
+ // delete
+
+ // update stats
+ if(requestList[i].tries > 1)
+
requestList[i].data.stats.currentRetries-= (requestList[i].tries - 1);
+
+ requestList[i].message=null;
+
freeInContinuations(requestList[i]);
+ requestList[i]=null;
+ requestList[i]=
requestList[--requestListIndex];
+
requestList[requestListIndex]=null;
+ }
+ return; // found !
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that the given entry does not occur
+ * in the continuations list (and if it does,
+ * null it out).
+ * @param entry
+ */
+
+ protected void freeInContinuations( RequestEntry entry )
+ {
+ RequestContinuation cur;
+
+ cur = start;
+ while (cur != null) {
+ if (cur.entry == entry)
+ cur.entry = null;
+ cur = cur.next;
+ }
+ }
+
+ /**
+ * We have determined success or failure for
+ * sending the query. Now update the state of
+ * the RM adequately. start contains the
+ * continuation that holds the state required
+ * to do this update.
+ * @param ok
+ */
+
+ protected void runContinuation( boolean ok )
+ {
+ RequestContinuation cur;
+
+ cur = start;
+ if (cur.entry != null) {
+ if (!ok) {
+ /* we did not send this entry, revert! */
+ log(Level.FINEST,"Sending canceled (would
block).");
+ cur.entry.message.setPriorityAndTTL(
cur.prevpri, cur.prevttl);
+ cur.entry.lasttime = cur.prevlt;
+ cur.entry.tries--;
+ }
+ else {
+ if (cur.entry.tries > 1) {
+ int nowTT;
+
+ nowTT=(int)
Scheduler.toSeconds(Scheduler.now());
+ if ( (nowTT - initialTTL) > lastDET) {
+ /* only consider congestion
control every
+ "average" TTL seconds,
otherwise the system
+ reacts to events that are far
too old! */
+ /* we performed retransmission,
treat as congestion (RFC 2001) */
+
+ debug("Received duplicate data,
changing CW ("+congestionWindow+
+ " to
"+((congestionWindow/2)+1)+
+ ") and SST ("+ssthresh+
+
"."+(congestionWindow/2)+")");
+
+ ssthresh= congestionWindow / 2;
+ if (ssthresh < 2)
+ ssthresh = 2;
+ congestionWindow= ssthresh + 1;
+ lastDET = nowTT;
+ }
+ cur.nc.stats.totalRetries++;
+ cur.nc.stats.currentRetries++;
+ }
+ }
+ }
+ start = cur.next;
+ }
+
+ /**
+ * Send the request from the requestList[requestIndex] out onto
+ * the network.
+ *
+ * @param requestIndex the index of the Request to issue
+ */
+
+ protected void issueRequest( int requestIndex )
+ {
+ RequestContinuation con;
+ RequestContinuation pos;
+ RequestEntry entry;
+ NodeContext nc;
+ long now;
+ CSMessage msg;
+ CSSession sockx;
+ boolean ok;
+
+ now=Scheduler.now();
+
+ con = new RequestContinuation();
+ con.next = null;
+ con.entry= entry= requestList[requestIndex];
+
+ if ((entry.lasttime+entry.message.getTTL()) > now -
TTL_DECREMENT) {
+ log(Level.WARNING,"Assertion failed: "+entry.lasttime+"
+ "+entry.message.getTTL()+" > "+now+" + "+TTL_DECREMENT);
+ assert(false);
+ }
+
+ if (entry.lasttime == 0) {
+ entry.message.setPriorityAndTTL(
entry.message.getPriority(), 0); /* to avoid assert failure */
+ con.ttl = initialTTL;
+ con.prevttl = con.ttl;
+ }
+ else {
+ con.ttl = entry.message.getTTL();
+ con.prevttl = con.ttl;
+ if (con.ttl > MAX_TTL) {
+ con.ttl = (int) (MAX_TTL + Crypto.nextInt((int)
(2*TTL_DECREMENT)));
+ entry.message.setPriorityAndTTL(
entry.message.getPriority(), (int) MAX_TTL); /* to avoid assert failure */
+ }
+ else if (con.ttl > initialTTL) {
+ /* switch to slow back-off */
+ int rd;
+ if (initialTTL == 0)
+ rd = con.ttl;
+ else
+ rd = con.ttl / initialTTL;
+ if (rd == 0)
+ rd = 1; /* how? */
+ rd = (int) (TTL_DECREMENT / rd);
+ if (rd == 0)
+ rd = 1;
+ con.ttl += Crypto.nextInt((int)
(Scheduler.MILLIS_50+rd));
+ /* rd == TTL_DECREMENT / (con.ttl / initialTTL)
+ saveguards
+ 50ms: minimum increment */
+ }
+ else {
+ con.ttl += Crypto.nextInt((int) (con.ttl + 2 *
TTL_DECREMENT)); /* exponential backoff with random factor */
+ }
+ }
+
+ con.prevlt = entry.lasttime;
+ entry.lasttime = now + 2 * TTL_DECREMENT;
+ if (Crypto.nextInt(1+entry.tries) > 1) {
+ /* do linear (in tries) extra back-off (in addition to
ttl)
+ to avoid repeatedly tie-ing with other peers; this is
somewhat
+ equivalent to what ethernet is doing, only that
'tries' is our
+ (rough) indicator for collisions. For ethernet
back-off, see:
+
http://www.industrialethernetuniversity.com/courses/101_4.htm
+ */
+ entry.lasttime += Crypto.nextInt((int) (TTL_DECREMENT *
(1+entry.tries)));
+ }
+
+ if
(!policy.checkAnonymityPolicy(CSMessage.IS_QUERY,entry.message.getByteSize()+HostIdentity.SIZE))
{
+ debug("DEBUG: not sending query due to anonymity policy
!");
+ return;
+ }
+
+ if (con.ttl < entry.message.getTTL()) {
+ log(Level.WARNING,"WARNING: assertion failed;
decrementing TTL from "+entry.message.getTTL()+" to "+con.ttl+" !");
+ assert(false);
+ }
+
+ con.prevpri = entry.message.getPriority();
+ if (con.prevpri > 0x0FFFFFF)
+ con.prevpri = Crypto.nextInt(0xFFFFFF); /* bound! */
+ entry.tries++;
+ if (entry.successful_replies > 0) {
+ /* do NOT change priority / ttl for n iterations
+ where n is the number of successful replies!*/
+ con.ttl = entry.message.getTTL();
+ entry.successful_replies /= 2; /* better than --?
better than = 0? */
+ }
+ else {
+ int tpriority;
+ int mpriority;
+ int count;
+
+ if (con.ttl > (con.prevpri+8)* TTL_DECREMENT) {
+ con.ttl =(int) ((con.prevpri+8) *
TTL_DECREMENT);
+ }
+
+ entry.message.setTTL(con.ttl);
+ tpriority = con.prevpri + Crypto.nextInt(entry.tries);
+ mpriority = priority.getMaxPriority();
+ /* adjust mpriority according to the number of queries
*/
+
+ count=entry.message.getQueriesCount();
+ if (count >= 2)
+ count--; /* discount super-query */
+ mpriority *= count;
+
+ if (tpriority > mpriority) {
+ /* mpriority is (2 * (current average priority
+ 2)) and
+ is used as the maximum priority that we use;
if the
+ calculated tpriority is above it, we reduce
tpriority
+ to random value between the average
(mpriority/2) but
+ bounded by mpriority */
+ tpriority = mpriority / 2 +
(Crypto.nextInt(1+mpriority/2));
+ }
+ entry.message.setPriority(tpriority);
+ }
+
+ if (isDebug()) {
+ int i;
+ int count;
+ String hex;
+
+ count=entry.message.getQueriesCount();
+ for (i=0; i<count; i++) {
+ hex=entry.message.getQuery(i).toHex();
+
+ if (con.prevlt == 0) {
+ log(Level.FINEST,i+"sending
"+entry.tries+"st time "+
+ "(last: NEVER; ttl
"+con.prevttl+")"+
+ " "+hex+"; ttl "+con.ttl+",
priority "+entry.message.getPriority()+" ("+initialTTL+")");
+ }
+ else {
+ log(Level.FINEST,i+" sending
"+entry.tries+"-th time "+
+ "(last: "+(now - con.prevlt)+"
ms ago; ttl "+con.prevttl+")"+
+ " "+hex+"; ttl "+con.ttl+",
priority "+entry.message.getPriority()+" ("+initialTTL+")");
+ }
+ }
+ }
+
+ con.nc= nc= entry.data;
+ nc.stats.requestsPending= requestListIndex;
+ nc.stats.requestsSent = requestListIndex;
+ nc.stats.currentTTL = con.ttl;
+ nc.stats.duplicationEstimate= duplicationEstimate;
+ nc.pmodel.progress(nc.stats, nc.data);
+
+ if (0 == (entry.tries % (MAX_TRIES * 50))) {
+ log(Level.WARNING,"WARNING:
"+entry.message.getQuery(0).toHex()+" seems to be not available on the
network");
+ entry.receiverNode.print(0);
+ }
+
+ msg = (CSMessage) PersistentHelper.copy(entry.message);
+ synchronized(lock) {
+ sockx = sock;
+ }
+
+ ok = false;
+ if (sockx != null) {
+ /* add con to the end of the (very short)
+ linked list */
+ pos = start;
+ if (pos == null) {
+ start = con;
+ }
+ else {
+ while (pos.next != null)
+ pos = pos.next;
+ pos.next = con;
+ }
+
+ /* destroyRM may set sock to null at ANY point! */
+ sockx.setBlocking(false);
+ ok = sockx.flushAndSend(msg);
+ sockx.setBlocking(true);
+ if (!ok) {
+ log(Level.WARNING,"WARNING: could not send
request to gnunetd, is it running ?");
+ runContinuation(false);
+ }
+ else {
+ /* receiverThread will call runContinuation */
+ }
+ }
+ }
+
+ /**
+ * Cron job that re-issues requests. Should compute how long to sleep
+ * (min ttl until next job is ready) and re-schedule itself
+ * accordingly!
+ */
+
+ protected void requestJob()
+ {
+ long minSleep;
+ long now;
+ long delta;
+ int i;
+ int pending;
+ int[] perm;
+
+ debug("CRON: requestJob running");
+
+ synchronized(lock) {
+ if (requestListIndex == 0) {
+ return;
+ }
+ now=Scheduler.now();
+ pending = 0;
+
+ for (i=0;i<requestListIndex;i++) {
+ if (requestList[i].lasttime
+requestList[i].message.getTTL() >= now) {
+ pending++;
+ }
+ }
+
+ minSleep = Scheduler.SECS_5; /* max-sleep! */
+ perm = Crypto.permute(requestListIndex);
+ for (i=0;i<requestListIndex;i++) {
+ int j = perm[i];
+ if ( (requestList[j].lasttime +
+
requestList[j].message.getTTL()) <= now - TTL_DECREMENT) {
+ int pOCWCubed;
+ int pendingOverCWin = pending -
congestionWindow;
+ if (pendingOverCWin <= 0)
+ pendingOverCWin = -1; /* avoid
0! */
+ pOCWCubed = pendingOverCWin *
+ pendingOverCWin *
+ pendingOverCWin;
+
+ if ( (pOCWCubed <= 0) ||
+ (pOCWCubed *
requestListIndex <= 0) /* see #642 */ ||
+ /* avoid no-start:
override congestionWindow occasionally... */
+ (0 ==
Crypto.nextInt(requestListIndex *pOCWCubed)) ) {
+ delta =
requestList[j].message.getTTL() + Scheduler.MILLIS_10;
+ issueRequest(j);
+ pending++;
+ }
+ else {
+ if (isDebug()) {
+ lpri++;
+ /* do not print ALL the
time, just once per iteration */
+ if ( (lpri %
(requestListIndex+1)) == 0)
+
log(Level.FINEST,"Congestion control: "+pending+" pending, "+congestionWindow+"
window; "+initialTTL+" initial TTL.");
+ }
+ delta = 0;
+ }
+ }
+ else {
+ delta = (requestList[j].lasttime +
TTL_DECREMENT +requestList[j].message.getTTL()) - now;
+
+ debug("Request "+i+":"+
+
Utils.toHex(requestList[j].message.getQuery(0).getInt(0))+" "+
+ "(TTL:
"+requestList[j].message.getTTL()+") "+
+ "is still pending for
"+Scheduler.toSeconds(delta)+"s.");
+ }
+
+ if ( delta < minSleep ) {
+ minSleep = delta;
+ }
+ }
+
+ if (minSleep < Scheduler.MILLIS_100) {
+ minSleep = Scheduler.MILLIS_100; /* maximum
resolution: 100ms */
+ }
+
+ if (requestListIndex > 0) {
+ debug("Scheduling next run for in
"+Scheduler.toMillis(minSleep)+"ms.");
+
+ scheduler.addJob(rrjob,minSleep);
+ }
+ else {
+ debug("No more jobs pending, cron not renewed
!");
+ }
+ }
+ }
+
+ /**
+ * This method receives data corresponding to the indicated filename
+ * (hashcode). Finds the Listener that scheduled this request and drop
+ * it from the list of pending requests.<p>
+ *
+ * @param msg the message received from gnunetd
+ */
+
+ protected void receive( CSResultChk msg )
+ {
+ int pos,i,j;
+ HashCode512 query;
+ RequestEntry entry;
+
+ /* check type of reply msg, fill in query */
+ query=HashCode512.create(PersistentHelper.toBytes(msg.result));
+ pos = -1;
+ /* find which query matches the reply, call the callback
+ and recycle the slot */
+ for (i=0;i<requestListIndex;i++) {
+ CSQuery acq;
+
+ acq = requestList[i].message;
+ j = (acq.getByteSize()-CSQuery.SIZE)/HashCode512.SIZE;
+ while ( j > 0 ) {
+ j--;
+ if (query.equals(acq.getQuery(j)))
+ pos = i;
+ }
+ }
+ if (pos == -1) {
+ int nowTT;
+
+ nowTT=(int) Scheduler.toSeconds(Scheduler.now());
+ duplicationEstimate++;
+ if ( (nowTT - initialTTL) > lastDET) {
+ /* only consider congestion control every
+ "average" TTL seconds, otherwise the system
+ reacts to events that are far too old! */
+
+ /* duplicate reply, treat as congestion (RFC
2001) */
+ debug("Received duplicate data, changing CW
("+congestionWindow+
+ " to "+((congestionWindow/2)+1)+
+ ") and SST ("+ssthresh+
+ "."+(congestionWindow/2)+")");
+
+ ssthresh = congestionWindow / 2;
+ if (ssthresh < 2)
+ ssthresh = 2;
+ congestionWindow
+ = ssthresh + 1;
+ lastDET = nowTT;
+ }
+
+ debug("Received useless data matching query
"+query.toHex()+" ("+duplicationEstimate+",
"+Scheduler.toSeconds(initialTTL)+"s) !");
+ return;
+ }
+
+ debug("Received reply for request "+pos+"
("+requestList[pos].message.getQuery(0).toHex()+")");
+
+ entry = requestList[pos];
+
+ if ( (entry.lasttime < Scheduler.now()) &&
+ (entry.lasttime != 0) ) {
+ int weight = 15;
+ int ettl = entry.message.getTTL();
+ if (ettl > TTL_DECREMENT)
+ ettl -= TTL_DECREMENT;
+ else
+ ettl = 0;
+ if ( (ettl > 4 * initialTTL) &&
+ ( (Scheduler.now() - entry.lasttime) <
initialTTL) ) {
+ weight = 127; /* eTTL is MUCH bigger than what
we currently expect AND the time
+ between the last query and the reply was in the
range of the
+ expected TTL => don't take ettl too much into
account! */
+ }
+ initialTTL = ((initialTTL) * weight + ettl) /
(weight+1);
+
+ /* RFC 2001: increase cwnd; note that we can't really
discriminate between
+ slow-start and cong. control mode since our RSS is too
small... */
+ if (congestionWindow < ssthresh)
+ congestionWindow += 2; /* slow start */
+ else
+ congestionWindow += 1; /* slower start :-) */
+ }
+ /* and finally use the entry to notify the node
+ that we got a reply! */
+
+ debug("DEBUG: request manager receives data for
"+entry.receiverNode);
+
+ if (!entry.receiver.listen(query,
+ msg,
+ this,
+ entry.data)) {
+
+ /* ABORT download, receiver method has
+ already notified the controller via
+ the pmodel callback, we need to stop
+ requesting... */
+
+ debug("DEBUG: entry.receiver aborted download !");
+
+ for (i=0;i<requestListIndex;i++) {
+ freeInContinuations(requestList[i]);
+ requestList[i]=null;
+ }
+ requestListIndex = 0;
+ }
+ }
+
+ /**
+ * Listen on socket and receive messages. Call requestManagerReceive
+ * on every reply. Never returns, if the RM dies, it will cancel us.
+ */
+
+ protected void receiveThread()
+ {
+ CSMessage buffer;
+ CSSession sockx;
+ PersistentDecoder decoder;
+
+ while (sock != null) {
+ synchronized(lock) {
+ sockx = sock;
+ }
+
+ if (sockx == null)
+ break;
+
+ //todo: a ne faire qu'une fois
+ decoder=new PersistentDecoder();
+ decoder.add(CSMessage.IS_RESULT,CSResult.class);
+ decoder.add(CSMessage.IS_RESULT_CHK,CSResultChk.class);
+
+ buffer = (CSMessage) sockx.receive(decoder);
+ if (buffer==null) {
+ if (sock == null)
+ break;
+ log(Level.WARNING,"Receive thread could not
read data from gnunetd, is the server running ?");
+ Scheduler.sleep(Scheduler.SECS_15);
+ continue;
+ }
+
+ if (buffer instanceof CSResult) {
+ int value;
+
+ value = ((CSResult) buffer).getResult();
+
+ synchronized(lock) {
+ if (start == null) {
+ log(Level.SEVERE,"Received
return value from gnunetd but I have no continuation! (bug!)");
+ }
+ else {
+
runContinuation(value!=0);//todo: value!=0 est le bon test ???
+ }
+ }
+ }
+ else if (buffer instanceof CSResultChk) {
+ synchronized(lock) {
+ receive((CSResultChk) buffer);
+ }
+ }
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNode.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNode.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNode.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,120 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * full CONTENT_SIZE'ed root node
+ * header of the RootNode (search result with meta-data)
+ *
+ * The structure of the root node - contains pertinent information for
+ * the file (file length, checksum, hashcode of main indirection node,
+ * description length, and description.
+ *
+ * The structure of the root node, including padding to make it to 1k.
+ */
+
+public class RootNode extends Node
+{
+ public static final int SIZE = 1024;
+ public static final int NOT_PADDED_SIZE = 564;
+
+
+ public RootNode()
+ {
+ super(ROOT_MAJOR_VERSION,ROOT_MINOR_VERSION);
+ }
+
+ public RootNode( FileIdentifier fid )
+ {
+ this();
+ fileIdentifier=(FileIdentifier) PersistentHelper.copy(fid);
//todo: copie utile ?
+ }
+
+ public String toString()
+ {
+ return "Root node [description="+description+",
filename="+fileName+", mimetype="+mimeType+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Convert this node to a human readable string.
+ * @return
+ */
+
+ public String toLabel()
+ {
+ StringBuffer buf;
+
+ buf=new StringBuffer();
+ if (mimeType.equals(GNUNET_DIRECTORY_MIME)) {
+ buf.append(GNDirectory.expandDirectoryName(fileName));
+ }
+ else {
+ buf.append(fileName);
+ }
+ buf.append(": ");
+ buf.append(description);
+ buf.append(" of type '");
+ buf.append(mimeType);
+ buf.append("' (size ");
+ buf.append(fileIdentifier.getFileLength());
+ buf.append(")\n");
+ buf.append(fileIdentifier.toURI());
+ return buf.toString();
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.putShort((short) ROOT_MAJOR_VERSION);
+ buf.putShort((short) ROOT_MINOR_VERSION);
+
+ fileIdentifier.writeBytes(buf);
+
+ AFSUtils.put(description,buf,256);
+ AFSUtils.put(fileName,buf,128);
+ AFSUtils.put(mimeType,buf,128);
+
+ for (i=NOT_PADDED_SIZE; i<SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int major,minor,i;
+ byte b;
+
+ major=buf.getShort() & 0x0000ffff;
+ minor=buf.getShort() & 0x0000ffff;
+
+ err.reportIf(major!=ROOT_MAJOR_VERSION,"bad major version");
+ err.reportIf(minor!=ROOT_MINOR_VERSION,"bad minor version");
+
+ fileIdentifier=new FileIdentifier();
+ fileIdentifier.readBytes(buf,err);
+
+ description=AFSUtils.get(buf,256);
+ fileName=AFSUtils.get(buf,128);
+ mimeType=AFSUtils.get(buf,128);
+
+ for (i=NOT_PADDED_SIZE; i<SIZE; i++) {
+ b=buf.get();
+ err.reportIf(b!=0,"bad zero-padding");
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNodeCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNodeCallback.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/RootNodeCallback.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ */
+
+public interface RootNodeCallback
+{
+ /**
+ * Callback function.
+ * @param root a root-node
+ * @param closure a closure
+ */
+
+ public void rootNode( RootNode root, Object closure );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/SBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/SBlock.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/SBlock.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,438 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * data structure SBlock
+ * data structure SBlock
+ * total: 1024 bytes
+ */
+
+public class SBlock extends Node
+{
+ public static final int ENCRYPTED_SIZE = 484;
+ public static final int SIGNED_SIZE = 504;
+ public static final int SIZE = 1024;
+
+ /** */
+ private int creationTime;
+
+ /** */
+ private int updateInterval;
+
+ /** N */
+ private HashCode512 nextIdentifier;
+
+ /** I */
+ private HashCode512 identifierIncrement;
+
+ /** R = H(N-I)^S where S= H(subspace) */
+ private HashCode512 identifier;
+
+ /** */
+ private Signature signature;
+
+ /** */
+ private PublicKey subspace;
+
+
+ public SBlock()
+ {
+ super(SBLOCK_MAJOR_VERSION,SBLOCK_MINOR_VERSION);
+ creationTime=0;
+ updateInterval=SBLOCK_UPDATE_NONE;
+ nextIdentifier=null;
+ identifierIncrement=null;
+ identifier=null;
+ signature=null;
+ subspace=null;
+ }
+
+ public SBlock( FileIdentifier fid )
+ {
+ this();
+ fileIdentifier=(FileIdentifier) PersistentHelper.copy(fid);
//todo: copie utile ?
+ }
+
+ public String toString()
+ {
+ return "S(igned)Block [description="+description+",
filename="+fileName+", mimetype="+mimeType+"]";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Convert a root-node to a string (to display it to the user).
+ * @return
+ */
+
+ public String toLabel()
+ {
+ HashCode512 ns;
+ String str;
+
+ //todo: add creation time & update frequency
+
+ if (mimeType.equals(GNUNET_DIRECTORY_MIME)) {
+ str=GNDirectory.expandDirectoryName(fileName);
+ }
+ else {
+ str=fileName;
+ }
+
ns=HashCode512.create(PersistentHelper.toBytes(subspace),0,PublicKey.SIZE);
+ return str+": "+description+" of type '"+mimeType+"' (size
"+fileIdentifier.getFileLength()+", namespace
"+ns.toHex()+")\n"+fileIdentifier.toURI();
+ }
+
+ public int getCreationTime()
+ {
+ return creationTime;
+ }
+
+ public void setCreationTime( int ctime )
+ {
+ creationTime=ctime;
+ }
+
+ public int getUpdateInterval()
+ {
+ return updateInterval;
+ }
+
+ /**
+ * @param interval the update frequency (0: never, -1: sporadic)
+ */
+
+ public void setUpdateInterval( int interval )
+ {
+ updateInterval=interval;
+ }
+
+ /**
+ * Returns K=N-I
+ * @return
+ */
+
+ public HashCode512 getKey()
+ {
+ HashCode512 h;
+
+ h=new HashCode512(nextIdentifier);
+ h.sub(identifierIncrement);
+ return h;
+ }
+
+ public HashCode512 getIdentifierIncrement()
+ {
+ return identifierIncrement;
+ }
+
+ public HashCode512 getIdentifier()
+ {
+ return identifier;
+ }
+
+ /**
+ * Compute the "current" ID of an updateable SBlock. Will set the ID of
the sblock itself for non-updatable content,
+ * the ID of the next identifier for sporadically updated SBlocks and
the ID computed from the timing function
+ * for periodically updated SBlocks.
+ *
+ * @param now the time for which the ID should be computed
+ * @return the resulting current ID (set)
+ */
+
+ public HashCode512 computeIdAtTime( int now )
+ {
+ int pos;
+ HashCode512 tmp,c;
+
+ if (updateInterval == SBLOCK_UPDATE_SPORADIC) {
+ return (HashCode512)
PersistentHelper.copy(nextIdentifier);
+ }
+
+ if (updateInterval == SBLOCK_UPDATE_NONE) {
+ /* H(N-I)^S is the current routing key, so N-I = k */
+ return getKey();
+ }
+
+ pos = creationTime;
+
+ c=getKey();
+
+ while (pos + updateInterval < now) {
+ pos += updateInterval;
+
+ tmp=new HashCode512(c);
+ tmp.add(identifierIncrement);
+
+ c=(HashCode512) PersistentHelper.copy(tmp);
+
+ debug("Update at
"+Utils.formatMoment(Scheduler.seconds(pos))+" should have key "+c.toHex());
+ }
+ return c;
+ }
+
+ public PublicKey getPublisherKey()
+ {
+ return subspace;
+ }
+
+ public HashCode512 getPublisherNameSpace()
+ {
+ return HashCode512.create(PersistentHelper.toBytes(subspace));
+ }
+
+ /**
+ * Build an (encrypted) SBlock.
+ *
+ * @param priv
+ * @param key the key for this SBlock
+ * @param nextKey the key for the next SBlock (if updateable)
+ * @return
+ */
+
+ public EncryptedSBlock encryptAndSign( PrivateKey priv, HashCode512
key, HashCode512 nextKey )
+ {
+ byte[] tmp;
+ HashCode512 hk;
+ byte[] iv,raw;
+ SessionKey skey;
+
+ // next identifier N
+ nextIdentifier=(HashCode512) PersistentHelper.copy(nextKey);
+
+ // identifier increment I=N-K
+ identifierIncrement=new HashCode512(nextKey);
+ identifierIncrement.sub(key);
+
+ // routing identifier R=H(K)^S =H(N-I)^S
+ hk=HashCode512.create(PersistentHelper.toBytes(key));
+
identifier=HashCode512.create(PersistentHelper.toBytes(priv.toPublicKey()));
+ identifier.xor(hk);
+
+ raw=new byte[SIZE];
+ writeBytes(ByteBuffer.wrap(raw));
+
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+ skey=key.extractKey(iv);
+
+ tmp=skey.encrypt(raw,0,ENCRYPTED_SIZE,iv);
+ if (tmp==null || tmp.length!=ENCRYPTED_SIZE) {
+ log(Level.SEVERE,"Failed to encrypt SBlock !");
+ return null;
+ }
+ System.arraycopy(tmp,0,raw,0,tmp.length);
+
+ signature=priv.sign(raw,0,SIGNED_SIZE);
+ if (signature==null) {
+ log(Level.SEVERE,"Failed to sign SBlock !");
+ return null;
+ }
+
+ subspace=priv.toPublicKey();
+ return new EncryptedSBlock(tmp,identifier,signature,subspace);
+ }
+
+ /**
+ * Verify that a given SBlock is well-formed.
+ * @return
+ */
+
+ public boolean verify()
+ {
+ byte[] iv,raw,tmp;
+ SessionKey skey;
+
+ raw=new byte[SIZE];
+ writeBytes(ByteBuffer.wrap(raw));
+
+ iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+ skey=getKey().extractKey(iv);
+
+ tmp=skey.encrypt(raw,0,ENCRYPTED_SIZE,iv);
+ if (tmp==null || tmp.length!=ENCRYPTED_SIZE) {
+ log(Level.SEVERE,"Failed to decrypt SBlock !");
+ return false;
+ }
+ System.arraycopy(tmp,0,raw,0,tmp.length);
+
+ return subspace.verify(signature,raw,0,SIGNED_SIZE);
+ }
+
+ /**
+ * Print the information contained in an SBlock.
+ *
+ * @param stream where to print the information to
+ */
+
+ public void print( PrintWriter stream )
+ {
+ HashCode512 hc;
+ int now,pos;
+ String fstring;
+ String fn;
+
+ /* if it is a GNUnet directory, replace suffix '/' with ".gnd"
*/
+ if (mimeType.equals(GNUNET_DIRECTORY_MIME)) {
+ fn = GNDirectory.expandDirectoryName(fileName);
+ }
+ else {
+ fn = fileName;
+ }
+
+ hc=HashCode512.create(PersistentHelper.toBytes(subspace));
+ stream.println(description+" ("+mimeType+") published by
"+hc.toHex());
+
+ fstring = fileIdentifier.toURI();
+ stream.println("gnunet-download -o \""+fn+"\" "+fstring);
+
+ switch (updateInterval) {
+ case SBLOCK_UPDATE_SPORADIC:
+ stream.println("Next update will be
"+nextIdentifier.toHex()+".");
+ break;
+
+ case SBLOCK_UPDATE_NONE:
+ stream.println("SBlock indicates no updates.");
+ break;
+
+ default:
+ pos = creationTime;
+
+ hc=new HashCode512(nextIdentifier);
+ hc.sub(identifierIncrement);
+
+ now=(int) Scheduler.toSeconds(Scheduler.now());
+ while (pos + updateInterval < now) {
+ HashCode512 tmp;
+
+ pos += updateInterval;
+
+ tmp=new HashCode512(hc);
+ tmp.add(identifierIncrement);
+
+ stream.println("Update due at
"+Utils.formatMoment(Scheduler.seconds(pos))+" has key "+tmp.toHex());
+ }
+ break;
+ }
+ }
+
+ public int getByteSize()
+ {
+ return SIZE;
+ }
+
+ public void writeBytes( ByteBuffer buf )
+ {
+ int i;
+
+ buf.putShort((short) SBLOCK_MAJOR_VERSION);
+ buf.putShort((short) SBLOCK_MINOR_VERSION);
+
+ if (fileIdentifier!=null) {
+ fileIdentifier.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<FileIdentifier.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ AFSUtils.put(description,buf,256);
+ AFSUtils.put(fileName,buf,64);
+ AFSUtils.put(mimeType,buf,64);
+
+ buf.putInt(creationTime);
+ buf.putInt(updateInterval);
+
+ if (nextIdentifier!=null) {
+ nextIdentifier.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ if (identifierIncrement!=null) {
+ identifierIncrement.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ if (identifier!=null) {
+ identifier.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<HashCode512.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ if (signature!=null) {
+ signature.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<Signature.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+
+ if (subspace!=null) {
+ subspace.writeBytes(buf);
+ }
+ else {
+ for (i=0; i<PublicKey.SIZE; i++) {
+ buf.put((byte) 0);
+ }
+ }
+ }
+
+ public void readBytes( ByteBuffer buf, ErrorReporter err )
+ {
+ int major,minor;
+
+ major=buf.getShort() & 0x0000ffff;
+ minor=buf.getShort() & 0x0000ffff;
+
+ err.reportIf(major!=SBLOCK_MAJOR_VERSION,"bad major version");
+ err.reportIf(minor!=SBLOCK_MINOR_VERSION,"bad minor version");
+
+ fileIdentifier=new FileIdentifier();
+ fileIdentifier.readBytes(buf,err);
+
+ description=AFSUtils.get(buf,256);
+ fileName=AFSUtils.get(buf,64);
+ mimeType=AFSUtils.get(buf,64);
+
+ creationTime=buf.getInt();
+ updateInterval=buf.getInt();
+
+ nextIdentifier=new HashCode512();
+ nextIdentifier.readBytes(buf,err);
+
+ identifierIncrement=new HashCode512();
+ identifierIncrement.readBytes(buf,err);
+
+ identifier=new HashCode512();
+ identifier.readBytes(buf,err);
+
+ signature=new Signature();
+ signature.readBytes(buf,err);
+
+ subspace=new PublicKey();
+ subspace.readBytes(buf,err);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchResultCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchResultCallback.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchResultCallback.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,29 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ *
+ */
+
+public interface SearchResultCallback
+{
+ /**
+ * Type of a callback method for results that have
+ * been received.
+ *
+ * @param root the RootNode of the result that has been received
+ */
+
+ public void searchResult( RootNode root );
+
+ /**
+ * @param root
+ * @param str
+ *
+ */
+
+ public void newNameFor( RootNode root, String str );
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchURI.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/SearchURI.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,34 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ */
+
+public class SearchURI extends GeneralURI
+{
+// public int action;
+ public HashCode512 namespace;
+ public HashCode512 keyhash;
+ public String[] keywords;
+ public int keycount;
+
+
+ public SearchURI()
+ {
+ super();
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/SendNSQueryContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/SendNSQueryContext.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/SendNSQueryContext.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,249 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ */
+
+public class SendNSQueryContext extends LoggedObject implements AFSConstants
+{
+ /** Time when the cron-job was first started. */
+ public long start;
+
+ /** How many cron-units may we run (total) ? */
+ public long timeout;
+
+ /** */
+ public CSSession sock;
+
+ /** */
+ public CSNSQuery query;
+
+
+ public SendNSQueryContext()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Retrieve an SBlock.
+ *
+ * @param policy
+ * @param scheduler
+ * @param prefs
+ * @param sockx socket to use to contact gnunetd
+ * @param s namespace which namespace to search
+ * @param k key to decrypt the SBlock in the namespace (query
+ * to identify the block is derived from k)
+ * @param testTerminate function to poll for abort
+ * @param resultCallback function to call for results
+ * @param closure argument to pass to resultCallback
+ * @return OK on success, false on error (= no result found)
+ */
+
+ public boolean searchSBlock( Policy policy, Scheduler scheduler, Prefs
prefs, CSSession sockx, HashCode512 s, HashCode512 k, TestTerminateThread
testTerminate, NSSearchResultCallback resultCallback, Object closure )
+ {
+ CSResultSBlock reply;
+ boolean ret;
+ HashCode512 hc,hk;
+ HashCode512 r; /* r = H(k) ^ s */
+ SendNSQueryContext sqc;
+ SBlock result;
+ ScheduledTask sendTask;
+
+ hk=HashCode512.create(PersistentHelper.toBytes(k));
+ r=new HashCode512(s);
+ r.xor(hk); /* compute routing key R */
+
+ sqc=new SendNSQueryContext();
+ sqc.sock = sockx;
+ sqc.query = new CSNSQuery();
+ sqc.query.priority = 1;
+ sqc.query.ttl = (int) (1+Crypto.nextLong(TTL_DECREMENT));
+ sqc.query.namespace=(HashCode512) PersistentHelper.copy(s);
+ sqc.query.identifier=(HashCode512) PersistentHelper.copy(r);
+
+ // add cron job to send search query
+ final SendNSQueryContext _sqc = sqc;
+ final Policy _policy = policy;
+ final Scheduler _scheduler = scheduler;
+
+ sendTask=new ScheduledTask("SEND-NSQUERY2",new AbstractAction()
{
+ public void perform()
+ {
+ sendNSQuery(_sqc,_policy,_scheduler);
+ }
+ });
+ scheduler.addJob(sendTask,0);
+
+ ret = false;
+ while (!testTerminate.test()) {
+ reply=(CSResultSBlock)
sock.receive(CSResultSBlock.class);
+ if (reply==null) {
+ log(Level.WARNING,"Unable to read *correct*
message.");
+ if (testTerminate.test())
+ break;
+ Scheduler.sleep(Scheduler.SECS_1);
+ continue;
+ }
+
+ debug("Received message from gnunetd");
+
+ if (!reply.getResult().verify()) {
+ log(Level.WARNING,"WARNING: SBlock received
from gnunetd failed verification.");
+ break;
+ }
+ new
Pseudonym(prefs).addNamespace(reply.getResult().getNameSpace());
+
+ // internal identifier (for routing HT, etc.) is "xor"
of the user-identifier with the namespace ID
+ // to avoid keyword collisions with realnames in the
global, 3HASH namespace
+
+ hc=reply.getResult().getNameSpace();
+ if (!hc.equals(s)) {
+ log(Level.WARNING,"WARNING: SBlock received
from gnunetd belongs to wrong namespace.");
+ break;
+ }
+ if (!r.equals(reply.getResult().getIdentifier()) ) {
+ log(Level.WARNING,"WARNING: SBlock received
from gnunetd has wrong identifier.");
+ break;
+ }
+
+ result=reply.getResult().decrypt(k);
+ resultCallback.searchResult(result,closure);
+ ret = true;
+ }
+
+ // delete cron job
+ scheduler.deleteJob(sendTask);
+ return ret;
+ }
+
+ public void sendNSQuery( SendNSQueryContext sqc, Policy policy,
Scheduler scheduler )
+ {
+ long now,remTime,new_ttl,new_priority;
+
+ debug("enter sendnsquery");
+
+ now=Scheduler.now();
+
+ if (sqc.timeout != 0) {
+ remTime = (int) (sqc.start - now + sqc.timeout);
+ if (remTime <= 0) {
+ log(Level.FINEST,"Exiting sendnsquery without
making a query.");
+ return;
+ }
+ }
+ else {
+ remTime = 0x7FFFFFFF; /* max signed int */
+ }
+
+ if
(policy.checkAnonymityPolicy(CSMessage.IS_NSQUERY,P2PNSQuery.SIZEX)) {
+ if (sqc.sock.send(sqc.query)) {
+ /* successful transmission to GNUnet,
+ increase ttl/priority for the next time */
+ new_ttl = sqc.query.ttl;
+ if (new_ttl > 0x00FFFFFF) {
+ new_ttl = Crypto.nextLong(0x00FFFFFF);
/* if we get too large, reduce! */
+ }
+ sqc.query.ttl= (int)
Crypto.nextLong(1+4*new_ttl);
+ new_priority = sqc.query.priority;
+ if (new_priority > 0xFFFFFF) {
+ new_priority =
Crypto.nextLong(0xFFFFFF); /* if we get too large, reduce! */
+ }
+ sqc.query.priority= (int)
Crypto.nextLong(1+4*new_priority);
+ }
+ else {
+ new_ttl = Scheduler.SECS_5; /* wait at least 5s
for gnunetd */
+ }
+ }
+ else {
+ new_ttl = TTL_DECREMENT;
+ }
+
+ /* Don't repeat a search faster than TTL_DEC seconds */
+ if (new_ttl < TTL_DECREMENT)
+ new_ttl = TTL_DECREMENT;
+
+ debug("Will wait for min("+new_ttl+", "+remTime+") ms");
+
+ /* Do not sleep longer than the amount of time we have until
+ we shut down */
+ if (new_ttl >= remTime)
+ new_ttl = remTime;
+
+ if (remTime > 0) {
+ debug("Reinstating sendnsquery in "+new_ttl);
+
+ final SendNSQueryContext _sqc = sqc;
+ final Policy _policy =
policy;
+ final Scheduler _scheduler = scheduler;
+
+ scheduler.addJob(new ScheduledTask("SEND-NSQUERY",new
AbstractAction() {
+ public void perform()
+ {
+ sendNSQuery(_sqc,_policy,_scheduler);
+ }
+ }),new_ttl);
+ }
+ }
+
+ /**
+ * Perform a namespace search.
+ *
+ * @param prefs
+ * @param scheduler
+ * @param policy
+ * @param sock
+ * @param keyStrings
+ * @param keywordCount
+ * @param handler
+ * @param handlerArgs
+ * @param testTerminate
+ * @param ttContext
+ * @return
+ */
+
+ public static boolean searchRBlock( Prefs prefs, final Scheduler
scheduler, final Policy policy, CSSession sock, String[] keyStrings, int
keywordCount, SearchResultCallback handler, Object handlerArgs,
TestTerminateThread testTerminate, Object ttContext )
+ {
+ SendQueriesContext sqc;
+ HashCode512[] keywords;
+ ScheduledTask sendJob;
+
+ keywords=AFSUtils.parseKeywords(keyStrings);
+
+ sqc=new SendQueriesContext(keywords,sock);
+
sqc.setTimeOut(Scheduler.seconds(prefs.getInt("AFS","SEARCHTIMEOUT",0)));
+
+ final SendQueriesContext _sqc = sqc;
+
+ sendJob=new ScheduledTask("SEND-RBLOCK",new AbstractAction() {
+ public void perform()
+ {
+ _sqc.repeatedlySend(scheduler,policy);
+ }
+ });
+ scheduler.addJob(sendJob,0);
+
+// receiveResults(sock,keywordCount,keywords,
messages,handler,handlerArgs,testTerminate,ttContext);
+ sqc.receive(prefs,handler,testTerminate);
+
+ scheduler.deleteJob(sendJob);
+ return true;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/SendQueriesContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/SendQueriesContext.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/SendQueriesContext.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,356 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Context of the send queries cron job.
+ */
+
+public class SendQueriesContext extends LoggedObject implements AFSConstants
+{
+ private static Comparator fComparator;
+
+ static {
+ fComparator=createFilenameComparator();
+ }
+
+ /** Time when the cron job was first started. */
+ private long start;
+
+ /** How many cron units may we run (total) ? */
+ private long timeout;
+
+ /** Socket for communication with gnunetd. */
+ private CSSession sock;
+
+ /** */
+ private HashCode512[] keywords;
+
+ /** Query messages. */
+ private CSQuery[] messages;
+
+ private PersistentDecoder decoder;
+
+ /**
+ * Construct a new query context.
+ *
+ * @param kwords the keywords (for decryption)
+ * @param s socket used to receive results from
+ */
+
+ public SendQueriesContext( HashCode512[] kwords, CSSession s )
+ {
+ super(true);
+ start=Scheduler.now();
+ timeout=0;
+
+ sock=s;
+
+ decoder=new PersistentDecoder();
+ decoder.add(CSMessage.IS_RESULT,CSResult.class);
+ decoder.add(CSMessage.IS_RESULT_3HASH,CSResult3Hash.class);
+
+ keywords=kwords;
+ messages=AFSUtils.buildMessages(keywords);
+ }
+
+ public String toString()
+ {
+ return "Send queries context";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public CSSession getSocket()
+ {
+ return sock;
+ }
+
+ public CSQuery[] getQueries()
+ {
+ return messages;
+ }
+
+ public void setTimeOut( long t )
+ {
+ timeout=t;
+ }
+
+ /**
+ * Repeatedly send out the queries to GNUnet.
+ * This cron-job self-terminates after sqc.timout cron-units have
passed.
+ * @param scheduler
+ * @param policy
+ */
+
+ public void repeatedlySend( final Scheduler scheduler, final Policy
policy )
+ {
+ long ttl;
+
+ ttl=send(policy);
+ if (ttl>0) {
+ scheduler.addJob(new ScheduledTask("REPEAT-SEND",new
AbstractAction() {
+ public void perform()
+ {
+ repeatedlySend(scheduler,policy);
+ }
+ }),ttl);
+ }
+ }
+
+ public int send( Policy policy )
+ {
+ long now;
+ int remTime,ttl,newTTL,newPriority,i;
+
+ now=Scheduler.now();
+
+ if (timeout != 0) {
+ remTime = (int) (start - now + timeout);
+ if (remTime <= 0)
+ return 0;
+ }
+ else {
+ remTime = 0x7FFFFFFF; // max signed int
+ }
+
+ ttl=0;
+ for (i=0; i<messages.length; i++) {
+ log(Level.FINEST,"Sending query with ttl
"+messages[i].getTTL()+".");
+
+ ttl=Crypto.nextInt((int) TTL_DECREMENT)+1;
+
+ if
(policy.checkAnonymityPolicy(CSMessage.IS_QUERY,messages[i].getByteSize()+HostIdentity.SIZE))
{
+ if (sock.send(messages[i])) {
+ // successful transmission, increase
ttl/priority for the next time
+ newTTL=messages[i].getTTL();
+ ttl=Math.max(newTTL,ttl);
+
+ if (newTTL>0xFFFFFF) {
+ newTTL=Crypto.nextInt(0xFFFFFF);
+ }
+
+ newPriority=messages[i].getPriority();
+ if (newPriority>0xFFFFFF) {
+
newPriority=Crypto.nextInt(0xFFFFFF);
+ }
+
+ messages[i].setPriorityAndTTL(
Crypto.nextInt(1+4*newPriority),Crypto.nextInt(1+4*newTTL));
+ }
+ }
+ }
+
+ // sleep approximately the time the longest ttl will take
+ ttl=ttl+Crypto.nextInt(ttl+1);
+
+ // don't repeat a search faster than TTL_DECREMENT seconds
+ if (ttl<TTL_DECREMENT) {
+ ttl=(int) TTL_DECREMENT;
+ }
+
+ log(Level.FINEST,"Will wait for min("+ttl+","+remTime+") ms.");
+
+ // do not sleep longer than the amount of time we have until we
shut down
+ if (ttl>=remTime) {
+ ttl=remTime;
+ }
+ return ttl;
+ }
+
+ /**
+ * Start retrieving results from GNUnet. This method terminates only
+ * if the testTerminate-method returns YES after a result was received
+ * or there is an error with reading from the socket.
+ *
+ * @param prefs
+ * @param handler the method to call on each result matching all
keywords
+ * @param testTerminate method used to check if we should termiante
+ */
+
+ public void receive( Prefs prefs, SearchResultCallback handler,
TestTerminateThread testTerminate )
+ {
+ ResultContext rc;
+ Persistent buffer;
+ CSResult3Hash reply;
+ byte[] rootNodeBytes;
+ int i;
+ HashCode512 tripleHash;
+
+ rc=new ResultContext(keywords.length);
+
+ while (!testTerminate.test()) {
+ buffer=sock.receive(decoder);
+ if (buffer==null) {
+ if (testTerminate.test())
+ break;
+ Scheduler.sleep(Scheduler.SECS_1);
+ continue;
+ }
+
+ if (buffer instanceof CSResult) {
+ log(Level.FINEST,"Daemon has acknowledeged.");
+ // ignore: confirmation of gnunetd that it
received a search request from the other thread
+ }
+ else {
+ reply=(CSResult3Hash) buffer;
+
+ // now decrypt the reply & call a method to use
it
+ tripleHash=reply.getTripleHash();
+ for (i=0; i<keywords.length; i++) {
+ if
(tripleHash.equals(messages[i].getQuery(0))) {
+
rootNodeBytes=reply.decrypt(keywords[i]);
+ if (rootNodeBytes==null) {
+
log(Level.SEVERE,"Failed to decrypt content !?");
+ continue;
+ }
+ if
(Node.extractMajorVersion(rootNodeBytes)!=ROOT_MAJOR_VERSION ||
Node.extractMinorVersion(rootNodeBytes)!=ROOT_MINOR_VERSION) {
+ log(Level.INFO,"Content
has unsupported version:
"+Node.extractMajorVersion(rootNodeBytes)+"."+Node.extractMinorVersion(rootNodeBytes));
+ continue; /* bah!
broken! */
+ }
+
+ filterResult(prefs,
+ (RootNode)
PersistentHelper.readFully(RootNode.class,ByteBuffer.wrap(rootNodeBytes)),
+ i,rc,handler);
+ }
+ else {
+ log(Level.FINEST,"Reply
"+reply.getDoubleHash().toHex()+" does not match expected hash
"+messages[i].getQuery(0).toHex());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Filter results that do not match ALL keywords.
+ * Display the result, but make sure that
+ * every file is only displayed once.
+ *
+ * @param prefs
+ * @param rootNode the new reply
+ * @param keyIndex for which key this result matches
+ * @param rc the context to keep track of which replies we
got so far
+ * @param handler
+ */
+
+ protected void filterResult( Prefs prefs, RootNode rootNode, int
keyIndex, ResultContext rc, SearchResultCallback handler )
+ {
+ Map tmp;
+ List tmp2;
+ String name;
+ int i,j;
+
+ tmp=rc.key2hash[keyIndex];
+ if
(tmp.containsKey(rootNode.getFileIdentifier().getFileQuery())) {
+ name=(String)
tmp.get(rootNode.getFileIdentifier().getFileQuery());
+ if (fComparator.compare(rootNode.getFileName(),name)>0)
{
+ log(Level.FINEST,"Daemon has returned *a
better* filename for already seen result.");
+
tmp.put(PersistentHelper.copy(rootNode.getFileIdentifier().getFileQuery()),rootNode.getFileName());
+
+
handler.newNameFor(rootNode,rootNode.getFileName());
+ return;
+ }
+
+ log(Level.FINEST,"Daemon has returned *already seen*
results to filter.");
+ return;
+ }
+
+ log(Level.FINEST,"Daemon has returned new results to filter.");
+ // add to the matching files for this key
+
tmp.put(PersistentHelper.copy(rootNode.getFileIdentifier().getFileQuery()),rootNode.getFileName());
+
+ // check if the file now matches all keys
+ for (i=0; i<keywords.length; i++) {
+ tmp2=new ArrayList(rc.key2hash[i].keySet());
+
+ for (j=0; j<tmp2.size(); j++) {
+ if
(tmp2.get(j).equals(rootNode.getFileIdentifier().getFileQuery())) {
+ break;
+ }
+ }
+
+ if (j == tmp2.size()) {
+ log(Level.FINEST,"Not enough results for the
AND query.");
+ return; // not found, exit !
+ }
+ }
+
+ // ok, rootNode matches all the AND criteria, display
+
+ // this check should be redundant...
+ if
(rc.results.contains(rootNode.getFileIdentifier().getFileQuery())) {
+ log(Level.FINEST,"We have seen this result before :
"+rootNode.getFileIdentifier().getFileQuery().toHex());
+ return;
+ }
+
+ // directory support...
+ new
GNDirectoryDatabase(prefs).makeRootNodeAvailable(rootNode,DIR_CONTEXT_SEARCH);
+
+
rc.results.add(PersistentHelper.copy(rootNode.getFileIdentifier().getFileQuery()));
+ handler.searchResult(rootNode);
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected static Comparator createFilenameComparator()
+ {
+ return new Comparator() {
+ public int compare( Object o1, Object o2 )
+ {
+ if (o1==null || o2==null) {
+ return (o1!=null ? 1 : (o2!=null ? -1 :
0));
+ }
+ return
evalCorrectness(o1.toString())-evalCorrectness(o2.toString());
+ }
+ };
+ }
+
+ protected static int evalCorrectness( String str )
+ {
+ int count,i;
+ char c;
+
+ for (i=count=0; i<str.length(); i++) {
+ c=str.charAt(i);
+ if (c<' ' || c>127) {
+ count+=2;
+ }
+ else if (c==' ') {
+ count++;
+ }
+ }
+ return -count;
+ }
+}
+
+class ResultContext extends Object
+{
+ /** the results we've got so far (hash of root-node) */
+ public List results;
+
+ /** unmatched ("AND") results so far, list of root-node hashes that
were received for each keyword */
+ public Map[] key2hash;
+
+
+ public ResultContext( int count )
+ {
+ super();
+ results=new ArrayList();
+
+ key2hash=new Map[count];
+ while (count>0) {
+ key2hash[--count]=new HashMap();
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/TestTerminateThread.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/TestTerminateThread.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/TestTerminateThread.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+/**
+ *
+ */
+
+public interface TestTerminateThread
+{
+ /**
+ * Method to test if the receive-thread should terminate.
+ * @return
+ */
+
+ public boolean test();
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/esed2/URI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/esed2/URI.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/esed2/URI.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,480 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * URI handling
+ * GNUnet AFS uri format
+ *
+ * gnunet://afs/action/tag1=value1?tag2=value2...
+ *
+ * where action is one of {download,search,insert,delete}
+ * and supported tags are
+ *
+ * tag description
+ * -------------------------------------------------------
+ * ns Namespace identifier
+ * kh Keyhash
+ * qh Queryhash
+ * crc File CRC32
+ * fn File name (suggested)
+ * size File length
+ * keyword Search keyword (can be specified multiple times)
+ * pseudonym Pseudonym for namespace insertion
+ * password Password for a pseudonym
+ *
+ * Not all tags make sense in all contexts.
+ *
+ * Parses and produces uri strings.
+ *
+ * What spaghetti ...
+ *
+ * How it works: first parse all tag/value pairs into a table. Take
+ * note of the "action" type. Then call a specific parser to create
+ * the actual data structure.
+ *
+ * Bugs: leaks mem if some tags except keyword or numeric tags
+ * are specified more than once.
+ */
+
+public class URI extends LoggedObject
+{
+ public static final String AFS_URI_PREFIX = "gnunet://afs/";
+
+ public static final int URI_ACTION_DOWNLOAD = 1;
+ public static final int URI_ACTION_SEARCH = 2;
+ public static final int URI_ACTION_INSERT = 3;
+ public static final int URI_ACTION_DELETE = 4;
+
+ public static final int GOT_FILENAME = (1<<0);
+ public static final int GOT_NS = (1<<1);
+ public static final int GOT_QH = (1<<2);
+ public static final int GOT_KH = (1<<3);
+ public static final int GOT_KEYWORD = (1<<4);
+ public static final int GOT_SIZE = (1<<5);
+ public static final int GOT_CRC = (1<<6);
+ public static final int GOT_PSEUDONYM = (1<<7);
+ public static final int GOT_PASSWORD = (1<<8);
+
+
+ public URI()
+ {
+ super(true);
+ }
+
+ public String toString()
+ {
+ return "xxx";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Parses an AFS URI string to an internal representation
+ *
+ * Usage:
+ * GeneralURI * block;
+ * parseURI(string, &block);
+ * if(block.action == URI_ACTION_DOWNLOAD) {
+ * downloadURI * bl;
+ * bl = (downloadURI *)block;
+ * ...
+ *
+ * @param uri an uri string
+ * @param block output, the parsed values
+ * @return false on failure
+ */
+
+ public boolean parseURI( String uri, GeneralURI[] block )
+ {
+ String scratch;
+ String uptr;
+ String sptr;
+ String nptr;
+ int action;
+ TagTable tags = new TagTable();
+ int tagcount = 0;
+ boolean ret = false;
+ int index;
+
+ if (uri==null || !uri.startsWith(AFS_URI_PREFIX)) {
+ return false;
+ }
+
+ /* parse action */
+ uptr = uri.substring(AFS_URI_PREFIX.length());
+
+ index=uptr.indexOf('/');
+ scratch=(index>=0 ? uptr.substring(0,index) : "");
+ uptr=uptr.substring(index+1);
+
+ if (uptr.length()==0) {
+ log(Level.SEVERE,"Premature end of URI");
+ return false;
+ }
+
+ if (scratch.equals("download")) {
+ action = URI_ACTION_DOWNLOAD;
+ }
+ else if (scratch.equals("search")) {
+ action = URI_ACTION_SEARCH;
+ }
+ else if (scratch.equals("insert")) {
+ action = URI_ACTION_INSERT;
+ }
+ else if (scratch.equals("delete")) {
+ action = URI_ACTION_DELETE;
+ }
+ else {
+ log(Level.SEVERE,"Unknown action in "+scratch);
+ return false;
+ }
+
+ /* parse all tags to a tagTable */
+ while (uptr.length()>0) {
+ index=uptr.indexOf('=');
+ nptr = (index>=0 ? uptr.substring(0,index) : "");
+ uptr=uptr.substring(index+1);
+
+ if (uptr.length()==0) {
+ log(Level.SEVERE,"Premature end of tag/name
pair (1)");
+ return false;
+ }
+
+ index=uptr.indexOf('?');
+ sptr = (index>=0 ? uptr.substring(0,index) : "");
+ uptr=uptr.substring(index+1);
+
+ /* get the value */
+
+ if (sptr.length()==0) {
+ log(Level.SEVERE,"Missing value for tag "+nptr);
+ return false;
+ }
+
+ tags.add(nptr,sptr);
+ }
+
+ switch (action) {
+ case URI_ACTION_DOWNLOAD:
+ ret = parseDownloadURI(tags,tagcount,block);
+ break;
+
+ case URI_ACTION_SEARCH:
+ ret = parseSearchURI(tags,tagcount,block);
+ break;
+
+ case URI_ACTION_INSERT:
+ ret = parseInsertURI(tags,tagcount,block);
+ break;
+
+ case URI_ACTION_DELETE:
+ ret = parseDeleteURI(tags,tagcount,block);
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ protected boolean parseDownloadURI( TagTable tags, int tagcount,
GeneralURI[] block )
+ {
+ int i;
+ int gotmask=0;
+ String tag,value;
+ DownloadURI ret;
+
+ long fileLength=0;
+ int fileCRC=0;
+ ChkHashes fileHashes=new ChkHashes();
+
+ ret = new DownloadURI();
+ ret.action = URI_ACTION_DOWNLOAD;
+
+ for(i=0; i<tagcount; i++) {
+ tag=tags.getTag(i);
+ value=tags.getValue(i);
+
+ if (tag.equals("filename")) {
+ ret.filename = value;
+ gotmask |= GOT_FILENAME;
+ }
+ else if (tag.equals("kh")) {
+ fileHashes.key=HashCode512.parse(value);
+ gotmask |= GOT_KH;
+ }
+ else if (tag.equals("qh")) {
+ fileHashes.query=HashCode512.parse(value);
+ gotmask |= GOT_QH;
+ }
+ else if (tag.equals("size")) {
+ fileLength=Long.parseLong(value);
+ gotmask |= GOT_SIZE;
+ }
+ else if (tag.equals("crc")) {
+ fileCRC=Integer.parseInt(value,16);
+ gotmask |= GOT_CRC;
+ }
+ else {
+ log(Level.WARNING,"Unknown tag "+tag+" in
download context");
+ }
+ }
+
+ ret.fid=new FileIdentifier(fileLength,fileCRC,fileHashes);
+
+ if((gotmask & GOT_CRC)==0 || (gotmask & GOT_KH)==0 || (gotmask
& GOT_QH)==0 || (gotmask & GOT_SIZE)==0) {
+ log(Level.SEVERE,"Insufficient tags for download");
+ return false;
+ }
+
+ block[0]=ret;
+ return true;
+ }
+
+ protected boolean parseSearchURI( TagTable tags, int tagcount,
GeneralURI[] block )
+ {
+ int i;
+ int gotmask=0;
+ String tag,value;
+ SearchURI ret;
+
+ ret = new SearchURI();
+ ret.action = URI_ACTION_SEARCH;
+
+ for(i=0;i<tagcount;i++) {
+ tag = tags.getTag(i);
+ value = tags.getValue(i);
+
+ if (tag.equals("namespace")) {
+ ret.namespace = HashCode512.parse(value);
+ if (ret.namespace==null) {
+ log(Level.SEVERE,"Namespace is not in
HEX format");
+ return false;
+ }
+ gotmask |= GOT_NS;
+ }
+ /* namespace keyhash identifier */
+ /* FIXME: either keywords or kh is redundant */
+ else if (tag.equals("kh")) {
+ ret.keyhash = HashCode512.parse(value);
+ if (ret.keyhash==null) {
+ log(Level.FINEST,"Namespace ID is not
in HEX format, using hash of ASCII text ("+value+").");
+ ret.keyhash=HashCode512.create(value);
+ }
+ gotmask |= GOT_KH;
+ }
+ else if (tag.equals("keyword")) {
+ String[] old;
+
+ old=ret.keywords;
+ ret.keycount++;
+ ret.keywords=new String[ret.keycount];
+
System.arraycopy(old,0,ret.keywords,0,old.length);
+
+ ret.keywords[ret.keycount-1] = value;
+ gotmask |= GOT_KEYWORD;
+ }
+ else {
+ log(Level.WARNING,"Unknown tag name "+tag+" in
search context");
+ }
+ }
+
+ if ((gotmask & GOT_KEYWORD)==0) {
+ log(Level.SEVERE,"Insufficient tags for search");
+ return false;
+ }
+
+ block[0]=ret;
+ return true;
+ }
+
+ protected boolean parseInsertURI( TagTable tags, int tagcount,
GeneralURI[] block )
+ {
+ int i;
+ int gotmask=0;
+ String tag,value;
+ InsertURI ret;
+
+ ret = new InsertURI();
+ ret.action = URI_ACTION_INSERT;
+
+ for(i=0;i<tags.getSize();i++) {
+ tag = tags.getTag(i);
+ value = tags.getValue(i);
+
+ if(tag.equals("filename")) {
+ ret.filename = value;
+ gotmask |= GOT_FILENAME;
+ }
+ else if(tag.equals("pseudonym")) {
+ ret.pseudonym = value;
+ gotmask |= GOT_PSEUDONYM;
+ }
+ else if(tag.equals("password")) {
+ ret.password = value;
+ gotmask |= GOT_PASSWORD;
+ }
+ else {
+ log(Level.WARNING,"Unknown tag name "+tag+" in
search context");
+ }
+ }
+
+ if ((gotmask & GOT_FILENAME)==0) {
+ log(Level.SEVERE,"Insufficient tags for insert");
+ return false;
+ }
+
+ block[0]=ret;
+ return true;
+ }
+
+ protected boolean parseDeleteURI( TagTable tags, int tagcount,
GeneralURI[] block )
+ {
+ int i;
+ int gotmask=0;
+ DeleteURI ret;
+ String tag,value;
+
+ ret = new DeleteURI();
+ ret.action = URI_ACTION_DELETE;
+
+ for(i=0; i<tags.getSize(); i++) {
+ tag = tags.getTag(i);
+ value = tags.getValue(i);
+
+ if (tag.equals("filename")) {
+ ret.filename=value;
+ gotmask |= GOT_FILENAME;
+ }
+ else {
+ log(Level.WARNING,"Unknown tag name "+tag+" in
search context");
+ }
+ }
+
+ if ((gotmask & GOT_FILENAME)==0) {
+ log(Level.SEVERE,"Insufficient tags for delete");
+ return false;
+ }
+ block[0]=ret;
+ return true;
+ }
+
+ /**
+ * Turns an internal representation into a AFS uri string
+ *
+ * @param block the values to print
+ * @param url output
+ * @return false on failure
+ */
+
+ public boolean produceURI( GeneralURI block, String[] url )
+ {
+ int i;
+
+ if (block==null) {
+ log(Level.SEVERE,"null block passed to produceURI()");
+ return false;
+ }
+
+ url[0]=AFS_URI_PREFIX;
+
+ switch(block.action) {
+ case URI_ACTION_DOWNLOAD:
+ DownloadURI du;
+
+ du = (DownloadURI) block;
+ url[0]+="download/";
+ url[0]+="kh="+du.fid.getFileKey().toHex()+"?";
+ url[0]+="qh="+du.fid.getFileQuery().toHex()+"?";
+ url[0]+="size="+du.fid.getFileLength()+"?";
+
url[0]+="crc="+Utils.toHex(du.fid.getFileCRC())+"?";
+ if (du.filename != null) {
+ url[0]+=du.filename+"?";
+ }
+ break;
+
+ case URI_ACTION_SEARCH:
+ SearchURI su;
+
+ su=(SearchURI) block;
+ url[0]+="search/";
+ if (su.namespace!=null) {
+ url[0]+="ns="+su.namespace.toHex()+"?";
+ }
+ if (su.keyhash!=null) {
+ url[0]+="kh="+su.keyhash.toHex()+"?";
+ }
+ for (i=0; i<su.keycount; i++) {
+ url[0]+="keyword="+su.keywords[i]+"?";
+ }
+ break;
+
+ case URI_ACTION_INSERT:
+ InsertURI iu;
+
+ iu=(InsertURI) block;
+ url[0]+="insert/";
+ if (iu.filename!=null) {
+ url[0]+=iu.filename+"?";
+ }
+ break;
+
+ case URI_ACTION_DELETE:
+ DeleteURI dd;
+
+ dd=(DeleteURI) block;
+ url[0]+="delete/";
+ if (dd.filename!=null) {
+ url[0]+=dd.filename+"?";
+ }
+ break;
+
+ default:
+ url[0]=null;
+ log(Level.SEVERE,"Unknown action
"+block.action);
+ return false;
+ }
+
+ if (url[0].endsWith("?")) {
+ url[0]=url[0].substring(0,url[0].length()-1);
+ }
+ return true;
+ }
+}
+
+/* internal struct for tag/value pairs */
+
+class TagTable extends Object
+{
+ private List all=new ArrayList();
+
+ public void add( String t, String v )
+ {
+ all.add(t);
+ all.add(v);
+ }
+ public int getSize()
+ {
+ return all.size()/2;
+ }
+
+ public String getTag( int i )
+ {
+ return (String) all.get(i*2);
+ }
+
+ public String getValue( int i )
+ {
+ return (String) all.get(i*2+1);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/CreatePseudonymDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/CreatePseudonymDialog.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/CreatePseudonymDialog.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,161 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class CreatePseudonymDialog extends GDialog implements ActionListener
+{
+ private SController controller;
+
+ private JTextField pseudoField;
+ private JPasswordField passwordField;
+ private JButton cancelButton;
+ private JButton createButton;
+
+
+ public CreatePseudonymDialog( SController ctr, GFrame f )
+ {
+ super(f,"pseudo-create");
+ setTitle("Create Pseudonym");
+ controller=ctr;
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent we)
+ {
+ onClose();
+ }
+ });
+ }
+
+ public String toString()
+ {
+ return "Create pseudonym dialog";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Setup a window to allow the user to create a pseudonym
+ * @return
+ */
+
+ public JComponent createContent()
+ {
+ JPanel panel,buttonPane;
+ GForm form;
+ JLabel iconLabel;
+
+ pseudoField=new JTextField(10);
+ passwordField=new JPasswordField(10);
+
+ form=new GForm();
+ form.addWidget("Pseudonym :",pseudoField);
+ form.addWidget("Password :",passwordField);
+
+ cancelButton=new JButton("Cancel");
+ cancelButton.addActionListener(this);
+
+ createButton=new JButton("Create");
+ createButton.addActionListener(this);
+
+ buttonPane=new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(3,0)));
+ buttonPane.add(createButton);
+
+ iconLabel=new JLabel(new
ImageIcon(controller.getResources().getImageURL("app.minilogo")));
+ iconLabel.setVerticalAlignment(SwingConstants.TOP);
+
+ panel=new JPanel(new BorderLayout(3,1));
+ panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ panel.add(iconLabel,BorderLayout.WEST);
+ panel.add(form,BorderLayout.CENTER);
+ panel.add(buttonPane,BorderLayout.SOUTH);
+
+ getRootPane().setDefaultButton(createButton);
+
+ return panel;
+ }
+
+ public void updateContent()
+ {
+ pseudoField.setText("");
+ passwordField.setText("");
+ }
+
+ public void leaveContent()
+ {
+ pseudoField.setText("");
+ passwordField.setText("");
+ }
+
+ public void actionPerformed( ActionEvent evt )
+ {
+ if (evt.getSource()==createButton) {
+ onCreate();
+ }
+ else if (evt.getSource()==cancelButton) {
+ onClose();
+ }
+ }
+
+ protected void onClose()
+ {
+ close();
+ }
+
+ protected void onCreate()
+ {
+ Task task;
+
+ final String _name = pseudoField.getText();
+ if (_name.length()==0) {
+ controller.guiMessage("Cowardly refusing to create
pseudonym without name.");
+ return;
+ }
+
+ final String _pass = new String(passwordField.getPassword());
+
+ pseudoField.setEnabled(false);
+ passwordField.setEnabled(false);
+ createButton.setEnabled(false);
+ cancelButton.setEnabled(false);
+
+ task=new Task("NEW-PSEUDO",new
org.gnu.freeway.util.AbstractAction() {
+ public void perform()
+ {
+ PrivateKey priv;
+ // we may want to do this in another thread to
keep the event manager running (and potentially even
+ // give feedback in the form of a popup
window). After all, this can take a while...
+ priv=new
Pseudonym(controller.getPreferences()).createPseudonym(_name,_pass);
+ if (priv==null) {
+ controller.guiMessage("Failed to create
pseudonym (see logs).");
+ }
+ controller.refreshMenus();
+
+ pseudoField.setEnabled(true);
+ passwordField.setEnabled(true);
+ createButton.setEnabled(true);
+ cancelButton.setEnabled(true);
+ close();
+ }
+ });
+ task.launch();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/DaemonWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/DaemonWindow.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/DaemonWindow.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,207 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class DaemonWindow extends GFrame
+{
+ private SController controller;
+ private UIResources resources;
+ private DaemonLauncher launcher;
+
+ private GStatus status;
+ private GConsole text;
+ private GBar bar;
+ private JTextField argsField;
+
+
+ public DaemonWindow( UIResources res, SController ctr )
+ {
+ super(ctr,"daemon-window");
+ setTitle("GNUnet Swing : Daemon");
+ controller=ctr;
+ resources=new UIResources(res,"daemon-window.xml");
+ resources.setGlobalTarget(this);
+ launcher=new DaemonLauncher(ctr.getApplication());
+
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing( WindowEvent evt )
+ {
+ onClose();
+ }
+ });
+ }
+
+ public String toString()
+ {
+ return "Daemon window";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public JComponent createContent()
+ {
+ JPanel panel;
+ Box box;
+
+ status=new GStatus();
+
+ text=new GConsole();
+
+ argsField=new JTextField(10);
+ argsField.setText("-L INFO");
+
+ bar=new GBar();
+ bar.add("Arguments :",argsField);
+ bar.add(resources.getAction("start.action"));
+ bar.add(resources.getAction("stop.action"));
+ bar.addSeparator();
+ bar.add(resources.getAction("close.action"));
+
+ resources.getAction("close.action").setEnabled(true);
+
+ box=new Box(BoxLayout.X_AXIS);
+ box.add(status);
+ box.add(Box.createHorizontalGlue());
+ box.add(text.createControls());
+
+ panel=new JPanel(new TileLayout());
+ panel.add(box/*status*/,TileLayout.NORTH);
+ panel.add(bar,TileLayout.SOUTH);
+ panel.add(text);
+
+ setJMenuBar(resources.getMenuBar("main.menu"));
+ return panel;
+ }
+
+ public void updateContent()
+ {
+ }
+
+ /**
+ * Launch gnunetd w/ checks
+ */
+
+ public void onLaunchDaemon()
+ {
+ GProgress dialog;
+ final boolean[] _beurk = new boolean[1];
+
+ if (launcher.checkDaemonRunning() ) {
+ showMessage("Daemon is already running !");
+ return;
+ }
+
+ _beurk[0]=false;
+ dialog=new GProgress(this,"launch-progress") {
+ public void perform()
+ {
+ String argLine;
+
+ argLine=(argsField!=null ? argsField.getText()
: null);
+
_beurk[0]=launcher.launchWithExec(this,argLine,new ConnectorEndPoint() {
+ public void gotMessage( int type,
String str )
+ {
+ text.println(str);
+ }
+ });
+ Scheduler.sleep(Scheduler.MILLIS_200);
+ }
+ };
+ dialog.setTitle("Launch daemon");
+ dialog.launch("Please wait while daemon is being
launched...\n");
+
+ if (!_beurk[0]) {
+ return;
+ }
+
+ resources.getAction("start.action").setEnabled(false);
+ resources.getAction("stop.action").setEnabled(false);
+ status.setText("Checking...");
+ argsField.setEnabled(false);
+ }
+
+ /**
+ * Kill gnunetd
+ */
+
+ public void onKillDaemon()
+ {
+ resources.getAction("start.action").setEnabled(false);
+ resources.getAction("stop.action").setEnabled(false);
+ status.setText("Waiting for daemon's response...");
+ argsField.setEnabled(false);
+
+ if (!launcher.checkDaemonRunning() ) {
+ showMessage("Daemon is not running...");
+ return;
+ }
+
+ if (launcher.killDaemon()) {
+ showMessage("Daemon agreed to shut down.");
+ }
+ else {
+ showMessage("Daemon refuses to shut down.");
+ }
+ }
+
+ public void cronCheckDaemon()
+ {
+ UIAction killEntry;
+ UIAction launchEntry;
+ UIAction statsEntry;
+ String host;
+ boolean isLocal;
+
+ killEntry=resources.getAction("advanced.kill.gnunetd");
+ launchEntry=resources.getAction("advanced.launch.gnunetd");
+ statsEntry=resources.getAction("file.show.stats");
+
+ host =
controller.getPreferences().getString("NETWORK","HOST",null);
+ isLocal=(host==null || host.equalsIgnoreCase("localhost"));
+
+ if (!launcher.checkDaemonRunning()) {
+ statsEntry.setEnabled(false);
+ killEntry.setEnabled(false);
+ launchEntry.setEnabled(isLocal);
+
+ resources.getAction("start.action").setEnabled(true);
+ resources.getAction("stop.action").setEnabled(false);
+ if (status!=null) {
+ status.setText("No daemon found.");
+ argsField.setEnabled(true);
+ }
+ }
+ else {
+ statsEntry.setEnabled(true);
+ killEntry.setEnabled(true);
+ launchEntry.setEnabled(false);
+
+ resources.getAction("start.action").setEnabled(false);
+ resources.getAction("stop.action").setEnabled(true);
+ if (status!=null) {
+ status.setText("Daemon is now up and running.");
+ argsField.setEnabled(false);
+ }
+ }
+ }
+
+ public void onClose()
+ {
+ close();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/DeletePseudonymDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/DeletePseudonymDialog.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/DeletePseudonymDialog.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,151 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ *
+ */
+
+public class DeletePseudonymDialog extends GDialog implements ActionListener,
ListSelectionListener
+{
+ private SController controller;
+ private Pseudonym pseudonym;
+
+ private JList pseudoList;
+ private JButton cancelButton;
+ private JButton deleteButton;
+
+
+ public DeletePseudonymDialog( SController ctr, GFrame f )
+ {
+ super(f,"pseudo-delete");
+ setTitle("Delete Pseudonym");
+ controller=ctr;
+ pseudonym=new Pseudonym(controller.getPreferences());
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent we)
+ {
+ onClose();
+ }
+ });
+ }
+
+ public String toString()
+ {
+ return "Delete pseudonym dialog";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Open a window to allow the user to delete a pseudonym
+ * @return
+ */
+
+ public JComponent createContent()
+ {
+ JScrollPane scrollPane;
+ JPanel panel,buttonPane,listPane;
+ JLabel iconLabel,titleLabel;
+
+ pseudoList=new JList(pseudonym.listPseudonyms());
+
pseudoList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ pseudoList.setLayoutOrientation(JList.VERTICAL);
+ pseudoList.setVisibleRowCount(5);
+ pseudoList.addListSelectionListener(this);
+
+ scrollPane=new JScrollPane(pseudoList);
+ scrollPane.setAlignmentX(LEFT_ALIGNMENT);
+
+ titleLabel=new JLabel("Select a pseudonym to delete :");
+
+ listPane = new JPanel();
+ listPane.setLayout(new BoxLayout(listPane,
BoxLayout.PAGE_AXIS));
+ listPane.add(titleLabel);
+ listPane.add(Box.createRigidArea(new Dimension(0,1)));
+ listPane.add(scrollPane);
+
+ cancelButton=new JButton("Cancel");
+ cancelButton.setEnabled(true);
+ cancelButton.addActionListener(this);
+
+ deleteButton=new JButton("Delete");
+ deleteButton.setEnabled(false);
+ deleteButton.addActionListener(this);
+
+ buttonPane=new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(3,0)));
+ buttonPane.add(deleteButton);
+
+ iconLabel=new JLabel(new
ImageIcon(controller.getResources().getImageURL("app.minilogo")));
+ iconLabel.setVerticalAlignment(SwingConstants.TOP);
+
+ panel=new JPanel(new BorderLayout(3,1));
+ panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ panel.add(iconLabel,BorderLayout.WEST);
+ panel.add(listPane,BorderLayout.CENTER);
+ panel.add(buttonPane,BorderLayout.SOUTH);
+
+ getRootPane().setDefaultButton(deleteButton);
+ return panel;
+ }
+
+ public void actionPerformed( ActionEvent evt )
+ {
+ if (evt.getSource()==deleteButton) {
+ onDelete();
+ }
+ else if (evt.getSource()==cancelButton) {
+ onClose();
+ }
+ }
+
+ public void valueChanged( ListSelectionEvent evt )
+ {
+ if (!evt.getValueIsAdjusting()) {
+
deleteButton.setEnabled(pseudoList.getSelectedIndex()>=0);
+ }
+ }
+
+ protected void onClose()
+ {
+ close();
+ }
+
+ protected void onDelete()
+ {
+// int index;
+
+// index=pseudoList.getSelectedIndex();
+/*
+ listModel.remove(index);
+
+ int size = listModel.getSize();
+
+ if (size == 0) { //Nobody's left, disable firing.
+ fireButton.setEnabled(false);
+ }
+ else { //Select an index.
+ if (index == listModel.getSize()) {
+ //removed item in last position
+ index--;
+ }
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }*/
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadAdapter.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadAdapter.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadAdapter.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,64 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.io.*;
+
+/**
+ *
+ */
+
+public class DownloadAdapter extends AbstractAdapter implements AFSConstants
+{
+ public DownloadAdapter()
+ {
+ super(8);
+ }
+
+ protected void fillFacets( Object obj, Object[] facets )
+ {
+ DownloadModel dlm;
+ double currentRetryAvg, averageBps, percentage;
+ long now;
+
+ dlm=(DownloadModel) obj;
+
+ currentRetryAvg=0;
+ averageBps=0;
+ percentage=0;
+
+ now=Scheduler.toSeconds(Scheduler.now());
+ if (dlm.pstats!=null) {
+ if (dlm.pstats.requestsSent>0) {
+ currentRetryAvg=(double)
dlm.pstats.currentRetries/(double) dlm.pstats.requestsSent;
+ }
+ if (now-dlm.downloadStartTime>0) {
+ averageBps=(double)
dlm.pstats.progress/(double) (now-dlm.downloadStartTime);
+ }
+ if (dlm.pstats.filesize>0) {
+ percentage=100.0*((double)
dlm.pstats.progress/(double) dlm.pstats.filesize);
+ }
+ }
+
+
facets[0]=dlm.fileName.substring(dlm.fileName.lastIndexOf(File.separator)+1);
+ facets[1]=(percentage!=0 ? (Object) new Double(percentage) :
(Object) "0%");
+ facets[2]=(dlm.pstats!=null ? (Object) new
Integer(dlm.pstats.progress) : (Object) "-");
+
facets[3]=String.valueOf(dlm.root.getFileIdentifier().getFileLength());
+ facets[4]=(dlm.pstats!=null ? (Object) new
Integer(dlm.pstats.requestsSent) : (Object) "-");
+ facets[5]=(dlm.pstats!=null ? (Object) new
Double(currentRetryAvg) : (Object) "-");
+ facets[6]=(dlm.pstats!=null ? (Object) new
Integer(dlm.pstats.totalRetries) : (Object) "-");
+ facets[7]=(dlm.pstats!=null ? (Object) new Double(averageBps) :
(Object) "-");
+
+ if (dlm.pstats!=null && dlm.pstats.filesize ==
dlm.pstats.progress) {
+ // reset the request counters (just cosmetic)
+ facets[4]="0";
+ facets[5]="0.0";
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadModel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadModel.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadModel.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,209 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+* State associated with a download window.
+ */
+
+public class DownloadModel extends Object implements AFSConstants
+{
+ /* Suitable values of DownloadModel.downloadStatus */
+ public static final int DOWNLOAD_COMPLETE = 0;
+ public static final int DOWNLOAD_FAILED = 1;
+ public static final int DOWNLOAD_ABORTED = 2;
+ public static final int DOWNLOAD_PENDING = 3;
+
+ public Node root;
+ public String fileName;
+ public Task downloadThread;
+ public RequestManager rm;
+ public Semaphore doneSem;
+ public long downloadStartTime;
+ public boolean successfulStart;
+ public int downloadStatus;
+ public long lastDisplayTime;
+ public JTable dlList;
+
+ public ProgressStats pstats;
+
+ private SController controller;
+
+
+ public String[] cols;
+
+
+ public DownloadModel( SController ctr )
+ {
+ super();
+ root=null;
+ fileName="";
+ downloadThread=null;
+ controller=ctr;
+ successfulStart=false;
+ pstats=null;
+ }
+
+ /**
+ * Main function of the download thread. This function terminates
+ * automagically once the download is complete or if the download
+ * entry is removed while dl. It is responsible for stopping the
+ * requestmanager.
+ * @throws InterruptedException
+ */
+
+ public void downloadFile_() throws InterruptedException
+ {
+ String mime;
+
+ controller.log(Level.FINEST,"Entering downloadFile_ for
"+fileName);
+
+ /* initiate download */
+ downloadStartTime=Scheduler.toSeconds(Scheduler.now());
+
+ /* this starts the "real" download thread in the background,
+ with "modelCallback" called back to tell us about the progress
*/
+ rm = new DownloadUtil().downloadFile((AbstractClient)
controller.getApplication(),
+ controller.getPolicy(),
+ null, //todo: va planter
+ root.getFileIdentifier(),fileName,new ProgressModel() {
+ public void progress( ProgressStats stats, Object data )
+ {
+ modelCallback(stats);
+ }
+ },this);
+ if (rm == null) {
+ controller.guiMessage("Could not download
"+fileName+".\nConsult logs.\n");
+ doneSem=null;
+ return;
+ }
+
+ /* Wait here until download is complete or the window is closed
or gnunet-gtk is terminated */
+ controller.log(Level.FINEST,"Waiting for DL completion.");
+
+ doneSem.acquire();
+
+ controller.log(Level.FINEST,"Download complete
("+downloadStatus+") enter destroyRequestManager.");
+
+ /* stop the RequestManager */
+ if (rm != null) {
+ rm.destroy();
+ }
+ else {
+ /* this can happen if the requestmanager initialization
+ * failed for reason or another (e.g. write permission
denied) */
+ controller.log(Level.WARNING,"rm was null !");
+ }
+
+ /*
+ ok, now why are we here? 4 possibilities:
+ a) download aborted (user closed window)
+ b) gnunet-gtk terminated (same as download aborted)
+ c) download failed (gnunetd exit, out-of-space)
+ d) download completed
+
+ In case "d" we show the "YAY" window
+ and wait for another signal.
+ In case "c" we show the "BAH" window
+ and wait for another signal.
+ */
+
+ switch (downloadStatus) {
+ case DOWNLOAD_COMPLETE:
+// gdk_threads_enter();
+ /* color successful dl green */
+// gtk_clist_freeze(GTK_CLIST(dlList));
+// row =
gtk_clist_find_row_from_data(GTK_CLIST(dlList),dlm);
+// gtk_clist_set_foreground(GTK_CLIST(dlList),
row,textColors[3]);
+//
gtk_clist_set_text(GTK_CLIST(dlList),row,1,"DONE");
+// gtk_clist_thaw(GTK_CLIST(dlList));
+
+ mime=root.getMimeType();
+ if (mime.equals(GNUNET_DIRECTORY_MIME)) {
+
controller.displayDirectory(fileName,root);
+ }
+
+// gdk_threads_leave();
+ doneSem.acquire(); /* wait for window closing */
+ break;
+
+ case DOWNLOAD_FAILED:
+// gdk_threads_enter();
+ /* color failed dl red */
+// gtk_clist_freeze(GTK_CLIST(dlList));
+// row =
gtk_clist_find_row_from_data(GTK_CLIST(dlList),dlm);
+//
gtk_clist_set_foreground(GTK_CLIST(dlList),row,textColors[4]);
+//
gtk_clist_set_text(GTK_CLIST(dlList),row,1,"FAIL");
+// gtk_clist_thaw(GTK_CLIST(dlList));
+// gdk_threads_leave();
+ doneSem.acquire(); /* wait for window closing */
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* finally, disentangle from the clist and free dlm resources */
+// gdk_threads_enter();
+// gtk_clist_freeze(GTK_CLIST(dlList));
+// row = gtk_clist_find_row_from_data(GTK_CLIST(dlList),dlm);
+// gtk_clist_set_row_data(GTK_CLIST(dlList),row,null);
+// gtk_clist_thaw(GTK_CLIST(dlList));
+// gdk_threads_leave();
+ doneSem=null;
+ fileName=null;
+ }
+
+ /**
+ * This method is called by the download code to notify the user
+ * interface of the download progress.
+ *
+ * @param stats the new statistical values
+ */
+
+ protected void modelCallback( ProgressStats stats )
+ {
+ long now;
+
+ if (downloadStatus != DOWNLOAD_PENDING)
+ return;
+
+ /* don't display more often than once/sec */
+ now=Scheduler.toSeconds(Scheduler.now());
+ if ((now-lastDisplayTime) < 1 && (stats.filesize !=
stats.progress))
+ return;
+
+ lastDisplayTime = now;
+
+ pstats=new ProgressStats(stats);
+
+
+ if (!successfulStart && stats.progress > 0) {
+//
gtk_clist_set_foreground(GTK_CLIST(dlList),row,textColors[1]);
+ successfulStart=true;
+ }
+
+ if (stats.filesize == stats.progress) {
+ /* reset the request counters (just cosmetic) */
+ controller.refreshMenus();
+
+ if (stats.filesize == 0)
+ downloadStatus = DOWNLOAD_FAILED;
+ else
+ downloadStatus = DOWNLOAD_COMPLETE;
+ doneSem.release(); /* signal: we're done with download
*/
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadWindow.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/DownloadWindow.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,125 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class DownloadWindow extends GFrame implements AFSConstants,
SwingConstants
+{
+ private SController controller;
+ private UIResources resources;
+
+ private DefaultListModel dModel;
+ private ListSelectionModel dSelection;
+ private GTable dTable;
+
+
+ public DownloadWindow( SController ctr )
+ {
+ super(ctr,"download-window");
+ setTitle("GNUnet Swing : Downloads");
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent we)
+ {
+ onClose();
+ }
+ });
+
+ controller=ctr;
+
+ resources=new UIResources("download.xml",controller.getCache());
+ resources.setGlobalTarget(this);
+ }
+
+ public String toString()
+ {
+ return "Download window";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public JComponent createContent()
+ {
+ JPanel panel;
+ GStatus status;
+ GBar bar;
+
+ resources.getAction("abort.id").setEnabled(false);
+ resources.getAction("close.id").setEnabled(true);
+
+ dModel=new DefaultListModel();
+ dSelection=new DefaultListSelectionModel();
+
+ dTable=new GTable(new DownloadAdapter(),dModel,dSelection);
+ dTable.setColumns(new GColumn[] {
+ new GColumn("Filename", 290, LEFT, true),
+ new GColumn("%", 50, CENTER, true), /*
Completion percentage */
+ new GColumn("Position", 70, RIGHT, true), /*
Bytes dl'ed so far */
+ new GColumn("Size", 70, RIGHT, true),
+ new GColumn("AReq", 40, LEFT, true), /*
Active block requests */
+ new GColumn("CR/A", 30, LEFT, true), /*
Current Retries per Active requests */
+ new GColumn("totR", 50, LEFT, true), /*
Total Retries */
+ new GColumn("BPS", 50, LEFT, true), /*
Current bytes per second -estimate */
+ });
+
+ status=new GStatus();
+
+ bar=new GBar();
+ bar.add(resources.getAction("abort.id"));
+ bar.add(resources.getAction("close.id"));
+
+ panel=new JPanel(new TileLayout());
+ panel.add(status,TileLayout.NORTH);
+ panel.add(bar,TileLayout.SOUTH);
+ panel.add(new JScrollPane(dTable));
+ return panel;
+ }
+
+ public void addDownload( String str, Node node )
+ {
+ DownloadModel dlm;
+
+ dlm=new DownloadModel(controller);
+ dlm.fileName=str;
+ dlm.root=(Node) PersistentHelper.copy(node);
+ dlm.lastDisplayTime=Scheduler.toSeconds(Scheduler.now());
+ dlm.dlList = dTable;
+ dlm.downloadStatus=DownloadModel.DOWNLOAD_PENDING;
+ dlm.doneSem=new Semaphore(0);
+
+// gtk_clist_freeze(GTK_CLIST(dTable));
+
+ dModel.addElement(dlm);
+
+// gtk_clist_set_foreground(GTK_CLIST(dTable),row,textColors[11]);
+// gtk_clist_thaw(GTK_CLIST(dTable));
+
+ /* create thread that runs the download */
+ dlm.downloadThread=new Task("DOWNLOAD-"+str,new
EvalAction(dlm,"downloadFile_"));
+ dlm.downloadThread.launch();
+ }
+
+ public void onClose()
+ {
+ close();
+ }
+
+ public void onAbort()
+ {
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/InsertDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/InsertDialog.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/InsertDialog.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,515 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.logging.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+/**
+ *
+ */
+
+public class InsertDialog extends GWizard
+{
+ private SController controller;
+ private UIResources resources;
+
+ private DirLocation chosenBase;
+ private FileLocation chosenFile;
+ private String chosenFilename;
+ private String chosenMime;
+ private String chosenDescription;
+ private boolean choosedToIndex;
+ private String[] chosenKeywords;
+
+
+ public InsertDialog( GFrame frame )
+ {
+ super(frame,"insert");
+ setTitle("GNUNet Swing : Insert");
+
+ controller=(SController) frame.getController();
+ resources=new
UIResources("insert.xml",frame.getController().getApplication().getPreferences().getSystemCache());
+ resources.setGlobalTarget(this);
+
+ init();
+ }
+
+ public String toString()
+ {
+ return "Insert dialog";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public UIResources getResources()
+ {
+ return resources;
+ }
+
+ public GWizardPage[] getSteps()
+ {
+ return new GWizardPage[] {
+ new ChooseFileStep(),
+ new EditAttributesStep(),
+ new ReviewStep()
+ };
+ }
+
+ protected void init()
+ {
+ Prefs prefs;
+
+ prefs=controller.getApplication().getPreferences();
+
+ chosenBase=new DirLocation("~");
+ chosenFile=null;
+ chosenFilename="";
+ chosenMime="";
+ chosenDescription="";
+ choosedToIndex=true;
+ chosenKeywords=new String[] {};
+ }
+
+ public void close()
+ {
+ Prefs prefs;
+
+ prefs=controller.getApplication().getPreferences();
+ //prefs.setGlobalString("insert","directory",(chosenBase!=null
? chosenBase.getLabel() : ""));
+
+ super.close();
+ }
+
+ public void doTask()
+ {
+ GProgress progress;
+
+ final boolean[] _beurk = new boolean[1];
+
+ _beurk[0]=false;
+
+ progress=new GProgress(this,"insert-progress") {
+ public void perform()
+ {
+ doInsert(this,false);
+ _beurk[0]=true;
+ }
+ };
+ progress.setTitle(chosenFile.getLabel());
+ progress.launch("Please wait while file is being
inserted...\n");
+ if (_beurk[0]) {
+ close();
+ }
+ }
+
+ protected void doInsert( final GProgress _progress, boolean
deleteAfterInsert )
+ {
+ Prefs prefs;
+ RootNode res;
+ CSSession sock;
+ Block top;
+ InsertUtil insertUtil;
+ FileIdentifier fid;
+ String fstring;
+
+ prefs=controller.getPreferences();
+ prefs.setString("GNUNET-INSERT","INDEX-CONTENT",(choosedToIndex
? "YES" : "NO"));
+
+ insertUtil=new InsertUtil(prefs);
+
+ controller.enterCritical();
+ try {
+ sock=controller.connect();
+
+ top=insertUtil.insertFile(sock,chosenFile.getPath(),new
ProgressModel() {
+ public void progress( ProgressStats stats,
Object data )
+ {
+ insertModelCallback(stats,_progress);
+ }
+ },null);
+ if (top!=null) {
+ //todo: erreur ? getName() ou getPath() ???
+
res=insertUtil.insertRoot(sock,top,chosenDescription,chosenFile.getName(),chosenMime,chosenKeywords);
+ }
+ else {
+ res=null;
+ }
+
+ controller.refreshMenus();
+
+ if (res!=null) {
+ fid=new FileIdentifier(top);
+
+ fstring = fid.toURI();
+
+ controller.infoMessage(false,"Successfully
processed "+chosenFile.getLabel()+"\n => "+fstring+"\n");
+ controller.log(Level.FINEST,"Successfully
processed "+chosenFile.getLabel()+"\n => "+fstring);
+ }
+ else {
+ controller.guiMessage("Insertion of
"+chosenFile.getLabel()+" FAILED !");
+ }
+
+ if (top!=null) {
+ top.destroy(null);
+ }
+ sock.disconnect();
+ }
+ finally {
+ /* insert complete */
+ controller.leaveCritical();
+ }
+
+ if (deleteAfterInsert) {
+ chosenFile.delete();
+ }
+ }
+
+ public void onQuit()
+ {
+ if (JOptionPane.showConfirmDialog(
+ this,
+ "Do you really want to close insertion dialog
?",
+ "Closing...",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ new
ImageIcon(resources.getImageURL("application.medium.icon"))
+ )==JOptionPane.YES_OPTION) {
+
+ close();
+ }
+ }
+
+ protected void insertModelCallback( ProgressStats stats, GProgress
progress )
+ {
+ progress.progress(
+ stats.progress,
+ stats.filesize,
+ stats.progress+" bytes of "+stats.filesize+"
"+(choosedToIndex ? "indexed" : "inserted")
+ );
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public class ChooseFileStep extends GWizardPage
+ {
+ private GStack stack;
+ private GText text;
+ private GFile file;
+
+
+ public ChooseFileStep()
+ {
+ super("Choose file");
+ }
+
+ public JComponent createView()
+ {
+ text=new GText();
+ text.setText("Choose file to be inserted.");
+
+ file=new GFile();
+
+ stack=new GStack();
+ stack.addWidget(text);
+ stack.addWidget(file);
+ return stack;
+ }
+
+ public void updateView()
+ {
+ file.selectFile("File to insert",chosenBase);
+ file.setSelectedLocation(chosenFile);
+ }
+
+ public String canLeaveView()
+ {
+ FileLocation f;
+
+ f=(FileLocation) file.getSelectedLocation();
+ if (f==null) {
+ return "You must choose a file to insert !";
+ }
+ if (!f.exists()) {
+ return "Please select a valid file !";
+ }
+ return null;
+ }
+
+ public void leaveView()
+ {
+ chosenFile=(FileLocation) file.getSelectedLocation();
+ chosenBase=file.getSelectedBase();
+ }
+ }
+
+ public class EditAttributesStep extends GWizardPage
+ {
+ private GStyledText text;
+ private JTextField filenameField;
+ private JTextField mimeField;
+ private JTextField descriptionField;
+ private JComboBox methodCombo;
+ private DefaultListModel keywordModel;
+ private JList keywordList;
+ private JTextField keywordField;
+ private JButton addButton;
+ private JButton deleteButton;
+
+
+ public EditAttributesStep()
+ {
+ super("Edit attributes");
+ }
+
+ public JComponent createView()
+ {
+ GStack stack;
+ GForm form;
+ GBar bar;
+
+ filenameField=new JTextField(10);
+ mimeField=new JTextField(10);
+ descriptionField=new JTextField(10);
+ methodCombo=new JComboBox(new String[] { "Index only",
"Full insertion" });
+
+ keywordModel=new DefaultListModel();
+
+ keywordList=new JList(keywordModel);
+
keywordList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ keywordList.setLayoutOrientation(JList.VERTICAL);
+ keywordList.setVisibleRowCount(5);
+ keywordList.addListSelectionListener(new
ListSelectionListener() {
+ public void valueChanged( ListSelectionEvent
evt )
+ {
+
deleteButton.setEnabled(!keywordList.isSelectionEmpty());
+ }
+ });
+
+ keywordField=new JTextField(10);
+ keywordField.addActionListener(new ActionListener() {
+ public void actionPerformed( ActionEvent evt )
+ {
+ addKeyword();
+ }
+ });
+
+ addButton=new JButton("Add keyword");
+ addButton.addActionListener(new ActionListener() {
+ public void actionPerformed( ActionEvent evt )
+ {
+ addKeyword();
+ }
+ });
+
+ deleteButton=new JButton("Delete keyword");
+ deleteButton.addActionListener(new ActionListener() {
+ public void actionPerformed( ActionEvent evt )
+ {
+ deleteKeyword();
+ }
+ });
+ deleteButton.setEnabled(false);
+
+ bar=new GBar();
+ bar.add(addButton);
+ bar.add(deleteButton);
+
+ text=new GStyledText();
+
StyleConstants.setFontFamily(text.style("fi"),"SansSerif");
+ StyleConstants.setBold(text.style("fi"),true);
+ StyleConstants.setItalic(text.style("fi"),true);
+ StyleConstants.setForeground(text.style("fi"),new
Color(33,87,136));
+
+ form=new GForm();
+ form.addWidget("Published filename :",filenameField);
+ form.addWidget("MIME type :",mimeField);
+ form.addWidget("Description :",descriptionField);
+ form.addWidget("Insertion method :",methodCombo);
+ form.addWidget("Keyword(s) used :",new
JScrollPane(keywordList),0);
+ form.addWidget("",keywordField);
+ form.addWidget("",bar);
+
+ stack=new GStack();
+ stack.addWidget(text);
+ stack.addWidget(form);
+ return stack;
+ }
+
+ public void updateView()
+ {
+ String[] keywords;
+ Prefs prefs;
+ StringBuffer description,mimetype;
+ int i;
+
+ text.clear();
+ text.print("Now, edit attributes file
<fi>"+chosenFile.getLabel()+"</fi> will be inserted with.");
+
+ // try to extract keywords
+ description=new StringBuffer();
+ mimetype=new StringBuffer();
+ keywords=new KeyWords().extractKeywords(new
File(chosenFile.getPath()),description,mimetype);
+ if (description.length()==0) {
+ description.append("No description supplied");
+ }
+ if (mimetype.length()==0) {
+ mimetype.append("unknown");
+ }
+
+ filenameField.setText(chosenFile.getName());
+ mimeField.setText(mimetype.toString());
+ descriptionField.setText(description.toString());
+
+ prefs=controller.getPreferences();
+ if
(prefs.testString("GNUNET-INSERT","INDEX-CONTENT","YES")) {
+ methodCombo.setSelectedIndex(0);
+ }
+ else {
+ methodCombo.setSelectedIndex(1);
+ }
+
+ keywordModel.clear();
+ for (i=0; i<keywords.length; i++) {
+ keywordModel.addElement(keywords[i]);
+ }
+ keywordField.setText("");
+ }
+
+ /**
+ * The keyword add button was clicked. Add whatever is in the
keyword box to the list of keywords.
+ */
+
+ protected void addKeyword()
+ {
+ String key;
+
+ key=keywordField.getText();
+ if (key==null || key.trim().length()==0) {
+ return;
+ }
+ key=key.trim();
+
+ keywordModel.addElement(key);
+
+ keywordField.setText("");
+ }
+
+ /**
+ * The keyword delete button was clicked. Delete the currently
selected keyword.
+ */
+
+ protected void deleteKeyword()
+ {
+ int[] indices;
+ int i;
+
+ indices=keywordList.getSelectedIndices();
+ for (i=indices.length-1; i>=0; i--) {
+ keywordModel.remove(indices[i]);
+ }
+ }
+
+ public void leaveView()
+ {
+ int i;
+
+ chosenFilename=filenameField.getText().trim();
+ if (chosenFilename.length()==0) {
+ chosenFilename="none specified";
+ }
+
+ chosenMime=mimeField.getText().trim();
+ if (chosenMime.length()==0) {
+ chosenMime="unknown";
+ }
+
+ chosenDescription=descriptionField.getText().trim();
+ if (chosenDescription.length()==0) {
+ chosenDescription="no description specified";
+ }
+
+ choosedToIndex=methodCombo.getSelectedIndex()==0;
+
+ chosenKeywords=new String[keywordModel.getSize()];
+ for (i=0; i<chosenKeywords.length; i++) {
+ chosenKeywords[i]=(String) keywordModel.get(i);
+ }
+ }
+ }
+
+ public class ReviewStep extends GWizardPage
+ {
+ private GStyledText text;
+ private Icon dot;
+
+
+ public ReviewStep()
+ {
+ super("Review");
+ }
+
+ public JComponent createView()
+ {
+ dot=new ImageIcon(resources.getImageURL("dot.icon"));
+
+ text=new GStyledText();
+ StyleConstants.setLeftIndent(text.style("ind"),10);
+ StyleConstants.setSpaceAbove(text.style("ind"),3);
+ StyleConstants.setSpaceBelow(text.style("ind"),1);
+
+ StyleConstants.setForeground(text.style("v"),new
Color(33,87,136));
+
StyleConstants.setFontFamily(text.style("v"),"Monospaced");
+
+
StyleConstants.setFontFamily(text.style("fi"),"SansSerif");
+ StyleConstants.setBold(text.style("fi"),true);
+ StyleConstants.setItalic(text.style("fi"),true);
+ StyleConstants.setForeground(text.style("fi"),new
Color(33,87,136));
+ return text;
+ }
+
+ public void updateView()
+ {
+ StringBuffer buf;
+ int i;
+
+ buf=new StringBuffer();
+ if (chosenKeywords.length>0) {
+ for (i=0; i<chosenKeywords.length; i++) {
+ buf.append(chosenKeywords[i]);
+ if (i<chosenKeywords.length-1) {
+ buf.append(", ");
+ }
+ }
+ }
+ else {
+ buf.append("(none)");
+ }
+
+ text.clear();
+ text.println("You'd like to insert file
<fi>"+chosenFile.getLabel()+"</fi>.");
+ text.print("Please check information you've entered are
correct. ");
+ text.println("Then click on Go button to perform
insertion.");
+ text.print("<ind>").print(dot).print(" Filename :
<v>"+chosenFilename).println("</v></ind>");
+ text.print("<ind>").print(dot).print(" Mime type :
<v>"+chosenMime).println("</v></ind>");
+ text.print("<ind>").print(dot).print(" Description :
<v>"+chosenDescription).println("</v></ind>");
+ text.print("<ind>").print(dot).print(" Insertion method
: <v>"+(choosedToIndex ? "Index only" : "Full
insertion")).println("</v></ind>");
+ text.print("<ind>").print(dot).print(" Keywords :
<v>"+buf).println("</v></ind>");
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/SController.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/SController.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/SController.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.beans.*;
+import java.io.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public interface SController extends Controller
+{
+ public static final String QUIT_EVENT = "*quit*";
+
+ public Service service( Class c );
+ public void log( Level level, String msg );
+ public void err( String msg, Throwable x );
+
+ public void refreshMenus();
+
+ public void download( Node node );
+
+ public Policy getPolicy();
+
+ public void guiMessage( String str );
+ public void infoMessage( boolean doPopup, String str );
+
+ public void displayDirectory( String fileName, Node root );
+ public void showStats();
+ public CSSession connect();
+
+ public void addPropertyChangeListener( PropertyChangeListener listener
);
+ public void addPropertyChangeListener( String name,
PropertyChangeListener listener );
+ public void removePropertyChangeListener( PropertyChangeListener
listener );
+ public void removePropertyChangeListener( String name,
PropertyChangeListener listener );
+
+ public UIResources getResources();
+
+ public void enterCritical();
+ public void leaveCritical();
+
+ public File getCache();
+ public Prefs getPreferences();
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/SearchAdapter.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/SearchAdapter.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/SearchAdapter.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,44 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.io.*;
+
+/**
+ *
+ */
+
+public class SearchAdapter extends AbstractAdapter implements AFSConstants
+{
+ public SearchAdapter()
+ {
+ super(7);
+ }
+
+ protected void fillFacets( Object obj, Object[] facets )
+ {
+ Node node;
+ String str;
+
+ node=(Node) obj;
+
+ facets[0]=node.getDescription();
+ facets[1]=new Long(node.getFileIdentifier().getFileLength());
+
+ str=node.getFileName();
+ if (node.getMimeType().equals(GNUNET_DIRECTORY_MIME) &&
!str.endsWith(File.separator) ) {
+ str+="/";
+ }
+
+ facets[2]=str;
+ facets[3]=new Long(node.getFileIdentifier().getFileCRC());
+ facets[4]=node.getFileIdentifier().getFileQuery().toHex();
+ facets[5]=node.getFileIdentifier().getFileKey().toHex();
+ facets[6]=node.getMimeType();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/SearchOverviewPanel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/SearchOverviewPanel.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/SearchOverviewPanel.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,122 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.*;
+import javax.swing.*;
+
+/**
+ * Box displaying search results for the swing client.
+ */
+
+public class SearchOverviewPanel extends JPanel implements ActionListener,
PropertyChangeListener
+{
+ private SController controller;
+
+ private JTabbedPane resultTab;
+ private JTextField inputField;
+ private JButton searchButton;
+
+
+ public SearchOverviewPanel( SController ctr )
+ {
+ super(new BorderLayout());
+ controller=ctr;
+
+ setup();
+ }
+
+ public String toString()
+ {
+ return "Search overview panel";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void setup()
+ {
+ GBar bar;
+
+ resultTab=new JTabbedPane();
+ resultTab.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
+
+ inputField=new JTextField(15);
+ inputField.addActionListener(this);
+
+ searchButton=new JButton("Search");
+ searchButton.addActionListener(this);
+
+ bar=new GBar();
+ bar.add("Keyword(s) :",inputField);
+ bar.add(searchButton);
+
+ add(bar,BorderLayout.SOUTH);
+ add(resultTab,BorderLayout.CENTER);
+ }
+
+ /**
+ * This method is called whenever the user clicks the search button of
the main window.
+ * @param evt
+ */
+
+ public void actionPerformed( ActionEvent evt )
+ {
+ SearchPanel panel;
+ String str;
+ int index;
+
+ str=inputField.getText();
+ if (str.length()==0) {
+ JOptionPane.showMessageDialog(this,"searchString ==
null");
+ return;
+ }
+
+ // remove heading spaces (parsing bugs if not)
+ str=str.trim();
+ if (str.length()==0) {
+ JOptionPane.showMessageDialog(this,"No search key
given!");
+ return;
+ }
+
+ // add a new page in the notebook with the search results.
getSearchWindow returns the page
+ panel=new SearchPanel(controller,str);
+ if (panel.launch()) {
+ index=resultTab.getTabCount();
+
+
panel.addPropertyChangeListener(SearchPanel.RESULTS_COUNT,this);
+
panel.addPropertyChangeListener(SearchPanel.CLOSE_ME,this);
+ resultTab.addTab(panel.getKeyword(),panel);
+ resultTab.setSelectedIndex(index);
+ }
+
+ // reset search line to empty
+ inputField.setText("");
+ }
+
+ public void propertyChange( PropertyChangeEvent evt )
+ {
+ if (evt.getPropertyName().equals(SearchPanel.RESULTS_COUNT)) {
+ updateTitleFor((SearchPanel) evt.getSource());
+ }
+ else if (evt.getPropertyName().equals(SearchPanel.CLOSE_ME)) {
+ close((SearchPanel) evt.getSource());
+ }
+ }
+
+ protected void updateTitleFor( SearchPanel p )
+ {
+
resultTab.setTitleAt(resultTab.indexOfComponent(p),p.getKeyword()+"
("+p.getResultCount()+")");
+ }
+
+ protected void close( SearchPanel p )
+ {
+ resultTab.remove(p);
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/SearchPanel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/SearchPanel.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/SearchPanel.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,472 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.event.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ * box displaying search results for the gtk+ client.
+ */
+
+public class SearchPanel extends JPanel implements AFSConstants, SwingConstants
+{
+ public static final String RESULTS_COUNT =
"#results#count#";
+ public static final String CLOSE_ME =
"#close#me#";
+
+ public static final int LM_TYPE_SEARCH = 1;
+ public static final int LM_TYPE_DIRECTORY = 2;
+ public static final int LM_TYPE_NSSEARCH = 3;
+
+ private SController controller;
+ private String keyword;
+ private ListModel2 model;
+ private ScheduledTask sendJob;
+
+ private GTable searchTable;
+ private DefaultListModel searchModel;
+ private ListSelectionModel searchSelection;
+
+ private UIResources resources;
+ private SelectDialog selectDialog;
+
+
+ public SearchPanel( SController ctr, String str )
+ {
+ super(new TileLayout());
+ keyword=str;
+ controller=ctr;
+ resources=new
UIResources("search-panel.xml",controller.getCache());
+ resources.setGlobalTarget(this);
+ setup();
+ }
+
+ public String toString()
+ {
+ return "Search panel";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void setup()
+ {
+ JPopupMenu popup;
+ GBar bar;
+
+ resources.getAction("download.id").setEnabled(false);
+
+ searchModel=new DefaultListModel();
+
+ searchSelection=new DefaultListSelectionModel();
+ searchSelection.addListSelectionListener(new
ListSelectionListener() {
+ public void valueChanged( ListSelectionEvent evt )
+ {
+
resources.getAction("download.id").setEnabled(!searchSelection.isSelectionEmpty());
+ }
+ });
+
+ bar=new GBar();
+ bar.add(resources.getAction("download.id"));
+ bar.add(resources.getAction("popup.searchClose"));
+ add(bar,TileLayout.SOUTH);
+
+ searchTable=new GTable(new
SearchAdapter(),searchModel,searchSelection);
+ searchTable.setColumns(new GColumn[] {
+ new GColumn("Description", 470, LEFT, true),
+ new GColumn("Size", 70, RIGHT, true),
+ new GColumn("Filename", 200, LEFT, true),
+ new GColumn("CRC", 40, LEFT, false),
+ new GColumn("HASH1", 40, LEFT, false),
+ new GColumn("HASH2", 40, LEFT, false),
+ new GColumn("Mimetype", 200, CENTER, true)
+ });
+ add(new JScrollPane(searchTable));
+
+ // when we are destroyed (e.g. search aborted), stop the search
thread
+ addHierarchyListener(new HierarchyListener() {
+ public void hierarchyChanged( HierarchyEvent e )
+ {
+ if (getParent()==null && (e.getChangeFlags() &
HierarchyEvent.PARENT_CHANGED)!=0) {
+ stopSearch();
+ }
+ }
+ });
+
+ // add a right button popup menu
+ resources.getAction("popup.selectall").setEnabled(true);
+ resources.getAction("popup.unselectall").setEnabled(true);
+
resources.getAction("popup.searchSelectByName").setEnabled(true);
+
resources.getAction("popup.searchSelectByDesc").setEnabled(true);
+
resources.getAction("popup.searchSelectByMime").setEnabled(true);
+
resources.getAction("popup.searchDownloadSelected").setEnabled(true);
+ resources.getAction("popup.searchClose").setEnabled(true);
+
+ popup=resources.getPopupMenu("popup.menu");
+
+ UIHelper.addPopupToComponent(popup,this);
+ UIHelper.addPopupToComponent(popup,bar);
+ UIHelper.addPopupToComponent(popup,searchTable);
+ }
+
+ public String getKeyword()
+ {
+ return keyword;
+ }
+
+ public int getResultCount()
+ {
+ return searchTable.getRowCount();
+ }
+
+ protected void addRow( Node node )
+ {
+ int size;
+
+ size=searchTable.getRowCount();
+
+ searchModel.addElement(node);
+
+ firePropertyChange(RESULTS_COUNT,size,size+1);
+ }
+
+ /**
+ * Returns a box containing the search results list.
+ * @return
+ */
+
+ public boolean launch()
+ {
+ boolean ok;
+
+ model=new ListModel2();
+ model.type = LM_TYPE_SEARCH;
+ model.doTerminate = false;
+ model.skipMenuRefresh = false;
+ model.sem = new Semaphore(0);
+ model.searchTable= searchTable;
+ model.SEARCH_socket_= ((AbstractClient)
controller.getApplication()).connect();
+ if (model.SEARCH_socket_ == null) {
+ return false;
+ }
+
+ /* start searching */
+ ok=startSearch();
+ if (!ok) {
+ model.SEARCH_socket_.disconnect();
+ return false;
+ }
+ return ok;
+ }
+
+ /**
+ * The main method of the search-thread.
+ *
+ * @return OK on success, false on error
+ */
+
+ protected boolean startSearch()
+ {
+ HashCode512[] keys;
+ final SendQueriesContext sqc;
+ Task receiveThread;
+ String[] keywords;
+ ArrayList list;
+ String[] p;
+ int i;
+ final Scheduler
scheduler=controller.getApplication().getScheduler();
+
+ list=new ArrayList();
+
+ p=keyword.split("\\s+");
+ for (i=0; i<p.length; i++) {
+ if (p[i].length()>0) {
+ list.add(p[i]);
+ }
+ }
+
+ keywords=(String[]) list.toArray(new String[list.size()]);
+
+ keys=AFSUtils.parseKeywords(keywords);
+ if (keys.length==0) {
+ return false;
+ }
+
+ sqc=new SendQueriesContext(keys,model.SEARCH_socket_);
+ sqc.setTimeOut(0x00FFFFFF); // "forever" is 6 months...
+
+ model.sqc = sqc;
+
+ sendJob=new ScheduledTask("SEND-JOB",new
org.gnu.freeway.util.AbstractAction() {
+ public void perform()
+ {
+
sqc.repeatedlySend(scheduler,controller.getPolicy());
+ }
+ });
+ scheduler.addJob(sendJob,0);
+
+ receiveThread=new Task("RECEIVE-RESULTS",new
org.gnu.freeway.util.AbstractAction() {
+ public void perform()
+ {
+ sqc.receive(
+ controller.getPreferences(),
+ new SearchResultCallback() {
+ public void searchResult(
RootNode root )
+ {
+ displayResultGTK(root);
+ }
+
+ public void newNameFor(
RootNode root, String str )
+ {
+ //fixme: 'd better
replace row in data model !
+ searchResult(root);
+ }
+ },
+ new TestTerminateThread() {
+ public boolean test()
+ {
+ return
model.doTerminate;
+ }
+ }
+ );
+
+ model.sem.release(); // signal: thread
terminated
+ }
+ });
+ receiveThread.launch();
+ return true;
+ }
+
+ /**
+ * Stop the search thread and free the model. This method MUST always
+ * be called when the search ends, either because the search was
+ * aborted or because gnunet-gtk exists as a whole.
+ */
+
+ protected void stopSearch()
+ {
+ Scheduler scheduler;
+
+ //todo: verifier que c'est appelé
+
+ controller.log(Level.FINEST,"stopSearch called");
+ /* this must be done as a cron-job, since otherwise
+ it may deadlock (this is called from the
+ gtk event thread, and cron may be waiting for
+ the gtk event lock, so we can't delete a cron
+ job in this thread */
+ model.doTerminate = true;
+
+ scheduler=controller.getApplication().getScheduler();
+ scheduler.addJob(new ScheduledTask("STOP-SEARCH",new
EvalAction(this,"stopSearch_")),0);
+ }
+
+ /**
+ * Cron job that stops the search.
+ * @throws InterruptedException
+ */
+
+ public void stopSearch_() throws InterruptedException
+ {
+ switch (model.type) {
+ case LM_TYPE_DIRECTORY:
+ break;
+
+ case LM_TYPE_SEARCH:
+ /* the terminated search thread ups this
semaphore
+ once it is done and we can free data
structures */
+ model.doTerminate = true;
+
+ /* this signals the download thread to
terminate */
+ model.SEARCH_socket_.disconnect();
+
+ /* stop the cron-job that does the requests */
+
controller.getApplication().getScheduler().deleteJob(sendJob);
+
+ /* wait for download thread signal */
+ model.sem.acquire();
+
+ /* Now we can finally free the shared data
structures.
+ Note that the terminated search thread freed
+ some of the memory that was allocated in
+ x (see receive_Results_)
+ we free the rest. */
+ model.sem=null;
+ break;
+
+ case LM_TYPE_NSSEARCH:
+ model.doTerminate = true;
+ /* this signals the download thread to
terminate */
+ model.SEARCH_socket_.disconnect();
+ /* wait for download thread signal */
+ model.sem.acquire();
+
+ /* Now we can finally free the shared data
structures.
+ Note that the terminated search thread freed
+ some of the memory that was allocated in
+ x (see receive_Results_)
+ we free the rest. */
+ model.sem = null;
+ break;
+
+ default:
+ controller.log(Level.SEVERE,"Unknown model.type
: "+model.type);
+ break;
+ }
+ }
+
+ /**
+ * Display results. This is a callback from receive_Results that is
+ * called on every new result.
+ *
+ * @param node Data about a file
+ */
+
+ public void displayResultGTK( Node node )
+ {
+ final Node _copy = (Node) PersistentHelper.copy(node);
+
+ if (model.doTerminate)
+ return;
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run()
+ {
+ addRow(_copy);
+
+ if (!model.skipMenuRefresh) {
+ controller.refreshMenus();
+ }
+ }
+ });
+ }
+
+ /**
+ * Selects all search results from the current search page.
+ */
+
+ public void onSelectAll()
+ {
+ searchTable.selectAll();
+ }
+
+ /**
+ * Unselects all search results from the current search page.
+ */
+
+ public void onClearSelection()
+ {
+ searchSelection.clearSelection();
+ }
+
+ public void onSelectByName()
+ {
+ if (selectDialog==null) {
+ selectDialog=new SelectDialog(controller,(GFrame)
SwingUtilities.windowForComponent(this));
+ }
+ selectDialog.setTitle("Select By Filename");
+ selectDialog.display();
+ if (selectDialog.getTypedText()!=null) {
+ if
(UIHelper.selectMatching(searchTable,2,selectDialog.getTypedText())==0) {
+ controller.guiMessage("No matches...");
+ }
+ }
+ }
+
+ public void onSelectByDescription()
+ {
+ if (selectDialog==null) {
+ selectDialog=new SelectDialog(controller,(GFrame)
SwingUtilities.windowForComponent(this));
+ }
+ selectDialog.setTitle("Select By Description");
+ selectDialog.display();
+ if (selectDialog.getTypedText()!=null) {
+ if
(UIHelper.selectMatching(searchTable,0,selectDialog.getTypedText())==0) {
+ controller.guiMessage("No matches...");
+ }
+ }
+ }
+
+ public void onSelectByMime()
+ {
+ if (selectDialog==null) {
+ selectDialog=new SelectDialog(controller,(GFrame)
SwingUtilities.windowForComponent(this));
+ }
+ selectDialog.setTitle("Select By Mimetype");
+ selectDialog.display();
+ if (selectDialog.getTypedText()!=null) {
+ if
(UIHelper.selectMatching(searchTable,6,selectDialog.getTypedText())==0) {
+ controller.guiMessage("No matches...");
+ }
+ }
+ }
+
+ /**
+ * This method is called whenever the user clicks the download button.
It opens the "save-as" dialog.
+ */
+
+ public void onDownload()
+ {
+ Node node;
+ int i;
+
+ // download all selected entries
+ for (i=searchSelection.getMaxSelectionIndex();
i>=searchSelection.getMinSelectionIndex(); i--) {
+ if (searchSelection.isSelectedIndex(i)) {
+ node=(Node) searchModel.get(i);
+ controller.download(node);
+
+ // Remove entry from search results.
+ // Yes, if the user cancel's the download, the
entry does not re-appear.
+ // That's intended, after all, if you cancel,
it's probably because it took too long to download anyway...
+ // If you really need it back, just search
again!
+
+ // already on swing thread
+ searchModel.remove(i);
+
firePropertyChange(RESULTS_COUNT,searchModel.getSize()+1,searchModel.getSize());
+ }
+ }
+ }
+
+ /**
+ * Remove the active page from the search results notebook.
+ * The respective search will be stopped as well
+ * (by a callback assigned to the page earlier on).
+ */
+
+ public void onAbortSearch()
+ {
+ firePropertyChange(CLOSE_ME,null,new Object());
+ }
+}
+
+/**
+ * Data for a search process
+ */
+
+class ListModel2 extends Object
+{
+ public int type;
+ public JTable searchTable;
+ public boolean doTerminate;
+ public Semaphore sem;
+ public CSSession SEARCH_socket_;
+ /** contents determined by type! */
+ public Object sqc;
+ /** don't refresh gtk menus (its slow)? (YES/NO) */
+ public boolean skipMenuRefresh;
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/SearchWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/SearchWindow.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/SearchWindow.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,280 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class SearchWindow extends GFrame implements AFSConstants
+{
+ private UIResources resources;
+
+ private SController controller;
+ private JMenuBar menuBar;
+ private SearchOverviewPanel searchPanel;
+ private GStatus statusBar;
+ private DaemonWindow daemonWin;
+
+
+ public SearchWindow( SController ctr )
+ {
+ super(ctr,"search-window");
+ setTitle("GNUnet Swing : Search");
+ controller=ctr;
+
+
+ resources=new
UIResources("search-window.xml",controller.getCache());
+ resources.setGlobalTarget(this);
+
+ daemonWin=new DaemonWindow(resources,controller);
+
+ init();
+ }
+
+ public String toString()
+ {
+ return "Main window";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void init()
+ {
+ JPanel panel;
+ UIAction entry;
+
+//resources.getImage("app.logo");
+
+ resources.getAction("download.show.id").setEnabled(true);
+
resources.getAction("download.show.id").setTarget(controller.getApplication());
+
+ getAction("file.insert").setEnabled(true);
+ getAction("file.download.uri").setEnabled(true);
+ getAction("file.import.directory").setEnabled(true);
+ getAction("file.unindex.file").setEnabled(true);
+ getAction("file.show.downloads").setEnabled(true);
+ getAction("file.show.messages").setEnabled(true);
+ getAction("file.show.stats").setEnabled(false);
+ getAction("file.quit").setEnabled(true);
+ getAction("advanced.manage.pseudonyms.create").setEnabled(true);
+ getAction("advanced.launch.gnunetd").setEnabled(false);
+ getAction("advanced.kill.gnunetd").setEnabled(false);
+ getAction("advanced.daemon.window").setEnabled(true);
+ getAction("help.about").setEnabled(true);
+
+ menuBar=resources.getMenuBar("window-menu");
+
menuBar.add(UIHelper.createLAFMenu(controller.getApplication()));
+
+ searchPanel=new SearchOverviewPanel(controller);
+
+ statusBar=new GStatus();
+
+ panel=new JPanel(new BorderLayout());
+ panel.add(searchPanel,BorderLayout.CENTER);
+ panel.add(statusBar,BorderLayout.NORTH);
+
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing( WindowEvent evt )
+ {
+ close();
+ }
+ });
+
+ setContent(panel);
+ setJMenuBar(menuBar);
+
+// getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+// setIconImage(new
ImageIcon(getClass().getClassLoader().getResource("images/gnunetd.png")).getImage());
+
+ entry=getAction("file.show.downloads");
+ entry.setEnabled(false);
+
+ refreshMenus();
+
+ Scheduler scheduler;
+
+ scheduler=controller.getApplication().getScheduler();
+ scheduler.addJob(new
ScheduledTask("REFRESH-CONNECTED-PEERS",new
EvalAction(this,"onRefreshConnectedPeers"),Scheduler.SECS_5),Scheduler.SECS_5);
+ }
+
+ protected UIAction getAction( String str )
+ {
+ return resources.getAction(str);
+ }
+
+ public void refreshMenus()
+ {
+ GNDirectoryDatabase db;
+ UIAction entry;
+ boolean
havePseudo,haveSearch,haveInsert,haveDirect,haveNamesp,haveAny;
+
+ db=new GNDirectoryDatabase(controller.getPreferences());
+
+ havePseudo = new
Pseudonym(controller.getPreferences()).havePseudonyms();
+ haveSearch =
db.iterateDirectoryDatabase(DIR_CONTEXT_SEARCH,null,null)>0;
+ haveInsert =
db.iterateDirectoryDatabase(DIR_CONTEXT_INSERT,null,null)>0;
+ haveDirect =
db.iterateDirectoryDatabase(DIR_CONTEXT_DIRECTORY,null,null)>0;
+ haveNamesp =
db.iterateDirectoryDatabase(DIR_CONTEXT_INSERT_SB,null,null)>0;
+ haveAny =
db.iterateDirectoryDatabase(DIR_CONTEXT_ALL,null,null)>0;
+
+ entry=getAction("advanced.assemble.directory.from.sr");
+ entry.setEnabled(haveSearch);
+
+ entry=getAction("advanced.assemble.directory.from.if");
+ entry.setEnabled(haveInsert);
+
+ entry=getAction("advanced.assemble.directory.from.lns");
+ entry.setEnabled(haveNamesp);
+
+ entry=getAction("advanced.assemble.directory.from.fid");
+ entry.setEnabled(haveDirect);
+
+ entry=getAction("advanced.assemble.directory.from.all");
+ entry.setEnabled(haveAny);
+
+ entry=getAction("advanced.manage.pseudonyms.delete");
+ entry.setEnabled(havePseudo);
+
+ entry=getAction("advanced.insert_into_namespace.select_sr");
+ entry.setEnabled(havePseudo && haveSearch);
+
+ entry=getAction("advanced.insert_into_namespace.select_if");
+ entry.setEnabled(havePseudo && haveInsert);
+
+ entry=getAction("advanced.insert_into_namespace.select_dd");
+ entry.setEnabled(havePseudo && haveDirect);
+
+ entry=getAction("advanced.insert_into_namespace.select_lns");
+ entry.setEnabled(havePseudo && haveNamesp);
+
+ entry=getAction("advanced.insert_into_namespace.select_all");
+ entry.setEnabled(havePseudo && haveAny);
+
+ entry=getAction("advanced.search.namespace");
+ entry.setEnabled(haveNamesp);
+
+ entry=getAction("advanced.reset_fid.list_sr");
+ entry.setEnabled(haveSearch);
+
+ entry=getAction("advanced.reset_fid.list_if");
+ entry.setEnabled(haveInsert);
+
+ entry=getAction("advanced.reset_fid.list_lns");
+ entry.setEnabled(haveNamesp);
+
+ entry=getAction("advanced.reset_fid.list_dd");
+ entry.setEnabled(haveDirect);
+
+ entry=getAction("advanced.reset_fid.all");
+ entry.setEnabled(haveAny);
+ }
+
+ public void cronCheckDaemon()
+ {
+ daemonWin.cronCheckDaemon();
+ }
+
+ /**
+ * This displays an about dialog
+ */
+
+ public void about()
+ {
+ String about;
+
+ about="\nGNUnet "+GNUNetDaemon.VERSION+
+ ", gnunet-swing
"+controller.getApplication().getVersion()+
+ "\n\n\n"+
+ "GNUnet is free software, released under GNU General
Public License version 2."+
+ "\n\n\n"+
+ "For more information, visit the GNUnet homepage at
\n\n"+
+ "http://www.ovmj.org/GNUnet/\n";
+
+ JOptionPane.showOptionDialog(this,about,"About
gnunet-swing",JOptionPane.YES_NO_OPTION,JOptionPane.INFORMATION_MESSAGE,null,new
Object[] { "Right" },null);
+ }
+
+ public void showStats()
+ {
+ controller.showStats();
+ }
+
+ public void destroy_stub()
+ {
+ }
+
+ public void onCreatePseudonym()
+ {
+ CreatePseudonymDialog dialog;
+
+ dialog=new CreatePseudonymDialog(controller,this);
+ dialog.display();
+ }
+
+ public void onDeletePseudonym()
+ {
+ DeletePseudonymDialog dialog;
+
+ dialog=new DeletePseudonymDialog(controller,this);
+ dialog.display();
+ }
+
+ public void openSelectFile()
+ {
+ new InsertDialog(this).display();
+ }
+
+ public void onDaemonWindow()
+ {
+ daemonWin.display();
+ }
+
+ public void onLaunchDaemon()
+ {
+ daemonWin.display();
+ daemonWin.onLaunchDaemon();
+ }
+
+ public void onKillDaemon()
+ {
+ daemonWin.display();
+ daemonWin.onKillDaemon();
+ }
+
+ public void onRefreshConnectedPeers()
+ {
+ CSSession sock;
+ CSResult rv;
+
+ sock=controller.connect();
+ try {
+ if (!sock.send(new CSGetClientCount())) {
+ return;
+ }
+
+ rv=(CSResult) sock.receive(CSResult.class);
+ if (rv==null) {
+ return;
+ }
+
+ statusBar.setText("Connected hosts : "+rv.getResult());
+ }
+ finally {
+ sock.disconnect();
+ }
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/SelectDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/SelectDialog.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/SelectDialog.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,165 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.event.*;
+import java.beans.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class SelectDialog extends GDialog implements PropertyChangeListener
+{
+ private SController controller;
+
+ private JTextField textField;
+ private JOptionPane optionPane;
+ private String typedText;
+
+
+ /**
+ * Creates the reusable dialog.
+ * @param ctr
+ * @param parent
+ */
+
+ public SelectDialog( SController ctr, GFrame parent )
+ {
+ super(parent,"select-dialog");
+ controller=ctr;
+ typedText=null;
+
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent we)
+ {
+ onClose();
+ }
+ });
+
+ // ensure the text field always gets the first focus
+ addComponentListener(new ComponentAdapter() {
+ public void componentShown( ComponentEvent evt )
+ {
+ textField.requestFocusInWindow();
+ }
+ });
+ }
+
+ public String toString()
+ {
+ return "Select dialog";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns null if the typed string was invalid;
+ * otherwise, returns the string as the user entered it.
+ * @return
+ */
+
+ public String getTypedText()
+ {
+ return typedText;
+ }
+
+ public JComponent createContent()
+ {
+ Object[] options;
+
+ textField=new JTextField(10);
+ textField.addActionListener(new ActionListener() {
+ public void actionPerformed( ActionEvent evt )
+ {
+ onEnter();
+ }
+ });
+
+ options=new Object[] { "Enter", "Cancel" };
+
+ optionPane=new JOptionPane(
+ new Object[] { "Pattern ?", textField },
+ JOptionPane.QUESTION_MESSAGE,
+ JOptionPane.YES_NO_OPTION,
+ new
ImageIcon(controller.getResources().getImageURL("app.minilogo")),
+ options,
+ options[0]
+ );
+ optionPane.addPropertyChangeListener(this);
+ return optionPane;
+ }
+
+ public void updateContent()
+ {
+ textField.setText("");
+ }
+
+ public void leaveContent()
+ {
+ textField.setText("");
+ }
+
+ /**
+ * This method reacts to state changes in the option pane.
+ * @param e
+ */
+
+ public void propertyChange( PropertyChangeEvent e )
+ {
+ String str;
+ Object value;
+
+ if (!isVisible() || e.getSource()!=optionPane) {
+ return;
+ }
+
+ str=e.getPropertyName();
+ if (str.equals(JOptionPane.VALUE_PROPERTY) ||
str.equals(JOptionPane.INPUT_VALUE_PROPERTY)) {
+ value=optionPane.getValue();
+
+ if (value==JOptionPane.UNINITIALIZED_VALUE) {
+ //ignore reset
+ return;
+ }
+
+ // if you don't do this, then if the user presses the
same button next time, no property change event will be fired.
+ optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE);
+
+ if (value.equals(optionPane.getOptions()[0])) {
+ onEnter();
+ }
+ else {
+ onClose();
+ }
+ }
+ }
+
+ protected void onEnter()
+ {
+ typedText=textField.getText().trim();
+ if (typedText.length()==0) {
+ textField.selectAll();
+
JOptionPane.showMessageDialog(SelectDialog.this,"Please, enter a non empty
response.","Try again",JOptionPane.ERROR_MESSAGE);
+ textField.requestFocusInWindow();
+
+ typedText=null;
+ }
+ else {
+ // we're done; clear and dismiss the dialog
+ close();
+ }
+ }
+
+ protected void onClose()
+ {
+ typedText=null;
+ close();
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/StatsWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/StatsWindow.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/StatsWindow.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,156 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.fs.swing;
+
+import org.gnu.freeway.protocol.fs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class StatsWindow extends GFrame implements AFSConstants, SwingConstants
+{
+ private SController controller;
+ private UIResources resources;
+
+ private GTable statTable;
+ private GStatus statusBar;
+
+
+ public StatsWindow( SController ctr )
+ {
+ super(ctr,"stats-window");
+ setTitle("GNUnet Swing : Statistics");
+ controller=ctr;
+
+ resources=new
UIResources("stats-window.xml",controller.getCache());
+ resources.setGlobalTarget(this);
+ }
+
+ public String toString()
+ {
+ return "Statistics window";
+ }
+
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public JComponent createContent()
+ {
+ JPanel panel;
+ GBar bar;
+
+ resources.getAction("main.refresh").setEnabled(true);
+ resources.getAction("main.close").setEnabled(true);
+
+ setJMenuBar(resources.getMenuBar("window-menu"));
+
+ bar=new GBar();
+ bar.add(resources.getAction("main.refresh"));
+ bar.add(resources.getAction("main.close"));
+
+ statTable=new GTable(new AbstractAdapter(2) {
+ protected void fillFacets( Object obj, Object[] facets )
+ {
+ Stat stat;
+
+ stat=(Stat) obj;
+ facets[0]=stat.getName();
+ facets[1]=new Long(stat.get());
+ }
+ });
+ statTable.setColumns(new GColumn[] {
+ new GColumn("Statistic", 400, RIGHT, true),
+ new GColumn("Value", 270, LEFT, true)
+ });
+
+ statusBar=new GStatus();
+ statusBar.setText("");
+
+ panel=new JPanel(new TileLayout());
+ panel.add(bar,TileLayout.SOUTH);
+ panel.add(statusBar,TileLayout.NORTH);
+ panel.add(new JScrollPane(statTable));
+ return panel;
+ }
+
+ public void onRefresh()
+ {
+ if (!refresh()) {
+ statusBar.setText("Failed to connect to daemon.");
+ }
+ }
+
+ public void onClose()
+ {
+ close();
+ }
+
+ protected boolean refresh()
+ {
+ Stat[] stats;
+ int[] sel;
+ CSSession sock;
+ DefaultListModel sModel;
+ //CSStatistics statMsg;
+ long t;
+ int totalCounters,count,i;
+
+ sel=statTable.getSelectedRows();
+
+ sModel=(DefaultListModel) statTable.getUnsortedModel();
+ sModel.clear();
+
+ /*sock=controller.connect();
+ try {
+ if (!sock.send(new CSStatisticsRequest())) {
+ controller.log(Level.WARNING,"Error sending
request for statistics to gnunetd.");
+ return false;
+ }
+
+ count=0;
+ totalCounters=1;
+
+ while (count<totalCounters) {
+ //statMsg=(CSStatistics)
sock.receive(CSStatistics.class);
+ //if (statMsg==null) {
+ // controller.log(Level.WARNING,"Error
receiving reply for statistics from gnunetd.");
+ // return false;
+ // }
+
+ if (count==0) {
+ t=Scheduler.toSeconds(Scheduler.now());
+ t-=statMsg.getStartTime();
+
+ statusBar.setText("Uptime :
"+Utils.formatDuration(Scheduler.seconds(t))+" (refreshed at
"+Utils.formatMoment(Scheduler.now())+")");
+
totalCounters=statMsg.getTotalCounters();
+ }
+
+ if (statMsg.getTotalCounters()!=totalCounters) {
+ controller.log(Level.WARNING,"Corrupted
data ?");
+ return false;
+ }
+
+ stats=statMsg.getStatistics();
+ for (i=0; i<stats.length; i++) {
+ sModel.addElement(stats[i]);
+ }
+ count+=stats.length;
+ }
+ }
+ finally {
+ sock.disconnect();
+ }*/
+ statTable.setSelectedRows(sel);
+ return true;
+ }
+}
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/about.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/about.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/about.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,128 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/about.c
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ * This file contains the about dialog.
+ **/
+
+#include "gnunet_afs_esed2.h"
+
+#include "helper.h"
+#include "about.h"
+
+#define ABOUT_STRING "\nGNUnet "\
+ VERSION\
+ ", gnunet-gtk "\
+ AFS_VERSION\
+ "\n\n\n"\
+ "GNUnet is free software, released under GNU General Public License version
2."\
+ "\n\n\n"\
+ "For more information, visit the GNUnet homepage at \n\n"\
+ "http://www.ovmj.org/GNUnet/\n"
+
+
+/**
+ * This displays an about window
+ **/
+void about(GtkWidget *dummy,
+ gpointer data) {
+ GtkWidget * window;
+ GtkWidget * box1;
+ GtkWidget * table;
+ GtkWidget * text;
+ GtkWidget * button;
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ NULL);
+
+ gtk_window_set_title(GTK_WINDOW(window),
+ "About gnunet-gtk");
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 600,
+ 300);
+
+ box1 = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER (window),
+ box1);
+ gtk_widget_show(box1);
+
+ table = gtk_table_new(2, 2, FALSE);
+ gtk_table_set_row_spacing(GTK_TABLE (table),
+ 0,
+ 2);
+ gtk_table_set_col_spacing(GTK_TABLE (table),
+ 0,
+ 2);
+ gtk_box_pack_start(GTK_BOX (box1),
+ table,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(table);
+
+ /* create a text widget */
+ text = gtk_text_new(NULL, NULL);
+ gtk_text_set_editable(GTK_TEXT (text),
+ FALSE);
+ gtk_table_attach(GTK_TABLE (table),
+ text,
+ 0,
+ 1,
+ 0,
+ 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 0, 0);
+ gtk_widget_show(text);
+ gtk_widget_realize(text);
+
+ /* write some about text */
+ gtk_text_freeze(GTK_TEXT (text));
+
+ gtk_text_insert(GTK_TEXT(text),
+ NULL,
+ &text->style->black,
+ NULL,
+ ABOUT_STRING, -1);
+
+ gtk_text_thaw(GTK_TEXT(text));
+
+ /* finish with a close button */
+ button = gtk_button_new_with_label("Right");
+ gtk_box_pack_start(GTK_BOX (box1),
+ button,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button);
+ gtk_widget_show(window);
+}
+
+/* end of about.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/about.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/about.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/about.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,35 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/about.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_ABOUT_H
+#define GTKUI_ABOUT_H
+
+/**
+ * This displays an about window
+ **/
+void about(GtkWidget *dummy,
+ gpointer data);
+
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/delete.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/delete.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/delete.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,175 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/delete.c
+ * @brief handles file deletions
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "main.h"
+#include "insertprogress.h"
+#include "delete.h"
+
+static gint setProgressValue(SaveCall *call) {
+ gtk_progress_set_value(GTK_PROGRESS(((SetProgress *)call->args)->bar),
+ ((SetProgress *)call->args)->val);
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+static void deleteModelCallback(ProgressStats * stats,
+ InsertModel * ilm) {
+ SetProgress progress;
+
+ progress.val = stats->progress;
+ progress.bar = ilm->progressBar;
+ gtkSaveCall((GtkFunction) setProgressValue, &progress);
+}
+
+static gint destroyProgressBar(SaveCall *call) {
+ gtk_widget_destroy((GtkWidget *) call->args);
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+static void deleteFileGtkThread(InsertModel * ilm) {
+ int res;
+ GNUNET_TCP_SOCKET * sock;
+
+ SEMAPHORE_DOWN(refuseToDie);
+ sock = getClientSocket();
+ if (sock == NULL) {
+ guiMessage("Failed to connect to gnunetd. Consult logs.");
+ SEMAPHORE_UP(refuseToDie);
+ return;
+ }
+
+ LOG(LOG_DEBUG,
+ "DEBUG: attempt to delete %s\n",
+ ilm->fileName);
+
+ res = deleteFile(sock,
+ ilm->fileName,
+ (ProgressModel)&deleteModelCallback,
+ ilm);
+
+ gtkSaveCall((GtkFunction) destroyProgressBar, ilm->progressBarWindow);
+ refreshMenuSensitivity();
+
+ if(res != OK)
+ guiMessage("Failed to delete\n\n%s",
+ ilm->fileName);
+ else
+ guiMessage("File deleted.");
+
+ releaseClientSocket(sock);
+
+ SEMAPHORE_UP(refuseToDie);
+
+ FREE(ilm->fileName);
+ FREE(ilm);
+}
+
+/**
+ * Callback for the file selection window. Launches the
+ * thread to delete the selected file.
+ *
+ * @param okButton not used
+ * @param window the file selection window
+ */
+static gint file_selected(GtkWidget * okButton,
+ GtkWidget * window) {
+ gchar * filename;
+ InsertModel * ilm;
+ PTHREAD_T deleteThread;
+
+ filename
+ = gtk_file_selection_get_filename(GTK_FILE_SELECTION(window));
+ if ( (filename == NULL) ||
+ (0 == assertIsFile(filename)) ) {
+ guiMessage("Please select a file!\n");
+ gtk_widget_destroy(window);
+ return FALSE;
+ }
+
+ if (filename[0] != '/')
+ errexit("FATAL: ASSERTION failed: path name does not start with a '/'");
+
+ ilm = MALLOC(sizeof(InsertModel));
+ ilm->fileName = expandFileName(filename);
+
+ strcpy(ilm->opDescription, "deleted");
+ createInsertProgressBar(ilm);
+ /* start the delete thread */
+ if (0 != PTHREAD_CREATE(&deleteThread,
+ (PThreadMain) deleteFileGtkThread,
+ ilm,
+ 16 * 1024))
+ errexit("FATAL: could not create delete thread (%s)!\n",
+ STRERROR(errno));
+ PTHREAD_DETACH(&deleteThread);
+
+ /* destroy the file selector */
+ gtk_widget_destroy(window);
+
+ return FALSE;
+}
+
+
+/**
+ * Close the open-file window.
+ **/
+static gint destroyOpenFile(GtkWidget * widget,
+ GtkWidget * window) {
+ LOG(LOG_DEBUG,
+ "DEBUG: destroying open-file window (%x)\n",
+ window);
+ return TRUE;
+}
+
+/**
+ * Pops up a file selector for the user. Callback starts
+ * the file deletion thread.
+ *
+ **/
+void openDeleteFile(void) {
+ GtkWidget * window;
+
+ window = gtk_file_selection_new("Choose file to be unindexed");
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyOpenFile),
+ window);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(file_selected),
+ window);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(window);
+}
+
+/* end of delete.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/delete.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/delete.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/delete.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,35 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/delete.h
+ * @author Igor Wronsky
+ **/
+
+#ifndef GTKUI_DELETE_H
+#define GTKUI_DELETE_H
+
+typedef struct {
+ size_t val;
+ GtkWidget *bar;
+} SetProgress;
+
+void openDeleteFile(void);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/directory.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/directory.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/directory.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,731 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/directory.c
+ * @brief Directory dialog for the AFS interface
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ **/
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "insertprogress.h"
+#include "directory.h"
+#include "directorydisplay.h"
+
+
+/**
+ * @brief state of the create Directory window
+ **/
+typedef struct {
+ char * fileName;
+ GtkWidget * editAttributesWindow;
+ GtkWidget * fileNameLine;
+ GtkWidget * descriptionLine;
+ GtkWidget * keywordLine;
+ GtkWidget * keywordList;
+ GtkWidget * availableList;
+ GtkWidget * selectedList;
+ RootNode ** availableEntries;
+ RootNode ** selectedEntries;
+ int availableCount;
+ int selectedCount;
+} AssembleWindowModel;
+
+/**
+ * Collects the results of the assembly dialog, creates an insertion
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void startAssemble(GtkWidget * dummy,
+ AssembleWindowModel * ewm) {
+ int i;
+ RootNode * coll;
+ GNUnetDirectory * dir;
+ char * name;
+ InsertModel * ilm;
+ char * fileName;
+ gchar * txt;
+ PTHREAD_T insertThread;
+
+ if (ewm->selectedCount == 0) {
+ guiMessage("WARNING: cowardly refusing to build empty directory.\n");
+ LOG(LOG_WARNING,
+ "WARNING: cowardly refusing to build empty directory.\n");
+ return;
+ }
+ ilm = MALLOC(sizeof(InsertModel));
+
+ name = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+ if (name == NULL)
+ name = "no description specified";
+
+ coll = MALLOC(ewm->selectedCount * sizeof(RootNode));
+ for (i=0;i<ewm->selectedCount;i++)
+ memcpy(&coll[i],
+ ewm->selectedEntries[i],
+ sizeof(RootNode));
+ dir = buildDirectory(ewm->selectedCount,
+ name,
+ coll);
+ FREE(coll);
+
+ /* get the published filename */
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->fileNameLine));
+ if (txt == NULL)
+ ilm->fileNameRoot = STRDUP("directory");
+ else
+ ilm->fileNameRoot = STRDUP(txt);
+
+ fileName = MALLOC(strlen("/tmp/gnunetdir_") + strlen(ilm->fileNameRoot) +
strlen(".XXXXXX") + 1);
+ strcpy(fileName, "/tmp/gnunetdir_");
+ strcat(fileName, ilm->fileNameRoot);
+ strcat(fileName, ".XXXXXX");
+ mkstemp(fileName);
+
+ if (SYSERR == writeGNUnetDirectory(dir, fileName)) {
+ LOG(LOG_WARNING,
+ "WARNING: could not write directory to temporary file.\n");
+ FREE(fileName);
+ FREE(dir);
+ FREE(ilm);
+ return;
+ }
+ ilm->fileName = fileName;
+ ilm->indexContent = NO;
+ ilm->deleteAfterInsert = YES;
+ /* get the new description, if any */
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+ if (txt == NULL)
+ ilm->description = STRDUP("no description specified");
+ else
+ ilm->description = STRDUP(txt);
+ ilm->mimetype = STRDUP(GNUNET_DIRECTORY_MIME);
+
+ /* get list of keywords */
+ ilm->num_keywords = GTK_CLIST(ewm->keywordList)->rows;
+ if (ilm->num_keywords > 0) {
+ ilm->keywords = (char**) MALLOC(ilm->num_keywords * sizeof(char*));
+ for(i=0;i<ilm->num_keywords;i++) {
+ gtk_clist_get_text(GTK_CLIST(ewm->keywordList),
+ i,
+ 0,
+ &txt);
+ ilm->keywords[i] = STRDUP(txt);
+ }
+ } else
+ ilm->keywords = NULL;
+
+ strcpy(ilm->opDescription, "processed");
+ createInsertProgressBar(ilm);
+ /* start the insert thread */
+ if (0 != PTHREAD_CREATE(&insertThread,
+ (PThreadMain) insertFileGtkThread,
+ ilm,
+ 16 * 1024))
+ errexit("FATAL: could not create insert thread (%s)!\n",
+ STRERROR(errno));
+ PTHREAD_DETACH(&insertThread);
+
+ /* destroy the "assemble directory" window */
+ gtk_widget_destroy(ewm->editAttributesWindow);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyAssembleWindow(GtkWidget * widget,
+ AssembleWindowModel * ewm) {
+ int i;
+
+ for (i=0;i<ewm->availableCount;i++)
+ FREE(ewm->availableEntries[i]);
+ for (i=0;i<ewm->selectedCount;i++)
+ FREE(ewm->selectedEntries[i]);
+ GROW(ewm->availableEntries,
+ ewm->availableCount,
+ 0);
+ GROW(ewm->selectedEntries,
+ ewm->selectedCount,
+ 0);
+ FREE(ewm);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_add_clicked(GtkWidget * w,
+ AssembleWindowModel * ewm) {
+ gchar * key;
+ gchar * newKeyword;
+ int i;
+
+ key = gtk_entry_get_text(GTK_ENTRY(ewm->keywordLine));
+ if (key == NULL) {
+ /* message to enter a string? */
+ return;
+ }
+
+ newKeyword = STRDUP(key);
+ key = newKeyword;
+
+ /* remove trailing & heading spaces */
+ i = strlen(key)-1;
+ while ( (newKeyword[i] == ' ') &&
+ (i >= 0) ) {
+ newKeyword[i--] = '\0';
+ }
+ while (*newKeyword == ' ')
+ newKeyword++;
+
+ if ( *newKeyword == '\0' ) {
+ /* message to enter more than spaces? */
+ } else {
+ gtk_clist_append(GTK_CLIST(ewm->keywordList),
+ &newKeyword);
+ }
+ FREE(key);
+ gtk_entry_set_text(GTK_ENTRY(ewm->keywordLine),
+ "");
+}
+
+
+/**
+ * The keyword delete button was clicked. Delete the
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_del_clicked(GtkWidget * w,
+ AssembleWindowModel * ewm) {
+ GList * tmp;
+
+ tmp = GTK_CLIST(ewm->keywordList)->selection;
+ if (NULL == tmp) {
+ /* message that keyword must be selected to delete one? */
+ return;
+ }
+ gtk_clist_remove(GTK_CLIST(ewm->keywordList),
+ (int)tmp->data);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_select_clicked(GtkWidget * w,
+ AssembleWindowModel * ewm) {
+ gchar * key[1];
+ GList * tmp;
+ int row;
+ int i;
+
+ tmp = GTK_CLIST(ewm->availableList)->selection;
+ if (NULL == tmp)
+ return;
+ row = (int) tmp->data;
+ if ( (row < 0) ||
+ (row >= ewm->availableCount) )
+ return; /* should never happen... */
+ key[0] = NULL;
+ gtk_clist_get_text(GTK_CLIST(ewm->availableList),
+ row,
+ 0,
+ &key[0]);
+ gtk_clist_append(GTK_CLIST(ewm->selectedList),
+ &key[0]);
+ gtk_clist_remove(GTK_CLIST(ewm->availableList),
+ row);
+ if (row > 0)
+ gtk_clist_select_row(GTK_CLIST(ewm->availableList),
+ row-1,
+ 0);
+ else
+ gtk_clist_select_row(GTK_CLIST(ewm->availableList),
+ 0,
+ 0);
+ GROW(ewm->selectedEntries,
+ ewm->selectedCount,
+ ewm->selectedCount+1);
+ ewm->selectedEntries[ewm->selectedCount-1]
+ = ewm->availableEntries[row];
+ for (i=row;i<ewm->availableCount-1;i++)
+ ewm->availableEntries[i]
+ = ewm->availableEntries[i+1];
+ GROW(ewm->availableEntries,
+ ewm->availableCount,
+ ewm->availableCount-1);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_deselect_clicked(GtkWidget * w,
+ AssembleWindowModel * ewm) {
+ gchar * key[1];
+ GList * tmp;
+ int row;
+ int i;
+
+ tmp = GTK_CLIST(ewm->selectedList)->selection;
+ if (NULL == tmp)
+ return;
+ row = (int) tmp->data;
+ if ( (row < 0) ||
+ (row >= ewm->selectedCount) )
+ return; /* should never happen... */
+ key[0] = NULL;
+ gtk_clist_get_text(GTK_CLIST(ewm->selectedList),
+ row,
+ 0,
+ &key[0]);
+ gtk_clist_append(GTK_CLIST(ewm->availableList),
+ &key[0]);
+ gtk_clist_remove(GTK_CLIST(ewm->selectedList),
+ row);
+ if (row > 0)
+ gtk_clist_select_row(GTK_CLIST(ewm->selectedList),
+ row-1,
+ 0);
+ else
+ gtk_clist_select_row(GTK_CLIST(ewm->selectedList),
+ 0,
+ 0);
+ GROW(ewm->availableEntries,
+ ewm->availableCount,
+ ewm->availableCount+1);
+ ewm->availableEntries[ewm->availableCount-1]
+ = ewm->selectedEntries[row];
+ for (i=row;i<ewm->selectedCount-1;i++)
+ ewm->selectedEntries[i]
+ = ewm->selectedEntries[i+1];
+ GROW(ewm->selectedEntries,
+ ewm->selectedCount,
+ ewm->selectedCount-1);
+}
+
+
+static void appendToCList(RootNode * root,
+ AssembleWindowModel * ewm) {
+ gchar * entry[1];
+
+ entry[0] = STRDUP(root->header.description);
+ gtk_clist_append(GTK_CLIST(ewm->availableList),
+ entry);
+ FREE(entry[0]);
+ GROW(ewm->availableEntries,
+ ewm->availableCount,
+ ewm->availableCount+1);
+ ewm->availableEntries[ewm->availableCount-1]
+ = MALLOC(sizeof(RootNode));
+ memcpy(ewm->availableEntries[ewm->availableCount-1],
+ root,
+ sizeof(RootNode));
+}
+
+
+/**
+ * Open a window to allow the user to build a directory.
+ *
+ * @param unused GTK handle that is not used
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleDirectoryDialog(GtkWidget * unused,
+ unsigned int context) {
+ AssembleWindowModel * ewm;
+ GtkWidget * window;
+ GtkWidget * vbox, * hbox;
+ GtkWidget * clist;
+ GtkWidget * scrolled_window;
+ GtkWidget * label;
+ GtkWidget * separator;
+ GtkWidget * button_add;
+ GtkWidget * button_delete;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+ GtkWidget * keyword_line;
+ gchar * titles[1] = { "Keyword(s) used" };
+ gchar * titlesAvailable[1] = { "Files available" };
+ gchar * titlesSelected[1] = { "Files selected"};
+ gchar * directoryMimetype[1] = { GNUNET_DIRECTORY_MIME };
+
+ ewm = MALLOC(sizeof(AssembleWindowModel));
+ /* create new window for editing */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ewm->editAttributesWindow = window;
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 620,
+ 480);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Assemble directory");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyAssembleWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ /* Create a line to change the published filename */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Published directory name:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->fileNameLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->fileNameLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->fileNameLine),
+ "");
+ gtk_widget_show(ewm->fileNameLine);
+
+ /* Create a line to change the description */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Description:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->descriptionLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->descriptionLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->descriptionLine),
+ "A GNUnet directory");
+ gtk_widget_show(ewm->descriptionLine);
+
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(separator);
+
+ /* add a list of keywords */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titles);
+ /* add mimetype as a keyword */
+ gtk_clist_append(GTK_CLIST(clist), directoryMimetype);
+ ewm->keywordList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+
+ /* add a line to input new keywords */
+ keyword_line = gtk_entry_new();
+ ewm->keywordLine = keyword_line;
+ gtk_box_pack_start(GTK_BOX(vbox),
+ keyword_line,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(keyword_line),
+ "");
+ gtk_signal_connect(GTK_OBJECT(keyword_line),
+ "activate",
+ GTK_SIGNAL_FUNC(button_add_clicked),
+ ewm);
+ gtk_widget_show(keyword_line);
+
+ /* add the buttons to add and delete keywords */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_add = gtk_button_new_with_label("Add keyword");
+ button_delete = gtk_button_new_with_label("Delete keyword");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_add,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_delete,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_add),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_add_clicked),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_delete),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_del_clicked),
+ ewm);
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_delete);
+
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(separator);
+
+ /* add the box for the two lists */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+
+ /* add a list of available entries */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titlesAvailable);
+ ewm->availableList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ /* add the known RootNodes to the list */
+ gtk_clist_freeze(GTK_CLIST(clist));
+ iterateDirectoryDatabase(context,
+ (RootNodeCallback)&appendToCList,
+ ewm);
+ gtk_clist_thaw(GTK_CLIST(clist));
+ gtk_widget_show(clist);
+
+
+ /* add a list of selected entries */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titlesSelected);
+ ewm->selectedList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+
+
+ /* add the box for the buttons to move between the
+ two lists */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+
+ button_add = gtk_button_new_with_label("=>");
+ button_delete = gtk_button_new_with_label("<=");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_add,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_delete,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_add),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_select_clicked),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_delete),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_deselect_clicked),
+ ewm);
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_delete);
+
+
+
+ /* add the insertion ok/cancel buttons */
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(separator);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_ok = gtk_button_new_with_label("Ok");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(startAssemble),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(window);
+}
+
+/**
+ * Callback for displaying user-selected directory
+ **/
+static gint importDirectoryCallback(GtkWidget * okButton,
+ GtkWidget * window)
+{
+ gchar * filename;
+
+ filename
+ = gtk_file_selection_get_filename(GTK_FILE_SELECTION(window));
+ if ( (filename == NULL) ||
+ (0 == assertIsFile(filename)) ) {
+ guiMessage("Please select a file!\n");
+ gtk_widget_destroy(window);
+ return FALSE;
+ }
+
+ displayDirectory(filename,
+ NULL);
+
+ gtk_widget_destroy(window);
+
+ return FALSE;
+}
+
+/**
+ * Asks user to select a .gnd directory (from disk) to be displayed
+ **/
+void importDirectory(void)
+{
+ GtkWidget * window;
+ char pattern[16];
+
+ window = gtk_file_selection_new("Choose directory to be imported");
+
+ sprintf(pattern, "*%s", GNUNET_DIRECTORY_EXT);
+ gtk_file_selection_complete(GTK_FILE_SELECTION(window),
+ pattern);
+
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(importDirectoryCallback),
+ window);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(window);
+
+}
+
+
+/* end of directory.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/directory.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/directory.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/directory.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,44 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/directory.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_DIRECTORY_H
+#define GTKUI_DIRECTORY_H
+
+
+
+/**
+ * Open a window to allow the user to build a directory.
+ *
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleDirectoryDialog(GtkWidget * unused,
+ unsigned int context);
+
+/**
+ * Asks user to select a .gnd directory (from disk) to be displayed
+ **/
+void importDirectory(void);
+
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.c
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.c
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,91 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/directorydisplay.c
+ * @brief code that displays the contents of a directory
+ * @author Christian Grothoff
+ **/
+
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "directory.h"
+#include "directorydisplay.h"
+#include "search.h"
+
+void displayDirectory(char * filename,
+ RootNode * rn) {
+ GtkWidget * box;
+ ListModel * model;
+ GNUnetDirectory * dir;
+ int i;
+
+ dir = readGNUnetDirectory(filename);
+ if (dir == NULL) {
+ LOG(LOG_WARNING,
+ "WARNING: directory downloaded (%s) has invalid format.\n",
+ filename);
+ guiMessage("WARNING: directory downloaded (%s) has invalid format.\n",
+ filename);
+ return;
+ }
+ model = (ListModel*) MALLOC(sizeof(ListModel));
+ model->type = LM_TYPE_DIRECTORY;
+ box = initializeSearchResultList(model);
+
+ /* do a nested freeze on the clist, for efficiency */
+ gtk_clist_freeze(GTK_CLIST(model->search_result_list));
+
+ for (i=0;i<ntohl(dir->number_of_files);i++) {
+ /* sneaky side-effect: add to state DB!
+ (note that if you download a directory
+ with gnunet-download, this does not happen
+ since we don't know the mime-type in
+ gnunet-download.) */
+ makeRootNodeAvailable(&((GNUnetDirectory_GENERIC*)dir)->contents[i],
+ DIR_CONTEXT_DIRECTORY);
+ model->skipMenuRefresh = (i==ntohl(dir->number_of_files)-1 ? NO : YES );
+ displayResultGTK(&((GNUnetDirectory_GENERIC*)dir)->contents[i],
+ model);
+ }
+ FREE(dir);
+
+ gtk_clist_thaw(GTK_CLIST(model->search_result_list));
+
+ if (rn != NULL ) {
+ rn->header.description[MAX_DESC_LEN-1] = '\0';
+ addToNotebook(rn->header.description,
+ box);
+ } else {
+ char * fileNameRoot = filename;
+ int i;
+
+ for (i=strlen(filename)-1;i>=0;i--) {
+ if (filename[i] == DIR_SEPARATOR) {
+ fileNameRoot = &filename[i+1];
+ break;
+ }
+ }
+ addToNotebook(fileNameRoot,
+ box);
+ }
+}
+
+/* end of directorydisplay.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.h
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/directorydisplay.h
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,36 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/directorydisplay.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_DIRECTORY_DISPLAY_H
+#define GTKUI_DIRECTORY_DISPLAY_H
+
+#include "gnunet_afs_esed2.h"
+#include <gtk/gtk.h>
+#include "directorydisplay.h"
+#include "download.h"
+
+void displayDirectory(char * filename,
+ RootNode * rn);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/download.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/download.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/download.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,887 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/download.c
+ * @brief code that handles the download window
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ * FIXME:
+ * - shutdown of gnunet-gtk does NOT terminate
+ * each of the pending downloads. There
+ * should be a handler function that is invoked
+ * whenever gnunet-gtk shuts down and that
+ * stops all pending downloads. This used to
+ * be implemented but got lost when Igor added
+ * the download window.
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "directorydisplay.h"
+#include "download.h"
+#include "main.h"
+
+GtkWidget * dlWindow = NULL;
+
+#define DEBUG_WRITE_CPSDATA NO
+
+/* Suitable values of DownloadModel.downloadStatus */
+#define DOWNLOAD_COMPLETE 0
+#define DOWNLOAD_FAILED 1
+#define DOWNLOAD_ABORTED 2
+#define DOWNLOAD_PENDING 3
+
+/* colors taken from x-chat source, regards */
+GdkColor textColors[] = {
+ {0, 0xcf3c, 0xcf3c, 0xcf3c}, /* 0 white */
+ {0, 0x0000, 0x0000, 0x0000}, /* 1 black */
+ {0, 0x0000, 0x0000, 0xcccc}, /* 2 blue */
+ {0, 0x0000, 0xcccc, 0x0000}, /* 3 green */
+ {0, 0xdddd, 0x0000, 0x0000}, /* 4 red */
+ {0, 0xaaaa, 0x0000, 0x0000}, /* 5 light red */
+ {0, 0xbbbb, 0x0000, 0xbbbb}, /* 6 purple */
+ {0, 0xffff, 0xaaaa, 0x0000}, /* 7 orange */
+ {0, 0xeeee, 0xdddd, 0x2222}, /* 8 yellow */
+ {0, 0x3333, 0xdede, 0x5555}, /* 9 green */
+ {0, 0x0000, 0xcccc, 0xcccc}, /* 10 aqua */
+ {0, 0x3333, 0xeeee, 0xffff}, /* 11 light aqua */
+ {0, 0x0000, 0x0000, 0xffff}, /* 12 blue */
+ {0, 0xeeee, 0x2222, 0xeeee}, /* 13 light purple */
+ {0, 0x7777, 0x7777, 0x7777}, /* 14 grey */
+ {0, 0x9999, 0x9999, 0x9999}, /* 15 light grey */
+ {0, 0xa4a4, 0xdfdf, 0xffff}, /* 16 marktext Back (blue) */
+ {0, 0x0000, 0x0000, 0x0000}, /* 17 marktext Fore (black) */
+ {0, 0xdf3c, 0xdf3c, 0xdf3c}, /* 18 foreground (white) */
+ {0, 0x0000, 0x0000, 0x0000}, /* 19 background (black) */
+ {0, 0x8c8c, 0x1010, 0x1010}, /* 20 tab New Data (dark red) */
+ {0, 0x0000, 0x0000, 0xffff}, /* 21 tab Nick Mentioned (blue) */
+ {0, 0xf5f5, 0x0000, 0x0000}, /* 22 tab New Message (red) */
+};
+
+static void selectAll(void);
+static void unSelectAll(void);
+static void removeFinished(void);
+static void abortHelper(void);
+static void hideHelper(void);
+static gint abortSelectedDownloads(GtkWidget * widget,
+ GtkWidget * clist);
+
+static GtkItemFactoryEntry dlWindowMenu[] = {
+ { "/Select all", NULL, selectAll, 0, "<Item>" },
+ { "/Unselect all", NULL, unSelectAll, 0, "<Item>" },
+ { "/sep1", NULL, NULL, 0, "<Separator>" },
+ { "/Remove selected", NULL, abortHelper, 0, "<Item>" },
+ { "/Remove finished", NULL, removeFinished, 0, "<Item>" },
+ { "/sep2", NULL, NULL, 0, "<Separator>" },
+ { "/Hide dl window", NULL, hideHelper, 0, "<Item>" }
+};
+
+static gint dlWindowMenuItems
+ = sizeof (dlWindowMenu) / sizeof (dlWindowMenu[0]);
+
+static void selectAll(void)
+{
+ GtkWidget * clist;
+
+ clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+ "LIST");
+ gtk_clist_select_all(GTK_CLIST(clist));
+}
+
+static void unSelectAll(void)
+{
+ GtkWidget * clist;
+
+ clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+ "LIST");
+ gtk_clist_unselect_all(GTK_CLIST(clist));
+}
+
+static void removeFinished(void)
+{
+ GtkCList * clist;
+ int i;
+ gchar * string;
+
+ unSelectAll();
+
+ clist = GTK_CLIST(gtk_object_get_data(GTK_OBJECT(dlWindow),
+ "LIST"));
+ gtk_clist_freeze(clist);
+
+ for(i=0;i<clist->rows;i++) {
+ gtk_clist_get_text(clist,
+ i,
+ 1,
+ &string);
+ if(strcmp(string, "DONE")==0)
+ gtk_clist_select_row(clist,
+ i,
+ 1);
+ else
+ gtk_clist_unselect_row(clist,
+ i,
+ 1);
+ }
+
+ gtk_clist_thaw(clist);
+
+ abortSelectedDownloads(NULL, GTK_WIDGET(clist));
+}
+
+static void hideHelper(void)
+{
+ if(dlWindow)
+ gtk_widget_hide(dlWindow);
+}
+
+static void abortHelper(void)
+{
+ GtkWidget * clist;
+
+ clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+ "LIST");
+ abortSelectedDownloads(NULL, clist);
+}
+
+/**
+ * Changes the current sort column and sorts the list.
+ **/
+static void sort_column_callback(GtkCList * clist,
+ gint column,
+ gpointer data) {
+ static int sortOrder[8]={0,0,0,0,0,0,0,0};
+
+ sortOrder[column]^=1;
+
+ if(sortOrder[column]==1)
+ gtk_clist_set_sort_type(clist,
+ GTK_SORT_ASCENDING);
+ else
+ gtk_clist_set_sort_type(clist,
+ GTK_SORT_DESCENDING);
+
+ /* Sort column 0 as string, 1 as percent and rest as numbers */
+ switch(column) {
+ case 0:
+ gtk_clist_set_compare_func(clist,
+ (GtkCListCompareFunc)alphaComp);
+ break;
+ case 1:
+ gtk_clist_set_compare_func(clist,
+ (GtkCListCompareFunc)percentComp);
+ break;
+ default:
+ gtk_clist_set_compare_func(clist,
+ (GtkCListCompareFunc)numericComp);
+ break;
+ }
+ gtk_clist_set_sort_column(clist, column);
+ gtk_clist_freeze(clist);
+ gtk_clist_sort(clist);
+ gtk_clist_thaw(clist);
+}
+
+/**
+ * "delete_event" handler for a download. Sets the
+ * download state as aborted, and releases the wait semaphore for
+ * the thread awaiting download completion. Returns TRUE so that
+ * gtk doesn't destroy the widget (its never destroyed).
+ *
+ * @param widget not used
+ * @param clist the list of downloads
+ **/
+static gint abortSelectedDownloads(GtkWidget * widget,
+ GtkWidget * clist) {
+ DownloadModel * dlm;
+ int row;
+ GList * tmp;
+
+ LOG(LOG_DEBUG,
+ "DEBUG: abortSelectedDownloads %x (%x)\n",
+ clist, widget);
+ if(!GTK_CLIST(clist))
+ return TRUE;
+
+ gtk_clist_freeze(GTK_CLIST(clist));
+
+ tmp = GTK_CLIST(clist)->selection;
+ while (tmp) {
+ row = (int)tmp->data;
+ tmp = tmp->next;
+
+ /* if dlm is not NULL, abort the download */
+ dlm = gtk_clist_get_row_data(GTK_CLIST(clist),
+ row);
+ if(dlm) {
+ dlm->downloadStatus = DOWNLOAD_ABORTED;
+ SEMAPHORE_UP(dlm->doneSem);
+ }
+
+ /* remove list entry */
+ gtk_clist_remove(GTK_CLIST(clist),
+ row);
+ }
+
+ gtk_clist_thaw(GTK_CLIST(clist));
+
+ return TRUE;
+}
+
+static gint displayStats(SaveCall *call) {
+ DLStats *dlStats = (DLStats *) call->args;
+ DownloadModel *dlm = dlStats->dlm;
+ gint row;
+
+ gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+ row = gtk_clist_find_row_from_data(GTK_CLIST(dlm->dlList),
+ dlStats->dlm);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 1,
+ dlStats->perc);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 2,
+ dlStats->pos);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 4,
+ dlStats->areq);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 5,
+ dlStats->cra);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 6,
+ dlStats->tr);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 7,
+ dlStats->kbs);
+ if(dlm->successfulStart == NO && dlStats->stats->progress > 0) {
+ gtk_clist_set_foreground(GTK_CLIST(dlm->dlList),
+ row,
+ &textColors[1]);
+ dlm->successfulStart = YES;
+ }
+ gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+
+ if (dlStats->stats->filesize == dlStats->stats->progress) {
+ /* reset the request counters (just cosmetic) */
+ snprintf(dlStats->areq, sizeof(dlStats->areq), "0");
+ snprintf(dlStats->cra, sizeof(dlStats->cra), "0.0");
+
+ gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 4,
+ dlStats->areq);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 5,
+ dlStats->cra);
+ gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+ refreshMenuSensitivity();
+
+ if (dlStats->stats->filesize == 0)
+ dlm->downloadStatus = DOWNLOAD_FAILED;
+ else
+ dlm->downloadStatus = DOWNLOAD_COMPLETE;
+ SEMAPHORE_UP(dlm->doneSem); /* signal: we're done with download */
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+ }
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * This method is called by the download code to notify the user
+ * interface of the download progress.
+ *
+ * @param stats the new statistical values
+ * @param dlm the accessor to the GTK window
+ **/
+static void modelCallback(ProgressStats * stats,
+ DownloadModel * dlm) {
+ double currentRetryAvg, averageBps, percentage;
+ DLStats dlStats;
+ TIME_T now;
+#if DEBUG_WRITE_CPSDATA
+ char scratch[128];
+ FILE * fp;
+#endif
+
+ if (dlm->downloadStatus != DOWNLOAD_PENDING)
+ return;
+
+ /* don't display more often than once/sec */
+ TIME(&now);
+ if ((now-(dlm->lastDisplayTime)) < 1 &&
+ (stats->filesize != stats->progress))
+ return;
+ else
+ dlm->lastDisplayTime = now;
+
+ if(stats->requestsSent > 0)
+ currentRetryAvg =
+ (double)stats->currentRetries /
+ (double)stats->requestsSent;
+ else
+ currentRetryAvg = 0;
+
+ if (now - dlm->downloadStartTime > 0)
+ averageBps =
+ (double)(stats->progress) /
+ ((double)(now - dlm->downloadStartTime));
+ else
+ averageBps = 0;
+
+#if DEBUG_WRITE_CPSDATA
+ sprintf(scratch, "/tmp/cps-%x.txt", dlm->root.header.fileIdentifier.crc);
+ fp = FOPEN(scratch, "a+");
+ fprintf(fp, "%d %d %d %f\n", (int)(now-(dlm->downloadStartTime)),
+ stats->progress,
+ stats->totalRetries,
+ averageBps);
+ fclose(fp);
+#endif
+
+ if(stats->filesize>0)
+ percentage = 100.0*((double)stats->progress/(double)stats->filesize);
+ else
+ percentage = 0;
+
+ snprintf(dlStats.pos, sizeof(dlStats.pos), "%d", stats->progress);
+ snprintf(dlStats.kbs, sizeof(dlStats.kbs), "%.1f", averageBps);
+ snprintf(dlStats.perc, sizeof(dlStats.perc), "%3.1f%%", percentage);
+ snprintf(dlStats.areq, sizeof(dlStats.areq), "%d", stats->requestsSent);
+ snprintf(dlStats.cra, sizeof(dlStats.cra), "%3.1f", currentRetryAvg);
+ snprintf(dlStats.tr, sizeof(dlStats.tr), "%d", stats->totalRetries);
+ dlStats.stats = stats;
+ dlStats.dlm = dlm;
+
+ gtkSaveCall((GtkFunction) displayStats, &dlStats);
+}
+
+gint setDownloadEntry(SaveCall *call) {
+ gint row;
+ DownloadModel *dlm = ((SetDownloadEntry *) call->args)->dlm;
+
+ gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+ row = gtk_clist_find_row_from_data(GTK_CLIST(dlm->dlList),
+ dlm);
+ gtk_clist_set_foreground(GTK_CLIST(dlm->dlList),
+ row,
+ ((SetDownloadEntry *) call->args)->color);
+ gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+ row,
+ 1,
+ ((SetDownloadEntry *) call->args)->text);
+ gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+gint disentangleFromCLIST(SaveCall *call) {
+ gint row;
+ DownloadModel *dlm = (DownloadModel *) call->args;
+
+ gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+ row = gtk_clist_find_row_from_data(GTK_CLIST(dlm->dlList),
+ dlm);
+ gtk_clist_set_row_data(GTK_CLIST(dlm->dlList),
+ row,
+ NULL);
+ gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * Main function of the download thread. This function terminates
+ * automagically once the download is complete or if the download
+ * entry is removed while dl. It is responsible for stopping the
+ * requestmanager.
+ *
+ * @param dlm the download model
+ **/
+static void downloadFile_(DownloadModel * dlm) {
+ char * mime;
+ SetDownloadEntry entry;
+
+ LOG(LOG_DEBUG,
+ "DEBUG: Entering downloadFile_ for %s (%x)\n",
+ dlm->fileName,
+ dlm);
+
+ /* initiate download */
+ TIME(&dlm->downloadStartTime);
+
+ /* this starts the "real" download thread in the background,
+ with "modelCallback" called back to tell us about the progress */
+ dlm->rm = downloadFile(&dlm->root.header.fileIdentifier,
+ dlm->fileName,
+ (ProgressModel)&modelCallback,
+ dlm);
+ if (dlm->rm == NULL) {
+ guiMessage("ERROR: could not download %s.\nConsult logs.\n",
+ dlm->fileName);
+ SEMAPHORE_FREE(dlm->doneSem);
+ FREE(dlm->fileName);
+ FREE(dlm);
+ return;
+ }
+ /* Wait here until download is complete or
+ the window is closed or gnunet-gtk is terminated */
+ LOG(LOG_DEBUG,
+ "DEBUG: waiting for DL completion (%x)\n",
+ dlm);
+ SEMAPHORE_DOWN(dlm->doneSem);
+ LOG(LOG_DEBUG,
+ "DEBUG: download complete (%d) enter destroyRequestManager (%x)\n",
+ dlm->downloadStatus,
+ dlm);
+
+ /* stop the RequestManager */
+ if (dlm->rm != NULL) {
+ destroyRequestManager(dlm->rm);
+ } else {
+ /* this can happen if the requestmanager initialization
+ * failed for reason or another (e.g. write permission denied) */
+ LOG(LOG_WARNING,
+ "WARNING: dlm->rm was NULL\n");
+ }
+
+ /*
+ ok, now why are we here? 4 possibilities:
+ a) download aborted (user closed window)
+ b) gnunet-gtk terminated (same as download aborted)
+ c) download failed (gnunetd exit, out-of-space)
+ d) download completed
+
+ In case "d" we show the "YAY" window
+ and wait for another signal.
+ In case "c" we show the "BAH" window
+ and wait for another signal.
+ */
+
+ switch (dlm->downloadStatus) {
+ case DOWNLOAD_COMPLETE:
+ /* color successful dl green */
+ entry.dlm = dlm;
+ entry.color = &textColors[3];
+ entry.text = "DONE";
+ gtkSaveCall((GtkFunction) setDownloadEntry, &entry);
+
+ switch (ntohs(dlm->root.header.major_formatVersion)) {
+ case ROOT_MAJOR_VERSION:
+ mime = dlm->root.header.mimetype;
+ break;
+ case SBLOCK_MAJOR_VERSION:
+ mime = ((SBlock*)&dlm->root)->mimetype;
+ break;
+ default:
+ mime = "unknown";
+ break;
+ }
+ if (0 == strcmp(mime,
+ GNUNET_DIRECTORY_MIME)) {
+ displayDirectory(dlm->fileName,
+ &dlm->root);
+ }
+ SEMAPHORE_DOWN(dlm->doneSem); /* wait for window closing */
+ break;
+ case DOWNLOAD_FAILED:
+ /* color failed dl red */
+ entry.dlm = dlm;
+ entry.color = &textColors[4];
+ entry.text = "FAIL";
+ gtkSaveCall((GtkFunction) setDownloadEntry, &entry);
+
+ SEMAPHORE_DOWN(dlm->doneSem); /* wait for window closing */
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* finally, disentangle from the clist and free dlm resources */
+ gtkSaveCall((GtkFunction) disentangleFromCLIST, dlm);
+ SEMAPHORE_FREE(dlm->doneSem);
+ FREE(dlm->fileName);
+ FREE(dlm);
+}
+
+/**
+ * Open the download window and start the download of a file in the
+ * background. The method executes during a signal handler, so a GTK
+ * lock is not required to to GUI operations.
+ *
+ * @param filename the name of the file to download
+ * (must copy, will be freed by caller)
+ * @param root information about what to download
+ * (will be freed by caller, must copy!)
+ **/
+void startDownload(gchar * filename,
+ RootNode * root) {
+ GtkWidget * clist;
+ char * fileNameRoot;
+ int i;
+ gint row;
+ DownloadModel * dlm;
+ gchar * fileInfo[8];
+ char * fstring;
+
+ dlm = MALLOC(sizeof(DownloadModel));
+ memset(dlm,
+ 0,
+ sizeof(DownloadModel));
+ dlm->fileName = STRDUP(filename);
+ memcpy(&dlm->root,
+ root,
+ sizeof(RootNode));
+
+ fileNameRoot = dlm->fileName;
+ for (i=strlen(dlm->fileName)-1;i>=0;i--) {
+ if (dlm->fileName[i] == DIR_SEPARATOR) {
+ fileNameRoot = &dlm->fileName[i+1];
+ break;
+ }
+ }
+
+ /* create new download window? */
+ if (!dlWindow) {
+ GtkWidget * scrolled_window;
+ GtkWidget * button;
+ GtkWidget * box;
+ GtkWidget * entry;
+ GtkWidget * menu;
+ GtkItemFactory *popupFactory;
+ static gchar * descriptions[] = {
+ "Filename",
+ "%", /* Completion percentage */
+ "Position", /* Bytes dl'ed so far */
+ "Size",
+ "AReq", /* Active block requests */
+ "CR/A", /* Current Retries per Active requests */
+ "totR", /* Total Retries */
+ "BPS", /* Current bytes per second -estimate */
+ };
+ static int widths[] = {
+ 290, 50, 70, 70, 40, 30, 50, 50
+ };
+
+ dlWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(dlWindow),
+ "gnunet-gtk: Downloads");
+ gtk_widget_set_usize(GTK_WIDGET(dlWindow),
+ 780, /* x-size */
+ 300); /* y-size */
+ gtk_signal_connect_object(GTK_OBJECT(dlWindow), "delete_event",
+ GTK_SIGNAL_FUNC(hideWindow),
+ GTK_OBJECT(dlWindow));
+ gtk_signal_connect_object(GTK_OBJECT(dlWindow), "destroy",
+ GTK_SIGNAL_FUNC(hideWindow),
+ GTK_OBJECT(dlWindow));
+
+
+ box = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(dlWindow),
+ box);
+ gtk_container_set_border_width(GTK_CONTAINER(dlWindow),
+ 8);
+
+ /* scrolled window for the dl list */
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(box),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ /* create a list to hold the downloads in */
+ clist = gtk_clist_new_with_titles(8, descriptions);
+ gtk_clist_set_selection_mode
+ (GTK_CLIST(clist),
+ GTK_SELECTION_EXTENDED);
+ for (i=0;i<8;i++) {
+ gtk_clist_set_column_width(GTK_CLIST(clist),
+ i,
+ widths[i]);
+ gtk_clist_column_title_active(GTK_CLIST(clist),
+ i);
+ }
+ gtk_signal_connect(GTK_OBJECT(clist),
+ "click-column",
+ GTK_SIGNAL_FUNC(sort_column_callback),
+ NULL);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_object_set_data(GTK_OBJECT(dlWindow),
+ "LIST",
+ clist);
+ gtk_widget_show(box);
+ gtk_widget_show(scrolled_window);
+ /* cancel/remove button */
+ button = gtk_button_new_with_label("Remove selected entries");
+ gtk_signal_connect (GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(abortSelectedDownloads),
+ clist);
+ gtk_box_pack_start(GTK_BOX(box),
+ button,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(button);
+
+ /* activate the pulldown menu option now */
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/File/Show downloads");
+ gtk_widget_set_sensitive(entry, TRUE);
+
+ /* add popup menu */
+ popupFactory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
+ NULL);
+ gtk_item_factory_create_items(popupFactory,
+ dlWindowMenuItems,
+ dlWindowMenu,
+ NULL);
+ menu = gtk_item_factory_get_widget (popupFactory, "<main>");
+ gtk_signal_connect(GTK_OBJECT(dlWindow),
+ "event",
+ GTK_SIGNAL_FUNC(popupCallback),
+ menu);
+ } else {
+ /* use existing dlWindow and list */
+ clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+ "LIST");
+ }
+
+ /* set default disp. values for new download */
+ fileInfo[0]=STRDUP(fileNameRoot);
+ fileInfo[1]="0%";
+ fileInfo[2]="-";
+ fileInfo[3]=MALLOC(32);
+ fileInfo[4]="-";
+ fileInfo[5]="-";
+ fileInfo[6]="-";
+ fileInfo[7]="-";
+ snprintf(fileInfo[3],
+ 32,
+ "%u",
+ (unsigned int) ntohl(dlm->root.header.fileIdentifier.file_length));
+
+ TIME(&dlm->lastDisplayTime);
+
+ gtk_clist_freeze(GTK_CLIST(clist));
+ row = gtk_clist_append(GTK_CLIST(clist),
+ fileInfo);
+ FREE(fileInfo[0]);
+ FREE(fileInfo[3]);
+ gtk_clist_set_foreground(GTK_CLIST(clist),
+ row,
+ &textColors[11]);
+ gtk_clist_set_row_data(GTK_CLIST(clist),
+ row,
+ dlm);
+ gtk_clist_thaw(GTK_CLIST(clist));
+
+ /* remember clist for updates in the DLM struct */
+ dlm->dlList = clist;
+ dlm->downloadStatus = DOWNLOAD_PENDING;
+ dlm->doneSem = SEMAPHORE_NEW(0);
+
+ /* display download window */
+ gtk_widget_show(clist);
+ gtk_widget_show(dlWindow);
+
+ /* append an info note */
+ fstring = fileIdentifierToString(&dlm->root.header.fileIdentifier);
+ infoMessage(NO, "gnunet-download -o \"%s\" %s\n",
+ dlm->fileName,
+ fstring);
+ FREE(fstring);
+
+ /* create thread that runs the download */
+ if (0 != PTHREAD_CREATE(&dlm->downloadThread,
+ (PThreadMain) &downloadFile_,
+ dlm,
+ 64 * 1024))
+ errexit("FATAL: could not create download thread (%s)!\n",
+ STRERROR(errno));
+ PTHREAD_DETACH(&dlm->downloadThread);
+}
+
+/**
+ * Starts a file download when user has filled in the fields
+ **/
+void fetchURICallback(GtkWidget * widget,
+ gpointer data) {
+ gchar * uri;
+ FileIdentifier * fid;
+ GtkWidget * entry;
+ RootNode root;
+
+ entry = gtk_object_get_data(GTK_OBJECT(data),
+ "entry");
+
+ uri = gtk_entry_get_text(GTK_ENTRY(entry));
+ if(!uri)
+ return;
+
+ fid = stringToFileIdentifier(uri);
+ if(!fid) {
+ guiMessage("Invalid gnunet AFS URI");
+ gtk_widget_destroy(data);
+ return;
+ }
+
+ root.header.major_formatVersion = ROOT_MAJOR_VERSION;
+ root.header.minor_formatVersion = ROOT_MINOR_VERSION;
+ memcpy(&root.header.fileIdentifier,
+ fid,
+ sizeof(FileIdentifier));
+ strcpy(root.header.mimetype,
+ "unknown");
+
+ /* FIXME: Future URLs should contain suggested filename
+ * (or the code should ask one from user) */
+ sprintf(root.header.filename, "unknown.%ld", (unsigned long)time(NULL));
+
+ startDownload(root.header.filename,
+ &root);
+
+ gtk_widget_destroy(data);
+}
+
+void fetchURI(GtkWidget * widget,
+ gpointer data) {
+ GtkWidget * window;
+ GtkWidget * vbox;
+ GtkWidget * label;
+ GtkWidget * entry;
+ GtkWidget * hbox;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 780,
+ 100);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Download URI");
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 15);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new("GNUnet AFS URI: ");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ entry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ entry,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(entry),
+ "activate",
+ GTK_SIGNAL_FUNC(fetchURICallback),
+ window);
+ gtk_object_set_data(GTK_OBJECT(window),
+ "entry",
+ entry);
+ gtk_widget_show(entry);
+
+ button_ok = gtk_button_new_with_label("Ok");
+ button_cancel = gtk_button_new_with_label("Cancel");
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(fetchURICallback),
+ window);
+ gtk_widget_show(hbox);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+ gtk_widget_show(window);
+
+
+
+}
+
+
+/* end of download.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/download.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/download.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/download.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,74 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/download.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_DOWNLOAD_H
+#define GTKUI_DOWNLOAD_H
+
+/**
+ * @brief state associated with a GTK download window
+ **/
+typedef struct {
+ RootNode root;
+ char * fileName;
+ PTHREAD_T downloadThread;
+ RequestManager * rm;
+ Semaphore * doneSem;
+ TIME_T downloadStartTime;
+ int successfulStart;
+ int downloadStatus;
+ TIME_T lastDisplayTime;
+ GtkWidget * dlList;
+} DownloadModel;
+
+typedef struct {
+ char pos[16], kbs[14], perc[14], areq[14], cra[14], tr[14];
+ DownloadModel *dlm;
+ ProgressStats *stats;
+} DLStats;
+
+typedef struct {
+ DownloadModel *dlm;
+ GdkColor *color;
+ gchar *text;
+} SetDownloadEntry;
+
+/**
+ * Open the download window and start the download of a file in the
+ * background. The method executes during a signal handler, so a GTK
+ * lock is not required to to GUI operations.
+ *
+ * @param filename the name of the file to download
+ * (must copy, will be freed by caller)
+ * @param dlm some information about what to download
+ * (will be freed by caller, must copy!)
+ **/
+void startDownload(gchar * filename,
+ RootNode * root);
+
+void fetchURI(GtkWidget * widget,
+ gpointer data);
+
+extern GtkWidget * dlWindow;
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/helper.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/helper.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/helper.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,1009 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file src/applications/afs/gtkui/helper.c
+ * @brief This file contains some GUI helper functions
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include <stdlib.h>
+#ifndef MINGW
+ #include <sys/wait.h>
+#endif
+#include "main.h"
+
+#define HELPER_DEBUG NO
+
+GtkWidget * infoWindow = NULL;
+static GtkWidget * infoText = NULL;
+
+/* are we waiting for gnunetd to start? */
+static int pollForLaunch = FALSE;
+
+/* PID of the main thread */
+static pthread_t mainThread;
+
+
+static SaveCall ** psc;
+static unsigned int pscCount;
+static Mutex sclock;
+
+/**
+ * Call a callback function from the mainloop/main thread ("SaveCall").
+ * Since GTK doesn't work with multi-threaded applications under Windows,
+ * all GTK operations have to be done in the main thread
+ **/
+void gtkSaveCall(GtkFunction func, void *args) {
+ SaveCall call;
+ int i;
+
+ call.args = args;
+ call.func = func;
+ MUTEX_LOCK(&sclock);
+ if ( (mainThread != 0) &&
+ (mainThread != pthread_self()) ) {
+ call.sem = SEMAPHORE_NEW(0);
+ GROW(psc,
+ pscCount,
+ pscCount+1);
+ psc[pscCount-1] = &call;
+ MUTEX_UNLOCK(&sclock);
+ gtk_idle_add(func, &call);
+ SEMAPHORE_DOWN(call.sem);
+ /* remove from psc list */
+ MUTEX_LOCK(&sclock);
+ for (i=0;i<pscCount;i++)
+ if (psc[i] == &call) {
+ psc[i] = psc[pscCount-1];
+ break;
+ }
+ if (i == pscCount)
+ errexit("ASSERTION failed at %s:%d\n",
+ __FILE__, __LINE__);
+ GROW(psc,
+ pscCount,
+ pscCount-1);
+ MUTEX_UNLOCK(&sclock);
+ SEMAPHORE_FREE(call.sem);
+ } else {
+ MUTEX_UNLOCK(&sclock);
+ call.sem = NULL;
+ func(&call);
+ }
+}
+
+/**
+ * Initialize "SaveCalls"
+ **/
+void gtkInitSaveCalls() {
+ MUTEX_CREATE_RECURSIVE(&sclock);
+ mainThread = pthread_self();
+}
+
+void gtkRunSomeSaveCalls() {
+ int i;
+
+ if (mainThread != pthread_self())
+ return;
+ MUTEX_LOCK(&sclock);
+ if (pscCount == 0) {
+ MUTEX_UNLOCK(&sclock);
+ return;
+ }
+ i = randomi(pscCount);
+ if (TRUE == g_idle_remove_by_data(psc[i]))
+ psc[i]->func(psc[i]);
+ MUTEX_UNLOCK(&sclock);
+ gnunet_util_sleep(50 * cronMILLIS);
+ /* sleep here is somewhat important, first of
+ all, after completion we need to give the
+ semaphore-mechanism time to remove the save-call
+ from the list to avoid running it twice;
+ also, this function might be called in a tight
+ loop (see search.c), so we should give the
+ other threads some time to run. */
+
+}
+
+void gtkDoneSaveCalls() {
+ int i;
+ mainThread = 0;
+ MUTEX_LOCK(&sclock);
+ for (i=0;i<pscCount;i++)
+ psc[i]->func(psc[i]);
+ i = pscCount;
+ MUTEX_UNLOCK(&sclock);
+ /* wait until all PSC-jobs have left
+ the gtkSaveCall method before destroying
+ the mutex! */
+ while (i != 0) {
+ gnunet_util_sleep(50 * cronMILLIS);
+ MUTEX_LOCK(&sclock);
+ i = pscCount;
+ MUTEX_UNLOCK(&sclock);
+ }
+ MUTEX_DESTROY(&sclock);
+}
+
+
+/**
+ * Called from a "SaveCall"-function to indicate that it is done
+ **/
+void gtkSaveCallDone(Semaphore *sem) {
+ if (sem)
+ SEMAPHORE_UP(sem);
+}
+
+/**
+ * Destroy a widget. Called from threads other than the main thread
+ **/
+gint doDestroyWidget(SaveCall *call) {
+ gtk_widget_destroy((GtkWidget *) call->args);
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * Callback for handling "delete_event": close the window
+ **/
+gint deleteEvent(GtkWidget * widget,
+ GdkEvent * event,
+ gpointer data) {
+#if DEBUG_HELPER
+ LOG(LOG_DEBUG,
+ "DEBUG: deleteEvent\n");
+#endif
+ return FALSE;
+}
+
+/**
+ * A callback to destroy any widget given as second argument
+ **/
+void destroyWidget(GtkWidget * dummy,
+ GtkWidget * widget) {
+#if DEBUG_HELPER
+ LOG(LOG_DEBUG,
+ "DEBUG: destroyWidget %d\n",
+ widget);
+#endif
+ gtk_widget_destroy(widget);
+}
+
+/**
+ * Callback function for guiMessage()
+ **/
+gint doGuiMessage(SaveCall *call) {
+ GtkWidget * window;
+ GtkWidget * label;
+ GtkWidget * box;
+ GtkWidget * button;
+
+ window = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Note");
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ NULL);
+
+ box = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ box);
+
+ label = gtk_label_new((gchar *) call->args);
+ free((gchar *) call->args); /* allocated in g_strdup_vprintf */
+ gtk_box_pack_start(GTK_BOX(box),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+
+ button = gtk_button_new_with_label("Ok");
+ gtk_signal_connect(GTK_OBJECT (button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,0);
+
+ gtk_window_set_position(GTK_WINDOW(window),
+ GTK_WIN_POS_MOUSE);
+ gtk_widget_show_all(window);
+ gtk_widget_grab_focus(button);
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * Displays an informative message to the user in a fresh window
+ **/
+void guiMessage(const char * format, ...) {
+ va_list args;
+ gchar *note;
+
+ va_start(args, format);
+ note = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ gtkSaveCall((GtkFunction) doGuiMessage, note);
+}
+
+/**
+ * Callback for infoMessage()
+ **/
+gint doInfoMessage(SaveCall *call) {
+ if(!infoWindow) {
+ GtkWidget * box1;
+ GtkWidget * button;
+ GtkWidget * scrolled_window;
+
+ infoWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect(GTK_OBJECT(infoWindow),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ NULL);
+
+ gtk_window_set_title(GTK_WINDOW(infoWindow),
+ "Information");
+ gtk_widget_set_usize(GTK_WIDGET(infoWindow),
+ 780,
+ 300);
+
+ box1 = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER (infoWindow),
+ box1);
+ gtk_widget_show(box1);
+
+ /* create a scrollable window */
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(box1),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+
+ /* create a text widget */
+ infoText = gtk_text_new(NULL, NULL);
+ gtk_text_set_editable(GTK_TEXT (infoText),
+ FALSE);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ infoText);
+ gtk_widget_show(infoText);
+ gtk_widget_realize(infoText);
+
+ /* finish with a close button */
+ button = gtk_button_new_with_label("Close");
+ gtk_box_pack_start(GTK_BOX (box1),
+ button,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_signal_connect_object(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(hideWindow),
+ GTK_OBJECT(infoWindow));
+ gtk_signal_connect_object(GTK_OBJECT(infoWindow), "delete_event",
+ GTK_SIGNAL_FUNC(hideWindow),
+ GTK_OBJECT(infoWindow));
+ gtk_signal_connect_object(GTK_OBJECT(infoWindow), "destroy",
+ GTK_SIGNAL_FUNC(hideWindow),
+ GTK_OBJECT(infoWindow));
+ gtk_widget_show(button);
+ }
+ if(((InfoMessage *) call->args)->doPopup==YES)
+ gtk_widget_show(infoWindow);
+
+ /* append the text */
+ gtk_text_freeze(GTK_TEXT(infoText));
+ gtk_text_insert(GTK_TEXT(infoText),
+ NULL,
+ &infoText->style->black,
+ NULL,
+ ((InfoMessage *) call->args)->note, -1);
+ gtk_text_thaw(GTK_TEXT(infoText));
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * Appends a message to the info window
+ *
+ * @param doPopup do we open the window, YES or NO
+ *
+ **/
+void infoMessage(int doPopup, const char * format, ...) {
+ va_list args;
+ InfoMessage info;
+
+ va_start(args, format);
+ info.note = g_strdup_vprintf(format, args);
+ va_end(args);
+ info.doPopup = doPopup;
+ gtkSaveCall((GtkFunction) doInfoMessage, &info);
+ g_free(info.note);
+}
+
+/**
+ * Appends a log entry to the info window
+ *
+ * @param txt the log entry
+ *
+ **/
+void addLogEntry(const char *txt) {
+ infoMessage(NO, txt);
+}
+
+GtkNotebook * notebook = NULL;
+
+gint doAddToNotebook(SaveCall *call) {
+ GtkWidget * label = gtk_label_new(((AddNotebook *) call->args)->labelName);
+ gtk_notebook_append_page(notebook, ((AddNotebook *) call->args)->frame,
label);
+ gtk_widget_show(((AddNotebook *) call->args)->frame);
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+void addToNotebook(char * labelName,
+ GtkWidget * frame) {
+ AddNotebook note;
+
+ note.labelName = labelName;
+ note.frame = frame;
+ /* add a new notebook for the search results */
+ gtkSaveCall((GtkFunction) doAddToNotebook, ¬e);
+}
+
+void hideWindow(GtkWidget * widget,
+ gpointer data) {
+ if(widget)
+ gtk_widget_hide(widget);
+}
+
+/**
+ * A dirty way to pump some stats from gnunetd
+ **/
+void showStats(GtkWidget * widget,
+ gpointer data) {
+ FILE *fp;
+ char * ptr;
+ char buffer[512];
+ char fn[512];
+ char path[260];
+ char * cfgFile;
+ GtkWidget * window;
+ GtkWidget * scrolled_window;
+ GtkWidget * clist;
+ GtkWidget * vbox;
+ GtkWidget * button;
+ gchar * results[2];
+ static gchar * descriptions[] = {
+ "Statistic",
+ "Value"
+ };
+ static int widths[] = {
+ 600, 70
+ };
+ int i;
+
+ /* create window etc */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "GNUnet: gnunetd statistics");
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 780,
+ 300);
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+
+ scrolled_window = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+
+ clist = gtk_clist_new_with_titles(2, descriptions);
+ for(i=0;i<2;i++)
+ gtk_clist_set_column_width(GTK_CLIST(clist),
+ i,
+ widths[i]);
+ gtk_clist_set_column_justification(GTK_CLIST(clist),
+ 1,
+ GTK_JUSTIFY_RIGHT);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+
+ button = gtk_button_new_with_label("Close");
+ gtk_signal_connect(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ button,
+ FALSE,
+ FALSE,
+ 0);
+
+ gtk_clist_freeze(GTK_CLIST(clist));
+
+ /* Poll for statistics */
+ /* FIXME: done with a kludge until someone bothers to
+ * use gnunet-stats code */
+
+ sprintf(fn, "gnunet-gtk_stats.XXXXXX");
+ mkstemp(fn);
+
+ cfgFile = getConfigurationString("FILES",
+ "gnunet.conf");
+ if(cfgFile == NULL) {
+ LOG(LOG_WARNING,
+ "WARNING: cfgFile was NULL (shouldn't happen ?!)\n");
+ cfgFile = STRDUP(DEFAULT_CLIENT_CONFIG_FILE);
+ }
+
+#ifdef MINGW
+ conv_to_win_path("/bin", path);
+ strcat(path, "\\");
+#else
+ strcpy(path, "");
+#endif
+
+ sprintf(buffer,
+ "%sgnunet-stats -c \"%s\" >%s",
+ path,
+ cfgFile,
+ fn);
+ system(buffer);
+ FREE(cfgFile);
+ fp=FOPEN(fn, "r");
+ if(!fp) {
+ gtk_widget_destroy(window);
+ guiMessage("Error reading gnunet-stats output from %s\n",
+ fn);
+ LOG(LOG_ERROR, "Unable to open %s for reading\n",
+ fn);
+ return;
+ }
+ while(!feof(fp)) {
+ fgets(buffer, 512, fp);
+ if(buffer[0]==0 || buffer[0]=='\n' || feof(fp))
+ continue;
+ ptr = buffer;
+ while(*ptr!=':') ptr++;
+ *ptr=0;
+ ptr++;
+ while(*ptr==' ') ptr++;
+ results[0]=buffer;
+ results[1]=ptr;
+
+ /* remove CR/LF */
+ while(*ptr != '\n') ptr++;
+ *ptr = 0;
+ if(*(ptr-1) == '\r') *(ptr-1) = 0;
+
+ gtk_clist_append(GTK_CLIST(clist),
+ results);
+ }
+ fclose(fp);
+ UNLINK(fn);
+
+ gtk_clist_thaw(GTK_CLIST(clist));
+
+ gtk_widget_show_all(window);
+}
+
+/**
+ * Checks if gnunetd is running
+ *
+ * NOTE: Uses CS_PROTO_CLIENT_COUNT query to determine if
+ * gnunetd is running
+ **/
+static int checkDaemonRunning(void) {
+ GNUNET_TCP_SOCKET * sock;
+ CS_HEADER csHdr;
+ int ret;
+
+ sock = getClientSocket();
+ if(sock == NULL) {
+ LOG(LOG_WARNING,
+ "WARNING: socket create failed, shouldn't happen.\n");
+ return SYSERR;
+ }
+
+ csHdr.size
+ = htons(sizeof(CS_HEADER));
+ csHdr.tcpType
+ = htons(CS_PROTO_CLIENT_COUNT);
+ if (SYSERR == writeToSocket(sock,
+ &csHdr)) {
+ LOG(LOG_DEBUG, "DEBUG: gnunetd is NOT running\n");
+ releaseClientSocket(sock);
+ return SYSERR;
+ }
+ if (SYSERR == readTCPResult(sock,
+ &ret)) {
+ LOG(LOG_DEBUG, "DEBUG: failed to read reply from gnunetd\n");
+ releaseClientSocket(sock);
+ return SYSERR;
+ }
+ releaseClientSocket(sock);
+
+ return OK;
+}
+
+#if LINUX || OSX || SOLARIS || SOMEBSD
+static int launchWithExec() {
+ pid_t pid;
+
+ pid = fork();
+ if (pid == 0) {
+ char * args[4];
+ char * path;
+ char * cp;
+
+ cp = getConfigurationString("MAIN", "ARGV[0]");
+ if (cp != NULL) {
+ int i = strlen(cp);
+ while ( (i >= 0) &&
+ (cp[i] != DIR_SEPARATOR) )
+ i--;
+ cp[i+1] = '\0';
+ path = MALLOC(i+1+strlen("gnunetd"));
+ strcpy(path, cp);
+ strcat(path, "gnunetd");
+ args[0] = path;
+ FREE(cp);
+ } else {
+ path = NULL;
+ args[0] = "gnunetd";
+ }
+ args[1] = "-c";
+ args[2] = getConfigurationString("FILES",
+ "gnunet.conf");
+ args[3] = NULL;
+ errno = 0;
+ nice(10); /* return value is not well-defined */
+ if (errno != 0) {
+ LOG(LOG_WARNING,
+ "WARNING: could not nice gnunetd (%s)\n",
+ STRERROR(errno));
+ }
+ if (path != NULL)
+ execv(path,
+ args);
+ else
+ execvp("gnunetd",
+ args);
+ LOG(LOG_FAILURE,
+ "FAILURE: could not exec gnunetd: %s\n",
+ STRERROR(errno));
+ if (path != NULL)
+ LOG(LOG_FAILURE,
+ "FAILURE: determined path to be %s\n",
+ path);
+ FREENONNULL(path); /* yeah, right, like we're likely to get
+ here... */
+ exit(-1);
+ } else {
+ pid_t ret;
+ int status;
+
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1) {
+ LOG(LOG_FAILURE,
+ "FAILURE: waitpid failed: %s\n",
+ STRERROR(errno));
+ return SYSERR;
+ }
+ if ( (WIFEXITED(status) &&
+ (0 != WEXITSTATUS(status)) ) ) {
+ guiMessage("Starting gnunetd failed, error code: %d",
+ WEXITSTATUS(status));
+ return SYSERR;
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ guiMessage("Starting gnunetd failed (core dumped)");
+ return SYSERR;
+ }
+#endif
+ if (WIFSIGNALED(status) ||
+ WTERMSIG(status) ) {
+ guiMessage("Starting gnunetd failed (aborted by signal)");
+ return SYSERR;
+ }
+ return OK;
+ }
+}
+#endif
+
+static int doLaunch() {
+
+#if LINUX || OSX || SOLARIS || SOMEBSD
+ return launchWithExec();
+#elif MINGW
+ char szCall[_MAX_PATH + 1], szWd[_MAX_PATH + 1], szCWd[_MAX_PATH + 1];
+ char *args[1];
+
+ conv_to_win_path("/bin/gnunetd.exe", szCall);
+ conv_to_win_path("/bin", szWd);
+ _getcwd(szCWd, _MAX_PATH);
+
+ chdir(szWd);
+ args[0] = NULL;
+ spawnvp(_P_NOWAIT, szCall, (const char *const *) args);
+ chdir(szCWd);
+
+ return OK;
+#else
+ /* any system out there that does not support THIS!? */
+ system("gnunetd"); /* we may not have nice,
+ so let's be minimalistic here. */
+ return OK;
+#endif
+}
+
+/**
+ * Launch gnunetd, don't check if its running
+ **/
+static void launchDaemonNoCheck(GtkWidget * widget,
+ gpointer data) {
+ /* sanity checks, not critical for ports */
+ char * host = getConfigurationString("NETWORK",
+ "HOST");
+ if (host != NULL) {
+ if (0 != strcmp(host,
+ "localhost")) {
+ char * hostname;
+ hostname = MALLOC(1024);
+ if (0 != gethostname(hostname, 1024)) {
+ LOG(LOG_ERROR,
+ "ERROR: failed to get hostname (%s)\n",
+ STRERROR(errno));
+ } else {
+ /* we could go crazy here and try to open a socket locally
+ and then attempt to connect to it using the NS lookup result
+ for "host" -- and do it for IPv4 and IPv6 and possibly still
+ be wrong due some crazy firewall configuration. Or we can
+ just do the simpelst thing (strcmp) and expect the user to
+ fix it if he cares to have the warning go away... */
+ if (0 != strcmp(host,
+ hostname)) {
+ guiMessage("WARNING: gnunetd is configured to run on host %s and\n"
+ "gnunet-gtk is running on host %s, which seems to be a
different machine.\n"
+ "gnunet-gtk can only start gnunetd on host %s.\n"
+ "This may not be what you want (it may not work).\n"
+ "I will proceed anyway, good luck.",
+ host,
+ hostname,
+ hostname);
+ }
+ }
+ FREE(hostname);
+ }
+ FREE(host);
+ }
+ /* end of sanity checks */
+ doLaunch();
+ pollForLaunch = TRUE;
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+/**
+ * Launch gnunetd w/ checks
+ **/
+void launchDaemon(GtkWidget * widget,
+ gpointer data) {
+ if (OK == checkDaemonRunning() ) {
+ guiMessage("gnunetd is already running");
+ return;
+ } else {
+ doLaunch();
+ pollForLaunch = TRUE;
+ }
+}
+
+/**
+ * Kill gnunetd
+ **/
+void killDaemon(GtkWidget * widget,
+ gpointer data) {
+ if (OK == checkDaemonRunning() ) {
+ GNUNET_TCP_SOCKET * sock;
+ CS_HEADER csHdr;
+ int ret;
+
+ sock = getClientSocket();
+ if (sock == NULL) {
+ /* well, probably already dead */
+ return;
+ }
+ csHdr.size
+ = htons(sizeof(CS_HEADER));
+ csHdr.tcpType
+ = htons(CS_PROTO_SHUTDOWN_REQUEST);
+ if (SYSERR == writeToSocket(sock,
+ &csHdr)) {
+ guiMessage("Error sending shutdown request to gnunetd");
+ releaseClientSocket(sock);
+ return;
+ }
+ if (SYSERR == readTCPResult(sock,
+ &ret)) {
+ guiMessage("Error reading shutdown reply from gnunetd");
+ releaseClientSocket(sock);
+ return;
+ }
+ if (ret == OK)
+ guiMessage("gnunetd agreed to shut down.");
+ else
+ guiMessage("gnunetd refuses to shut down (reply=%d).",
+ ret);
+ releaseClientSocket(sock);
+ } else {
+ guiMessage("gnunetd is not running...");
+ }
+}
+
+/**
+ * Ask if the user wishes to start gnunetd
+ **/
+static void initDaemonStartDialog(void) {
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *okay_button;
+ GtkWidget *no_button;
+
+ dialog = gtk_dialog_new();
+ label = gtk_label_new("gnunetd (daemon) doesn't seem to be running.\nWould
you like to start it?\n");
+ gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
+ label);
+
+ okay_button = gtk_button_new_with_label("Yes!");
+ no_button = gtk_button_new_with_label("Naah");
+
+ gtk_signal_connect(GTK_OBJECT(okay_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(launchDaemonNoCheck),
+ dialog);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ okay_button);
+ gtk_signal_connect(GTK_OBJECT(no_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ dialog);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ no_button);
+
+ gtk_widget_show_all(dialog);
+}
+
+/**
+ * Checks if gnunetd is running and if not, prompts user
+ * to run gnunetd. Always returns OK.
+ **/
+int checkForDaemon(void) {
+ if (SYSERR == checkDaemonRunning()) {
+ char * host;
+
+ host = getConfigurationString("NETWORK",
+ "HOST");
+ if (host != NULL && strcmp(host,"localhost")==0 )
+ initDaemonStartDialog();
+ else
+ guiMessage("gnunetd doesn't seem to be running.\n"
+ "Unfortunately, gnunet-gtk can't identify config entry"
+ "\n\nNETWORK/HOST '%s'\n\n"
+ "as a local machine, so gnunetd can not be\n"
+ "launched by gnunet-gtk.",
+ (host == NULL ? "" : host) );
+ }
+
+ return OK;
+}
+
+static gint doUpdateMenus(SaveCall * call) {
+ static GtkWidget * killEntry = NULL;
+ static GtkWidget * launchEntry = NULL;
+ static GtkWidget * statsEntry = NULL;
+ static int once = 1;
+ static int isLocal;
+ char * host;
+ int ret;
+
+ ret = * (int*) call->args;
+ if (once) {
+ once = 0;
+ killEntry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/"
+ "Kill gnunetd");
+ launchEntry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/"
+ "Launch gnunetd");
+ statsEntry = gtk_item_factory_get_widget(itemFactory,
+ "/File/"
+ "Show gnunetd stats");
+ host = getConfigurationString("NETWORK",
+ "HOST");
+ if ( (host == NULL) ||
+ (strcmp(host, "localhost")==0) )
+ isLocal = TRUE;
+ else
+ isLocal = FALSE;
+ FREENONNULL(host);
+ }
+ if (ret == SYSERR) {
+ gtk_widget_set_sensitive(statsEntry, FALSE);
+ gtk_widget_set_sensitive(killEntry, FALSE);
+ gtk_widget_set_sensitive(launchEntry, (TRUE & isLocal) );
+ } else {
+ gtk_widget_set_sensitive(statsEntry, TRUE);
+ gtk_widget_set_sensitive(killEntry, TRUE);
+ gtk_widget_set_sensitive(launchEntry, FALSE);
+
+ if (pollForLaunch == TRUE) {
+ pollForLaunch = FALSE;
+ guiMessage("gnunetd is now up and running");
+ }
+ }
+ gtkSaveCallDone(call->sem);
+ return FALSE;
+}
+
+void cronCheckDaemon(void * dummy) {
+ static int last = 42;
+ int ret;
+
+ ret = checkDaemonRunning();
+ if (ret != last) {
+ last = ret;
+ gtkSaveCall((GtkFunction) doUpdateMenus, &ret);
+ }
+}
+
+
+/**
+ * A function for numeric comparisons of strings
+ **/
+gint numericComp(GtkCList *clist,
+ gconstpointer ptr1,
+ gconstpointer ptr2) {
+ double value1;
+ double value2;
+ GtkCListRow * row1 = (GtkCListRow *) ptr1;
+ GtkCListRow * row2 = (GtkCListRow *) ptr2;
+
+ value1 = atof(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text);
+ value2 = atof(GTK_CELL_TEXT(row2->cell[clist->sort_column])->text);
+
+ if(value1>value2)
+ return(-1);
+ else if(value1==value2)
+ return(0);
+ else
+ return(1);
+}
+
+/**
+ * A function for case-insensitive text comparisons
+ **/
+gint alphaComp(GtkCList *clist,
+ gconstpointer ptr1,
+ gconstpointer ptr2) {
+ char * text1;
+ char * text2;
+ GtkCListRow * row1 = (GtkCListRow *) ptr1;
+ GtkCListRow * row2 = (GtkCListRow *) ptr2;
+
+ text1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text;
+ text2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text;
+
+ return (strcasecmp(text1,text2));
+}
+
+/**
+ * A function for percentage comparisons
+ **/
+gint percentComp(GtkCList *clist,
+ gconstpointer ptr1,
+ gconstpointer ptr2) {
+ char * tmp1;
+ char * tmp2;
+ double value1;
+ double value2;
+ GtkCListRow * row1 = (GtkCListRow *) ptr1;
+ GtkCListRow * row2 = (GtkCListRow *) ptr2;
+
+ tmp1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text;
+ tmp2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text;
+
+ /* Hack for DONE strings :) */
+ if(strstr(tmp1,"%") == 0) {
+ if(strstr(tmp2,"%") == 0)
+ return 0; /* Both "DONE" */
+ else
+ return -1; /* A done, B not */
+ }
+ if(strstr(tmp2,"%")==0)
+ return 1; /* B done, A not */
+
+ /* Both have %, must remove */
+ tmp1 = STRDUP(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text);
+ tmp2 = STRDUP(GTK_CELL_TEXT(row2->cell[clist->sort_column])->text);
+
+ tmp1[strlen(tmp1)-1]=0;
+ tmp2[strlen(tmp2)-1]=0;
+
+ value1 = atof(tmp1);
+ value2 = atof(tmp2);
+
+ FREE(tmp1);
+ FREE(tmp2);
+
+ if(value1>value2)
+ return(-1);
+ else if(value1==value2)
+ return(0);
+ else
+ return(1);
+}
+
+/**
+ * A general right-button popup menu callback
+ **/
+gboolean popupCallback(GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *menu )
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+
+ /* Only take button presses */
+ if (event->type != GDK_BUTTON_PRESS)
+ return FALSE;
+
+ if (bevent->button != 3)
+ return FALSE;
+
+ /* Show the menu */
+ gtk_widget_show(menu);
+ gtk_menu_popup (GTK_MENU(menu), NULL, NULL,
+ NULL, NULL, bevent->button, bevent->time);
+
+ return TRUE;
+}
+
+/* end of helper.c */
+
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/helper.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/helper.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/helper.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,157 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/helper.h
+ * @author Igor Wronsky
+ **/
+
+#ifndef GTKUI_HELPER_H
+#define GTKUI_HELPER_H
+
+
+#include "platform.h"
+
+/* for GTK 2 */
+#define GTK_ENABLE_BROKEN
+
+#include <gtk/gtk.h>
+#include <gtk/gtktext.h>
+
+typedef struct {
+ Semaphore *sem;
+ void *args;
+ GtkFunction func;
+} SaveCall;
+
+typedef struct {
+ int doPopup;
+ gchar *note;
+} InfoMessage;
+
+typedef struct {
+ char *labelName;
+ GtkWidget *frame;
+} AddNotebook;
+
+
+/* callback: window close: close the window */
+gint deleteEvent(GtkWidget * widget,
+ GdkEvent * event,
+ gpointer data);
+
+/**
+ * A callback to destroy any widget given as second argument
+ *
+ */
+void destroyWidget(GtkWidget * dummy, GtkWidget * widget);
+
+/**
+ * Displays an informative message to the user
+ */
+void guiMessage(const char * format, ...);
+
+/**
+ * Appends a message to the info window
+ */
+void infoMessage(int doPopup, const char * format, ...);
+
+/**
+ * Appends a log entry to the info window
+ *
+ * @param txt the log entry
+ *
+ **/
+void addLogEntry(const char *txt);
+
+void addToNotebook(char * labelName,
+ GtkWidget * frame);
+
+void hideWindow(GtkWidget * widget,
+ gpointer data);
+
+void showStats(GtkWidget * widget,
+ gpointer data);
+
+int checkForDaemon(void);
+
+void cronCheckDaemon(void * dummy);
+void launchDaemon(GtkWidget * widget,
+ gpointer data);
+void killDaemon(GtkWidget * widget,
+ gpointer data);
+
+/**
+ * A function for numeric comparisons of strings
+ **/
+gint numericComp(GtkCList *clist,
+ gconstpointer ptr1,
+ gconstpointer ptr2);
+
+/**
+ * A function for case-insensitive text comparisons
+ **/
+gint alphaComp(GtkCList *clist,
+ gconstpointer ptr1,
+ gconstpointer ptr2);
+
+/**
+ * A function for comparisons of percentages
+ **/
+gint percentComp(GtkCList *clist,
+ gconstpointer ptr1,
+ gconstpointer ptr2);
+
+/**
+ * A general right-button popup menu callback
+ **/
+gboolean popupCallback(GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *menu );
+
+/**
+ * Call a callback function from the mainloop/main thread ("SaveCall").
+ * Since GTK doesn't work with multi-threaded applications under Windows,
+ * all GTK operations have to be done in the main thread
+ **/
+void gtkSaveCall(GtkFunction func, void *args);
+
+/**
+ * Initialize "SaveCalls"
+ **/
+void gtkInitSaveCalls();
+
+void gtkDoneSaveCalls();
+
+void gtkRunSomeSaveCalls();
+
+/**
+ * Called from a "SaveCall"-function to indicate that it is done
+ **/
+void gtkSaveCallDone(Semaphore *sem);
+
+/**
+ * Destroy a widget. Called from threads other than the main thread
+ **/
+gint doDestroyWidget(SaveCall *call);
+
+extern GtkNotebook * notebook;
+extern GtkWidget * infoWindow;
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/insert.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/insert.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/insert.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,1297 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/insert.c
+ * @brief handles file insertions in the GTK GUI
+ * @author Igor Wronsky
+ * @author Christian Grothoff (refactoring, added bugs)
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "insertprogress.h"
+#include "insert.h"
+#include "main.h"
+
+/**
+ * @brief state of the edit RootNode window
+ **/
+typedef struct {
+ char * fileName;
+ GtkWidget * editAttributesWindow;
+ GtkWidget * fileNameLine;
+ GtkWidget * descriptionLine;
+ GtkWidget * mimeLine;
+ GtkWidget * indexButton;
+ GtkWidget * checkCopy;
+ GtkWidget * keywordLine;
+ GtkWidget * keywordList;
+} EditWindowModel;
+
+/**
+ * @brief state of the edit RootNode window
+ **/
+typedef struct {
+ char * fileName;
+ GtkWidget * editAttributesWindow;
+ GtkWidget * fileNameLine;
+ GtkWidget * descriptionLine;
+ GtkWidget * indexButton;
+ GtkWidget * checkCopy;
+ GtkWidget * keywordLine;
+ GtkWidget * keywordList;
+ GtkWidget * gkeywordLine;
+ GtkWidget * gkeywordList;
+} EditDirectoryWindowModel;
+
+
+/**
+ * Collects the results of editAttributes, creates an insertion
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void startInsert(GtkWidget * dummy,
+ EditWindowModel * ewm) {
+ InsertModel * ilm;
+ gchar * txt;
+ int i;
+ PTHREAD_T insertThread;
+
+ ilm = MALLOC(sizeof(InsertModel));
+ ilm->fileName = STRDUP(ewm->fileName);
+
+ /* get content indexing style */
+ if (gtk_toggle_button_get_active((GtkToggleButton *)ewm->indexButton) ==
TRUE)
+ ilm->indexContent = YES;
+ else
+ ilm->indexContent = NO;
+ ilm->copyFile = gtk_toggle_button_get_active(
+ (GtkToggleButton *)ewm->checkCopy);
+
+ /* get the published filename */
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->fileNameLine));
+ if (txt == NULL)
+ ilm->fileNameRoot = STRDUP("none specified");
+ else
+ ilm->fileNameRoot = STRDUP(txt);
+
+ /* get the new description, if any */
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+ if (txt == NULL)
+ ilm->description = STRDUP("no description specified");
+ else
+ ilm->description = STRDUP(txt);
+
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->mimeLine));
+ if (txt == NULL)
+ ilm->mimetype = STRDUP("unknown");
+ else
+ ilm->mimetype = STRDUP(txt);
+
+ /* get new list of keywords */
+ ilm->num_keywords = GTK_CLIST(ewm->keywordList)->rows;
+
+ if (ilm->num_keywords > 0) {
+ ilm->keywords = (char**) MALLOC(ilm->num_keywords * sizeof(char*));
+ for(i=0;i<ilm->num_keywords;i++) {
+ gtk_clist_get_text(GTK_CLIST(ewm->keywordList),
+ i,
+ 0,
+ &txt);
+ ilm->keywords[i] = STRDUP(txt);
+ }
+ } else
+ ilm->keywords = NULL;
+
+ if(ilm->indexContent == YES)
+ strcpy(ilm->opDescription, "indexed");
+ else
+ strcpy(ilm->opDescription, "inserted");
+ createInsertProgressBar(ilm);
+ /* start the insert thread */
+ if (0 != PTHREAD_CREATE(&insertThread,
+ (PThreadMain) insertFileGtkThread,
+ ilm,
+ 16 * 1024))
+ errexit("FATAL: could not create insert thread (%s)!\n",
+ STRERROR(errno));
+ PTHREAD_DETACH(&insertThread);
+
+ /* destroy the "editAttributes" window */
+ gtk_widget_destroy(ewm->editAttributesWindow);
+}
+
+/**
+ * Collects the results of editAttributes, creates an insertion
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void startInsertDirectory(GtkWidget * dummy,
+ EditDirectoryWindowModel * ewm) {
+ InsertDirectoryModel * ilm;
+ gchar * txt;
+ int i;
+ PTHREAD_T insertThread;
+
+ ilm = MALLOC(sizeof(InsertDirectoryModel));
+ ilm->fileName = STRDUP(ewm->fileName);
+
+ /* get content indexing style */
+ if (gtk_toggle_button_get_active((GtkToggleButton *)ewm->indexButton) ==
TRUE)
+ ilm->indexContent = YES;
+ else
+ ilm->indexContent = NO;
+ ilm->copyFile = gtk_toggle_button_get_active(
+ (GtkToggleButton *)ewm->checkCopy);
+ /* get the published filename */
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->fileNameLine));
+ if (txt == NULL)
+ ilm->fileNameRoot = STRDUP("none specified");
+ else
+ ilm->fileNameRoot = STRDUP(txt);
+
+ /* get the new description, if any */
+ txt = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+ if (txt == NULL)
+ ilm->description = STRDUP("no description specified");
+ else
+ ilm->description = STRDUP(txt);
+
+ ilm->mimetype = STRDUP(GNUNET_DIRECTORY_MIME);
+
+ /* get new list of keywords */
+ ilm->num_keywords = GTK_CLIST(ewm->keywordList)->rows;
+
+ if (ilm->num_keywords > 0) {
+ ilm->keywords = (char**) MALLOC(ilm->num_keywords * sizeof(char*));
+ for(i=0;i<ilm->num_keywords;i++) {
+ gtk_clist_get_text(GTK_CLIST(ewm->keywordList),
+ i,
+ 0,
+ &txt);
+ ilm->keywords[i] = STRDUP(txt);
+ }
+ } else
+ ilm->keywords = NULL;
+
+ /* get new list of keywords */
+ ilm->num_gkeywords = GTK_CLIST(ewm->gkeywordList)->rows;
+
+ if (ilm->num_gkeywords > 0) {
+ ilm->gkeywords = (char**) MALLOC(ilm->num_gkeywords * sizeof(char*));
+ for(i=0;i<ilm->num_gkeywords;i++) {
+ gtk_clist_get_text(GTK_CLIST(ewm->gkeywordList),
+ i,
+ 0,
+ &txt);
+ ilm->gkeywords[i] = STRDUP(txt);
+ }
+ } else
+ ilm->gkeywords = NULL;
+
+ if(ilm->indexContent == YES)
+ strcpy(ilm->opDescription, "indexed");
+ else
+ strcpy(ilm->opDescription, "inserted");
+ /*
+ FIXME: allow setting this as an option in the dialog!
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "EXTRACT-KEYWORDS",
+ "NO"));
+ */
+ createInsertDirectoryProgressBar(ilm);
+ /* start the insert thread */
+ if (0 != PTHREAD_CREATE(&insertThread,
+ (PThreadMain) insertDirectoryGtkThread,
+ ilm,
+ 16 * 1024))
+ errexit("FATAL: could not create insert thread (%s)!\n",
+ STRERROR(errno));
+ PTHREAD_DETACH(&insertThread);
+ /* destroy the "editAttributes" window */
+ gtk_widget_destroy(ewm->editAttributesWindow);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_add_clicked(GtkWidget * w,
+ EditWindowModel * ewm) {
+ gchar * key;
+ gchar * newKeyword;
+ int i;
+
+ key = gtk_entry_get_text(GTK_ENTRY(ewm->keywordLine));
+ if (key == NULL) {
+ /* message to enter a string? */
+ return;
+ }
+
+ newKeyword = STRDUP(key);
+ key = newKeyword;
+
+ /* remove trailing & heading spaces */
+ i = strlen(key)-1;
+ while ( (newKeyword[i] == ' ') &&
+ (i >= 0) ) {
+ newKeyword[i--] = '\0';
+ }
+ while (*newKeyword == ' ')
+ newKeyword++;
+
+ if ( *newKeyword == '\0' ) {
+ /* message to enter more than spaces? */
+ } else {
+ gtk_clist_append(GTK_CLIST(ewm->keywordList),
+ &newKeyword);
+ }
+ FREE(key);
+ gtk_entry_set_text(GTK_ENTRY(ewm->keywordLine),
+ "");
+}
+
+/**
+ * The keyword delete button was clicked. Delete the
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_del_clicked(GtkWidget * w,
+ EditWindowModel * ewm) {
+ GList * tmp;
+
+ tmp = GTK_CLIST(ewm->keywordList)->selection;
+ if (NULL == tmp) {
+ /* message that keyword must be selected to delete one? */
+ return;
+ }
+ gtk_clist_remove(GTK_CLIST(ewm->keywordList),
+ (int)tmp->data);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_dir_add_clicked1(GtkWidget * w,
+ EditDirectoryWindowModel * ewm) {
+ gchar * key;
+ gchar * newKeyword;
+ int i;
+
+ key = gtk_entry_get_text(GTK_ENTRY(ewm->keywordLine));
+ if (key == NULL) {
+ /* message to enter a string? */
+ return;
+ }
+
+ newKeyword = STRDUP(key);
+ key = newKeyword;
+
+ /* remove trailing & heading spaces */
+ i = strlen(key)-1;
+ while ( (newKeyword[i] == ' ') &&
+ (i >= 0) ) {
+ newKeyword[i--] = '\0';
+ }
+ while (*newKeyword == ' ')
+ newKeyword++;
+
+ if ( *newKeyword == '\0' ) {
+ /* message to enter more than spaces? */
+ } else {
+ gtk_clist_append(GTK_CLIST(ewm->keywordList),
+ &newKeyword);
+ }
+ FREE(key);
+ gtk_entry_set_text(GTK_ENTRY(ewm->keywordLine),
+ "");
+}
+
+/**
+ * The keyword delete button was clicked. Delete the
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_dir_del_clicked1(GtkWidget * w,
+ EditDirectoryWindowModel * ewm) {
+ GList * tmp;
+
+ tmp = GTK_CLIST(ewm->keywordList)->selection;
+ if (NULL == tmp) {
+ /* message that keyword must be selected to delete one? */
+ return;
+ }
+ gtk_clist_remove(GTK_CLIST(ewm->keywordList),
+ (int)tmp->data);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_dir_add_clicked2(GtkWidget * w,
+ EditDirectoryWindowModel * ewm) {
+ gchar * key;
+ gchar * newKeyword;
+ int i;
+
+ key = gtk_entry_get_text(GTK_ENTRY(ewm->gkeywordLine));
+ if (key == NULL) {
+ /* message to enter a string? */
+ return;
+ }
+
+ newKeyword = STRDUP(key);
+ key = newKeyword;
+
+ /* remove trailing & heading spaces */
+ i = strlen(key)-1;
+ while ( (newKeyword[i] == ' ') &&
+ (i >= 0) ) {
+ newKeyword[i--] = '\0';
+ }
+ while (*newKeyword == ' ')
+ newKeyword++;
+
+ if ( *newKeyword == '\0' ) {
+ /* message to enter more than spaces? */
+ } else {
+ gtk_clist_append(GTK_CLIST(ewm->gkeywordList),
+ &newKeyword);
+ }
+ FREE(key);
+ gtk_entry_set_text(GTK_ENTRY(ewm->gkeywordLine),
+ "");
+}
+
+/**
+ * The keyword delete button was clicked. Delete the
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_dir_del_clicked2(GtkWidget * w,
+ EditDirectoryWindowModel * ewm) {
+ GList * tmp;
+
+ tmp = GTK_CLIST(ewm->gkeywordList)->selection;
+ if (NULL == tmp) {
+ /* message that keyword must be selected to delete one? */
+ return;
+ }
+ gtk_clist_remove(GTK_CLIST(ewm->gkeywordList),
+ (int)tmp->data);
+}
+
+/**
+ * The index/insert file button was clicked.
+ *
+ * @param w the button
+ * @param ewm state of the edit window
+ **/
+static void button_index_clicked(GtkWidget * w,
+ EditWindowModel * ewm) {
+ gtk_widget_set_sensitive(ewm->checkCopy, w == ewm->indexButton);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyEditWindow(GtkWidget * widget,
+ EditWindowModel * ewm) {
+ FREE(ewm->fileName);
+ FREE(ewm);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyEditDirectoryWindow(GtkWidget * widget,
+ EditDirectoryWindowModel * ewm) {
+ FREE(ewm->fileName);
+ FREE(ewm);
+}
+
+/**
+ * Show user a window to edit information related to this file.
+ * After user is done, call startInsert.
+ *
+ * @param filename the name of the file that is inserted
+ * @param fileNameRoot the short name of the file
+ * @param description the description of the file
+ * @param mimetype the mimetype of the ile
+ * @param keywords the extracted keywords of the file
+ * @param num_keywords the number of keywords extracted
+ **/
+static void editAttributes(char * filename,
+ char * fileNameRoot,
+ char * description,
+ char * mimetype,
+ char ** keywords,
+ int num_keywords) {
+ EditWindowModel * ewm;
+ GtkWidget * window;
+ GtkWidget * vbox, * hbox;
+ GtkWidget * clist;
+ GtkWidget * scrolled_window;
+ GtkWidget * label;
+ GtkWidget * separator;
+ GtkWidget * button_add;
+ GtkWidget * button_delete;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+ GtkWidget * keyword_line;
+ GSList * group;
+ GtkWidget * indexbutton1;
+ GtkWidget * indexbutton2;
+ GtkWidget * check_copy;
+ int doIndex;
+ gchar * titles[1] = { "Keyword(s) used" };
+ int i;
+
+ ewm = MALLOC(sizeof(EditWindowModel));
+ ewm->fileName = STRDUP(filename);
+ /* create new window for editing */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ewm->editAttributesWindow = window;
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 400,
+ 480);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Edit attributes");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyEditWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ /* Create a line to change the published filename */
+ label = gtk_label_new("Published filename:");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->fileNameLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ ewm->fileNameLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->fileNameLine),
+ fileNameRoot);
+ gtk_widget_show(ewm->fileNameLine);
+
+ /* Create a line to change the mime type */
+ label = gtk_label_new("Mimetype:");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->mimeLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ ewm->mimeLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->mimeLine),
+ mimetype);
+ gtk_widget_show(ewm->mimeLine);
+
+ /* Create a line to change the description */
+ label = gtk_label_new("Description:");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->descriptionLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ ewm->descriptionLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->descriptionLine),
+ description);
+ gtk_widget_show(ewm->descriptionLine);
+
+ /* add buttons to select the insertion method */
+ label = gtk_label_new("Insertion method:");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ indexbutton1 = gtk_radio_button_new_with_label(NULL,
+ "Index only");
+ ewm->indexButton = indexbutton1;
+ gtk_signal_connect(GTK_OBJECT(indexbutton1),
+ "toggled",
+ GTK_SIGNAL_FUNC(button_index_clicked),
+ ewm);
+ gtk_box_pack_start(GTK_BOX (hbox),
+ indexbutton1,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(indexbutton1);
+ group = gtk_radio_button_group(GTK_RADIO_BUTTON(indexbutton1));
+ indexbutton2 = gtk_radio_button_new_with_label(group,
+ "Full insertion");
+ gtk_signal_connect(GTK_OBJECT(indexbutton2),
+ "toggled",
+ GTK_SIGNAL_FUNC(button_index_clicked),
+ ewm);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ indexbutton2,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(indexbutton2);
+ if (testConfigurationString("GNUNET-INSERT",
+ "INDEX-CONTENT",
+ "YES") == YES) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton1),
+ TRUE);
+ doIndex = 1;
+ }
+ else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton2),
+ TRUE);
+ doIndex = 0;
+ }
+
+ check_copy = gtk_check_button_new_with_label("Copy file to shared dir");
+ ewm->checkCopy = check_copy;
+ gtk_box_pack_start(GTK_BOX(hbox),
+ check_copy,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_copy),
+ ! testConfigurationString("GNUNET-INSERT",
+ "LINK",
+ "YES"));
+ gtk_widget_set_sensitive(check_copy, doIndex);
+ gtk_widget_show(check_copy);
+
+ /* add a list of keywords */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titles);
+ ewm->keywordList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+
+ /* add the pre-extracted keywords to the list */
+ gtk_clist_freeze(GTK_CLIST(clist));
+ for(i=0;i<num_keywords;i++) {
+ gtk_clist_append(GTK_CLIST(clist),
+ &keywords[i]);
+ }
+ gtk_clist_thaw(GTK_CLIST(clist));
+ gtk_widget_show(clist);
+
+ /* add a line to input new keywords */
+ keyword_line = gtk_entry_new();
+ ewm->keywordLine = keyword_line;
+ gtk_box_pack_start(GTK_BOX(vbox),
+ keyword_line,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(keyword_line),
+ "");
+ gtk_signal_connect(GTK_OBJECT(keyword_line),
+ "activate",
+ GTK_SIGNAL_FUNC(button_add_clicked),
+ ewm);
+ gtk_widget_show(keyword_line);
+
+ /* add the buttons to add and delete keywords */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_add = gtk_button_new_with_label("Add keyword");
+ button_delete = gtk_button_new_with_label("Delete keyword");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_add,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_delete,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_add),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_add_clicked),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_delete),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_del_clicked),
+ ewm);
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_delete);
+
+ /* add the insertion ok/cancel buttons */
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(separator);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_ok = gtk_button_new_with_label("Ok");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(startInsert),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(window);
+}
+
+/**
+ * Launches the attribute edit routine for the selected file,
+ * after keywords have been extracted.
+ *
+ * @param filename the selected filename
+ */
+static void file_selected(char *filename) {
+ int i;
+ char * fileNameRoot;
+ char * description;
+ char * mimetype;
+ char ** keywords;
+ int num_keywords;
+
+
+ /* if filename is '/home/user/foo', use 'foo' as the filenameRoot */
+ fileNameRoot = NULL;
+ for (i=strlen(filename)-1;i>=0;i--) {
+ if (filename[i] == DIR_SEPARATOR) {
+ fileNameRoot = STRDUP(&filename[i+1]);
+ break;
+ }
+ }
+ if (i == -1)
+ errexit("FATAL: ASSERTION failed: filename does not start with a '/'");
+
+ /* try to extract keywords */
+ description = NULL;
+ mimetype = NULL;
+ num_keywords = 0;
+ keywords = NULL;
+ extractKeywords(filename,
+ &description,
+ &mimetype,
+ &keywords,
+ &num_keywords);
+
+ if (description == NULL)
+ description = STRDUP("No description supplied");
+ if (mimetype == NULL)
+ mimetype = STRDUP("unknown");
+
+ /* allow the user to edit the insertion related info */
+ editAttributes(filename,
+ fileNameRoot,
+ description,
+ mimetype,
+ keywords,
+ num_keywords);
+
+ for (i=0;i<num_keywords;i++)
+ FREE(keywords[i]);
+ FREENONNULL(keywords);
+ FREE(mimetype);
+ FREE(description);
+ FREE(filename);
+ FREE(fileNameRoot);
+}
+
+
+
+/**
+ * Show user a window to edit information related to this file.
+ * After user is done, call startInsert.
+ *
+ * @param filename the name of the file that is inserted
+ * @param fileNameRoot the short name of the file
+ **/
+static void editDirectoryAttributes(char * filename,
+ char * fileNameRoot) {
+ EditDirectoryWindowModel * ewm;
+ GtkWidget * window;
+ GtkWidget * vbox, * hbox;
+ GtkWidget * clist;
+ GtkWidget * scrolled_window;
+ GtkWidget * label;
+ GtkWidget * separator;
+ GtkWidget * button_add;
+ GtkWidget * button_delete;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+ GtkWidget * keyword_line;
+ GSList * group;
+ GtkWidget * indexbutton1;
+ GtkWidget * indexbutton2;
+ GtkWidget * check_copy;
+ int doIndex;
+ gchar * titles[1] = { "Keyword(s) used for directory" };
+ gchar * gtitles[1] = { "Keyword(s) used for all files in directory" };
+
+ ewm = MALLOC(sizeof(EditDirectoryWindowModel));
+ ewm->fileName = STRDUP(filename);
+ /* create new window for editing */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ewm->editAttributesWindow = window;
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 400,
+ 480);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Edit attributes");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyEditDirectoryWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ /* Create a line to change the published filename */
+ label = gtk_label_new("Published directoryname:");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->fileNameLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ ewm->fileNameLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->fileNameLine),
+ fileNameRoot);
+ gtk_widget_show(ewm->fileNameLine);
+
+
+ /* Create a line to change the description */
+ label = gtk_label_new("Description:");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->descriptionLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ ewm->descriptionLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->descriptionLine),
+ "No description supplied");
+ gtk_widget_show(ewm->descriptionLine);
+
+ /* add buttons to select the insertion method */
+ label = gtk_label_new("Insertion method (for files in directory):");
+ gtk_box_pack_start(GTK_BOX(vbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ indexbutton1 = gtk_radio_button_new_with_label(NULL,
+ "Index only");
+ gtk_signal_connect(GTK_OBJECT(indexbutton1),
+ "toggled",
+ GTK_SIGNAL_FUNC(button_index_clicked),
+ ewm);
+ ewm->indexButton = indexbutton1;
+ gtk_box_pack_start(GTK_BOX (hbox),
+ indexbutton1,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(indexbutton1);
+ group = gtk_radio_button_group(GTK_RADIO_BUTTON(indexbutton1));
+ indexbutton2 = gtk_radio_button_new_with_label(group,
+ "Full insertion");
+ gtk_signal_connect(GTK_OBJECT(indexbutton2),
+ "toggled",
+ GTK_SIGNAL_FUNC(button_index_clicked),
+ ewm);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ indexbutton2,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(indexbutton2);
+ if (testConfigurationString("GNUNET-INSERT",
+ "INDEX-CONTENT",
+ "YES") == YES) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton1),
+ TRUE);
+ doIndex = 1;
+ }
+ else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton2),
+ TRUE);
+ doIndex = 0;
+ }
+
+
+ check_copy = gtk_check_button_new_with_label("Copy file to shared dir");
+ ewm->checkCopy = check_copy;
+ gtk_box_pack_start(GTK_BOX(hbox),
+ check_copy,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_copy),
+ ! testConfigurationString("GNUNET-INSERT",
+ "LINK",
+ "YES"));
+ gtk_widget_set_sensitive(check_copy, doIndex);
+ gtk_widget_show(check_copy);
+
+
+
+
+ /* add list of local keywords */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titles);
+ ewm->keywordList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+
+ /* add a line to input new keywords */
+ keyword_line = gtk_entry_new();
+ ewm->keywordLine = keyword_line;
+ gtk_box_pack_start(GTK_BOX(vbox),
+ keyword_line,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(keyword_line),
+ "");
+ gtk_signal_connect(GTK_OBJECT(keyword_line),
+ "activate",
+ GTK_SIGNAL_FUNC(button_dir_add_clicked1),
+ ewm);
+ gtk_widget_show(keyword_line);
+
+ /* add the buttons to add and delete keywords */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_add = gtk_button_new_with_label("Add keyword");
+ button_delete = gtk_button_new_with_label("Delete keyword");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_add,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_delete,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_add),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_dir_add_clicked1),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_delete),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_dir_del_clicked1),
+ ewm);
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_delete);
+
+
+
+ /* add list of global keywords */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, gtitles);
+ ewm->gkeywordList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+
+ /* add a line to input new keywords */
+ keyword_line = gtk_entry_new();
+ ewm->gkeywordLine = keyword_line;
+ gtk_box_pack_start(GTK_BOX(vbox),
+ keyword_line,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(keyword_line),
+ "");
+ gtk_signal_connect(GTK_OBJECT(keyword_line),
+ "activate",
+ GTK_SIGNAL_FUNC(button_dir_add_clicked2),
+ ewm);
+ gtk_widget_show(keyword_line);
+
+ /* add the buttons to add and delete keywords */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_add = gtk_button_new_with_label("Add keyword");
+ button_delete = gtk_button_new_with_label("Delete keyword");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_add,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_delete,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_add),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_dir_add_clicked2),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_delete),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_dir_del_clicked2),
+ ewm);
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_delete);
+
+
+
+ /* add the insertion ok/cancel buttons */
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(separator);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_ok = gtk_button_new_with_label("Ok");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(startInsertDirectory),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(window);
+}
+
+
+/**
+ * Insert a directory.
+ **/
+static void directory_selected(char * filename) {
+ int i;
+ char * fileNameRoot;
+
+ /* if filename is '/home/user/foo/', use 'foo.gnd' as the filenameRoot */
+ fileNameRoot = NULL;
+ if (filename[strlen(filename)-1] == DIR_SEPARATOR)
+ filename[strlen(filename)-1] = '\0';
+ for (i=strlen(filename)-1;i>=0;i--) {
+ if (filename[i] == DIR_SEPARATOR) {
+ fileNameRoot =
MALLOC(strlen(&filename[i+1])+1+strlen(GNUNET_DIRECTORY_EXT));
+ strcpy(fileNameRoot, &filename[i+1]);
+ strcat(fileNameRoot, GNUNET_DIRECTORY_EXT);
+ break;
+ }
+ }
+ if (strlen(filename) == 0) {
+ fileNameRoot = STRDUP("");
+ } else {
+ if (i == -1)
+ errexit("FATAL: ASSERTION failed: filename does not start with a %c",
+ DIR_SEPARATOR);
+ }
+
+ /* allow the user to edit the insertion related info */
+ editDirectoryAttributes(filename,
+ fileNameRoot);
+ FREE(filename);
+ FREE(fileNameRoot);
+}
+
+/**
+ * Callback for the file selection window.
+ *
+ * @param okButton not used
+ * @param window the file selection window
+ */
+static gint gtk_file_selected(GtkWidget * okButton,
+ GtkWidget * window) {
+ gchar * filename;
+ char * fn;
+
+ filename
+ = gtk_file_selection_get_filename(GTK_FILE_SELECTION(window));
+ if (filename == NULL) {
+ gtk_widget_destroy(window);
+ return FALSE;
+ }
+ if ( (NO == isDirectory(filename)) &&
+ (0 == assertIsFile(filename)) ) {
+ guiMessage("%s is not a file!\n",
+ filename);
+ gtk_widget_destroy(window);
+ return FALSE;
+ }
+
+ fn = STRDUP((char *) filename);
+
+ /* destroy the open-file window */
+ gtk_widget_destroy(window);
+
+ if (isDirectory(fn))
+ directory_selected(fn);
+ else
+ file_selected(fn);
+
+ return FALSE;
+}
+
+/**
+ * Close the open-file window.
+ **/
+static gint destroyOpenFile(GtkWidget * widget,
+ GtkWidget * window) {
+ LOG(LOG_DEBUG,
+ "DEBUG: destroying open-file window (%x)\n",
+ window);
+ return TRUE;
+}
+
+#ifdef MINGW
+/* Remember the previously selected path */
+static char szFilename[_MAX_PATH + 1] = "\0";
+#endif
+
+/**
+ * Pops up a file selector for the user.
+ *
+ * Explanation: In insertion, functions will be called in the
+ * following order,
+ *
+ * openSelectFile[OK click->]gtk_file_selected->file_selected->
+ * editAttributes[OK click]->startInsert->newthread.insertFile_
+ **/
+void openSelectFile(void) {
+#ifndef MINGW
+ GtkWidget * window;
+
+ window = gtk_file_selection_new("Choose file to be inserted");
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyOpenFile),
+ window);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_file_selected),
+ window);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(window);
+#else
+ OPENFILENAME theDlg;
+
+ memset(&theDlg, 0, sizeof(OPENFILENAME));
+ szFilename[0] = '\0';
+
+ theDlg.lStructSize = sizeof(OPENFILENAME);
+ theDlg.hwndOwner = GetActiveWindow();
+ theDlg.lpstrFile = szFilename;
+ theDlg.nMaxFile = _MAX_PATH;
+ theDlg.Flags = OFN_FILEMUSTEXIST | OFN_SHAREAWARE;
+ if (GetOpenFileName(&theDlg))
+ file_selected(STRDUP(theDlg.lpstrFile));
+#endif
+}
+
+/* end of insert.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/insert.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/insert.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/insert.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,30 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/insert.h
+ * @author Igor Wronsky
+ **/
+
+#ifndef GTKUI_INSERT_H
+#define GTKUI_INSERT_H
+
+void openSelectFile(void);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.c
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.c
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,547 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/insertprogress.c
+ * @brief handles file insertions in the GTK GUI
+ * @author Igor Wronsky
+ * @author Christian Grothoff (refactoring, added bugs)
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "platform.h"
+#if USE_LIBEXTRACTOR
+#include <extractor.h>
+#endif
+#include "helper.h"
+#include "insertprogress.h"
+#include "insert.h"
+#include "main.h"
+
+typedef struct {
+ GtkWidget *bar;
+ size_t progress;
+} SetStat;
+
+
+static gint setInsertProgressVal(SaveCall *call) {
+ gtk_progress_set_value(GTK_PROGRESS(((SetStat *)call->args)->bar),
+ ((SetStat *)call->args)->progress);
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+typedef struct {
+ GtkWidget * bar;
+ size_t value;
+} SetAdj;
+
+
+static gint updateAdjustment(SaveCall * call) {
+ GtkObject * adj;
+
+ adj = gtk_adjustment_new(0, 0,
+ ((SetAdj *)call->args)->value,
+ 1, 0, 0);
+ gtk_progress_set_adjustment(GTK_PROGRESS(((SetAdj *)call->args)->bar),
+ GTK_ADJUSTMENT(adj));
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+
+
+/**
+ * Callback function to update the insert progressbar.
+ *
+ * @param stats Statistics related to insert progression
+ * @param ilm Data related to the insertion
+ **/
+static void insertModelCallback(ProgressStats * stats,
+ InsertModel * ilm) {
+ SetStat stat;
+
+ stat.bar = ilm->progressBar;
+ stat.progress = stats->progress;
+ gtkSaveCall((GtkFunction) setInsertProgressVal, &stat);
+}
+
+
+/**
+ * Callback function to update the insert progressbar.
+ *
+ * @param stats Statistics related to insert progression
+ * @param ilm Data related to the insertion
+ **/
+static void insertDirectoryModelCallback(ProgressStats * stats,
+ InsertDirectoryModel * ilm) {
+ SetStat stat;
+
+ stat.bar = ilm->progressBar;
+ stat.progress = stats->progress;
+ gtkSaveCall((GtkFunction) setInsertProgressVal, &stat);
+}
+
+static gint destroyInsertProgressBar(SaveCall *call) {
+ gtk_widget_destroy((GtkWidget *) call->args);
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * A function to be run by the insert thread. Does the
+ * actual insertion.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertFileGtkThread(InsertModel * ilm) {
+ int res;
+ GNUNET_TCP_SOCKET * sock;
+ Block * top;
+ int i;
+
+ SEMAPHORE_DOWN(refuseToDie);
+ if (ilm->indexContent == YES) {
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "INDEX-CONTENT",
+ "YES"));
+
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "LINK",
+ ilm->copyFile == YES ? "NO" : "YES"));
+
+ } else {
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "INDEX-CONTENT",
+ "NO"));
+ }
+ sock = getClientSocket();
+ if (sock == NULL) {
+ SEMAPHORE_UP(refuseToDie);
+ return; /* warning should have been printed */
+ }
+ top = insertFile(sock,
+ ilm->fileName,
+ (ProgressModel)&insertModelCallback,
+ ilm);
+ if (top != NULL) {
+ res = insertRoot(sock,
+ top,
+ ilm->description,
+ ilm->fileNameRoot,
+ ilm->mimetype,
+ ilm->num_keywords,
+ ilm->keywords,
+ NULL);
+ } else {
+ res = SYSERR;
+ }
+ gtkSaveCall((GtkFunction) destroyInsertProgressBar,
+ ilm->progressBarWindow);
+ refreshMenuSensitivity();
+ if (res == OK) {
+ FileIdentifier fid;
+ char * fstring;
+
+ memcpy(&fid.chk, &top->chk, sizeof(CHK_Hashes));
+ fid.crc = htonl(crc32N(top->data, top->len));
+ {
+ unsigned int fs = (unsigned int) top->filesize;
+ fid.file_length = htonl(fs);
+ }
+
+ fstring = fileIdentifierToString(&fid);
+
+ infoMessage(NO,
+ "Successfully processed %s\n => %s\n",
+ ilm->fileName,
+ fstring);
+ LOG(LOG_DEBUG,
+ "DEBUG: Successfully processed %s\n => %s\n",
+ ilm->fileName,
+ fstring);
+ FREE(fstring);
+ } else {
+ guiMessage("Insertion of %s FAILED!\n",
+ ilm->fileName);
+ }
+ if(top != NULL)
+ top->vtbl->done(top, NULL);
+ releaseClientSocket(sock);
+
+ /* insert complete */
+ SEMAPHORE_UP(refuseToDie);
+ for (i=0;i<ilm->num_keywords;i++)
+ FREE(ilm->keywords[i]);
+ FREENONNULL(ilm->keywords);
+ if(ilm->deleteAfterInsert == YES)
+ UNLINK(ilm->fileName);
+ FREE(ilm->fileName);
+ FREE(ilm->mimetype);
+ FREE(ilm->description);
+ FREE(ilm->fileNameRoot);
+ FREE(ilm);
+}
+
+/**
+ * Callback for handling "delete_event": keep the window OPEN.
+ **/
+static gint refuseDeleteEvent(GtkWidget * widget,
+ GdkEvent * event,
+ gpointer data) {
+ LOG(LOG_DEBUG,
+ "DEBUG: refuseDeleteEvent\n");
+ return TRUE;
+}
+
+
+void createInsertProgressBar(InsertModel * ilm) {
+ GtkWidget * window;
+ GtkWidget * box;
+ GtkObject * adjustment;
+ char format[128];
+ int fileLength;
+
+ /* create a new window for a progressbar */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ilm->progressBarWindow = window;
+ gtk_window_set_title(GTK_WINDOW(window),
+ ilm->fileName);
+ box = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ box);
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(refuseDeleteEvent),
+ NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ sprintf(format,
+ "%%v bytes %s",
+ ilm->opDescription);
+
+ /* create the actual progressbar */
+ fileLength = getFileSize(ilm->fileName);
+ ilm->progressBar = gtk_progress_bar_new();
+ gtk_progress_set_show_text(GTK_PROGRESS(ilm->progressBar),
+ 1);
+ gtk_progress_set_format_string(GTK_PROGRESS(ilm->progressBar),
+ (gchar*)format);
+ adjustment = gtk_adjustment_new(0,
+ 0,
+ fileLength,
+ 1,
+ 0,
+ 0);
+ gtk_progress_set_adjustment(GTK_PROGRESS(ilm->progressBar),
+ GTK_ADJUSTMENT(adjustment));
+ gtk_box_pack_start(GTK_BOX(box),
+ ilm->progressBar,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(ilm->progressBar),
+ GTK_PROGRESS_LEFT_TO_RIGHT);
+ gtk_widget_show(ilm->progressBar);
+ gtk_widget_show(box);
+ gtk_widget_show(window);
+}
+
+
+void createInsertDirectoryProgressBar(InsertDirectoryModel * ilm) {
+ GtkWidget * window;
+ GtkWidget * box;
+ char format[128];
+ int fileLength;
+
+ /* create a new window for a progressbar */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ilm->progressBarWindow = window;
+ gtk_window_set_title(GTK_WINDOW(window),
+ ilm->fileName);
+ box = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ box);
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(refuseDeleteEvent),
+ NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ sprintf(format,
+ "%%v bytes %s",
+ ilm->opDescription);
+
+ /* create the actual progressbar */
+ fileLength = getFileSize(ilm->fileName);
+ ilm->progressBar = gtk_progress_bar_new();
+ gtk_progress_set_show_text(GTK_PROGRESS(ilm->progressBar),
+ 1);
+ gtk_progress_set_format_string(GTK_PROGRESS(ilm->progressBar),
+ (gchar*)format);
+ ilm->adjustment = gtk_adjustment_new(0,
+ 0,
+ 10000,
+ 1,
+ 0,
+ 0);
+ gtk_progress_set_adjustment(GTK_PROGRESS(ilm->progressBar),
+ GTK_ADJUSTMENT(ilm->adjustment));
+ gtk_box_pack_start(GTK_BOX(box),
+ ilm->progressBar,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(ilm->progressBar),
+ GTK_PROGRESS_LEFT_TO_RIGHT);
+ gtk_widget_show(ilm->progressBar);
+
+ ilm->progressBar2 = gtk_progress_bar_new();
+ gtk_progress_set_show_text(GTK_PROGRESS(ilm->progressBar2),
+ 1);
+ gtk_progress_set_format_string(GTK_PROGRESS(ilm->progressBar2),
+ (gchar*)format);
+ ilm->adjustment2 = gtk_adjustment_new(0,
+ 0,
+ fileLength,
+ 1,
+ 0,
+ 0);
+ gtk_progress_set_adjustment(GTK_PROGRESS(ilm->progressBar2),
+ GTK_ADJUSTMENT(ilm->adjustment2));
+ gtk_box_pack_start(GTK_BOX(box),
+ ilm->progressBar2,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(ilm->progressBar2),
+ GTK_PROGRESS_LEFT_TO_RIGHT);
+ gtk_widget_show(ilm->progressBar2);
+ gtk_widget_show(box);
+ gtk_widget_show(window);
+}
+
+
+
+
+/**
+ * Insert a single file.
+ *
+ * @param filename the name of the file to insert
+ * @param fid resulting file identifier for the node
+ * @returns OK on success, SYSERR on error
+ **/
+static int gtkInsertDirectoryWrapper(GNUNET_TCP_SOCKET * sock,
+ char * filename,
+ FileIdentifier * fid,
+ InsertDirectoryModel * ilm) {
+ Block * top;
+ cron_t startTime;
+ InsertModel ifm;
+ SetAdj adj;
+
+ ifm.fileName = filename;
+ ifm.fileNameRoot = NULL;
+ ifm.description = NULL;
+ ifm.mimetype = NULL;
+ ifm.keywords = NULL;
+ ifm.num_keywords = 0;
+ memcpy(ifm.opDescription,
+ ilm->opDescription,
+ sizeof(ilm->opDescription));
+ ifm.indexContent = ilm->indexContent;
+ ifm.progressBar = ilm->progressBar;
+ ifm.progressBarWindow = ilm->progressBarWindow;
+ ifm.deleteAfterInsert = ilm->deleteAfterInsert;
+
+ adj.bar = ilm->progressBar;
+ adj.value = getFileSize(filename);
+
+ gtkSaveCall((GtkFunction) updateAdjustment,
+ &adj);
+
+ cronTime(&startTime);
+ top = insertFile(sock,
+ filename,
+ (ProgressModel) &insertDirectoryModelCallback,
+ &ifm);
+ if (top == NULL) {
+ /* print error message here? Probably better once
+ at the top-level... */
+ return SYSERR;
+ } else {
+ SetStat stat;
+
+ memcpy(&fid->chk,
+ &top->chk,
+ sizeof(CHK_Hashes));
+ fid->crc = htonl(crc32N(top->data, top->len));
+ fid->file_length = htonl(top->filesize);
+ if (NO == isDirectory(filename)) {
+ if (top->filesize != getFileSize(filename))
+ abort();
+ ilm->pos += top->filesize;
+ stat.bar = ilm->progressBar2;
+ stat.progress = ilm->pos;
+ gtkSaveCall((GtkFunction) setInsertProgressVal,
+ &stat);
+ }
+ top->vtbl->done(top, NULL);
+ return OK;
+ }
+}
+
+
+/**
+ * A function to be run by the insert thread. Does the
+ * actual insertion.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertDirectoryGtkThread(InsertDirectoryModel * ilm) {
+ int res;
+ GNUNET_TCP_SOCKET * sock;
+ RootNode * top;
+ int i;
+ SetStat stat;
+ FileIdentifier fid;
+#if USE_LIBEXTRACTOR
+ EXTRACTOR_ExtractorList * extractors;
+#endif
+
+ SEMAPHORE_DOWN(refuseToDie);
+ ilm->pos = 0;
+ stat.bar = ilm->progressBar2;
+ stat.progress = ilm->pos;
+ gtkSaveCall((GtkFunction) setInsertProgressVal,
+ &stat);
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "BUILDDIR",
+ "YES"));
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "RECURSIVE",
+ "YES"));
+
+ if (ilm->indexContent == YES) {
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "INDEX-CONTENT",
+ "YES"));
+
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "LINK",
+ ilm->copyFile == YES ? "NO" : "YES"));
+ } else {
+ FREENONNULL(setConfigurationString("GNUNET-INSERT",
+ "INDEX-CONTENT",
+ "NO"));
+ }
+ sock = getClientSocket();
+ if (sock == NULL) {
+ SEMAPHORE_UP(refuseToDie);
+ return; /* warning should have been printed */
+ }
+
+
+#if USE_LIBEXTRACTOR
+ extractors = getExtractors();
+#endif
+ top = insertRecursively(sock,
+ ilm->fileName,
+ &fid,
+ ilm->gkeywords,
+ ilm->num_gkeywords,
+#if USE_LIBEXTRACTOR
+ extractors,
+#else
+ NULL,
+#endif
+ (ProgressModel)&insertModelCallback,
+ ilm,
+ (InsertWrapper)>kInsertDirectoryWrapper,
+ ilm);
+#if USE_LIBEXTRACTOR
+ EXTRACTOR_removeAll(extractors);
+#endif
+ if (top != NULL) {
+ unsigned int priority;
+
+ priority = getConfigurationInt("GNUNET-INSERT",
+ "CONTENT-PRIORITY");
+ res = OK;
+ for (i=0;i<ilm->num_keywords;i++) {
+ if (SYSERR == insertRootWithKeyword(sock,
+ top,
+ ilm->keywords[i],
+ priority))
+ res = SYSERR;
+ }
+ makeRootNodeAvailable(top, DIR_CONTEXT_INSERT);
+ } else {
+ res = SYSERR;
+ }
+
+ gtkSaveCall((GtkFunction) destroyInsertProgressBar,
+ ilm->progressBarWindow);
+
+ refreshMenuSensitivity();
+ if (res == OK) {
+ char * fstring;
+
+ fstring = fileIdentifierToString(&top->header.fileIdentifier);
+
+ infoMessage(NO,
+ "Successfully processed %s\n => %s\n",
+ ilm->fileName,
+ fstring);
+ LOG(LOG_DEBUG,
+ "DEBUG: Successfully processed %s\n => %s\n",
+ ilm->fileName,
+ fstring);
+ FREE(fstring);
+ } else {
+ guiMessage("Insertion of %s FAILED!\n",
+ ilm->fileName);
+ }
+ FREENONNULL(top);
+ releaseClientSocket(sock);
+
+ /* insert complete */
+ SEMAPHORE_UP(refuseToDie);
+ for (i=0;i<ilm->num_keywords;i++)
+ FREE(ilm->keywords[i]);
+ FREENONNULL(ilm->keywords);
+ if(ilm->deleteAfterInsert == YES)
+ UNLINK(ilm->fileName);
+ FREE(ilm->fileName);
+ FREE(ilm->mimetype);
+ FREE(ilm->description);
+ FREE(ilm->fileNameRoot);
+ FREE(ilm);
+}
+
+
+
+/* end of insertprogress.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.h
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/insertprogress.h
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,95 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/insertprogress.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_INSERT_PROGRESS_H
+#define GTKUI_INSERT_PROGRESS_H
+
+#include "platform.h"
+#include <gtk/gtk.h>
+
+/**
+ * @brief state associated with an insertion
+ **/
+typedef struct {
+ char * fileName;
+ char * fileNameRoot;
+ char * description;
+ char * mimetype;
+ char ** keywords;
+ int num_keywords;
+ char opDescription[32]; /* used in progressBar */
+ int indexContent;
+ int copyFile;
+ GtkWidget * progressBar;
+ GtkWidget * progressBarWindow;
+ int deleteAfterInsert;
+} InsertModel;
+
+/**
+ * @brief state associated with an insertion
+ **/
+typedef struct {
+ char * fileName;
+ char * fileNameRoot;
+ char * description;
+ char * mimetype;
+ char ** keywords;
+ int num_keywords;
+ char opDescription[32]; /* used in progressBar */
+ int indexContent;
+ int copyFile;
+ GtkWidget * progressBar;
+ GtkWidget * progressBarWindow;
+ int deleteAfterInsert;
+ char ** gkeywords;
+ int num_gkeywords;
+ GtkObject * adjustment;
+ GtkObject * adjustment2;
+ GtkWidget * progressBar2;
+ unsigned long long pos;
+} InsertDirectoryModel;
+
+/**
+ * Main function of the insert thread. Does the
+ * actual insertion.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertFileGtkThread(InsertModel * ilm);
+
+
+/**
+ * A function to be run by the insert thread. Does the
+ * actual insertion for directories.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertDirectoryGtkThread(InsertDirectoryModel * ilm);
+
+
+void createInsertProgressBar(InsertModel * ilm);
+
+void createInsertDirectoryProgressBar(InsertDirectoryModel * ilm);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/main.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/main.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/main.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,867 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/main.c
+ * @brief This is the main file for the gtk+ user interface.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ *
+ * Basic structure of the code is this:
+ * main
+ * -> search -> saveas -> download
+ * -> insert
+ * -> directory -> insert
+ * -> pseudonyms (create|delete)
+ * -> namespace insert/update
+ * -> about
+ *
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "search.h"
+#include "insert.h"
+#include "delete.h"
+#include "about.h"
+#include "directory.h"
+#include "download.h"
+#include "namespace.h"
+#include "pseudonyms.h"
+#include "main.h"
+#include "statistics.h"
+
+/**
+ * This semaphore can be used to prevent the main window
+ * from killing GTK at an unhealty time. It is used by
+ * the un-interruptable but GUI-updating insert thread.
+ **/
+Semaphore * refuseToDie;
+
+/**
+ * Provides access to toggling pulldown menu shadings
+ **/
+GtkItemFactory * itemFactory = NULL;
+
+static GtkWidget * main_window_input_line = NULL;
+
+/**
+ * Shows the info window
+ **/
+static void show_infowindow(GtkButton * button,
+ gpointer dummy) {
+ if(infoWindow)
+ gtk_widget_show(infoWindow);
+ else
+ infoMessage(YES, "This window will show messages and the URIs of inserted
content etc\ninfo that might require copy-pasting elsewhere to be used...\n\n");
+}
+
+/**
+ * Shows the download window if some dl has been started
+ **/
+static void show_dlwindow(GtkButton * button,
+ gpointer dummy) {
+ if(dlWindow)
+ gtk_widget_show(dlWindow);
+}
+
+
+/**
+ * This method is called whenever the user clicks the
+ * search button of the main window.
+ *
+ * @param widget not used
+ * @param notebook the notebook where the opened result window will be put.
+ **/
+static void search(GtkWidget * widget,
+ GtkNotebook * notebook) {
+ gchar * searchString;
+ gchar * searchStringStart;
+ GtkWidget * tmp;
+ int i;
+
+ searchString
+ = gtk_entry_get_text(GTK_ENTRY(main_window_input_line));
+ if (searchString == NULL) {
+ guiMessage("searchString == NULL\n");
+ return;
+ }
+ searchString = STRDUP(searchString);
+
+ /* Remove heading spaces (parsing bugs if not) */
+ i = strlen(searchString)-1;
+ while ( (i>=0) &&
+ (searchString[i]==' ') ) {
+ searchString[i] = '\0';
+ i--;
+ }
+ searchStringStart = searchString;
+ while (*searchStringStart == ' ')
+ searchStringStart++;
+ if (*searchStringStart=='\0') {
+ guiMessage("No search key given!\n");
+ FREE(searchString);
+ return;
+ }
+
+ /* add a new page in the notebook with the
+ search results. getSearchWindow returns
+ the page */
+ tmp = getSearchWindow(searchStringStart);
+ if (tmp != NULL)
+ addToNotebook(searchStringStart,
+ tmp);
+
+ /* reset search line to empty */
+ gtk_entry_set_text(GTK_ENTRY(main_window_input_line),
+ "");
+ FREE(searchString);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyMain(GtkWidget * widget,
+ gpointer data) {
+ gdk_threads_leave(); /* avoid deadlock! */
+ stopCron();
+ delCronJob((CronJob)&cronCheckDaemon,
+ 30 * cronSECONDS,
+ NULL);
+ startCron();
+ gdk_threads_enter();
+ if (notebook != NULL) {
+ int i = 0;
+ while(gtk_notebook_get_nth_page(notebook, 0) != NULL) {
+ LOG(LOG_DEBUG,
+ "DEBUG: Removing search page %d\n",
+ i++);
+ gtk_notebook_remove_page(notebook, 0);
+ }
+ }
+ gdk_threads_leave(); /* avoid deadlock! */
+ SEMAPHORE_DOWN(refuseToDie);
+ gdk_threads_enter();
+ gtk_main_quit(); /* back to main method! */
+}
+
+/**
+ * Remove all of the root-nodes of a particular type
+ * from the directory database.
+ *
+ * @param unused GTK handle that is not used
+ * @param contexts bitmask of the databases that should be emptied.
+ **/
+static void emptyDirectoryDatabaseInd(GtkWidget * unused,
+ unsigned int contexts) {
+ emptyDirectoryDatabase(contexts);
+ refreshMenuSensitivity();
+}
+
+
+/**
+ * Method called from menu bar "File-Quit". Wrapper around
+ * destroy.
+ **/
+static void destroy_stub(void) {
+ destroyMain(NULL, NULL);
+}
+
+/**
+ * The pulldown menus.
+ **/
+static GtkItemFactoryEntry menu_items[] = {
+ { "/_File",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/File/_Insert",
+ "<control>I",
+ openSelectFile,
+ 0,
+ NULL },
+ { "/File/_Download URI",
+ "<control>D",
+ fetchURI,
+ 0,
+ NULL },
+ { "/File/Import di_rectory",
+ "<control>r",
+ importDirectory,
+ 0,
+ NULL },
+ { "/File/sep1",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+ { "/File/_Unindex file",
+ "<control>U",
+ openDeleteFile,
+ 0,
+ NULL },
+ { "/File/sep1",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+ { "/File/Show downloads",
+ "<control>w",
+ show_dlwindow,
+ 0,
+ NULL },
+ { "/File/Show messages",
+ "<control>m",
+ show_infowindow,
+ 0,
+ NULL },
+ { "/File/Show gnunetd stats",
+ NULL,
+ showStats,
+ 0,
+ NULL },
+ { "/File/_Plot gnunetd stats",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/File/Plot gnunetd stats/_Connectivity",
+ NULL,
+ displayStatistics,
+ STAT_CONNECTIVITY,
+ NULL },
+ { "/File/Plot gnunetd stats/C_PU Load",
+ NULL,
+ displayStatistics,
+ STAT_CPU_LOAD,
+ NULL },
+ { "/File/Plot gnunetd stats/_Inbound Traffic",
+ NULL,
+ displayStatistics,
+ STAT_IN_TRAFFIC,
+ NULL },
+ { "/File/Plot gnunetd stats/_Outbound Traffic",
+ NULL,
+ displayStatistics,
+ STAT_OUT_TRAFFIC,
+ NULL },
+ { "/File/sep1",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+ { "/File/_Quit",
+ "<control>Q",
+ destroy_stub,
+ 0,
+ NULL },
+ { "/_Advanced",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+
+ { "/Advanced/_Assemble Directory",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/Advanced/Assemble Directory/from _search results",
+ NULL,
+ openAssembleDirectoryDialog,
+ DIR_CONTEXT_SEARCH,
+ NULL },
+ { "/Advanced/Assemble Directory/from _inserted files",
+ NULL,
+ openAssembleDirectoryDialog,
+ DIR_CONTEXT_INSERT,
+ NULL },
+ { "/Advanced/Assemble Directory/from local _namespaces",
+ NULL,
+ openAssembleDirectoryDialog,
+ DIR_CONTEXT_INSERT_SB,
+ NULL },
+ { "/Advanced/Assemble Directory/from file identifiers from downloaded
_directories",
+ NULL,
+ openAssembleDirectoryDialog,
+ DIR_CONTEXT_DIRECTORY,
+ NULL },
+ { "/Advanced/Assemble Directory/sepx1",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+ { "/Advanced/Assemble Directory/from _all known file identifiers",
+ NULL,
+ openAssembleDirectoryDialog,
+ DIR_CONTEXT_ALL,
+ NULL },
+
+ { "/Advanced/sep1",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+
+ { "/Advanced/Manage _Pseudonyms",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/Advanced/Manage Pseudonyms/_Create new pseudonym",
+ NULL,
+ &openCreatePseudonymDialog,
+ 0,
+ NULL },
+ { "/Advanced/Manage Pseudonyms/_Delete pseudonym",
+ NULL,
+ &openDeletePseudonymDialog,
+ 0,
+ NULL },
+ { "/Advanced/_Insert into Namespace",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/Advanced/Insert into Namespace/Select from _search results",
+ NULL,
+ &openAssembleNamespaceDialog,
+ DIR_CONTEXT_SEARCH,
+ NULL },
+ { "/Advanced/Insert into Namespace/Select from _inserted files",
+ NULL,
+ &openAssembleNamespaceDialog,
+ DIR_CONTEXT_INSERT,
+ NULL },
+ { "/Advanced/Insert into Namespace/Select from results from downloaded
_directories",
+ NULL,
+ &openAssembleNamespaceDialog,
+ DIR_CONTEXT_INSERT,
+ NULL },
+ { "/Advanced/Insert into Namespace/Select from results from local
_namespaces",
+ NULL,
+ &openAssembleNamespaceDialog,
+ DIR_CONTEXT_INSERT_SB,
+ NULL },
+ { "/Advanced/Insert into Namespace/sepx2",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+ { "/Advanced/Insert into Namespace/Select from _all known file identifiers",
+ NULL,
+ &openAssembleNamespaceDialog,
+ DIR_CONTEXT_ALL,
+ NULL },
+ /* { "/Advanced/_Update content in Namespace",
+ NULL,
+ NULL,
+ 0,
+ NULL }, */
+ { "/Advanced/_Search Namespace",
+ "<control>S",
+ &searchNamespace,
+ 0,
+ NULL },
+
+ { "/Advanced/sep2",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+
+ { "/Advanced/_Reset File Identifiers",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/Advanced/Reset File Identifiers/List of _search results",
+ NULL,
+ emptyDirectoryDatabaseInd,
+ DIR_CONTEXT_SEARCH,
+ NULL },
+ { "/Advanced/Reset File Identifiers/List of _inserted files",
+ NULL,
+ emptyDirectoryDatabaseInd,
+ DIR_CONTEXT_INSERT,
+ NULL },
+ { "/Advanced/Reset File Identifiers/List of entries in local _namespaces",
+ NULL,
+ emptyDirectoryDatabaseInd,
+ DIR_CONTEXT_INSERT_SB,
+ NULL },
+ { "/Advanced/Reset File Identifiers/List of files from downloaded
_directories",
+ NULL,
+ emptyDirectoryDatabaseInd,
+ DIR_CONTEXT_DIRECTORY,
+ NULL },
+ { "/Advanced/Reset File Identifiers/sepx3",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+ { "/Advanced/Reset File Identifiers/_All known file identifiers",
+ NULL,
+ emptyDirectoryDatabaseInd,
+ DIR_CONTEXT_ALL,
+ NULL },
+
+ { "/Advanced/sep3",
+ NULL,
+ NULL,
+ 0,
+ "<Separator>" },
+
+ { "/Advanced/Launch gnunetd",
+ NULL,
+ launchDaemon,
+ 0,
+ NULL },
+
+ { "/Advanced/Kill gnunetd",
+ NULL,
+ killDaemon,
+ 0,
+ NULL },
+
+/*
+ { "/_Options",
+ NULL,
+ NULL,
+ 0,
+ "<Branch>" },
+ { "/Options/Preferences(not impl)",
+ NULL,
+ NULL,
+ 0,
+ NULL },
+*/
+ { "/_Help",
+ NULL,
+ NULL,
+ 0,
+ "<LastBranch>" },
+ { "/Help/_About",
+ NULL,
+ about,
+ 0,
+ NULL },
+};
+
+gint doRefreshMenuSensitivity(SaveCall *call) {
+ int havePseudo;
+ int haveSearch;
+ int haveInsert;
+ int haveDirect;
+ int haveNamesp;
+ int haveAny;
+ int value;
+ GtkWidget * entry;
+
+ havePseudo = havePseudonyms();
+ if (NO == havePseudo)
+ value = FALSE;
+ else
+ value = TRUE;
+ haveSearch = 0 < iterateDirectoryDatabase(DIR_CONTEXT_SEARCH, NULL, NULL);
+ haveInsert = 0 < iterateDirectoryDatabase(DIR_CONTEXT_INSERT, NULL, NULL);
+ haveDirect = 0 < iterateDirectoryDatabase(DIR_CONTEXT_DIRECTORY, NULL, NULL);
+ haveNamesp = 0 < iterateDirectoryDatabase(DIR_CONTEXT_INSERT_SB, NULL, NULL);
+ haveAny = 0 < iterateDirectoryDatabase(DIR_CONTEXT_ALL, NULL, NULL);
+
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Manage Pseudonyms/"
+ "Delete pseudonym");
+ gtk_widget_set_sensitive(entry, value);
+
+
+ if ( (NO == havePseudo) || (NO == haveSearch) )
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Insert into Namespace/"
+ "Select from search results");
+ gtk_widget_set_sensitive(entry, value);
+
+ if ( (NO == havePseudo) || (NO == haveInsert) )
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Insert into Namespace/"
+ "Select from inserted files");
+ gtk_widget_set_sensitive(entry, value);
+ if ( (NO == havePseudo) || (NO == haveDirect) )
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Insert into Namespace/"
+ "Select from results from downloaded
directories");
+ gtk_widget_set_sensitive(entry, value);
+
+ if ( (NO == havePseudo) || (NO == haveNamesp) )
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Insert into Namespace/"
+ "Select from results from local
namespaces");
+ gtk_widget_set_sensitive(entry, value);
+
+ if ( (NO == havePseudo) || (NO == haveAny) )
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Insert into Namespace/"
+ "Select from all known file identifiers");
+ gtk_widget_set_sensitive(entry, value);
+ if ( NO == haveAny)
+ value = FALSE;
+ else
+ value = TRUE;
+
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Assemble Directory/"
+ "from all known file identifiers");
+ gtk_widget_set_sensitive(entry, value);
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Reset File Identifiers/"
+ "All known file identifiers");
+ gtk_widget_set_sensitive(entry, value);
+
+
+ if ( NO == haveSearch)
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Assemble Directory/"
+ "from search results");
+ gtk_widget_set_sensitive(entry, value);
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Reset File Identifiers/"
+ "List of search results");
+ gtk_widget_set_sensitive(entry, value);
+
+ if ( NO == haveInsert)
+ value = FALSE;
+ else
+ value = TRUE;
+
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Assemble Directory/"
+ "from inserted files");
+ gtk_widget_set_sensitive(entry, value);
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Reset File Identifiers/"
+ "List of inserted files");
+ gtk_widget_set_sensitive(entry, value);
+
+
+ if ( NO == haveDirect)
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Assemble Directory/"
+ "from file identifiers from downloaded
directories");
+ gtk_widget_set_sensitive(entry, value);
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Reset File Identifiers/"
+ "List of files from downloaded
directories");
+ gtk_widget_set_sensitive(entry, value);
+
+
+ if ( NO == haveNamesp)
+ value = FALSE;
+ else
+ value = TRUE;
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Assemble Directory/"
+ "from local namespaces");
+ gtk_widget_set_sensitive(entry, value);
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Insert into Namespace/"
+ "Select from results from local
namespaces");
+ gtk_widget_set_sensitive(entry, value);
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/Advanced/Reset File Identifiers/"
+ "List of entries in local namespaces");
+ gtk_widget_set_sensitive(entry, value);
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+void refreshMenuSensitivity() {
+ gtkSaveCall((GtkFunction) doRefreshMenuSensitivity, NULL);
+}
+
+/**
+ * This creates the main window
+ **/
+static void makeMainWindow() {
+ GtkWidget * window;
+ GtkWidget * button;
+ GtkWidget * hbox;
+ GtkWidget * vbox;
+ GtkWidget * menubar;
+ GtkWidget * table;
+ GtkWidget * label;
+ GtkWidget * entry;
+ GtkAccelGroup * accel_group;
+
+ gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
+
+ /* create main window */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "GNUnet: gtk+ GUI");
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 780, /* x-size */
+ 300); /* y-size */
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ NULL);
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyMain),
+ NULL);
+ gtk_widget_show(vbox);
+
+
+ /* create pulldown menues */
+ accel_group = gtk_accel_group_new ();
+ itemFactory = gtk_item_factory_new(GTK_TYPE_MENU_BAR,
+ "<main>",
+ accel_group);
+ gtk_item_factory_create_items(itemFactory,
+ nmenu_items,
+ menu_items,
+ NULL);
+ gtk_window_add_accel_group(GTK_WINDOW (window),
+ accel_group);
+ menubar = gtk_item_factory_get_widget(itemFactory,
+ "<main>");
+ gtk_box_pack_start(GTK_BOX (vbox),
+ menubar,
+ FALSE,
+ TRUE,
+ 0);
+
+
+
+ /* set some default options and show */
+
+
+ entry = gtk_item_factory_get_widget(itemFactory,
+ "/File/Show downloads");
+ gtk_widget_set_sensitive(entry, FALSE);
+ refreshMenuSensitivity();
+
+ gtk_widget_show(menubar);
+
+ /* a table to put the search results notebook to */
+ table = gtk_table_new(6, 6, TRUE);
+ gtk_box_pack_start(GTK_BOX (vbox),
+ table,
+ TRUE,
+ TRUE, 0);
+ gtk_widget_show(table);
+
+ /* add "notebook" for search results */
+ notebook = GTK_NOTEBOOK(gtk_notebook_new());
+ gtk_notebook_set_scrollable(notebook,
+ TRUE);
+ gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook),
+ GTK_POS_TOP);
+ gtk_table_attach_defaults(GTK_TABLE(table),
+ GTK_WIDGET(notebook),
+ 0, 6, 0, 6);
+ gtk_widget_show(GTK_WIDGET(notebook));
+
+ /* BEGIN of SEARCH BOX CODE */
+ /* At the bottom, put the search box */
+ hbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX (vbox),
+ hbox,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(hbox);
+
+ /* search entry label */
+ label = gtk_label_new("Keyword(s):");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+
+ /* search input line */
+ main_window_input_line = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ main_window_input_line,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(main_window_input_line),
+ "activate",
+ GTK_SIGNAL_FUNC(search),
+ notebook);
+ gtk_widget_show(main_window_input_line);
+
+ /* search button */
+ button = gtk_button_new_with_label("Search");
+ gtk_signal_connect(GTK_OBJECT (button),
+ "clicked",
+ GTK_SIGNAL_FUNC(search), notebook);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+ /* END of SEARCH BOX CODE */
+
+ /* show the main window */
+ gtk_widget_show(window);
+}
+
+/**
+ * Perform option parsing from the command line.
+ **/
+static int parseOptions(int argc,
+ char ** argv) {
+ int c;
+
+ while (1) {
+ int option_index=0;
+ static struct GNoption long_options[] = {
+ LONG_DEFAULT_OPTIONS,
+ { 0,0,0,0 }
+ };
+
+ c = GNgetopt_long(argc,
+ argv,
+ "vhdc:L:H:",
+ long_options,
+ &option_index);
+
+ if (c == -1)
+ break; /* No more flags to process */
+ if (YES == parseDefaultOptions(c, GNoptarg))
+ continue;
+ switch(c) {
+ case 'v':
+ printf("GNUnet v%s, AFS v%s\n",
+ VERSION,
+ AFS_VERSION);
+ return SYSERR;
+ case 'h': {
+ static Help help[] = {
+ HELP_CONFIG,
+ HELP_HELP,
+ HELP_HOSTNAME,
+ HELP_LOGLEVEL,
+ HELP_VERSION,
+ HELP_END,
+ };
+ formatHelp("gnunet-gtk [OPTIONS]",
+ "Start GNUnet GTK user interface.",
+ help);
+ return SYSERR;
+ }
+ default:
+ LOG(LOG_FAILURE,
+ "Unknown option %c. Aborting.\n"
+ "Use --help to get a list of options.\n",
+ c);
+ return SYSERR;
+ } /* end of parsing commandline */
+ }
+ if (GNoptind < argc) {
+ fprintf(stderr,
+ "Invalid arguments: ");
+ while (GNoptind < argc)
+ fprintf(stderr,
+ "%s ",
+ argv[GNoptind++]);
+ fprintf(stderr,
+ "\n");
+ return SYSERR;
+ }
+ return OK;
+}
+
+/**
+ * The main function.
+ **/
+int main(int argc,
+ char * argv[]) {
+
+ if (SYSERR == initUtil(argc, argv, &parseOptions))
+ return(0);
+ initGTKStatistics();
+
+ startCron();
+ refuseToDie = SEMAPHORE_NEW(1);
+
+ g_thread_init(NULL);
+ /* gdk_threads_init(); */
+ gtk_init(&argc, &argv);
+ gtkInitSaveCalls();
+
+ makeMainWindow();
+ /* Check if gnunetd is running */
+ checkForDaemon();
+
+ /* refresh the kill/launch sensitivities once per 30 secs */
+ addCronJob((CronJob)&cronCheckDaemon,
+ 0,
+ 30 * cronSECONDS,
+ NULL);
+ startAFSPriorityTracker();
+ gdk_threads_enter();
+ setCustomLogProc(addLogEntry);
+#ifdef MINGW
+ FreeConsole();
+#endif
+ gtk_main();
+ setCustomLogProc(NULL);
+ gdk_threads_leave();
+ gtkDoneSaveCalls();
+ stopCron();
+ stopAFSPriorityTracker();
+ LOG(LOG_DEBUG, "DEBUG: GUI leaving...\n");
+ SEMAPHORE_FREE(refuseToDie);
+
+ doneGTKStatistics();
+ doneUtil();
+ return 0;
+}
+
+/* end of main.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/main.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/main.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/main.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,39 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/main.h
+ * @author Igor Wronsky
+ **/
+
+#ifndef GTKUI_MAIN_H
+#define GTKUI_MAIN_H
+
+/**
+ * This semaphore can be up'ed by threads that will
+ * need the GUI and that want to prevent gnunet-gtk
+ * from exiting while they are running.
+ **/
+extern Semaphore * refuseToDie;
+
+extern GtkItemFactory * itemFactory;
+
+void refreshMenuSensitivity();
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,1492 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/namespace.c
+ * @brief Namespace dialog for the AFS interface
+ * @author Christian Grothoff
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "namespace.h"
+#include "search.h"
+#include "main.h"
+
+/**
+ * @brief state of the insert into namespace window
+ **/
+typedef struct {
+ char * fileName;
+ GtkWidget * window;
+ GtkWidget * passwordLine;
+ GtkWidget * pseudonymList;
+ GtkWidget * sblockList;
+ GtkWidget * availableList;
+ GtkWidget * updateInterval;
+ GtkWidget * currentKey;
+ GtkWidget * nextKey;
+ SBlock ** updateableEntries;
+ int updateableCount;
+ HashCode160 selectedPseudonym;
+} NamespaceInsertWindowModel;
+
+static int parseTime(char * t) {
+ int pos;
+ int start;
+ int ret;
+ unsigned int val;
+ char * tmp;
+
+ ret = 0;
+ pos = 0;
+
+ while (t[pos] != '\0') {
+ start = pos;
+ while ( (t[pos] != ' ') &&
+ (t[pos] != '\0') )
+ pos++;
+ tmp = STRNDUP(&t[start],
+ pos - start);
+ if (1 != sscanf(tmp,
+ "%u",
+ &val) )
+ return -1; /* parse error */
+ FREE(tmp);
+ while ( t[pos] == ' ')
+ pos++;
+ start = pos;
+ while ( (t[pos] != ' ') &&
+ (t[pos] != '\0') )
+ pos++;
+ if (0 == strncasecmp(&t[start],
+ "minute",
+ strlen("minute")))
+ ret += 60 * val;
+ else if (0 == strncasecmp(&t[start],
+ "second",
+ strlen("second")))
+ ret += val;
+ else if (0 == strncasecmp(&t[start],
+ "hour",
+ strlen("hour")))
+ ret += 60 * 60 * val;
+ else if (0 == strncasecmp(&t[start],
+ "day",
+ strlen("day")))
+ ret += 24 * 60 * 60 * val;
+ else
+ return -1; /* parse error */
+ while ( t[pos] == ' ')
+ pos++;
+ }
+ return ret;
+}
+
+/**
+ * Collects the results of the assembly dialog, creates an insertion
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void buildNSEntry(GtkWidget * dummy,
+ NamespaceInsertWindowModel * ewm) {
+ GList * tmp;
+ int row;
+ gchar * key[1];
+ char * currentKey;
+ char * nextKey;
+ char * name;
+ char * pass;
+ char * message;
+ Hostkey pseudo;
+ HashCode160 k; /* = key, next - increment */
+ HashCode160 n; /* = next or namespace ID depending where in code */
+ TIME_T interval;
+ GNUNET_TCP_SOCKET * sock;
+ SBlock * sb;
+ RootNode * rn;
+ HexName hex1;
+ HexName hex2;
+ char * updateInterval;
+ TIME_T now;
+ TIME_T creationTime;
+ char * desc;
+ char * mime;
+
+ updateInterval =
gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry));
+ if (updateInterval == NULL) {
+ guiMessage("ERROR: you must specify an update frequency.\n");
+ return;
+ }
+ if (strcmp(updateInterval, "--no updates--") == 0) {
+ interval = 0;
+ } else if (strcmp(updateInterval, "--sporadic updates--") == 0) {
+ interval = -1;
+ } else {
+ interval = parseTime(updateInterval);
+ if (interval == -1) {
+ guiMessage("ERROR: parsing of time interval failed. "
+ "Use \"(INT [seconds|minutes|hours])*\" format.\n");
+ return;
+ }
+ }
+ tmp = GTK_CLIST(ewm->pseudonymList)->selection;
+ if (NULL == tmp) {
+ guiMessage("ERROR: you must select a pseudonym (Error #1).\n");
+ return;
+ }
+ row = (int) tmp->data;
+ if ( row < 0 ) {
+ guiMessage("ERROR: you must select a pseudonym (Error #2).\n");
+ return;
+ }
+
+ key[0] = NULL;
+ gtk_clist_get_text(GTK_CLIST(ewm->pseudonymList),
+ row,
+ 0,
+ &key[0]);
+ name = key[0];
+ if (name == NULL) {
+ guiMessage("ERROR: you must select a pseudonym (Error #3).\n");
+ return; /* should never happen... */
+ }
+ pass = gtk_entry_get_text(GTK_ENTRY(ewm->passwordLine));
+ if (strlen(pass) == 0)
+ pass = NULL;
+ pseudo = readPseudonym(name, pass);
+ if (pseudo == NULL) {
+ guiMessage("ERROR: password specified invalid for pseudonym.\n");
+ return;
+ }
+
+ currentKey = gtk_entry_get_text(GTK_ENTRY(ewm->currentKey));
+ nextKey = gtk_entry_get_text(GTK_ENTRY(ewm->nextKey));
+
+ tmp = GTK_CLIST(ewm->availableList)->selection;
+ if (NULL == tmp) {
+ guiMessage("ERROR: you must select a file.\n");
+ return;
+ }
+ row = (int) tmp->data;
+ if ( row < 0 ) {
+ guiMessage("ERROR: you must select a file.\n");
+ freeHostkey(pseudo);
+ return;
+ }
+ rn = gtk_clist_get_row_data(GTK_CLIST(ewm->availableList),
+ row);
+
+ /* select/derive current & next IDs! */
+ tmp = GTK_CLIST(ewm->sblockList)->selection;
+ if ( (NULL == tmp) ||
+ ( ((int)tmp->data) == 0) ) {
+ /* "--no update--" or nothing selected, pick random IDs */
+ tryhex2hashOrHashString(currentKey,
+ &k);
+ switch(interval) {
+ case SBLOCK_UPDATE_NONE:
+ /* no updates => next == this */
+ memcpy(&n,
+ &k,
+ sizeof(HashCode160));
+ break;
+ case SBLOCK_UPDATE_SPORADIC:
+ /* sporadic update; pick specified ID if given,
+ otherwise go random */
+ tryhex2hashOrHashString(nextKey,
+ &n);
+ break;
+ default:
+ /* periodic update, the very first next id will be random */
+ makeRandomId(&n);
+ break;
+ }
+
+ TIME(&creationTime);
+ } else {
+ SBlock * pred;
+
+ row = ((int) tmp->data) - 1; /* -1: first item is always "no update" */
+ if (row >= ewm->updateableCount) {
+ guiMessage("ERROR: this should never happen.\n");
+ freeHostkey(pseudo);
+ return;
+ }
+ pred = ewm->updateableEntries[row];
+ /* now, compute CURRENT ID and next ID */
+ TIME(&now);
+ computeIdAtTime(pred,
+ now,
+ &k);
+ if ( (interval != SBLOCK_UPDATE_NONE) &&
+ (interval != SBLOCK_UPDATE_SPORADIC) ) {
+ int delta;
+ /* periodic update */
+ delta = now - ntohl(pred->creationTime);
+ delta = delta / ntohl(pred->updateInterval);
+ if (delta == 0)
+ delta = 1; /* force to be in the future from the updated block! */
+ creationTime = ntohl(pred->creationTime) + delta *
ntohl(pred->updateInterval);
+
+ /* periodic update, compute _next_ ID as increment! */
+ addHashCodes(&k,
+ &pred->identifierIncrement,
+ &n); /* n = k + inc */
+ } else {
+ if (interval == SBLOCK_UPDATE_SPORADIC) {
+ /* sporadic update, pick random next ID
+ if not specified! */
+ tryhex2hashOrHashString(nextKey,
+ &n);
+ TIME(&creationTime);
+ } else {
+ /* updating non-updateable SBlock!? Should
+ never happen! */
+ guiMessage("ERROR: attempt to update an non-updateble SBlock, this
should never happen!\n");
+ BREAK();
+ return;
+ }
+ }
+ } /* end of selecting k and n based on update mechanism */
+
+ name = getFilenameFromNode(rn);
+ desc = getDescriptionFromNode(rn);
+ mime = getMimetypeFromNode(rn);
+ sb = buildSBlock(pseudo,
+ &rn->header.fileIdentifier,
+ desc,
+ name,
+ mime,
+ creationTime,
+ interval,
+ &k,
+ &n);
+ FREE(desc);
+ FREE(mime);
+ freeHostkey(pseudo);
+ if (sb == NULL) {
+ FREE(name);
+ guiMessage("ERROR: failed to build SBlock. Consult logs.");
+ return;
+ }
+ sock = getClientSocket();
+ if (sock == NULL) {
+ FREE(sb);
+ FREE(name);
+ guiMessage("ERROR: could not connect to gnunetd.");
+ return;
+ }
+ if (OK != insertSBlock(sock,
+ sb)) {
+ guiMessage("ERROR: failed to insert SBlock. "
+ "Consult logs.");
+ releaseClientSocket(sock);
+ FREE(name);
+ FREE(sb);
+ return;
+ }
+ releaseClientSocket(sock);
+ /* obtain "n = S", the namespace ID */
+ hash(&sb->subspace,
+ sizeof(PublicKey),
+ &n);
+ FREE(sb);
+
+ currentKey = STRDUP(currentKey); /* would be destroyed with window */
+
+ /* destroy the window */
+ gtk_widget_destroy(ewm->window);
+ refreshMenuSensitivity();
+
+ hash2hex(&n, &hex1);
+ hash2hex(&k, &hex2);
+ message = MALLOC(512);
+ sprintf(message,
+ "%s inserted into namespace as\n"
+ " gnunet://afs/%s/%s\n",
+ name,
+ (char*)&hex1,
+ (currentKey[0] != '\0') ? currentKey : (char*)&hex2);
+ LOG(LOG_DEBUG,
+ "DEBUG: %s\n",
+ message);
+ infoMessage(NO, message);
+ /* FIXME remind about the next key ... if its a verbal one,
+ it can't be reconstructed from the sblock later :( */
+ FREE(currentKey);
+ FREE(message);
+ FREE(name);
+}
+
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyNamespaceInsertWindow(GtkWidget * widget,
+ NamespaceInsertWindowModel * ewm) {
+ int i;
+ GList * tmp;
+ int row;
+
+ gtk_clist_freeze(GTK_CLIST(ewm->availableList));
+ tmp = GTK_CLIST(ewm->availableList)->selection;
+ while (tmp) {
+ row = (int)tmp->data;
+ tmp = tmp->next;
+
+ FREENONNULL(gtk_clist_get_row_data(GTK_CLIST(ewm->availableList),
+ row));
+ gtk_clist_remove(GTK_CLIST(ewm->availableList),
+ row);
+ }
+ gtk_clist_thaw(GTK_CLIST(ewm->availableList));
+
+ for (i=0;i<ewm->updateableCount;i++)
+ FREE(ewm->updateableEntries[i]);
+ GROW(ewm->updateableEntries,
+ ewm->updateableCount,
+ 0);
+ FREE(ewm);
+}
+
+static void appendToCList(RootNode * root,
+ NamespaceInsertWindowModel * ewm) {
+ gchar * entry[1];
+ char * name;
+ char * desc;
+ char * mime;
+ RootNode * copy;
+ int row;
+
+ entry[0] = MALLOC(strlen(root->header.filename)+
+ strlen(root->header.description)+
+ strlen(root->header.mimetype)+
+ 128);
+ name = getFilenameFromNode(root);
+ desc = getDescriptionFromNode(root);
+ mime = getMimetypeFromNode(root);
+ sprintf(entry[0],
+ "%s, %s (%s, %u bytes)",
+ name,
+ desc,
+ mime,
+ (unsigned int) ntohl(root->header.fileIdentifier.file_length));
+ FREE(name);
+ FREE(desc);
+ FREE(mime);
+ row = gtk_clist_append(GTK_CLIST(ewm->availableList),
+ entry);
+ FREE(entry[0]);
+ copy = MALLOC(sizeof(RootNode));
+ memcpy(copy,
+ root,
+ sizeof(RootNode));
+ /* note: if you wish any clist to be sortable, you must
+ store the associated data in the list itself, not in an external
+ array! (same goes for all clists, but currently only "Files"
+ is sortable of the namespace related lists) - IW */
+ gtk_clist_set_row_data(GTK_CLIST(ewm->availableList),
+ row,
+ copy);
+}
+
+static void checkUpdateableSBlocks(SBlock * sb,
+ NamespaceInsertWindowModel * ewm) {
+ gchar * entry[1];
+ HashCode160 namespace;
+ SBlock * tmp;
+ int i;
+
+ if (SBLOCK_UPDATE_NONE == ntohl(sb->updateInterval))
+ return; /* non-updateable SBlock */
+
+ /* check if namespace *matches* selected Pseudonym! */
+ hash(&sb->subspace,
+ sizeof(PublicKey),
+ &namespace);
+ if (! equalsHashCode160(&ewm->selectedPseudonym,
+ &namespace) )
+ return;
+
+ /* check if SBlock is valid */
+ if (SYSERR == verifySBlock(sb))
+ return;
+
+ /* skip if duplicate periodical (essentially irrelevant which
+ * of the blocks gets updated, the result is the same).
+ * FIXME? this causes trouble if two unrelated sblocks have
+ * identical identifierIncrement, which should be unlikely? */
+ if(ntohl(sb->updateInterval)>0) {
+ for(i=0;i<ewm->updateableCount;i++) {
+ tmp = ewm->updateableEntries[i];
+ if ((0 == memcmp(&tmp->identifierIncrement,
+ &sb->identifierIncrement,
+ sizeof(HashCode160))) ) {
+ LOG(LOG_DEBUG,
+ "DEBUG: skipping duplicate SBlock entry ...\n");
+ return;
+ }
+ }
+ }
+
+ /* ok, all checks pass: add */
+ sb->filename[MAX_FILENAME_LEN/2-1] = 0;
+ sb->description[MAX_DESC_LEN-1] = 0;
+ sb->mimetype[MAX_MIMETYPE_LEN/2-1] = 0;
+ entry[0] = MALLOC(strlen(sb->filename)+
+ strlen(sb->description)+
+ strlen(sb->mimetype)+
+ 128);
+ sprintf(entry[0],
+ "%s, %s (%s, %u bytes)",
+ sb->filename,
+ sb->description,
+ sb->mimetype,
+ (unsigned int) ntohl(sb->fileIdentifier.file_length));
+ gtk_clist_append(GTK_CLIST(ewm->sblockList),
+ entry);
+ FREE(entry[0]);
+ GROW(ewm->updateableEntries,
+ ewm->updateableCount,
+ ewm->updateableCount+1);
+ ewm->updateableEntries[ewm->updateableCount-1]
+ = MALLOC(sizeof(SBlock));
+ memcpy(ewm->updateableEntries[ewm->updateableCount-1],
+ sb,
+ sizeof(SBlock));
+}
+
+/**
+ * Only the ewm argument may be used since
+ * we may also be called from enter_callback
+ * which is called whenever the user presses
+ * ENTER in the password line.
+ */
+static void pselectCallback(GtkWidget * unused,
+ gint rowX,
+ gint column,
+ GdkEventButton * event,
+ NamespaceInsertWindowModel * ewm) {
+ GList * tmp;
+ int row;
+ gchar * key[1];
+ char * name;
+ char * pass;
+ Hostkey pseudo;
+ PublicKey pkey;
+ int i;
+ gchar * titlesNo[1] = { "--no update--" };
+
+ /* first, clear off the old sblock list */
+ gtk_clist_freeze(GTK_CLIST(ewm->sblockList));
+ gtk_clist_clear(GTK_CLIST(ewm->sblockList));
+ gtk_clist_append(GTK_CLIST(ewm->sblockList),
+ &titlesNo[0]);
+ gtk_clist_thaw(GTK_CLIST(ewm->sblockList));
+
+ /* update ewm->selectedPseudonym */
+ tmp = GTK_CLIST(ewm->pseudonymList)->selection;
+ if (NULL == tmp) {
+ return;
+ }
+ row = (int) tmp->data;
+ if ( row < 0 ) {
+ return;
+ }
+
+ key[0] = NULL;
+ gtk_clist_get_text(GTK_CLIST(ewm->pseudonymList),
+ row,
+ 0,
+ &key[0]);
+ name = key[0];
+ if (name == NULL) {
+ return;
+ }
+ pass = gtk_entry_get_text(GTK_ENTRY(ewm->passwordLine));
+ if (strlen(pass) == 0)
+ pass = NULL;
+ pseudo = readPseudonym(name, pass);
+ if (pseudo == NULL) {
+ return; /* wait for password to be entered... */
+ }
+ getPublicKey(pseudo,
+ &pkey);
+ freeHostkey(pseudo);
+ hash(&pkey,
+ sizeof(PublicKey),
+ &ewm->selectedPseudonym);
+
+ /* clear entries from possible previous selection */
+ for (i=0;i<ewm->updateableCount;i++)
+ FREE(ewm->updateableEntries[i]);
+ GROW(ewm->updateableEntries,
+ ewm->updateableCount,
+ 0);
+
+ gtk_clist_freeze(GTK_CLIST(ewm->sblockList));
+ iterateDirectoryDatabase(DIR_CONTEXT_INSERT_SB,
+ (RootNodeCallback)&checkUpdateableSBlocks,
+ ewm);
+ gtk_clist_thaw(GTK_CLIST(ewm->sblockList));
+}
+
+static void enter_callback(GtkWidget * unused,
+ NamespaceInsertWindowModel * ewm) {
+ pselectCallback(NULL, 0, 0, NULL, ewm);
+}
+
+static void selectFrequencyCallback(GtkWidget * unused,
+ NamespaceInsertWindowModel * ewm) {
+ gchar * choice;
+ GList * tmp;
+
+ tmp = GTK_CLIST(ewm->sblockList)->selection;
+ if ( (NULL == tmp) ||
+ ( ((int)tmp->data) == 0) ) {
+ choice =
gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry));
+ if (strcmp(choice, "--sporadic updates--") == 0) {
+ gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+ gtk_widget_set_sensitive(ewm->nextKey, TRUE);
+ } else if (strcmp(choice, "--no updates--") == 0) {
+ gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+ gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+ gtk_entry_set_text(GTK_ENTRY(ewm->nextKey),
+ "");
+ } else {
+ /* periodic */
+ gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+ gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+ gtk_entry_set_text(GTK_ENTRY(ewm->nextKey),
+ "");
+ }
+ } else {
+ /* determined by SBlock, and SBlock has
+ already set the entries correctly */
+ }
+}
+
+/**
+ * The user selected an SBlock for an update. Set the "update
+ * interval" field according to the update interval
+ * found in the SBlock.
+ */
+static void selectSBlockCallback(GtkWidget * unused,
+ gint rowX,
+ gint column,
+ GdkEventButton * event,
+ NamespaceInsertWindowModel * ewm) {
+ SBlock * pred;
+ TIME_T interval;
+ GList * tmp;
+ int row;
+ unsigned int days, hours, minutes, seconds;
+ char * txt;
+ HexName hex;
+ HashCode160 currentId;
+ HashCode160 nextId;
+ TIME_T now;
+
+ gtk_entry_set_text(GTK_ENTRY(ewm->nextKey),
+ "");
+
+ tmp = GTK_CLIST(ewm->sblockList)->selection;
+ if ( (NULL == tmp) ||
+ ( ((int)tmp->data) == 0) ) {
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry),
+ "--no updates--");
+ gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+ gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+ gtk_widget_set_sensitive(ewm->updateInterval, TRUE);
+ gtk_entry_set_text(GTK_ENTRY(ewm->currentKey),
+ "");
+ gtk_entry_set_text(GTK_ENTRY(ewm->nextKey),
+ "");
+ return;
+ }
+
+ row = ((int) tmp->data) - 1; /* -1: first item is always "no update" */
+ if (row >= ewm->updateableCount) {
+ guiMessage("ERROR: this should never happen.\n");
+ gtk_widget_set_sensitive(ewm->currentKey, FALSE);
+ gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+ gtk_widget_set_sensitive(ewm->updateInterval, FALSE);
+ return;
+ }
+ pred = ewm->updateableEntries[row];
+
+ interval = ntohl(pred->updateInterval);
+ if (interval == SBLOCK_UPDATE_SPORADIC) {
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry),
+ "--sporadic updates--");
+ hash2hex(&pred->nextIdentifier,
+ &hex);
+ gtk_entry_set_text(GTK_ENTRY(ewm->currentKey),
+ (gchar*)&hex);
+
+ gtk_widget_set_sensitive(ewm->currentKey, FALSE);
+ gtk_widget_set_sensitive(ewm->nextKey, TRUE);
+ gtk_widget_set_sensitive(ewm->updateInterval, FALSE);
+ return;
+ }
+ seconds = interval % 60;
+ interval = interval / 60;
+ minutes = interval % 60;
+ interval = interval / 60;
+ hours = interval % 24;
+ interval = interval / 24;
+ days = interval;
+ txt = MALLOC(256);
+ sprintf(txt,
+ "%u day%s %u hour%s %u minute%s %u second%s",
+ days,
+ (days == 1) ? "" : "s",
+ hours,
+ (hours == 1) ? "" : "s",
+ minutes,
+ (minutes == 1) ? "" : "s",
+ seconds,
+ (seconds == 1) ? "" : "s");
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry),
+ txt);
+ FREE(txt);
+ /* periodic: all are pre-determined! */
+
+ TIME(&now);
+ computeIdAtTime(pred, now, ¤tId);
+ computeIdAtTime(pred, now + ntohl(pred->updateInterval), &nextId);
+ hash2hex(¤tId,
+ &hex);
+ gtk_entry_set_text(GTK_ENTRY(ewm->currentKey),
+ (gchar*)&hex);
+ hash2hex(&nextId,
+ &hex);
+ gtk_entry_set_text(GTK_ENTRY(ewm->nextKey),
+ (gchar*)&hex);
+ gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+ gtk_widget_set_sensitive(ewm->currentKey, FALSE);
+ gtk_widget_set_sensitive(ewm->updateInterval, FALSE);
+}
+
+
+/**
+ * Open a window to allow the user to build a namespace entry.
+ *
+ * @param unused GTK handle that is not used
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleNamespaceDialog(GtkWidget * unused,
+ unsigned int context) {
+ NamespaceInsertWindowModel * ewm;
+ GtkWidget * window;
+ GtkWidget * vbox, *vboxX, * hbox, * hboxX;
+ GtkWidget * clist;
+ GtkWidget * scrolled_window;
+ GtkWidget * label;
+ GtkWidget * separator;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+ GtkWidget * combo;
+ GList * glist;
+ int i;
+ int cnt;
+ char ** list;
+ gchar * titles[1] = { "Pseudonyms" };
+ gchar * titlesNo[1] = { "--no update--" };
+ gchar * titlesSBlocks[1] = { "Updateable SBlocks for pseudonym" };
+ gchar * titlesAvailable[1] = { "Files available" };
+
+ ewm = MALLOC(sizeof(NamespaceInsertWindowModel));
+ memset(ewm, 0, sizeof(NamespaceInsertWindowModel));
+
+ /* create new window for editing */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ewm->window = window;
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 780,
+ 580);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Insert into Namespace");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 15);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyNamespaceInsertWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+
+ /* arrange a pseudonym box left to a "select SBlock to update" box */
+ hbox = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+
+ /* add a list of pseudonyms */
+ vboxX = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(hbox),
+ vboxX);
+ gtk_widget_show(vboxX);
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vboxX),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titles);
+ ewm->pseudonymList = clist;
+ gtk_clist_set_column_width(GTK_CLIST(clist),
+ 0,
+ 150);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+ /* add the known Pseudonyms to the list */
+ list = NULL;
+ cnt = listPseudonyms(&list);
+ if (cnt > 0) {
+ gtk_clist_freeze(GTK_CLIST(clist));
+ for (i=0;i<cnt;i++) {
+ gtk_clist_append(GTK_CLIST(clist),
+ &list[i]);
+ FREE(list[i]);
+ }
+ gtk_clist_thaw(GTK_CLIST(clist));
+ }
+ FREENONNULL(list);
+ /* add callback: if a pseudonym is
+ selected, the "updateable SBlocks" list
+ must be updated! */
+ gtk_signal_connect(GTK_OBJECT(clist),
+ "select_row",
+ GTK_SIGNAL_FUNC(pselectCallback),
+ ewm);
+
+
+
+ /* Create a line to enter the password */
+ hboxX = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vboxX),
+ hboxX,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(hboxX);
+ label = gtk_label_new("Pseudonym Password:");
+ gtk_box_pack_start(GTK_BOX(hboxX),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->passwordLine = gtk_entry_new();
+ gtk_entry_set_visibility(GTK_ENTRY(ewm->passwordLine), FALSE);
+ gtk_box_pack_start(GTK_BOX(hboxX),
+ ewm->passwordLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->passwordLine),
+ "");
+ gtk_widget_show(ewm->passwordLine);
+ gtk_signal_connect(GTK_OBJECT(ewm->passwordLine),
+ "activate",
+ GTK_SIGNAL_FUNC(enter_callback),
+ ewm);
+
+ /* add separator */
+ separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ separator,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(separator);
+ /* ok, now another feature in the hbox:
+ select which SBlock to update! */
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titlesSBlocks);
+ ewm->sblockList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+ /* add the known SBlocks to the list */
+ list = NULL;
+ gtk_clist_freeze(GTK_CLIST(clist));
+ gtk_clist_append(GTK_CLIST(clist),
+ &titlesNo[0]);
+ gtk_clist_thaw(GTK_CLIST(clist));
+ gtk_signal_connect(GTK_OBJECT(clist),
+ "select_row",
+ GTK_SIGNAL_FUNC(selectSBlockCallback),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(clist),
+ "unselect_row",
+ GTK_SIGNAL_FUNC(selectSBlockCallback),
+ ewm);
+
+ /* add separator */
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(separator);
+
+ /* add interval / non-periodic selection */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Update frequency:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+
+ combo = gtk_combo_new();
+ ewm->updateInterval = combo;
+ gtk_container_add(GTK_CONTAINER(hbox),
+ combo);
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry),
+ "--no updates--");
+ glist = NULL;
+ glist = g_list_append(glist, "--no updates--");
+ glist = g_list_append(glist, "--sporadic updates--");
+ glist = g_list_append(glist, "12 hours");
+ glist = g_list_append(glist, "1 day");
+ glist = g_list_append(glist, "2 days");
+ glist = g_list_append(glist, "7 days");
+ glist = g_list_append(glist, "30 days");
+ glist = g_list_append(glist, "2 hours 30 minutes");
+
+ gtk_combo_set_popdown_strings(GTK_COMBO(combo),
+ glist) ;
+ gtk_signal_connect(GTK_OBJECT(GTK_COMBO(combo)->entry),
+ "changed",
+ GTK_SIGNAL_FUNC(selectFrequencyCallback),
+ ewm);
+ gtk_widget_show(combo);
+
+ /* add keyword boxes */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Current keyword: ");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->currentKey = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(ewm->currentKey),
+ "");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->currentKey,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(ewm->currentKey);
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Future keyword: ");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->nextKey = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(ewm->nextKey),
+ "");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->nextKey,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+ gtk_widget_show(ewm->nextKey);
+
+ /* add separator */
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(separator);
+
+
+ /* add the box for the two lists */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+
+ /* add a list of available entries */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titlesAvailable);
+ ewm->availableList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_clist_set_sort_column(GTK_CLIST(clist),0);
+ gtk_clist_set_auto_sort(GTK_CLIST(clist),TRUE);
+ /* add the known RootNodes to the list */
+ gtk_clist_freeze(GTK_CLIST(clist));
+ iterateDirectoryDatabase(context,
+ (RootNodeCallback)&appendToCList,
+ ewm);
+ gtk_clist_thaw(GTK_CLIST(clist));
+ gtk_widget_show(clist);
+
+
+ /* add the insertion ok/cancel buttons */
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(separator);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(hbox);
+ button_ok = gtk_button_new_with_label("Ok");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(buildNSEntry),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(window);
+}
+
+
+/* ********************** SEARCH ******************** */
+/* ********************** SEARCH ******************** */
+/* ********************** SEARCH ******************** */
+
+/**
+ * @brief state of the namespace search window
+ **/
+typedef struct {
+ GtkWidget * window;
+ GtkWidget * namespaceCombo;
+ GtkWidget * searchkeyLine;
+} NamespaceSearchWindowModel;
+
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyNamespaceSearchWindow(GtkWidget * widget,
+ NamespaceSearchWindowModel * ewm) {
+ FREE(ewm);
+}
+
+typedef struct {
+ HashCode160 n;
+ HashCode160 k;
+ ListModel * model;
+ HashCode160 * seen;
+ int seenCount;
+ HashCode160 * results;
+ int resultCount;
+} NSSearchThreadData;
+
+/**
+ * The main method of the search-thread.
+ *
+ * @param n namespace to search
+ * @param k code to search for
+ * @param model Data related to the search
+ * @return OK on success, SYSERR on error
+ **/
+static int startNamespaceSearchThread(HashCode160 * n,
+ HashCode160 * k,
+ ListModel * model);
+
+/**
+ * Run the namespace search. Starts the search
+ * thread and adds a new tab to the window list.
+ */
+static void startSearch(HashCode160 * n,
+ HashCode160 * k) {
+ ListModel * model;
+ int ok;
+ GtkWidget * box;
+
+ /* start search! */
+ model = (ListModel*) MALLOC(sizeof(ListModel));
+ model->type = LM_TYPE_NSSEARCH;
+ model->doTerminate = NO;
+ model->skipMenuRefresh = NO;
+ model->SEARCH_socket_ = NULL;
+
+ box = initializeSearchResultList(model);
+
+ /* start searching */
+ ok = startNamespaceSearchThread(n,
+ k,
+ model);
+ if (ok == SYSERR) {
+ LOG(LOG_DEBUG,
+ "DEBUG: startNamespaceSearchThread failed\n");
+ releaseClientSocket(model->SEARCH_socket_);
+ gtkSaveCall((GtkFunction) doDestroyWidget, box);
+ FREE(model);
+ } else {
+ char * label;
+ HexName hex1;
+ HexName hex2;
+
+ label = MALLOC(128);
+ hash2hex(n, &hex1);
+ hash2hex(k, &hex2);
+ sprintf(label,
+ "%.8s/%.8s", /* %8s: otherwise MUCH too long... */
+ (char*)&hex1,
+ (char*)&hex2);
+ addToNotebook(label,
+ box);
+ FREE(label);
+
+ LOG(LOG_DEBUG,
+ "DEBUG: namespace search initiated for %s %s\n",
+ (char *)&hex1,
+ (char *)&hex2);
+ }
+}
+
+static void displayResultGTK_(SBlock * sb,
+ NSSearchThreadData * sqc) {
+ HashCode160 curK;
+ int i;
+ HexName hex;
+
+ hash(sb, sizeof(SBlock), &curK);
+ hash2hex(&curK,
+ &hex);
+ LOG(LOG_DEBUG,
+ "DEBUG: got namespace result for %s\n",
+ (char *)&hex);
+ for (i=0;i<sqc->resultCount;i++)
+ if (equalsHashCode160(&curK,
+ &sqc->results[i])) {
+ LOG(LOG_DEBUG,
+ "DEBUG: displayResultGTK_ skipping previously seen entry %s\n",
+ (char *)&hex);
+ return; /* displayed already */
+ }
+ GROW(sqc->results,
+ sqc->resultCount,
+ sqc->resultCount+1);
+ memcpy(&sqc->results[sqc->resultCount-1],
+ &curK,
+ sizeof(HashCode160));
+ displayResultGTK((RootNode*)sb,
+ sqc->model);
+ refreshMenuSensitivity();
+ GROW(sqc->seen,
+ sqc->seenCount,
+ sqc->seenCount+1);
+ memcpy(&sqc->seen[sqc->seenCount-1],
+ &sqc->k,
+ sizeof(HashCode160));
+
+ /* now search for update if possible! */
+ computeIdAtTime(sb,
+ TIME(NULL),
+ &curK);
+ for (i=0;i<sqc->seenCount;i++)
+ if (equalsHashCode160(&curK,
+ &sqc->seen[i])) {
+ HashCode160 ns;
+ hash(&sb->subspace,
+ sizeof(PublicKey),
+ &ns);
+ hash2hex(&ns,
+ &hex);
+ guiMessage("Found the most recent version for a hit\n"
+ "in your original search in namespace\n\n"
+ "%s\n\nGood.",
+ STRDUP((char *)&hex));
+ LOG(LOG_DEBUG,
+ "DEBUG: namespace result %s is the most recent\n",
+ (char*)&hex);
+ return; /* found most up-to-date / all versions! */
+ }
+ /* else: start new parallel search! */
+ LOG(LOG_DEBUG,
+ "DEBUG: starting parallel search for the current version of %s\n",
+ (char*)&hex);
+ startSearch(&sqc->n,
+ &curK);
+}
+
+int searchSBlock_(NSSearchThreadData * sqc) {
+ LOG(LOG_DEBUG,
+ "DEBUG: enter searchSBlock_\n");
+
+ sqc->seen = NULL;
+ sqc->seenCount = 0;
+ sqc->results = NULL;
+ sqc->resultCount = 0;
+ sqc->model->SEARCH_socket_ = getClientSocket();
+ if (sqc->model->SEARCH_socket_ != NULL) {
+ searchSBlock(sqc->model->SEARCH_socket_,
+ &sqc->n,
+ &sqc->k,
+ (TestTerminateThread)&testTermination,
+ sqc->model,
+ (NSSearchResultCallback)&displayResultGTK_,
+ sqc);
+ } else {
+ LOG(LOG_DEBUG,
+ "DEBUG: socket was NULL in searchSBlock_\n");
+ }
+ GROW(sqc->seen,
+ sqc->seenCount,
+ 0);
+ GROW(sqc->results,
+ sqc->resultCount,
+ 0);
+ FREE(sqc);
+ return 0;
+}
+
+/**
+ * The main method of the search-thread.
+ *
+ * @param n namespace to search
+ * @param k code to search for
+ * @param model Data related to the search
+ * @return OK on success, SYSERR on error
+ **/
+static int startNamespaceSearchThread(HashCode160 * n,
+ HashCode160 * k,
+ ListModel * model) {
+ NSSearchThreadData * sqc;
+
+ sqc = MALLOC(sizeof(NSSearchThreadData));
+ memcpy(&sqc->n,
+ n,
+ sizeof(HashCode160));
+ memcpy(&sqc->k,
+ k,
+ sizeof(HashCode160));
+ sqc->model = model;
+ if (0 != PTHREAD_CREATE(&model->thread,
+ (PThreadMain) &searchSBlock_,
+ sqc,
+ 16 * 1024)) {
+ errexit("FATAL: could not create SBlock search thread (%s)!\n",
+ STRERROR(errno));
+ }
+ return OK;
+}
+
+
+/**
+ * Start the namespace search. This method obtains
+ * n and k from the input window and then calls
+ * the actual startSearch function.
+ *
+ * @param dummy not used
+ * @param ewm the state of the search window
+ **/
+static void searchNS(GtkWidget * dummy,
+ NamespaceSearchWindowModel * ewm) {
+ HashCode160 n;
+ HashCode160 k;
+ char * c;
+
+ c = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ewm->namespaceCombo)->entry));
+ if (SYSERR == tryhex2hash(c, &n)) {
+ guiMessage("ERROR: must specify valid HEX code for namespace.");
+ return;
+ }
+ c = gtk_entry_get_text(GTK_ENTRY(ewm->searchkeyLine));
+ if (strlen(c) == 0) {
+ guiMessage("ERROR: must specify string (or HEX code) for search key.");
+ return;
+ }
+ tryhex2hashOrHashString(c, &k);
+
+ /* destroy the window */
+ gtk_widget_destroy(ewm->window);
+ startSearch(&n, &k);
+}
+
+/**
+ * Open a window to allow the user to search a namespace
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 argument that is always 0
+ **/
+void searchNamespace(GtkWidget * unused,
+ unsigned int unused2) {
+ NamespaceSearchWindowModel * ewm;
+ GtkWidget * window;
+ GtkWidget * vbox, * hbox;
+ GtkWidget * label;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+ GList * glist;
+ HashCode160 * list;
+ int ret;
+ int i;
+
+ ewm = MALLOC(sizeof(NamespaceSearchWindowModel));
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ewm->window = window;
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 650,
+ 120);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Search Namespace");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyNamespaceSearchWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+
+ /* Create a line to enter the namespace identifier */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Namespace identifier:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+
+ ewm->namespaceCombo = gtk_combo_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->namespaceCombo,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->namespaceCombo)->entry),
+ "");
+ glist = NULL;
+ glist = g_list_append(glist,
+ "");
+ list = NULL;
+ ret = listNamespaces(&list);
+
+ for (i=0;i<ret;i++) {
+ HexName hex;
+ hash2hex(&list[i],
+ &hex);
+ LOG(LOG_DEBUG,
+ "DEBUG: appending namespace id %s\n",
+ (char*)&hex);
+ glist = g_list_append(glist,
+ STRDUP((char*)&hex));
+ /* FIXME: memory from STRDUP above is probably not
+ * freed anywhere - but must be DUPED or gnunet-gtk
+ * will display only a single hash multiple times */
+ }
+ if (ret > 0)
+ FREE(list);
+ gtk_combo_set_popdown_strings(GTK_COMBO(ewm->namespaceCombo),
+ glist) ;
+ gtk_widget_show(ewm->namespaceCombo);
+
+
+ /* Create a line to enter the search key identifier */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Search key identifier:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->searchkeyLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->searchkeyLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->searchkeyLine),
+ "");
+ gtk_signal_connect(GTK_OBJECT(ewm->searchkeyLine),
+ "activate",
+ GTK_SIGNAL_FUNC(searchNS),
+ ewm);
+ gtk_widget_show(ewm->searchkeyLine);
+
+ /* add the ok/cancel buttons */
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(hbox);
+ button_ok = gtk_button_new_with_label("Search");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(searchNS),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(window);
+
+}
+
+
+/* end of namespace.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/namespace.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,48 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/namespace.h
+ * @brief namespace insert and search entry points
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_NAMESPACE_H
+#define GTKUI_NAMESPACE_H
+
+
+
+/**
+ * Open a window to allow the user to build a namespace.
+ *
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleNamespaceDialog(GtkWidget * unused,
+ unsigned int unused2);
+
+/**
+ * Open a window to allow the user to search a namespace
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 argument that is always 0
+ **/
+void searchNamespace(GtkWidget * unused,
+ unsigned int unused2);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,402 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/pseudonyms.c
+ * @brief dialogs for creating and deleting pseudonyms
+ * @author Christian Grothoff
+ **/
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "insertprogress.h"
+#include "pseudonyms.h"
+#include "main.h"
+
+/**
+ * @brief state of the CreatePseudonym window
+ **/
+typedef struct {
+ GtkWidget * window;
+ GtkWidget * pseudonymLine;
+ GtkWidget * passwordLine;
+} CreatePseudonymWindowModel;
+
+
+/**
+ * Collects the results of the assembly dialog, creates an insertion
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void create_ok(GtkWidget * dummy,
+ CreatePseudonymWindowModel * ewm) {
+ char * name;
+ char * pass;
+ Hostkey ps;
+
+ name = gtk_entry_get_text(GTK_ENTRY(ewm->pseudonymLine));
+ if (name == NULL) {
+ guiMessage("WARNING: cowardly refusing to create pseudonym without
name.\n");
+ return;
+ }
+ name = STRDUP(name);
+ pass = gtk_entry_get_text(GTK_ENTRY(ewm->passwordLine));
+ if (pass != NULL)
+ pass = STRDUP(pass);
+ gtk_widget_destroy(ewm->window);
+
+ /* we may want to do this in another thread
+ to keep the event manager running (and potentially
+ even give feedback in the form of a popup window).
+ After all, this can take a while... */
+ ps = createPseudonym(name, pass);
+ if (ps == NULL)
+ guiMessage("WARNING: failed to create pseudonym (see logs).\n");
+ else
+ freeHostkey(ps);
+ refreshMenuSensitivity();
+ FREE(name);
+ FREENONNULL(pass);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyPCWindow(GtkWidget * widget,
+ CreatePseudonymWindowModel * ewm) {
+ FREE(ewm);
+}
+
+
+/**
+ * Open a window to allow the user to create a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openCreatePseudonymDialog(GtkWidget * unused,
+ unsigned int unused2) {
+ CreatePseudonymWindowModel * ewm;
+ GtkWidget * vbox;
+ GtkWidget * hbox;
+ GtkWidget * label;
+ GtkWidget * button_ok;
+ GtkWidget * button_cancel;
+ GtkWidget * separator;
+
+ ewm = MALLOC(sizeof(CreatePseudonymWindowModel));
+ /* create new window for editing */
+ ewm->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize(GTK_WIDGET(ewm->window),
+ 400,
+ 120);
+ gtk_window_set_title(GTK_WINDOW(ewm->window),
+ "Create Pseudonym");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(ewm->window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(ewm->window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(ewm->window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyPCWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(ewm->window),
+ 10);
+
+ /* Create a line to change the pseudonym */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Pseudonym:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->pseudonymLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->pseudonymLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->pseudonymLine),
+ "");
+ gtk_widget_show(ewm->pseudonymLine);
+
+ /* Create a line to change the description */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new("Password:");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ label,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(label);
+ ewm->passwordLine = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),
+ ewm->passwordLine,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_entry_set_text(GTK_ENTRY(ewm->passwordLine),
+ "");
+ gtk_widget_show(ewm->passwordLine);
+
+ separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),
+ separator,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(separator);
+
+ /* add the insertion ok/cancel buttons */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_ok = gtk_button_new_with_label("Ok");
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_ok,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_ok),
+ "clicked",
+ GTK_SIGNAL_FUNC(create_ok),
+ ewm);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ ewm->window);
+ gtk_widget_show(button_ok);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(ewm->window);
+}
+
+
+
+/**
+ * @brief state of the DeletePseudonym window
+ **/
+typedef struct {
+ GtkWidget * window;
+ char * selected;
+ GtkWidget * pseudonymList;
+} DeletePseudonymWindowModel;
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyDPWindow(GtkWidget * widget,
+ DeletePseudonymWindowModel * ewm) {
+ FREE(ewm);
+}
+
+/**
+ * The keyword delete button was clicked. Delete the
+ * currently selected pseudonym.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_del_clicked(GtkWidget * w,
+ DeletePseudonymWindowModel * ewm) {
+ GList * tmp;
+ gchar * key[1];
+ int row;
+
+ tmp = GTK_CLIST(ewm->pseudonymList)->selection;
+ if (NULL == tmp) {
+ /* message that keyword must be selected to delete one? */
+ return;
+ }
+ row = (int) tmp->data;
+ if (row < 0)
+ return; /* should never happen... */
+ key[0] = NULL;
+ gtk_clist_get_text(GTK_CLIST(ewm->pseudonymList),
+ row,
+ 0,
+ &key[0]);
+ if (key[0] == NULL)
+ return;
+ if (OK != deletePseudonym(key[0]))
+ guiMessage("WARNING: failed to delete pseudonym (see logs).\n");
+ gtk_clist_remove(GTK_CLIST(ewm->pseudonymList),
+ row);
+ refreshMenuSensitivity();
+}
+
+/**
+ * Open a window to allow the user to delete a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openDeletePseudonymDialog(GtkWidget * unused,
+ unsigned int unused2) {
+ DeletePseudonymWindowModel * ewm;
+ GtkWidget * window;
+ GtkWidget * vbox, * hbox;
+ GtkWidget * clist;
+ GtkWidget * scrolled_window;
+ GtkWidget * button_delete;
+ GtkWidget * button_cancel;
+ gchar * titles[1] = { "Pseudonyms" };
+ int i;
+ int cnt;
+ char ** list;
+
+ ewm = MALLOC(sizeof(DeletePseudonymWindowModel));
+ /* create new window for editing */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ ewm->window = window;
+ gtk_widget_set_usize(GTK_WIDGET(window),
+ 250,
+ 300);
+ gtk_window_set_title(GTK_WINDOW(window),
+ "Delete Pseudonym");
+
+ /* add container for window elements */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ gtk_widget_show(vbox);
+
+ /* when user clicks on close box, always "destroy" */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ ewm);
+ /* whenever edit window gets destroyed,
+ free *ALL* ewm data */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroyDPWindow),
+ ewm);
+
+ gtk_container_set_border_width(GTK_CONTAINER(window),
+ 10);
+
+ /* add a list of pseudonyms */
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+ clist = gtk_clist_new_with_titles(1, titles);
+ ewm->pseudonymList = clist;
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ clist);
+ gtk_widget_show(clist);
+ /* add the known RootNodes to the list */
+ list = NULL;
+ cnt = listPseudonyms(&list);
+ if (cnt > 0) {
+ gtk_clist_freeze(GTK_CLIST(clist));
+ for (i=0;i<cnt;i++) {
+ gtk_clist_append(GTK_CLIST(clist),
+ &list[i]);
+ FREE(list[i]);
+ }
+ gtk_clist_thaw(GTK_CLIST(clist));
+ }
+ FREENONNULL(list);
+
+ /* add the buttons to add and delete keywords */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ hbox,
+ FALSE,
+ TRUE,
+ 0);
+ gtk_widget_show(hbox);
+ button_delete = gtk_button_new_with_label("Delete Pseudonym");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_delete,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_delete),
+ "clicked",
+ GTK_SIGNAL_FUNC(button_del_clicked),
+ ewm);
+ gtk_widget_show(button_delete);
+
+
+ button_cancel = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(GTK_BOX(hbox),
+ button_cancel,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_signal_connect(GTK_OBJECT(button_cancel),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(button_cancel);
+
+ /* all clear, show the window */
+ gtk_widget_show(window);
+}
+
+
+/* end of pseudonyms.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/pseudonyms.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,46 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/pseudonyms.h
+ * @brief Pseudonym creation and deletion dialogs
+ * @author Christian Grothoff
+ **/
+#ifndef GTKUI_PSEUDONYMS_H
+#define GTKUI_PSEUDONYMS_H
+
+/**
+ * Open a window to allow the user to create a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openCreatePseudonymDialog(GtkWidget * unused,
+ unsigned int unused2);
+
+/**
+ * Open a window to allow the user to delete a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openDeletePseudonymDialog(GtkWidget * unused,
+ unsigned int unused2);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,192 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file src/applications/afs/gtkui/saveas.c
+ * @brief open a save-as window.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "download.h"
+#include "saveas.h"
+
+/**
+ * @brief state of the SaveAs window
+ **/
+typedef struct {
+ RootNode root;
+ GtkWidget * w;
+ char * filename;
+} SaveAs;
+
+/**
+ * Destroy the DownloadModel data structure of the
+ * saveas dialog.
+ *
+ * @param widget not used
+ * @param saveas state associated with the SaveAs window
+ **/
+static gint destroySaveAs(GtkWidget * widget,
+ SaveAs * saveas) {
+ LOG(LOG_DEBUG,
+ "DEBUG: destroying saveas window (%x)\n",
+ saveas);
+ FREENONNULL(saveas->filename);
+ FREE(saveas);
+ return TRUE;
+}
+
+/**
+ * Get the selected filename and start downloading
+ *
+ * @param okButton not used
+ * @param saveas state of the saveas window
+ **/
+static gint file_ok_sel(GtkWidget * okButton,
+ SaveAs * saveas) {
+ gchar * filename;
+
+ LOG(LOG_DEBUG,
+ "DEBUG: file_ok_sel (%x)\n",
+ saveas);
+ filename
+ = gtk_file_selection_get_filename(GTK_FILE_SELECTION(saveas->w));
+ startDownload(filename, &saveas->root);
+ gtk_widget_destroy(saveas->w);
+ /* destroySaveAs does: "FREE(saveas);" */
+ return FALSE;
+}
+
+/**
+ * Open the window that prompts the user for the filename. This
+ * method must open the window, copy the arguments and return. After
+ * the method returns, the arguments passed to it will be freed, so
+ * pointer should not be retained. The method executes during a
+ * signal handler, so a GTK lock is not required to to GUI operations.
+ *
+ * @param root search result of the file to download
+ **/
+void openSaveAs(RootNode * root) {
+ SaveAs * saveas;
+ int i;
+
+ saveas = MALLOC(sizeof(SaveAs));
+ memcpy(&saveas->root,
+ root,
+ sizeof(RootNode));
+ saveas->filename = NULL;
+
+ /* if the search result specified a suggested
+ filename, fill it in! */
+ switch (ntohs(root->header.major_formatVersion)) {
+ case ROOT_MAJOR_VERSION:
+ /* if it is a GNUnet directory, replace suffix '/' with ".gnd" */
+ if (0 == strcmp(root->header.mimetype,
+ GNUNET_DIRECTORY_MIME)) {
+ saveas->filename = expandDirectoryName(root->header.filename);
+ } else
+ saveas->filename = STRDUP(root->header.filename);
+ break;
+ case SBLOCK_MAJOR_VERSION:
+ if (0 == strcmp(&((SBlock*)root)->mimetype[0],
+ GNUNET_DIRECTORY_MIME)) {
+ saveas->filename = expandDirectoryName(root->header.filename);
+ } else
+ saveas->filename = STRDUP(((SBlock*)root)->filename);
+ break;
+ default:
+ LOG(LOG_WARNING,
+ "WARNING: unknown format version: %d\n",
+ ntohs(root->header.major_formatVersion));
+ /* how did we get here!? */
+ break;
+ }
+
+ if ( (saveas->filename == NULL) ||
+ (saveas->filename[0] == 0) ||
+ (testConfigurationString("GNUNET-GTK",
+ "ALWAYS-ASK-SAVEAS",
+ "YES")) ) {
+ GtkWidget * window;
+
+ window = gtk_file_selection_new("save as");
+ saveas->w = window;
+
+ /* callbacks (destroy, ok, cancel) */
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(destroySaveAs),
+ saveas);
+ /* Connect the ok_button to file_ok_sel function */
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(file_ok_sel),
+ saveas);
+ /* Connect the cancel_button to destroy the widget */
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_widget_show(window);
+ } else {
+ char * tmp;
+ char * downloadDir;
+
+ /* sanity check the filename */
+ for(i=0;i<strlen(saveas->filename);i++) {
+ switch(saveas->filename[i]) {
+ case '*':
+ case '/':
+ case '\\':
+ case '?':
+ case ':':
+ saveas->filename[i]='_';
+ break;
+ default:
+ break;
+ }
+ }
+
+ downloadDir = getConfigurationString("AFS",
+ "DOWNLOADDIR");
+ if(downloadDir != NULL) {
+ char * expanded;
+
+ expanded = expandFileName(downloadDir);
+ if((SYSERR == mkdirp(expanded)))
+ LOG(LOG_WARNING,
+ "WARNING: Unable to create %s\n",
+ expanded);
+ CHDIR(expanded);
+ FREE(expanded);
+ }
+ FREENONNULL(downloadDir);
+
+ tmp = expandFileName(saveas->filename);
+ FREE(saveas->filename);
+ startDownload(tmp,
+ &saveas->root);
+ FREE(saveas);
+ FREE(tmp);
+ }
+}
+
+/* end of saveas.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/saveas.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,43 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/saveas.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_SAVEAS_H
+#define GTKUI_SAVEAS_H
+
+#include "platform.h"
+
+/**
+ * Open the window that prompts the user for the
+ * filename.
+ * This method must open the window,
+ * copy the arguments and return. After the method
+ * returns, the arguments passed to it will be
+ * freed, so pointer should not be retained.
+ * The method executes during a signal handler,
+ * so a GTK lock is not required to to GUI
+ * operations.
+ **/
+void openSaveAs(RootNode * root);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/search.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/search.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/search.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,852 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/search.c
+ * @brief box displaying search results for the gtk+ client.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "download.h"
+#include "saveas.h"
+#include "search.h"
+#include "main.h"
+
+static void searchSelectAll(void);
+static void searchSelectNone(void);
+static void searchSelectByName(void);
+static void searchSelectByDesc(void);
+static void searchSelectByMime(void);
+static void searchClose(void);
+static void searchDownloadSelected(void);
+
+static GtkItemFactoryEntry searchWindowMenu[] = {
+ { "/Select all", NULL, searchSelectAll, 0, "<Item>" },
+ { "/Unselect all", NULL, searchSelectNone, 0, "<Item>" },
+ { "/sep1", NULL, NULL, 0, "<Separator>" },
+ { "/Select by filename", NULL, searchSelectByName, 0, "<Item>" },
+ { "/Select by description", NULL, searchSelectByDesc, 0, "<Item>" },
+ { "/Select by mimetype", NULL, searchSelectByMime, 0, "<Item>" },
+ { "/sep2", NULL, NULL, 0, "<Separator>" },
+ { "/Download selected", NULL, searchDownloadSelected, 0, "<Item>" },
+ { "/sep3", NULL, NULL, 0, "<Separator>" },
+ { "/Abort search", NULL, searchClose, 0, "<Item>" }
+};
+
+static gint searchWindowMenuItems
+ = sizeof (searchWindowMenu) / sizeof (searchWindowMenu[0]);
+
+/**
+ * Selects all search results from the current search page.
+ **/
+static void searchSelectAll(void)
+{
+ gint pagenr;
+ GtkWidget * page;
+ ListModel * model;
+
+ pagenr = gtk_notebook_get_current_page(notebook);
+ if(pagenr<0)
+ return;
+ page = gtk_notebook_get_nth_page(notebook,
+ pagenr);
+ if(!page)
+ return;
+ model = gtk_object_get_data(GTK_OBJECT(page),
+ "MODEL");
+ if(model)
+ gtk_clist_select_all(GTK_CLIST(model->search_result_list));
+}
+
+/**
+ * Unselects all search results from the current search page.
+ **/
+static void searchSelectNone(void)
+{
+ gint pagenr;
+ GtkWidget * page;
+ ListModel * model;
+
+ pagenr = gtk_notebook_get_current_page(notebook);
+ if(pagenr<0)
+ return;
+ page = gtk_notebook_get_nth_page(notebook,
+ pagenr);
+ if(!page)
+ return;
+ model = gtk_object_get_data(GTK_OBJECT(page),
+ "MODEL");
+ if(model)
+ gtk_clist_unselect_all(GTK_CLIST(model->search_result_list));
+}
+
+static void selectByCallback(GtkWidget * dummy,
+ GtkWidget * entry) {
+ gchar * nameString;
+ gint pagenr;
+ GtkWidget * page;
+ GtkCList * clist;
+ ListModel * model;
+ int i;
+ int j;
+ int * data;
+ int column;
+
+ data = gtk_object_get_data(GTK_OBJECT(entry),
+ "COLUMNID");
+ column = *data;
+ FREE(data);
+
+ nameString = gtk_entry_get_text(GTK_ENTRY(entry));
+ if(nameString == NULL) {
+ guiMessage("nameString == NULL\n");
+ return;
+ }
+ if(nameString[0] == 0) {
+ guiMessage("Naah...\n");
+ return;
+ }
+
+ pagenr = gtk_notebook_get_current_page(notebook);
+ if(pagenr<0)
+ return;
+ page = gtk_notebook_get_nth_page(notebook,
+ pagenr);
+ if(!page)
+ return;
+ model = gtk_object_get_data(GTK_OBJECT(page),
+ "MODEL");
+ if(model) {
+ gchar * tmp;
+ gchar * haystack;
+ gchar * needle;
+ int hits = 0;
+
+ needle = STRDUP(nameString);
+ for(i=0;i<strlen(needle);i++)
+ needle[i]=tolower(needle[i]);
+
+ clist = GTK_CLIST(model->search_result_list);
+
+ gtk_clist_freeze(clist);
+ for(i=0;i<clist->rows;i++) {
+ gtk_clist_get_text(clist,
+ i,
+ column,
+ &tmp);
+ haystack = STRDUP(tmp);
+ for(j=0;j<strlen(haystack);j++)
+ haystack[j]=tolower(haystack[j]);
+
+ if(strstr(haystack, needle)!=NULL) {
+ gtk_clist_select_row(clist,
+ i,
+ 1);
+ hits++;
+ }
+ FREE(haystack);
+ }
+
+ gtk_clist_thaw(clist);
+ if(hits==0)
+ guiMessage("No matches...");
+ FREE(needle);
+ }
+}
+
+
+static void searchSelectByColumn(int column)
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *button;
+ int * data;
+
+ data = MALLOC(sizeof(int));
+ *data = column;
+
+ window = gtk_window_new(GTK_WINDOW_DIALOG);
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window),
+ vbox);
+ label = gtk_label_new("Pattern? ");
+ gtk_container_add (GTK_CONTAINER(vbox),
+ label);
+ entry = gtk_entry_new();
+ gtk_object_set_data(GTK_OBJECT(entry),
+ "COLUMNID",
+ data);
+ gtk_entry_set_text(GTK_ENTRY(entry),
+ "");
+ gtk_signal_connect(GTK_OBJECT(entry),
+ "activate",
+ GTK_SIGNAL_FUNC(selectByCallback),
+ entry);
+ gtk_signal_connect(GTK_OBJECT(entry),
+ "activate",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_container_add(GTK_CONTAINER(vbox),
+ entry);
+ button = gtk_button_new_with_label("Ok");
+ gtk_container_add(GTK_CONTAINER(vbox),
+ button);
+ gtk_signal_connect(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(selectByCallback),
+ entry);
+ gtk_signal_connect(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(destroyWidget),
+ window);
+ gtk_window_set_position(GTK_WINDOW(window),
+ GTK_WIN_POS_MOUSE);
+ gtk_widget_show_all(window);
+ gtk_widget_grab_focus(entry);
+
+}
+
+static void searchSelectByName(void)
+{
+ searchSelectByColumn(2);
+}
+
+static void searchSelectByDesc(void)
+{
+ searchSelectByColumn(0);
+}
+
+static void searchSelectByMime(void) {
+ searchSelectByColumn(6);
+}
+
+/**
+ * Remove the active page from the search results notebook.
+ * The respective search will be stopped as well
+ * (by a callback assigned to the page earlier on).
+ **/
+void searchClose(void) {
+ gint pagenr;
+
+ pagenr = gtk_notebook_get_current_page(notebook);
+ gtk_notebook_remove_page(notebook, pagenr);
+ /* Need to refresh the widget --
+ This forces the widget to redraw itself. */
+ gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+/**
+ * This method is called whenever the user clicks the
+ * download button. It opens the "save-as" dialog.
+ *
+ * @param widget not used
+ * @param listModel Data related to the search
+ **/
+static void downloadGTK(GtkWidget * widget,
+ ListModel * listModel);
+
+
+static void searchDownloadSelected(void) {
+ gint pagenr;
+ GtkWidget * page;
+ ListModel * model;
+
+ pagenr = gtk_notebook_get_current_page(notebook);
+ if(pagenr<0)
+ return;
+ page = gtk_notebook_get_nth_page(notebook,
+ pagenr);
+ if(!page)
+ return;
+ model = gtk_object_get_data(GTK_OBJECT(page),
+ "MODEL");
+ if(model)
+ downloadGTK(NULL, model);
+}
+
+/**
+ * This method is called whenever the user clicks the
+ * download button. It opens the "save-as" dialog.
+ *
+ * @param widget not used
+ * @param listModel Data related to the search
+ **/
+static void downloadGTK(GtkWidget * widget,
+ ListModel * listModel) {
+ gint row;
+ GList * tmp;
+
+ tmp=GTK_CLIST(listModel->search_result_list)->selection;
+
+ if ( !tmp ) {
+ guiMessage("Nothing selected!\n");
+ return;
+ }
+
+ gtk_clist_freeze(GTK_CLIST(listModel->search_result_list));
+
+ /* download all selected entries */
+ while (tmp) {
+ RootNode * rootNode;
+
+ row = (int)tmp->data;
+ tmp = tmp->next;
+ rootNode = (RootNode *)
+ gtk_clist_get_row_data(GTK_CLIST(listModel->search_result_list),
+ row);
+ openSaveAs(rootNode);
+
+ /* Remove entry from search results. Yes,
+ if the user cancel's the download, the
+ entry does not re-appear. That's intended,
+ after all, if you cancel, it's probably because
+ it took too long to download anyway...
+ If you really need it back, just search again! */
+ gtk_clist_remove(GTK_CLIST(listModel->search_result_list),
+ row);
+
+ FREE(rootNode);
+ }
+ gtk_clist_thaw(GTK_CLIST(listModel->search_result_list));
+}
+
+static void freeSearchList(GtkWidget * dummy,
+ GtkCList * clist) {
+ int i;
+
+ /* Free clist stored rootNode data */
+ gtk_clist_freeze(clist);
+ for(i=0;i<clist->rows;i++) {
+ RootNode * rootNode;
+
+ rootNode = (RootNode *)
+ gtk_clist_get_row_data(clist,
+ i);
+ FREE(rootNode);
+ }
+ gtk_clist_clear(clist);
+ gtk_clist_thaw(clist);
+}
+
+static gint doDisplayResult(SaveCall *call) {
+ int newrow;
+ GtkWidget *search_result_list = ((Result *) call->args)->search_result_list;
+
+ gtk_clist_freeze(GTK_CLIST(search_result_list));
+ newrow = gtk_clist_append(GTK_CLIST(search_result_list),
+ ((Result *) call->args)->results);
+ gtk_clist_set_row_data(GTK_CLIST(search_result_list),
+ newrow,
+ ((Result *) call->args)->rootCopy);
+ gtk_clist_thaw(GTK_CLIST(search_result_list));
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+/**
+ * Display results. This is a callback from receiveResults that is
+ * called on every new result.
+ *
+ * @param rootNode Data about a file
+ * @param model Data related to the search
+ **/
+void displayResultGTK(RootNode * rootNode,
+ ListModel * model) {
+ RootNode * rootCopy;
+ SBlock * sb;
+ gchar * results[7];
+ Result result;
+ int i;
+
+ if(model->doTerminate == YES)
+ return;
+
+ rootCopy = MALLOC(sizeof(RootNode));
+ memcpy(rootCopy,
+ rootNode,
+ sizeof(RootNode));
+
+ switch (ntohs(rootNode->header.major_formatVersion)) {
+ case ROOT_MAJOR_VERSION:
+ /* ensure well-formed strings */
+ rootNode->header.description[MAX_DESC_LEN-1] = 0;
+ rootNode->header.filename[MAX_FILENAME_LEN-1] = 0;
+ rootNode->header.mimetype[MAX_MIMETYPE_LEN-1] = 0;
+
+ results[0] = STRDUP(rootNode->header.description);
+ results[1] = MALLOC(32);
+ sprintf(results[1],
+ "%u",
+ (unsigned int) ntohl(rootNode->header.fileIdentifier.file_length));
+ if ( (0 == strcmp(rootNode->header.mimetype,
+ GNUNET_DIRECTORY_MIME)) &&
+ (rootNode->header.filename[strlen(rootNode->header.filename)-1] !=
DIR_SEPARATOR) ) {
+ results[2] = MALLOC(strlen(rootNode->header.filename)+2);
+ strcpy(results[2], rootNode->header.filename);
+ strcat(results[2], "/");
+ } else
+ results[2] = STRDUP(rootNode->header.filename);
+ results[3] = MALLOC(12);
+ sprintf(results[3],
+ "%X",
+ (unsigned int) ntohl(rootNode->header.fileIdentifier.crc));
+ results[4] = MALLOC(sizeof(HexName));
+ hash2hex(&rootNode->header.fileIdentifier.chk.query,
+ (HexName*) results[4]);
+ results[5] = MALLOC(sizeof(HexName));
+ hash2hex(&rootNode->header.fileIdentifier.chk.key,
+ (HexName*) results[5]);
+ results[6] = STRDUP(rootNode->header.mimetype);
+ break;
+ case SBLOCK_MAJOR_VERSION:
+ sb = (SBlock*) rootNode;
+ sb->description[MAX_DESC_LEN-1] = 0;
+ sb->filename[MAX_FILENAME_LEN/2-1] = 0;
+ sb->mimetype[MAX_MIMETYPE_LEN/2-1] = 0;
+
+ results[0] = STRDUP(rootNode->header.description);
+ results[1] = MALLOC(32);
+ sprintf(results[1],
+ "%u",
+ (unsigned int) ntohl(sb->fileIdentifier.file_length));
+ results[2] = STRDUP(sb->filename);
+ results[3] = MALLOC(32);
+ sprintf(results[3],
+ "%u",
+ (unsigned int) ntohl(sb->fileIdentifier.crc));
+ results[4] = MALLOC(sizeof(HexName));
+ hash2hex(&sb->fileIdentifier.chk.query,
+ (HexName*) results[4]);
+ results[5] = MALLOC(sizeof(HexName));
+ hash2hex(&sb->fileIdentifier.chk.key,
+ (HexName*) results[5]);
+ results[6] = STRDUP(sb->mimetype);
+ break;
+ default:
+ LOG(LOG_ERROR,
+ "ERROR: search result received of unsupported type %d\n",
+ ntohs(rootNode->header.major_formatVersion));
+ return;
+ }
+ result.search_result_list = model->search_result_list;
+ result.rootCopy = rootCopy;
+ memcpy(&result.results, &results, sizeof(results));
+ gtkSaveCall((GtkFunction) doDisplayResult, &result);
+ if (model->skipMenuRefresh != YES)
+ refreshMenuSensitivity();
+
+ for (i=0;i<7;i++)
+ FREE(results[i]);
+}
+
+/**
+ * Struct to pass a couple of arguments to a new
+ * thread "receiveResults_".
+ **/
+typedef struct {
+ char * searchString;
+ ListModel * model;
+} _receiveResultArgs_;
+
+/**
+ * Should the receive thread abort? (has the window been closed?)
+ * This is a callback for the receiveResults method (since
+ * not every "error" on the socket corresponds to a closed
+ * window!).
+ *
+ * @return YES if we should abort
+ **/
+int testTermination(ListModel * model) {
+ return model->doTerminate;
+}
+
+/**
+ * Main method of the receiveResults-threads.
+ * Runs the receiveResults method and frees some
+ * data structures once the search is aborted.
+ * See also "stopSearch".
+ **/
+static void * receiveResults_(_receiveResultArgs_ * args) {
+ char ** keywords;
+ int num_Words;
+ int inWord;
+ char * c;
+
+
+ num_Words = 0;
+ for (inWord = 0, c = args->searchString; *c != '\0'; ++c) {
+ if (isspace(*c)) {
+ inWord = 0;
+ } else if (!inWord) {
+ inWord = 1;
+ ++num_Words;
+ }
+ }
+
+ if (num_Words == 0) {
+ FREENONNULL(args->searchString);
+ FREE(args);
+ LOG(LOG_FAILURE, "No keywords specified!\n");
+ return NULL;
+ }
+ keywords = MALLOC(num_Words * sizeof(char *));
+ num_Words = 0;
+ for (inWord = 0, c = args->searchString; *c != '\0'; ++c) {
+ if (isspace(*c)) {
+ inWord = 0;
+ *c = '\0';
+ } else if (!inWord) {
+ keywords[num_Words] = c;
+ inWord = 1;
+ ++num_Words;
+ }
+ }
+ searchRBlock(args->model->SEARCH_socket_,
+ keywords,
+ num_Words,
+ (SearchResultCallback) &displayResultGTK,
+ args->model,
+ (TestTerminateThread)&testTermination,
+ args->model);
+ FREE(args->searchString);
+ FREE(keywords);
+ FREE(args);
+ return NULL;
+}
+
+/**
+ * The main method of the search-thread.
+ *
+ * @param searchString What to look for
+ * @param model Data related to the search
+ * @return OK on success, SYSERR on error
+ **/
+static int startSearchThread(char * searchString,
+ ListModel * model) {
+ _receiveResultArgs_ * receiveArgs;
+
+ receiveArgs = MALLOC(sizeof(_receiveResultArgs_));
+ receiveArgs->searchString = STRDUP(searchString);
+ receiveArgs->model = model;
+ if (0 != PTHREAD_CREATE(&model->thread,
+ (PThreadMain) &receiveResults_,
+ receiveArgs,
+ 16 * 1024)) {
+ errexit("FATAL: could not create receive thread (%s)!\n",
+ STRERROR(errno));
+ }
+ return OK;
+}
+
+/**
+ * Cron job that stops the search.
+ */
+static void stopSearch_(ListModel * model) {
+ void * unused;
+
+ switch (model->type) {
+ case LM_TYPE_DIRECTORY:
+ break;
+ case LM_TYPE_SEARCH:
+ /* the terminated search thread ups this semaphore
+ once it is done and we can free data structures */
+ model->doTerminate = YES;
+
+ /* this signals the download thread to terminate */
+ closeSocketTemporarily(model->SEARCH_socket_);
+
+ /* wait for download thread signal */
+ PTHREAD_JOIN(&model->thread,
+ &unused);
+
+ /* Now we can finally free the shared data structures.
+ Note that the terminated search thread freed
+ some of the memory that was allocated in
+ startSearchThread (see receiveResults_)
+ we free the rest. */
+ releaseClientSocket(model->SEARCH_socket_);
+ break;
+ case LM_TYPE_NSSEARCH:
+ model->doTerminate = YES;
+ /* this signals the download thread to terminate */
+ closeSocketTemporarily(model->SEARCH_socket_);
+ /* wait for download thread signal */
+ PTHREAD_JOIN(&model->thread,
+ &unused);
+ releaseClientSocket(model->SEARCH_socket_);
+ break;
+ default:
+ LOG(LOG_ERROR,
+ "ERROR: Unknown model->type %d\n",
+ model->type);
+ break;
+ }
+ if (model->sem != NULL)
+ SEMAPHORE_UP(model->sem);
+ FREE(model);
+}
+
+/**
+ * Stop the search thread and free the model. This method MUST always
+ * be called when the search ends, either because the search was
+ * aborted or because gnunet-gtk exists as a whole.
+ *
+ * @param widget the window (not used)
+ * @param model the model with the socket
+ **/
+static void stopSearch(GtkWidget * widget,
+ ListModel * model) {
+ Semaphore * cs;
+ LOG(LOG_DEBUG,
+ "DEBUG: stopSearch called\n");
+ /* this must be done as a cron-job, since otherwise
+ it may deadlock (this is called from the
+ gtk event thread, and cron may be waiting for
+ the gtk event lock, so we can't delete a cron
+ job in this thread */
+ model->doTerminate = YES;
+ cs = SEMAPHORE_NEW(0);
+ model->sem = cs;
+ addCronJob((CronJob) &stopSearch_,
+ 0, 0, model);
+ /* this is always the gtk-thread, so we must
+ not block; instead, run gtk-savecalls! */
+ while (SYSERR == SEMAPHORE_DOWN_NONBLOCKING(cs))
+ gtkRunSomeSaveCalls();
+ SEMAPHORE_FREE(cs);
+}
+
+/**
+ * Changes the current sort column and sorts the list.
+ **/
+static void sort_column_callback(GtkCList * clist,
+ gint column,
+ gpointer data) {
+ static int sortOrder[7]={0,0,0,0,0,0,0};
+
+ sortOrder[column]^=1;
+
+ if(sortOrder[column]==1)
+ gtk_clist_set_sort_type(clist,
+ GTK_SORT_ASCENDING);
+ else
+ gtk_clist_set_sort_type(clist,
+ GTK_SORT_DESCENDING);
+
+ /* Sort all columns as strings except 1 (size) */
+ if (column == 1)
+ gtk_clist_set_compare_func(clist,
+ (GtkCListCompareFunc)numericComp);
+ else
+ gtk_clist_set_compare_func(clist,
+ (GtkCListCompareFunc)alphaComp);
+ gtk_clist_set_sort_column(clist, column);
+ gtk_clist_freeze(clist);
+ gtk_clist_sort(clist);
+ gtk_clist_thaw(clist);
+}
+
+static gint doInitSearchResultList(SaveCall *call) {
+ ListModel *model;
+ GtkWidget * scrolled_window;
+ GtkWidget * button;
+ GtkWidget * box;
+ GtkWidget * search_result_list;
+ GtkWidget * menu;
+ GtkItemFactory * popupFactory;
+ static gchar * descriptions[] = {
+ "Description",
+ "Size",
+ "Filename",
+ "CRC",
+ "HASH1",
+ "HASH2",
+ "Mimetype"
+ };
+ /* widths of the colums in the search results */
+ static int widths[] = {
+ 470, 70, 200, 40, 40, 40, 200,
+ };
+ int i;
+
+ model = ((InitResultList *)call->args)->model;
+
+ box = gtk_vbox_new(FALSE, 0);
+ /* scrolled window for the list */
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(box),
+ scrolled_window,
+ TRUE,
+ TRUE,
+ 0);
+ gtk_widget_show(scrolled_window);
+
+ /* result set list */
+ search_result_list
+ = gtk_clist_new_with_titles(7, descriptions);
+ model->search_result_list
+ = search_result_list;
+ gtk_signal_connect(GTK_OBJECT(search_result_list),
+ "destroy",
+ GTK_SIGNAL_FUNC(freeSearchList),
+ search_result_list);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ search_result_list);
+ gtk_widget_show(search_result_list);
+
+ gtk_clist_set_selection_mode
+ (GTK_CLIST(search_result_list),
+ GTK_SELECTION_EXTENDED);
+ /* set passive titles as default */
+ gtk_clist_column_titles_passive
+ (GTK_CLIST(search_result_list));
+
+ /* allow sorting by description, size, filename and mimetype */
+ gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+ 0);
+ gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+ 1);
+ gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+ 2);
+ gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+ 6);
+ gtk_signal_connect(GTK_OBJECT(search_result_list),
+ "click-column",
+ GTK_SIGNAL_FUNC(sort_column_callback),
+ NULL);
+
+ /* description left, size right justification */
+ gtk_clist_set_column_justification
+ (GTK_CLIST(search_result_list),
+ 0,
+ GTK_JUSTIFY_LEFT);
+ gtk_clist_set_column_justification
+ (GTK_CLIST(search_result_list),
+ 1,
+ GTK_JUSTIFY_RIGHT);
+
+ /* set column widths */
+ for (i=0;i<7;i++)
+ gtk_clist_set_column_width(GTK_CLIST(search_result_list),
+ i,
+ widths[i]);
+
+ /* download button */
+ button = gtk_button_new_with_label("Download");
+ gtk_signal_connect (GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(downloadGTK),
+ model);
+ gtk_box_pack_start(GTK_BOX(box),
+ button,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show(button);
+ /* generic: on delete request, just do it (always OK) */
+ gtk_signal_connect(GTK_OBJECT(scrolled_window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(deleteEvent),
+ NULL);
+ /* when we are destroyed (e.g. search aborted), stop the search thread */
+ gtk_signal_connect(GTK_OBJECT(scrolled_window),
+ "destroy",
+ GTK_SIGNAL_FUNC(stopSearch),
+ model);
+ /* store a pointer to the model for main-menu access */
+ gtk_object_set_data(GTK_OBJECT(box),
+ "MODEL",
+ model);
+
+ /* add a right button popup menu */
+ popupFactory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
+ NULL);
+ gtk_item_factory_create_items(popupFactory,
+ searchWindowMenuItems,
+ searchWindowMenu,
+ NULL);
+ menu = gtk_item_factory_get_widget (popupFactory, "<main>");
+ gtk_signal_connect(GTK_OBJECT(box),
+ "event",
+ GTK_SIGNAL_FUNC(popupCallback),
+ menu);
+
+ ((InitResultList *)call->args)->ret = box;
+
+ gtkSaveCallDone(call->sem);
+
+ return FALSE;
+}
+
+GtkWidget * initializeSearchResultList(ListModel * model) {
+ InitResultList init;
+
+ init.model = model;
+ gtkSaveCall((GtkFunction) doInitSearchResultList,
+ &init);
+ return init.ret;
+}
+
+/**
+ * Returns a box containing the search results list.
+ **/
+GtkWidget * getSearchWindow(gchar * title) {
+ GtkWidget * box;
+ ListModel * model;
+ int ok;
+
+ model = (ListModel*) MALLOC(sizeof(ListModel));
+ model->sem = NULL;
+ model->type = LM_TYPE_SEARCH;
+ model->doTerminate = NO;
+ model->skipMenuRefresh = NO;
+ model->SEARCH_socket_
+ = getClientSocket();
+ if (model->SEARCH_socket_ == NULL) {
+ FREE(model);
+ return NULL;
+ }
+
+ box = initializeSearchResultList(model);
+
+ /* start searching */
+ ok = startSearchThread(title,
+ model);
+ if (ok == SYSERR) {
+ releaseClientSocket(model->SEARCH_socket_);
+ gtk_widget_destroy(box);
+ FREE(model);
+ return NULL;
+ } else {
+ /* return complete box such that it gets displayed */
+ return box;
+ }
+}
+
+/* end of search.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/search.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/search.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/search.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,101 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/search.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_SEARCH_H
+#define GTKUI_SEARCH_H
+
+/**
+ * Data for a search process
+ **/
+typedef struct {
+ int type;
+ GtkWidget * search_result_list;
+ int doTerminate;
+ GNUNET_TCP_SOCKET * SEARCH_socket_;
+ PTHREAD_T thread; /* search thread */
+ int skipMenuRefresh; /* don't refresh gtk menus (its slow)? (YES/NO) */
+ Semaphore * sem; /* shutdown signaling */
+} ListModel;
+
+typedef struct {
+ GtkWidget *search_result_list;
+ RootNode *rootCopy;
+ gchar * results[7];
+} Result;
+
+typedef struct {
+ ListModel *model;
+ GtkWidget *ret;
+} InitResultList;
+
+#define LM_TYPE_SEARCH 1
+#define LM_TYPE_DIRECTORY 2
+#define LM_TYPE_NSSEARCH 3
+
+/**
+ * Get the window with the search results.
+ **/
+GtkWidget * getSearchWindow(gchar * searchString);
+
+GtkWidget * initializeSearchResultList(ListModel * model);
+
+/**
+ * Should the receive thread abort? (has the window been closed?)
+ * This is a callback for the receiveResults method (since
+ * not every "error" on the socket corresponds to a closed
+ * window!).
+ *
+ * @return YES if we should abort
+ **/
+int testTermination(ListModel * model);
+
+/**
+ * Display results. This is a callback from receiveResults that is
+ * called on every new result.
+ *
+ * @param rootNode Data about a file
+ * @param model Data related to the search
+ **/
+void displayResultGTK(RootNode * rootNode,
+ ListModel * model);
+
+/**
+ * Some search list row has been unselected
+ **/
+void unselect_row_callbackGTK(GtkWidget *widget,
+ gint row,
+ gint column,
+ GdkEventButton *event,
+ gpointer data);
+
+/**
+ * User has selected some search list row
+ **/
+void select_row_callbackGTK(GtkWidget *widget,
+ gint row,
+ gint column,
+ GdkEventButton *event,
+ gpointer data);
+
+#endif
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.c 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.c 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,963 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Portions of this code were adopted from the
+ gnome-system-monitor v2.0.5, (C) 2001 Kevin Vandersloot
+
+
+ Todo:
+ - add any more StatEntries, update menu accordingly.
+*/
+
+#include "gnunet_afs_esed2.h"
+#include "statistics.h"
+#include "helper.h"
+#include "main.h"
+#include <glib.h>
+
+#define UPDATE_INTERVAL (30 * cronSECONDS)
+
+typedef struct {
+ char * statName;
+ long long value;
+ long long lvalue;
+ cron_t delta;
+} StatPair;
+
+static StatPair * lastStatValues;
+static unsigned int lsv_size;
+static cron_t lastUpdate;
+static Mutex lock;
+
+static void updateStatValues(GNUNET_TCP_SOCKET * sock) {
+ STATS_CS_MESSAGE * statMsg;
+ CS_HEADER csHdr;
+ unsigned int count;
+ unsigned int i;
+ int j;
+ int mpos;
+ int found;
+ char * optName;
+ cron_t now;
+ cron_t prev;
+
+ cronTime(&now);
+ MUTEX_LOCK(&lock);
+ if (now - lastUpdate < UPDATE_INTERVAL) {
+ MUTEX_UNLOCK(&lock);
+ return;
+ }
+ prev = lastUpdate;
+ lastUpdate = now;
+ csHdr.size
+ = htons(sizeof(CS_HEADER));
+ csHdr.tcpType
+ = htons(STATS_CS_PROTO_GET_STATISTICS);
+ if (SYSERR == writeToSocket(sock,
+ &csHdr)) {
+ MUTEX_UNLOCK(&lock);
+ return;
+ }
+ statMsg
+ = MALLOC(MAX_BUFFER_SIZE);
+ statMsg->totalCounters
+ = htonl(1); /* to ensure we enter the loop */
+ count = 0;
+ while ( count < ntohl(statMsg->totalCounters) ) {
+ if (SYSERR == readFromSocket(sock,
+ (CS_HEADER**)&statMsg)) {
+ FREE(statMsg);
+ MUTEX_UNLOCK(&lock);
+ return;
+ }
+ if (ntohs(statMsg->header.size) < sizeof(STATS_CS_MESSAGE)) {
+ LOG(LOG_WARNING,
+ "WARNING: received malformed stats message (%d < %d)\n",
+ ntohs(statMsg->header.size),
+ sizeof(STATS_CS_MESSAGE) );
+ break;
+ }
+ mpos = sizeof(unsigned long long) * ntohl(statMsg->statCounters);
+ if ( ((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))
+ [ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE) - 1] != '\0') {
+ LOG(LOG_WARNING,
+ "WARNING: received malformed stats message (does not end with
0-termination)\n");
+ break;
+ }
+ for (i=0;i<ntohl(statMsg->statCounters);i++) {
+ optName = &((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos];
+ if ( (mpos > ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE)) ||
+ (mpos + strlen(optName) + 1 >
+ ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE)) ) {
+ LOG(LOG_WARNING,
+ "WARNING: received malformed stats message (%d > %d)\n",
+ mpos+strlen(optName)+1,
+ ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE));
+ break; /* out of bounds! */
+ }
+ found = -1;
+ for (j=0;j<lsv_size;j++)
+ if (0 == strcmp(optName,
+ lastStatValues[j].statName))
+ found = j;
+ if (found == -1) {
+ found = lsv_size;
+ GROW(lastStatValues,
+ lsv_size,
+ lsv_size+1);
+ lastStatValues[found].statName
+ = STRDUP(optName);
+ }
+ lastStatValues[found].lvalue
+ = lastStatValues[found].value;
+ lastStatValues[found].value
+ = ntohll(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values[i]);
+ lastStatValues[found].delta
+ = now-prev;
+ mpos += strlen(optName)+1;
+ }
+ count += ntohl(statMsg->statCounters);
+ } /* end while */
+ FREE(statMsg);
+ MUTEX_UNLOCK(&lock);
+}
+
+static int getStatValue(long long * value,
+ long long * lvalue,
+ cron_t * dtime,
+ GNUNET_TCP_SOCKET * sock,
+ const char * optName) {
+ unsigned int i;
+
+ *value = 0;
+ if (lvalue != NULL)
+ *lvalue = 0;
+ updateStatValues(sock);
+ MUTEX_LOCK(&lock);
+ for (i=0;i<lsv_size;i++) {
+ if (0 == strcmp(optName,
+ lastStatValues[i].statName)) {
+ *value = lastStatValues[i].value;
+ if (lvalue != NULL)
+ *lvalue = lastStatValues[i].lvalue;
+ if (dtime != NULL)
+ *dtime = lastStatValues[i].delta;
+ MUTEX_UNLOCK(&lock);
+ return OK;
+ }
+ }
+ MUTEX_UNLOCK(&lock);
+ return SYSERR;
+}
+
+/**
+ * Callback function to obtain the latest stats
+ * data for this stat display.
+ */
+typedef int (*UpdateData)(GNUNET_TCP_SOCKET * sock,
+ const void * closure,
+ gfloat ** data);
+
+static int getConnectedNodesStat(GNUNET_TCP_SOCKET * sock,
+ const void * closure,
+ gfloat ** data) {
+ long long val;
+ char * cmh;
+ long long cval;
+
+ cmh = getConfigurationOptionValue(sock,
+ "gnunetd",
+ "connection-max-hosts");
+ if (cmh == NULL)
+ return SYSERR;
+ cval = atoll(cmh);
+ FREE(cmh);
+ if (OK != getStatValue(&val,
+ NULL,
+ NULL,
+ sock,
+ "# currently connected nodes"))
+ return SYSERR;
+ data[0][0] = 0.8 * val / cval;
+ return OK;
+}
+
+static int getCPULoadStat(GNUNET_TCP_SOCKET * sock,
+ const void * closure,
+ gfloat ** data) {
+ long long val;
+
+ if (OK != getStatValue(&val,
+ NULL,
+ NULL,
+ sock,
+ "% of allowed cpu load"))
+ return SYSERR;
+ data[0][0] = val / 125.0;
+ return OK;
+}
+
+static int getTrafficRecvStats(GNUNET_TCP_SOCKET * sock,
+ const void * closure,
+ gfloat ** data) {
+ long long total;
+ long long noise;
+ long long content;
+ long long queries;
+ long long ltotal;
+ long long lnoise;
+ long long lcontent;
+ long long lqueries;
+ long long band;
+ long long tmp;
+ long long ltmp;
+ cron_t dtime;
+ char * available;
+
+ MUTEX_LOCK(&lock);
+ if (OK != getStatValue(&total,
+ <otal,
+ &dtime,
+ sock,
+ "# bytes decrypted"))
+ return SYSERR;
+ if (OK != getStatValue(&noise,
+ &lnoise,
+ NULL,
+ sock,
+ "# bytes of noise received"))
+ return SYSERR;
+ content = lcontent = 0;
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes received of type 17")) {
+ content += tmp;
+ lcontent += ltmp;
+ }
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes received of type 18")) {
+ content += tmp;
+ lcontent += ltmp;
+ }
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes received of type 20")) {
+ content += tmp;
+ lcontent += ltmp;
+ }
+ queries = lqueries = 0;
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes received of type 16")) {
+ queries += tmp;
+ lqueries += ltmp;
+ }
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes received of type 19")) {
+ queries += tmp;
+ lqueries += ltmp;
+ }
+
+ MUTEX_UNLOCK(&lock);
+ available = getConfigurationOptionValue(sock,
+ "LOAD",
+ "MAXNETDOWNBPSTOTAL");
+ if (available == NULL)
+ return SYSERR;
+ band = atoll(available) * dtime / cronSECONDS;
+ FREE(available);
+ total -= ltotal;
+ noise -= lnoise;
+ queries -= lqueries;
+ content -= lcontent;
+ if (band <= 0) {
+ data[0][0] = 0.0;
+ data[0][1] = 0.0;
+ data[0][2] = 0.0;
+ data[0][3] = 0.0;
+ return OK;
+ }
+ data[0][0] = 0.8 * noise / band; /* red */
+ data[0][1] = 0.8 * (content+noise) / band; /* green */
+ data[0][2] = 0.8 * (queries+content+noise) / band; /* yellow */
+ data[0][3] = 0.8 * total / band; /* blue */
+ /*printf("I: %f %f %f\n",
+ data[0][0],
+ data[0][1],
+ data[0][2]);*/
+
+ return OK;
+}
+
+
+static int getTrafficSendStats(GNUNET_TCP_SOCKET * sock,
+ const void * closure,
+ gfloat ** data) {
+ long long total;
+ long long noise;
+ long long content;
+ long long queries;
+ long long ltotal;
+ long long lnoise;
+ long long lcontent;
+ long long lqueries;
+ long long band;
+ long long tmp;
+ long long ltmp;
+ cron_t dtime;
+ char * available;
+
+ MUTEX_LOCK(&lock);
+ if (OK != getStatValue(&total,
+ <otal,
+ &dtime,
+ sock,
+ "# encrypted bytes sent"))
+ return SYSERR;
+ if (OK != getStatValue(&noise,
+ &lnoise,
+ NULL,
+ sock,
+ "# bytes noise sent"))
+ return SYSERR;
+ content = lcontent = 0;
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes transmitted of type 17")) {
+ content += tmp;
+ lcontent += ltmp;
+ }
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes transmitted of type 18")) {
+ content += tmp;
+ lcontent += ltmp;
+ }
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes transmitted of type 20")) {
+ content += tmp;
+ lcontent += ltmp;
+ }
+ queries = lqueries = 0;
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes transmitted of type 16")) {
+ queries += tmp;
+ lqueries += ltmp;
+ }
+ if (OK == getStatValue(&tmp,
+ <mp,
+ NULL,
+ sock,
+ "# bytes transmitted of type 19")) {
+ queries += tmp;
+ lqueries += ltmp;
+ }
+ MUTEX_UNLOCK(&lock);
+ available = getConfigurationOptionValue(sock,
+ "LOAD",
+ "MAXNETUPBPSTOTAL");
+ if (available == NULL)
+ return SYSERR;
+ band = atoll(available) * dtime / cronSECONDS;
+ FREE(available);
+ total -= ltotal;
+ noise -= lnoise;
+ queries -= lqueries;
+ content -= lcontent;
+ if (band <= 0) {
+ data[0][0] = 0.0;
+ data[0][1] = 0.0;
+ data[0][2] = 0.0;
+ data[0][3] = 0.0;
+ return OK;
+ }
+ data[0][0] = 0.8 * noise / band; /* red */
+ data[0][1] = 0.8 * (noise + content) / band; /* green */
+ data[0][2] = 0.8 * (noise + content + queries) / band; /* yellow */
+ data[0][3] = 0.8 * total / band; /* yellow */
+ /* printf("O: %f %f %f\n",
+ data[0][0],
+ data[0][1],
+ data[0][2]);*/
+
+ return OK;
+}
+
+
+
+typedef struct SE_ {
+ char * paneName;
+ char * frameName;
+ UpdateData getData;
+ void * get_closure;
+ unsigned int count;
+ int fill; /* YES / NO */
+} StatEntry;
+
+#define STATS_COUNT 4
+
+static StatEntry stats[] = {
+ {
+ "Connectivity",
+ "# connected nodes (100% = connection table size)",
+ &getConnectedNodesStat,
+ NULL,
+ 1,
+ NO,
+ },
+ {
+ "CPU load",
+ "CPU load (in percent of allowed load)",
+ &getCPULoadStat,
+ NULL,
+ 1,
+ NO,
+ },
+ {
+ "Inbound Traffic",
+ "Noise (red), Content (green), Queries (yellow), other (blue)",
+ &getTrafficRecvStats,
+ NULL,
+ 4,
+ YES,
+ },
+ {
+ "Outbound Traffic",
+ "Noise (red), Content (green), Queries (yellow), other (blue)",
+ &getTrafficSendStats,
+ NULL,
+ 4,
+ YES,
+ },
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 1,
+ NO,
+ },
+};
+
+static void statClose(void);
+
+static GtkItemFactoryEntry statWindowMenu[] = {
+ { "/Close display",
+ NULL,
+ statClose,
+ 0,
+ "<Item>"
+ }
+};
+static gint statWindowMenuItems
+ = sizeof (statWindowMenu) / sizeof (statWindowMenu[0]);
+
+typedef struct {
+ gint type;
+ guint count;
+ guint speed;
+ guint draw_width, draw_height;
+ guint num_points;
+ guint allocated;
+ GdkColor *colors;
+ gfloat **data, **odata;
+ guint data_size;
+ gint colors_allocated;
+ GtkWidget *main_widget;
+ GtkWidget *disp;
+ GtkWidget *label;
+ GdkPixmap *pixmap;
+ GdkGC *gc;
+ int timer_index;
+ gboolean draw;
+ GNUNET_TCP_SOCKET * sock;
+ int statIdx;
+} LoadGraph;
+
+#define MAX_COLOR 4
+
+typedef struct {
+ gint graph_update_interval;
+ GdkColor bg_color;
+ GdkColor frame_color;
+ GdkColor mem_color[MAX_COLOR];
+} ProcConfig;
+
+typedef struct ProcData {
+ ProcConfig config;
+ LoadGraph *mem_graph;
+ int statIdx;
+} ProcData;
+
+#define GNOME_PAD_SMALL 2
+#define FRAME_WIDTH 0
+
+/**
+ * Remove the active page from the notebook.
+ **/
+static void statClose(void) {
+ gint pagenr;
+
+ pagenr = gtk_notebook_get_current_page(notebook);
+ gtk_notebook_remove_page(notebook, pagenr);
+ /* Need to refresh the widget --
+ This forces the widget to redraw itself. */
+ gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+/* Redraws the backing pixmap for the load graph and updates the window */
+static void load_graph_draw(LoadGraph *g) {
+ guint i;
+ guint j;
+ gint dely;
+ float delx;
+
+ if (!g->disp->window)
+ return;
+
+ g->draw_width = g->disp->allocation.width;
+ g->draw_height = g->disp->allocation.height;
+
+ if (!g->pixmap)
+ g->pixmap = gdk_pixmap_new (g->disp->window,
+ g->draw_width, g->draw_height,
+ gtk_widget_get_visual (g->disp)->depth);
+
+ /* Create GC if necessary. */
+ if (!g->gc) {
+ g->gc = gdk_gc_new (g->disp->window);
+ gdk_gc_copy (g->gc, g->disp->style->white_gc);
+ }
+
+ /* Allocate colors. */
+ if (!g->colors_allocated) {
+ GdkColormap *colormap;
+
+ colormap = gdk_window_get_colormap (g->disp->window);
+ for (i=0;i<2+g->count;i++)
+ gdk_color_alloc (colormap, &(g->colors [i]));
+
+ g->colors_allocated = 1;
+ }
+ /* Erase Rectangle */
+ gdk_gc_set_foreground (g->gc, &(g->colors [0]));
+ gdk_draw_rectangle (g->pixmap,
+ g->gc,
+ TRUE, 0, 0,
+ g->disp->allocation.width,
+ g->disp->allocation.height);
+
+ /* draw frame */
+ gdk_gc_set_foreground (g->gc, &(g->colors [1]));
+ gdk_draw_rectangle (g->pixmap,
+ g->gc,
+ FALSE, 0, 0,
+ g->draw_width,
+ g->disp->allocation.height);
+
+ dely = g->draw_height / 5;
+ for (i = 1; i <5; i++) {
+ gint y1 = g->draw_height + 1 - i * dely;
+ gdk_draw_line (g->pixmap, g->gc,
+ 0, y1, g->draw_width, y1);
+ }
+
+ gdk_gc_set_line_attributes (g->gc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND,
GDK_JOIN_MITER );
+ delx = (float)g->draw_width / ( g->num_points - 1);
+
+ for (j=0;j<g->count;j++) {
+ gdk_gc_set_foreground (g->gc, &(g->colors [j + 2]));
+ for (i = 0; i < g->num_points - 1; i++) {
+ gint x1 = i * delx;
+ gint x2 = (i + 1) * delx;
+ gint y1 = g->data[i][j] * g->draw_height - 1;
+ gint y2 = g->data[i+1][j] * g->draw_height - 1;
+
+ if ((g->data[i][j] != -1) && (g->data[i+1][j] != -1)) {
+ if (stats[g->statIdx].fill == NO) {
+ gdk_draw_line(g->pixmap, g->gc,
+ g->draw_width - x2,
+ g->draw_height - y2,
+ g->draw_width - x1,
+ g->draw_height - y1);
+ } else {
+ GdkPoint points[4];
+
+ points[0].x = g->draw_width - x2;
+ points[0].y = g->draw_height - y2;
+ points[1].x = g->draw_width - x1;
+ points[1].y = g->draw_height - y1;
+ points[2].x = g->draw_width - x1;
+ points[3].x = g->draw_width - x2;
+ if (j == 0) {
+ points[2].y = g->draw_height;
+ points[3].y = g->draw_height;
+ } else {
+ gint ly1 = g->data[i][j-1] * g->draw_height - 1;
+ gint ly2 = g->data[i+1][j-1] * g->draw_height - 1;
+ points[2].y = g->draw_height - ly1;
+ points[3].y = g->draw_height - ly2;
+ }
+ gdk_draw_polygon(g->pixmap,
+ g->gc,
+ 1,
+ points,
+ 4);
+ }
+ }
+ }
+ }
+
+ gdk_gc_set_line_attributes (g->gc, 1, GDK_LINE_SOLID, GDK_CAP_ROUND,
GDK_JOIN_MITER );
+
+ gdk_draw_pixmap (g->disp->window,
+ g->disp->style->fg_gc [GTK_WIDGET_STATE(g->disp)],
+ g->pixmap,
+ 0, 0,
+ 0, 0,
+ g->disp->allocation.width,
+ g->disp->allocation.height);
+}
+
+
+/* Updates the load graph when the timeout expires */
+static int load_graph_update(LoadGraph *g) {
+ guint i;
+ guint j;
+
+ for (i=0;i<g->num_points;i++)
+ memcpy(g->odata[i],
+ g->data[i],
+ g->data_size * g->count);
+ stats[g->statIdx].getData(g->sock,
+ stats[g->statIdx].get_closure,
+ g->data);
+ for (i=0;i<g->num_points-1;i++)
+ for (j=0;j<g->count;j++)
+ g->data[i+1][j] = g->odata[i][j];
+ if (g->draw)
+ load_graph_draw (g);
+ return TRUE;
+}
+
+static void load_graph_unalloc (LoadGraph *g) {
+ int i;
+ if (!g->allocated)
+ return;
+ for (i = 0; i < g->num_points; i++) {
+ FREE(g->data[i]);
+ FREE(g->odata[i]);
+ }
+ FREE(g->data);
+ FREE(g->odata);
+ g->data = g->odata = NULL;
+ if (g->pixmap) {
+ gdk_pixmap_unref(g->pixmap);
+ g->pixmap = NULL;
+ }
+ g->allocated = FALSE;
+}
+
+static void load_graph_alloc (LoadGraph *g) {
+ int i;
+ int j;
+
+ if (g->allocated)
+ return;
+
+ g->data = MALLOC(sizeof(gfloat *) * g->num_points);
+ g->odata = MALLOC(sizeof(gfloat*) * g->num_points);
+ g->data_size = sizeof (gfloat);
+ for (i = 0; i < g->num_points; i++) {
+ g->data[i] = MALLOC(g->data_size * g->count);
+ g->odata[i] = MALLOC(g->data_size * g->count);
+ }
+ for (i=0;i<g->num_points;i++)
+ for (j=0;j<g->count;j++)
+ g->data[i][j] = -1;
+ g->allocated = TRUE;
+}
+
+static gint load_graph_configure(GtkWidget *widget,
+ GdkEventConfigure *event,
+ gpointer data_ptr) {
+ LoadGraph *c = (LoadGraph *) data_ptr;
+
+ if (c->pixmap) {
+ gdk_pixmap_unref (c->pixmap);
+ c->pixmap = NULL;
+ }
+
+ if (!c->pixmap)
+ c->pixmap = gdk_pixmap_new (widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ gtk_widget_get_visual (c->disp)->depth);
+
+ gdk_draw_rectangle (c->pixmap,
+ widget->style->black_gc,
+ TRUE, 0,0,
+ widget->allocation.width,
+ widget->allocation.height);
+ gdk_draw_pixmap (widget->window,
+ c->disp->style->fg_gc [GTK_WIDGET_STATE(widget)],
+ c->pixmap,
+ 0, 0,
+ 0, 0,
+ c->disp->allocation.width,
+ c->disp->allocation.height);
+ load_graph_draw (c);
+ return TRUE;
+}
+
+static gint load_graph_expose(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data_ptr) {
+ LoadGraph *g = (LoadGraph *) data_ptr;
+
+ gdk_draw_pixmap (widget->window,
+ widget->style->fg_gc [GTK_WIDGET_STATE(widget)],
+ g->pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ return FALSE;
+}
+
+static void load_graph_stop (LoadGraph *g) {
+ if (g->timer_index != -1) {
+ gtk_timeout_remove (g->timer_index);
+ g->timer_index = -1;
+ }
+ if (!g)
+ return;
+ g->draw = FALSE;
+}
+
+static void load_graph_destroy(GtkWidget *widget,
+ gpointer data_ptr) {
+ LoadGraph *g = (LoadGraph *) data_ptr;
+ load_graph_stop(g);
+ if (g->timer_index != -1)
+ gtk_timeout_remove (g->timer_index);
+ if (g->sock != NULL)
+ releaseClientSocket(g->sock);
+ load_graph_unalloc(g);
+ FREE(g->colors);
+ FREE(g);
+}
+
+static LoadGraph * load_graph_new(ProcData *procdata) {
+ LoadGraph *g;
+ unsigned int i;
+
+ if ( (procdata->statIdx < 0) ||
+ (procdata->statIdx >= STATS_COUNT) ) {
+ LOG(LOG_ERROR,
+ "ERROR: invalid statIdex %d!",
+ procdata->statIdx);
+ return NULL;
+ }
+ if (stats[procdata->statIdx].count > MAX_COLOR) {
+ LOG(LOG_ERROR,
+ "ERROR: more colors requested than available!\n");
+ return NULL;
+ }
+
+ g = MALLOC(sizeof(LoadGraph));
+ g->sock = getClientSocket();
+ g->statIdx = procdata->statIdx;
+ g->count = stats[g->statIdx].count;
+ g->speed = procdata->config.graph_update_interval;
+ g->num_points = 600;
+ g->colors = MALLOC(sizeof(GdkColor) * (2+g->count));
+ g->colors[0] = procdata->config.bg_color;
+ g->colors[1] = procdata->config.frame_color;
+ for (i=0;i<g->count;i++)
+ g->colors[2+i] = procdata->config.mem_color[i];
+ g->timer_index = -1;
+ g->draw = FALSE;
+ g->main_widget = gtk_vbox_new (FALSE, FALSE);
+ gtk_widget_show (g->main_widget);
+ g->disp = gtk_drawing_area_new();
+ gtk_widget_show (g->disp);
+ gtk_signal_connect (GTK_OBJECT (g->disp),
+ "expose_event",
+ GTK_SIGNAL_FUNC (load_graph_expose), g);
+ gtk_signal_connect (GTK_OBJECT(g->disp),
+ "configure_event",
+ GTK_SIGNAL_FUNC (load_graph_configure), g);
+ gtk_signal_connect (GTK_OBJECT(g->disp),
+ "destroy",
+ GTK_SIGNAL_FUNC (load_graph_destroy), g);
+ gtk_widget_set_events(g->disp, GDK_EXPOSURE_MASK);
+ gtk_box_pack_start(GTK_BOX (g->main_widget), g->disp, TRUE, TRUE, 0);
+ load_graph_alloc(g);
+ gtk_widget_show_all (g->main_widget);
+ g->timer_index = gtk_timeout_add(g->speed,
+ (GtkFunction) load_graph_update, g);
+
+ return g;
+}
+
+static void load_graph_start(LoadGraph *g) {
+ if (!g)
+ return;
+
+ if (g->timer_index == -1)
+ g->timer_index = gtk_timeout_add(g->speed,
+ (GtkFunction) load_graph_update, g);
+
+ g->draw = TRUE;
+}
+
+static GtkWidget * create_sys_view(ProcData * procdata) {
+ GtkWidget * vbox;
+ GtkWidget * mem_frame;
+ GtkWidget * menu;
+ GtkItemFactory * popupFactory;
+ LoadGraph * mem_graph;
+
+ mem_graph = load_graph_new(procdata);
+ if (mem_graph == NULL)
+ return NULL; /* oops */
+ vbox = gtk_vbox_new (FALSE, 0);
+
+ mem_frame = gtk_frame_new (stats[procdata->statIdx].frameName);
+ gtk_container_set_border_width (GTK_CONTAINER (mem_frame), GNOME_PAD_SMALL);
+ gtk_box_pack_start (GTK_BOX (vbox), mem_frame, TRUE, TRUE, 0);
+
+
+ gtk_container_add (GTK_CONTAINER (mem_frame), mem_graph->main_widget);
+ gtk_container_set_border_width (GTK_CONTAINER (mem_graph->main_widget),
+ GNOME_PAD_SMALL);
+ procdata->mem_graph = mem_graph;
+
+ /* FIXME: this should add a right-button popup-menu
+ but does not. Somehow popupCallback() never gets called,
+ i.e. event does not occur or has been blocked or
+ overridden somewhere. Trying to connect to some other
+ widget instead of vbox stays equally unresponsive */
+ popupFactory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
+ NULL);
+ gtk_item_factory_create_items(popupFactory,
+ statWindowMenuItems,
+ statWindowMenu,
+ NULL);
+ menu = gtk_item_factory_get_widget(popupFactory, "<main>");
+ gtk_signal_connect(GTK_OBJECT(vbox),
+ "event",
+ GTK_SIGNAL_FUNC(popupCallback),
+ menu);
+ gtk_widget_show_all (vbox);
+
+ return vbox;
+}
+
+
+static GtkWidget * create_main_window(int stat) {
+ GtkWidget *sys_box;
+ ProcData procdata;
+
+
+ memset(&procdata, 0, sizeof(ProcData));
+ procdata.config.graph_update_interval
+ = UPDATE_INTERVAL / cronMILLIS;
+ procdata.statIdx = stat;
+ gdk_color_parse("black",
+ &procdata.config.bg_color);
+ gdk_color_parse("gray",
+ &procdata.config.frame_color);
+ gdk_color_parse("red",
+ &procdata.config.mem_color[0]);
+ gdk_color_parse("green",
+ &procdata.config.mem_color[1]);
+ gdk_color_parse("yellow",
+ &procdata.config.mem_color[2]);
+ gdk_color_parse("blue",
+ &procdata.config.mem_color[3]);
+ if (MAX_COLOR != 4)
+ errexit("Assertion failed! MAX_COLOR wrong!");
+ sys_box = create_sys_view(&procdata);
+ if (sys_box == NULL)
+ return NULL;
+ load_graph_start(procdata.mem_graph);
+ return sys_box;
+}
+
+
+/**
+ * Display the statistics.
+ */
+void displayStatistics(GtkWidget * widget,
+ gpointer data) {
+ int dptr;
+ GtkWidget * wid;
+
+ dptr = (int) data;
+ if ( (dptr < 0) ||
+ (dptr >= STATS_COUNT) ) {
+ LOG(LOG_ERROR,
+ "ERROR: displayStatistics called with invalid argument (%d)\n",
+ dptr);
+ } else {
+ wid = create_main_window(dptr);
+ if (wid != NULL)
+ addToNotebook(stats[dptr].paneName,
+ wid);
+ }
+}
+
+void initGTKStatistics() {
+ MUTEX_CREATE_RECURSIVE(&lock);
+}
+
+void doneGTKStatistics() {
+ unsigned int i;
+ for (i=0;i<lsv_size;i++)
+ FREE(lastStatValues[i].statName);
+ GROW(lastStatValues,
+ lsv_size,
+ 0);
+ MUTEX_DESTROY(&lock);
+}
+
+
+/* end of statistics.c */
Added: freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.h 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/protocol/fs/swing/statistics.h 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,52 @@
+/*
+ This file is part of GNUnet
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/statistics.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_STATISTICS_H
+#define GTKUI_STATISTICS_H
+
+/* for GTK 2 */
+#define GTK_ENABLE_BROKEN
+
+#include "platform.h"
+#include <gtk/gtk.h>
+#include <gtk/gtktext.h>
+
+/**
+ * Display the statistics.
+ */
+void displayStatistics(GtkWidget * widget,
+ gpointer data);
+
+
+void initGTKStatistics();
+
+void doneGTKStatistics();
+
+#define STAT_CONNECTIVITY 0
+#define STAT_CPU_LOAD 1
+#define STAT_IN_TRAFFIC 2
+#define STAT_OUT_TRAFFIC 3
+
+
+#endif
Modified: freeway/src/org/gnu/freeway/server/CPluginLoader.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CPluginLoader.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/server/CPluginLoader.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -28,8 +28,10 @@
import org.gnu.freeway.AbstractApplication;
import org.gnu.freeway.cwrappers.util.SwitchTableGenerator;
import org.gnu.freeway.transport.Session;
+import org.gnu.freeway.util.DatastoreValue;
import org.gnu.freeway.util.LoggedObject;
import org.gnu.freeway.util.NativeService;
+import org.gnu.freeway.util.crypto.HashCode512;
import org.gnu.freeway.util.crypto.HostIdentity;
import org.gnu.freeway.util.net.CSMessage;
import org.gnu.freeway.util.net.CSNativeMessage;
@@ -286,4 +288,8 @@
cCallClientExitHandler(CoreAPI._, handle._, client);
}
+ public int callDatumIterator(Handle handle, HashCode512 key,
DatastoreValue value, Handle closure) {
+ return 0;
+ }
+
}
Modified: freeway/src/org/gnu/freeway/server/NativeCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/server/NativeCallback.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/server/NativeCallback.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -22,6 +22,9 @@
import java.nio.ByteBuffer;
import org.gnu.freeway.transport.Session;
+import org.gnu.freeway.util.DatastoreValue;
+import org.gnu.freeway.util.DatumIterator;
+import org.gnu.freeway.util.crypto.HashCode512;
import org.gnu.freeway.util.crypto.HostIdentity;
import org.gnu.freeway.util.net.CSHandler;
import org.gnu.freeway.util.net.CSMessage;
@@ -36,7 +39,7 @@
* @brief
* @author mdonoughe
*/
-public class NativeCallback implements BufferFillCallback, PerNodeCallback,
ClientExitHandler, CSHandler, P2PHandler {
+public class NativeCallback implements BufferFillCallback, PerNodeCallback,
ClientExitHandler, CSHandler, P2PHandler, DatumIterator {
private CPluginLoader.Handle handle;
@@ -87,4 +90,12 @@
return false;
return CPluginLoader._.callP2PHandle(handle,
session.getRemote(), (P2PNativeMessage) message);
}
+
+ //DatumIterator
+ public int datumIteration(HashCode512 key, DatastoreValue value, Object
closure) {
+ assert(closure instanceof CPluginLoader.Handle);
+ if(handle == null || handle._ == 0)
+ return 0;
+ return CPluginLoader._.callDatumIterator(handle, key, value,
(CPluginLoader.Handle) closure);
+ }
}
Added: freeway/src/org/gnu/freeway/services/DatastoreService.java
===================================================================
--- freeway/src/org/gnu/freeway/services/DatastoreService.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/services/DatastoreService.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,41 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.services;
+import org.gnu.freeway.server.CPluginLoader;
+import org.gnu.freeway.util.NativeService;
+import org.gnu.freeway.cwrappers.*;
+
+/**
+ * @file StatsService.java
+ * @brief
+ * @author mdonoughe
+ */
+public interface DatastoreService extends NativeService {
+ public CLong getSize();
+ public CInt put(ConstCHashCode512 key, ConstCDatastoreValue value);
+ public CInt putUpdate(ConstCHashCode512 key, ConstCDatastoreValue
value);
+ //public CInt get(ConstCHashCode512 key, CUnsignedInt type,
CPluginLoader.Handle iter, CPluginLoader.Handle closure);
+ //TODO: we need a CCallback type so we can getValue() C callbacks into
NativeCallbacks and Java callbacks as themselves
+ // the C side would need the oposite done so Java callbacks could
be called as if they were C
+ public CInt fast_get(ConstCHashCode512 key);
+ //TODO: implement array handling
+ //public CInt getRandom(ConstCHashCode512 approx, CUnsignedInt
sizeLimit, CHashCode512 key, CDatastoreValue[] value, CUnsignedInt type);
+ public CInt del(ConstCHashCode512 key, ConstCDatastoreValue cvalue);
+}
Added: freeway/src/org/gnu/freeway/services/c/DatastoreService.java
===================================================================
--- freeway/src/org/gnu/freeway/services/c/DatastoreService.java
2006-08-21 05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/services/c/DatastoreService.java
2006-08-21 11:53:42 UTC (rev 3286)
@@ -0,0 +1,39 @@
+// This class was autogenerated by SwitchTableGenerator
+package org.gnu.freeway.services.c;
+
+import org.gnu.freeway.cwrappers.CInt;
+import org.gnu.freeway.cwrappers.CLong;
+import org.gnu.freeway.cwrappers.ConstCHashCode512;
+import org.gnu.freeway.cwrappers.ConstCDatastoreValue;
+import org.gnu.freeway.server.CPluginLoader;
+
+public class DatastoreService extends AbstractNativeService implements
NativeService, org.gnu.freeway.services.DatastoreService {
+
+ public CLong getSize() {
+ return (CLong) loader.callC(handle, 0, 4, new Object[] {});
+ }
+
+ public CInt put(ConstCHashCode512 arg0, ConstCDatastoreValue arg1) {
+ return (CInt) loader.callC(handle, 1, 85, new Object[] {arg0,
arg1});
+ }
+
+ public CInt putUpdate(ConstCHashCode512 arg0, ConstCDatastoreValue
arg1) {
+ return (CInt) loader.callC(handle, 2, 85, new Object[] {arg0,
arg1});
+ }
+
+ public CInt fast_get(ConstCHashCode512 arg0) {
+ return (CInt) loader.callC(handle, 3, 13, new Object[] {arg0});
+ }
+
+ public CInt del(ConstCHashCode512 arg0, ConstCDatastoreValue arg1) {
+ return (CInt) loader.callC(handle, 4, 85, new Object[] {arg0,
arg1});
+ }
+
+ public DatastoreService() {
+ this(CPluginLoader._);
+ }
+
+ public DatastoreService(CPluginLoader loader) {
+ super(loader);
+ }
+}
Property changes on:
freeway/src/org/gnu/freeway/services/c/DatastoreService.java
___________________________________________________________________
Name: svn:mime-type
+ text/cpp
Modified: freeway/src/org/gnu/freeway/services/c/StatsService.java
===================================================================
--- freeway/src/org/gnu/freeway/services/c/StatsService.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/services/c/StatsService.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -1,14 +1,10 @@
// This class was autogenerated by SwitchTableGenerator
package org.gnu.freeway.services.c;
-import org.gnu.freeway.util.AbstractNativeService;
-import org.gnu.freeway.util.NativeService;
-import org.gnu.freeway.util.ServiceException;
import org.gnu.freeway.cwrappers.CInt;
import org.gnu.freeway.cwrappers.ConstCLong;
import org.gnu.freeway.cwrappers.CLong;
import org.gnu.freeway.cwrappers.ConstCInt;
-import org.gnu.freeway.cwrappers.CString;
import org.gnu.freeway.cwrappers.ConstCString;
import org.gnu.freeway.server.CPluginLoader;
@@ -30,23 +26,6 @@
loader.callC(handle, 3, 42, new Object[] {arg0, arg1});
}
- public void init() throws ServiceException {
- super.init();
- System.err.println("Loaded stats service.");
- CInt c = create(new CString("test"));
- System.err.println("created stat \"test\":" + c.getValue());
- set(c, new CLong(12345));
- c = create(new CString("test2"));
- System.err.println("created stat \"test2\":" + c.getValue());
- set(c, new CLong(54321));
- c = create(new CString("-1"));
- System.err.println("created stat \"-1\":" + c.getValue());
- set(c, new CLong(-1));
- c = create(new CString("-2"));
- System.err.println("created stat \"-2\":" + c.getValue());
- set(c, new CLong(-2));
- }
-
public StatsService() {
this(CPluginLoader._);
}
Modified: freeway/src/org/gnu/freeway/test/MySQLTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/MySQLTest.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/test/MySQLTest.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -4,8 +4,8 @@
package org.gnu.freeway.test;
-import org.gnu.freeway.protocol.afs.*;
-import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.fs.*;
+import org.gnu.freeway.protocol.fs.esed2.*;
import org.gnu.freeway.util.*;
import org.gnu.freeway.util.crypto.*;
Added: freeway/src/org/gnu/freeway/util/DatastoreValue.java
===================================================================
--- freeway/src/org/gnu/freeway/util/DatastoreValue.java 2006-08-21
05:13:22 UTC (rev 3285)
+++ freeway/src/org/gnu/freeway/util/DatastoreValue.java 2006-08-21
11:53:42 UTC (rev 3286)
@@ -0,0 +1,81 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ * @file DatastoreValue.java
+ * @brief
+ * @author mdonoughe
+ */
+public class DatastoreValue {
+ private int size;
+ private int type;
+ private int prio;
+ private int anonymityLevel;
+ private long expirationTime;
+
+ public DatastoreValue(int size, int type, int prio, int anonymityLevel,
long expirationTime) {
+ this.size = size;
+ this.type = type;
+ this.prio = prio;
+ this.anonymityLevel = anonymityLevel;
+ this.expirationTime = expirationTime;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public void setPrio(int prio) {
+ this.prio = prio;
+ }
+
+ public void setAnonymityLevel(int anonymityLevel) {
+ this.anonymityLevel = anonymityLevel;
+ }
+
+ public void setExpirationTime(long expirationTime) {
+ this.expirationTime = expirationTime;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public int getPrio() {
+ return prio;
+ }
+
+ public int getAnonymityLevel() {
+ return anonymityLevel;
+ }
+
+ public long getExpirationTime() {
+ return expirationTime;
+ }
+}
Added: freeway/src/org/gnu/freeway/util/DatumIterator.java
===================================================================
--- freeway/src/org/gnu/freeway/util/DatumIterator.java 2006-08-21 05:13:22 UTC
(rev 3285)
+++ freeway/src/org/gnu/freeway/util/DatumIterator.java 2006-08-21 11:53:42 UTC
(rev 3286)
@@ -0,0 +1,31 @@
+ /*
+ This file is part of Freeway
+
+ Freeway is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Freeway is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Freeway; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.util.crypto.HashCode512;
+
+/**
+ * @file DatumIterator.java
+ * @brief
+ * @author mdonoughe
+ */
+public interface DatumIterator {
+ int datumIteration(HashCode512 key, DatastoreValue value, Object
closure);
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r3286 - in freeway: . native src/org/gnu/freeway src/org/gnu/freeway/cwrappers src/org/gnu/freeway/cwrappers/util src/org/gnu/freeway/protocol src/org/gnu/freeway/protocol/fs src/org/gnu/freeway/protocol/fs/esed2 src/org/gnu/freeway/protocol/fs/swing src/org/gnu/freeway/server src/org/gnu/freeway/services src/org/gnu/freeway/services/c src/org/gnu/freeway/test src/org/gnu/freeway/util,
mdonoughe <=