ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/pws2006/event.pod
Revision: 1.1
Committed: Thu Jan 12 01:48:49 2006 UTC (20 years, 8 months ago) by root
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 Ereignisgesteuerte Programmierung mit Perl - die Realität
2    
3     =head2 Zusammenfassung
4    
5     Ereignisgesteuerte Programmierung ist hinreichend bekannt. Trotzdem
6     wird sie relativ selten benutzt - in Modulen so gut wie garnicht. Ich
7     möchte hier verschiedene Arten der Ereignissteuerung sauber von anderen
8     Formen der Programmierung zu trennen, vorhandene Ereignismodule und ihre
9     Unterschiede vorstellen. Vor allem sollen Möglichkeiten gezeigt werden,
10     wie man als Modulautor - je nach gewünschtem Aufwand - verschiedene APIs
11     anbieten kann.
12    
13     =head1 Ereignissteuerung - Kurzeinführung
14    
15     =head2 Nicht Ereignisgesteuert
16    
17     =head3 blocking vs. blocking
18    
19     =head2 Continuation Passing
20    
21     =head2 Coroutinen/Threads
22    
23    
24     =head1 Event-Modelle
25    
26     =head2 Event
27    
28     =head2 Glib/Gtk2, Tk,
29    
30     =head2 Coroutinen - Coro::AIO
31    
32    
33     =head1 Das Problem für Modulautoren
34    
35    
36     =head1 Lösungen/Workarounds
37    
38     =head2 Wozu? Blocking reicht - LWP
39    
40     =head2 Überlass es dem User - Net::Knuddels, IO::AIO
41    
42     =head2 Leg dich fest - Coro::Event
43    
44     =head2 Bilde ein Modell auf ein anderes ab - Glib::Event
45    
46     =head2 Threads - ???
47    
48     =head2 AnyEvent - Coro::AIO, Net::FCP
49    
50     -------------------------------------------------------------------------------
51    
52     =head3 Freenet für den Benutzer
53    
54     Freenet speichert nur relativ kleine Dokumente (<= 1MB) am Stück. Diese
55     Dokumente können beliebige Daten enthalten, z.B. HTML-Seiten. Diese
56     Datenblöcke können mit einem für jeden Block eindeutigen Schlüssel
57     abgerufen werden, z.B. in einem Web-Browser.
58    
59     Hier ist ein Beispiel für eine solche HTML-Seite:
60    
61     =for html
62     <img src="freenet1.png" />
63    
64     =for latex
65     \begin{center}\includegraphics[scale=0.55]{freenet1.png}\end{center}
66    
67     =for pp
68     \IMAGE{src="freenet1.png"}
69    
70     Die hier verwendete URL ist:
71    
72     http://129.13.162.73:8888/...
73     ...SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM/YoYo//Controversy.html
74    
75     =for latex
76     \begin{sloppypar}
77    
78     C<http://129.13.162.73:8888> ist die Adresse meines Freenet-Daemons,
79     üblicherweise C<http://127.0.0.1:8888>, der Sicherheit wegen sollte man
80     immer einen eigenen Freenet-Daemon laufen lassen. Der Schlüssel der
81     ``Freesite'' ist C<SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM> und in dieser wird
82     wiederum das Unterdokument C<YoYo//Controversy.html> angezeigt.
83    
84     =for latex
85     \end{sloppypar}
86    
87     Diese Seite ist Teil eines sehr bekannten Einsprungpunktes ins
88     Freenet. Generell bevorzugt das Freenet-Projekt keine bestimmten
89     Einsprungpunkte. Die Regel ist: kennt man den Einsprungpunkt nicht, findet
90     man die Inhalte nicht; es ist sogar unmöglich zu bestimmten, wieviel oder
91     welche Informationen im Freenet gespeichert sind: Ein Index a'la google
92     ist prinzipbedingt nicht möglich.
93    
94     Obwohl es einige Freenet-Spider und Directory-Freesites gibt ist der
95     überwältigende Teil der Information im Freenet nicht darüber zu
96     erreichen.
97    
98     Neben HTML-Inhalten gibt es auch Mail-, Foren- und Chat-Systeme im
99     Freenet.
100    
101     Die Einspeisung von Daten kann von überall im Netz
102     geschehen. Mehrfacheinspeisung ist möglich (und meistens notwendig) und
103     führt nicht zur Dupliziering von Daten.
104    
105     Natürlich gibts es auch einige Nachteile, die teilweise mit dem Design
106     und teilweise mit der Implementierung zusammenhängen. So ist der einzige
107     verfügbare Freenet-Daemon in Java implementiert und daher leider extrem
108     unportabel (läuft praktisch nur auf Linux/x86 und Windows, da das
109     benötige Sun-Java-JDK auf anderen Platformen nicht verfügbar oder
110     veraltet ist), sehr langsam und vor allem speicherhungrig, und häufig
111     auch sehr instabil.
112    
113     Die aktuellen Versionen des Freenet-Daemons sind alle als ``nicht sicher''
114     deklariert, da erst die Release 1.0 verspricht, alle Ideen umgesetzt zu
115     haben, wovon das Projekt noch 0.5 Versionen entfernt ist :)
116    
117     Designbedingt ist das Freenet eher langsam (extrem hohe Latenz,
118     akzeptabler Durchsatz) und für interaktive Benutzung teilweise
119     ungeeignet, bzw. eine echte Geduldsprobe.
120    
121     Durch die Art der Speicherung kann nicht garantiert werden, daß
122     Informationen überhaupt wiedergefunden werden. ``Unpopuläre'' Information
123     verschwindet, sofern sie nicht regelmäßig eingespeist oder abgerufen
124     wird, automatisch wieder aus dem Freenet.
125    
126     =head3 Die Freenet-Architektur
127    
128     =for latex
129     \begin{sloppypar}
130    
131     Herkömmliche File-Sharing-Netzwerke haben zwei große Nachteile: Entweder
132     sind sie nicht wirklich anonym (selbst anonymisierende Netzwerke oder
133     Proxies sind üblicherweise nicht vor Zugriff durch Regierungen sicher
134     (ein bekanntes Beispiel ist I<JAP>, L<http://anon.inf.tu-dresden.de>, das
135     zwar vollmundig vollkommene Anonymität garantiert, aber schon einmal durch
136     die Polizei gezwungen wurde, Log-Informationenen herauszugeben, wozu die
137     Betreiber nach deutschem Recht verpflichtet sind). Oder sie skalieren
138     nicht, da sie Broadcast-Algorithmen benutzen, die größere Netzwerke von
139     vorneherein ausschließen (bekanntes Beispiel dafür ist I<gnutella>).
140    
141     =for latex
142     \end{sloppypar}
143    
144     Das erste Problem umgeht Freenet (Achtung, vereinfacht!), indem es
145     jeden Datenblock hasht und aus diesem Hash einen Schlüssel für die
146     Verschlüsselung generiert und die Daten damit verschlüsselt (sic).
147    
148     Der Schlüssel wird ein zweitesmal gehasht. Dieser Hash wird zur
149     eindeutigen ID des Datenblocks. Diese ID und der verschlüsselte
150     Datenblock werden ins Freenet eingespeist. Dabei wird immer derselbe
151     Key generiert (da er nur von den Daten abhängt), und der verschlüsselte
152     Block ist ebenfalls immer gleich. Daher kann er problemlos mehrfach und
153     von unterschiedlichen Parteien eingespeist werden ohne die Daten faktisch
154     mehrfach im Freenet abzulegen.
155    
156     Das ist sicher, da ein kryptographisch sicherer Hash (der die Grundlage
157     des Systems bilden muß) nur in eine Richtung funktioniert: Aus der ID
158     (== Hash des Hashes der Daten) kann nicht auf den Schlüssel geschlossen
159     werden. Speichert also ein Netzknoten die Daten und die ID dazu, kann man
160     die Daten zwar abrufen, jedoch nicht entschlüsseln. Selbst der Besitzer
161     eines Knotens kann mit vollkommenen Wissen über alle Vorgänge seines
162     Knotens die Inhalte nicht lesen.
163    
164     Eine weitere Konsequenz dieses Verfahrens ist die Tatsache, das Dokumente
165     niemals verändert werden können: eine Änderung bewirkt eine Änderung
166     des Schlüssels und damit eine neue ID, praktisch eine völlig neue URL.
167    
168     Möchte man z.B. das grüne Blatt der Freesite ``Thought Crime'' herunterladen so
169     wird man mit folgendem Freenet-Key konfrontiert:
170    
171     CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI,blzDbhN~8Q28esq4JrfRWw
172    
173     C<CHK> steht für I<Content-Hash-Key>, der häufigste Schlüsseltyp im
174     Freenet, der das oben beschriebene Verfahren benutzt. Der CHK besteht
175     aus zwei Teilen: der ID, unter der der Schlüssel im Freenet abgelegt
176     wird (C<pfAv9IejYPLQwaTLXDguEkUhiNUMAwI>) und dem Schlüssel, mit dem die
177     Daten verschlüsselt wurden (C<blzDbhN~8Q28esq4JrfRWw>), durch ein Komma
178     getrennt.
179    
180     Das sind übrigens (mehr oder weniger) base64-encodete Daten, dekodiert
181     sieht der Schlüssel so aus:
182    
183     CHK@
184     ID = a5f02ff487a360f2d0c1a4cb5c382e12452188d50c0302
185     Key = 6e5cc36e137ef10dbc7acab826b7d15b
186    
187     (Man kann daraus tatsächlich ablesen, das der Datenblock 4k groß (0x0c
188     am Ende der ID bedeutet 2**12) ist und Twofish benutzt. Aber derartige
189     Details überläßt man lieber einem Perl-Modul).
190    
191     Nur der erste Teil wird als Anfrage ins Freenet geschickt, der zweite Teil
192     bleibt im lokalen Freenet-Daemon. Wird nun der Datenblock geliefert, kann
193     er mit dem Schlüssel dekodiert werden und wird an den Benutzer geliefert.
194    
195     Das zweite Problem (der Skalierbarkeit) wird durch lineare
196     statt exponentielle Suche gelöst: mit jeder Anfrage wird eine
197     I<Hops-To-Live>-Angabe (HTL, ähnlich wie das TTL in IPv4)
198     verknüpft. Diese Zahl, die üblicherweise zwischen 5 und 25 (maximal)
199     liegt, gibt an, wie viele Knoten die Anfrage maximal weitergeleitet
200     wird. Jeder Knoten, der die Daten nicht lokal vorrätig hat, leitet sie an
201     denjenigen Knoten weiter, der die Daten am wahrscheinlichsten hat. Da das
202     Verfahren linear ist und nicht exponentiell, skaliert es ebenfalls linear
203     mit der Netzwerkgröße.
204    
205     Lädt man das Dokument, wird man mit Metadaten und ``normalen'' Daten
206     konfrontiert.
207    
208     Metadaten:
209    
210     Revision=1
211     EndPart
212     Document
213     Info.Format=image/png
214     End
215    
216     Daten:
217    
218     PNG^Z...
219    
220     Nun stellt sich die Frage, wie man sicherstellt, daß Daten im Freenet
221     bleiben, da es nur endliche Speicherkapazität hat. Die Antwort ist
222     überraschend: Es geht nicht. Wenn ein Knoten sich entscheidet, Daten
223     zu löschen oder Platz für neue Daten zu schaffen, gehen zwangsläufig
224     andere Daten verloren. Dagegen hilft nur wiederholtes Einfügen und der
225     Wunsch, daß die eigenen Daten beliebt genug sind, damit sie abgerufen
226     werden und damit länger überleben.
227    
228     Absolute Anonymität und nichtnachvollziehbarkeit von Transaktionen ist
229     eben nicht vereinbar mit garantierter Datenspeicherung. Könnte man
230     garantiert nachweisen, das ein Dokument existiert, so wäre es für
231     einen Knotenbetreiber schlecht möglich, Wissen über die Art der Daten
232     abzustreiten, die er speichert.
233    
234     =head4 SSK-Schlüssel
235    
236     Der zweite gebräuchliche Schlüsseltyp, mit dem man im Freenet
237     konfrontiert wird, ist ein C<SSK>-Schlüssel (C<SSK> steht für I<Signed
238     Subspace Key>). Hier ist einer:
239    
240     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM
241    
242     Zwei Dinge unterscheiden Sie von CHK-Schlüsseln:
243    
244     Erstens ist ein SSK-Schlüssel eigentlich ein Schlüsselpaar, ein
245     Schlüssel (der private Schlüssel) muss für das Einfügen von Daten
246     benutzt werden, der zweite Schlüssel (der öffentliche Key) wird für
247     das Auslesen der Daten benutzt. Da die Daten signiert sind, kann nur der
248     oder die Besitzer des privaten Schlüssels Daten unter diesem einfügen,
249     während die Besitzer des öffentlichen Schlüssels überprüfen können,
250     ob die Daten tatsächlich mit dem richtigen Schlüssel signiert wurden.
251    
252     Zweitens ist die ID, unter dem diese Daten abgelegt werden, nicht von den
253     Daten sondern nur vom ``Namen'' (dem Key) abhängig, man kann also URIs
254     erzeugen auf Dateien, die noch nicht eingefügt wurden.
255    
256     Diese Art Schlüssel ist relativ ineffizient und kann keine großen
257     Datenmengen speichern (<= 32KB!), SSKs werden also vorwiegend für
258     Weiterleitungen auf CHKs benutzt.
259    
260     Der obige SSK gehört übrigens zur ``Freenet Explained''-Freesite, die die
261     einzelnen Schlüsseltypen (und mehr) erklärt. Finden kann man sie hier:
262    
263     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
264    
265     bzw., wenn man einen Freenet-Daemon am Laufen hat, hier:
266    
267     http://127.0.0.1:8888/SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
268    
269     =head2 Und das ganze in Perl
270    
271     Das ganze wäre relativ witzlos, wenn man nur irgendwelche
272     langweiligen Tools und/oder Java benutzen kann. Es gibt
273     zwei Perl-Module (bzw. Modulfamilien), C<Net::Freenet::FCP>
274     (L<http://www.sf.net/projects/perlfcp>) und C<Net::FCP>.
275    
276     Ersteres ist älter (und vielleicht besser benannt) und konzentriert
277     sich mehr auf die Verarbeitung von Metadaten, ist aber recht schwach
278     beim eigentlichen Protokoll-Handling. Letzteres ist sehr gut beim
279     Protokoll-Handling und stark beim Kodieren und Dekodieren von Daten,
280     überläßt das Metadatenhandling aber dem Programmierer.
281    
282     C<Net::FCP> stammt von mir und ist entstanden, weil ich das andere Modul,
283     das es nur im Freenet gab, nicht herunterladen konnte (tja, so ist das
284     eben als Freenetter). Daher gehe ich auch nur auf dieses Modul ein :)
285    
286     =head3 Installation
287    
288     Zuerst braucht man einen Freenet-Daemon
289     (L<http://www.freenetproject.org>). Die Installation ist unglaublich komplex
290     (unter Windows gibt es einen Installer, unter Unix ein paar Skripte). Der
291     Daemon braucht eine Weile, bis er warm wird (Minuten bis Stunden), was man
292     durch Surf-Versuche unterstützen sollte.
293    
294     Das C<Net::FCP>-Paket gibt es ganz normal per CPAN.
295    
296     =head3 Einfache Abfragen
297    
298     Die Abkürzung I<FCP> steht für I<Freenet Client Protocol> und ist das
299     Protokoll zwischen Freenet-Anwendungen und dem Freenet-Daemon. Es ist
300     unglaublich ineffizient und noch dazu nicht verschlüsselt. Dies ist der
301     Grund für die Empfehlung, den Daemon nur auf dem lokalen Rechner laufen
302     zu lassen: wäre dumm, wenn man aus dem hochsicheren Freenet über eine
303     ungesicherte Internetverbindung die Windows-Sourcen herunterlädt und
304     sich erwischen läßt (ist alles schon vorgekommen) ...
305    
306     In Perl geht dies denkbar einfach:
307    
308     use Net::FCP;
309     my $fcp = new Net::FCP;
310    
311     Man kann dem Konstruktor von C<Net::FCP> direkt Namen und Port des
312     Freenet-Daemons übergeben. Üblicherweise holt er sich diese aber aus den
313     Environment-Variablen C<FREDHOST> und C<FREDPORT>, die auf C<127.0.0.1>
314     bzw. C<8481> defaulten.
315    
316     Übrigens wird nur eine virtuelle Verbindung aufgebaut. Möchte man
317     sicherstellen, daß tatsächlich ein Daemon erreichbar ist, kann man einen
318     C<client_hello>-Request ausführen oder einfach loslegen.
319    
320     Im Normalfall heißt das also:: no configuration required.
321    
322     Hat man ein C<Net::FCP>-Objekt, kann man schon alle Requests durchführen,
323     die man machen möchte:
324    
325     my ($meta, $data) = @{ $fcp->client_get (
326     "freenet:SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//"
327     15
328     ) };
329    
330     =for latex
331     \begin{sloppypar}
332    
333     Der C<client_get>-Request erwartet zwei Argumente: eine Freenet-URI
334     (C<< freenet:<freenet-schlüssel> >>) und eine HTL. Letztere muss man
335     nicht angeben, man sollte es aber, und vor allem sollte man dem Benutzer
336     die Wahl der HTL überlassen. Das dritte, optionale, Argument ist für
337     spezielle Anwendungen: am besten ignorieren.
338    
339     =for latex
340     \end{sloppypar}
341    
342     Als Ergebnis erhält man immer eine Array-Referenz mit den Metadaten
343     und den Daten. Immer. Sollte ein Fehler auftreten wird eine Exception
344     ausgelöst (auf Perl: das Modul C<die>d). C<eval {}> hilft also im
345     Zweifelsfalle, für einfache Anwendungen ist das aber overkill.
346    
347     Bricht das Programm ab, so sieht das folgendermaßen aus:
348    
349     Net::FCP::Exception<<short_data,reason:unexpected eof or
350     internal node error>>
351    
352     Oder, wenn die Daten nicht gefunden wurden:
353    
354     Net::FCP::Exception<<data_not_found,>>
355    
356     Letzteres bedeutet übrigens nicht aufgeben, sondern nochmal versuchen,
357     z.B. mit einer höheren HTL. Ein fehlgeschlagener Versuch mit hoher HTL
358     bedeutet immer noch nicht aufgeben. Daemons in der ``Nähe'' wissen nun,
359     das die Daten verlangt werden. Nach einiger Zeit ist es wahrscheinlich,
360     das die Daten auf einmal in der Nähe liegen: daher nie aufgeben, nochmal
361     probieren. Niemand hat behauptet, man bekäme die Daten einfach aus dem
362     Freenet wieder heraus...
363    
364     Die Metadaten sind als Textdokument gespeichert. Das Format
365     dieser Metadaten ist aber relativ ``krank'', weshalb das Perl-Modul
366     einen Hash mit den geparsten Daten liefert (genaugenommen ein
367     C<Net::FCP::Metadata>-Objekt, aber dieses Modul ist noch in Entwicklung).
368    
369     Die Metadaten und die eigentlichen Nutzdaten kann man ausgeben:
370    
371     use Data::Dumper;
372     print STDERR Dumper $meta;
373     print $data;
374    
375     Damit haben wir im wesentlichen den Quelltext für das ``Tool''
376     C<eg/fetch1>, mit dem man I<einen> Freenet-Datenblock herunterladen kann.
377    
378     Ich benutze es üblicherweise so:
379    
380     eg/fetch1 SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3// >data
381    
382     Das schreibt die Daten in eine Datei und gibt gleichzeitig dessen
383     Metadaten aus.
384    
385     Für den Key C<SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//> ergibt dies:
386    
387     'version' => { 'revision' => '1' },
388     'document' => [
389     {
390     'info' => { 'format' => 'image/png' },
391     'redirect' => { 'target' => 'freenet:CHK@pMKW...KAwI,7...-25C2w' },
392     'name' => 'activelink.png'
393     },
394     {
395     'info' => { 'format' => 'text/plain' },
396     'redirect' => { 'target' => 'freenet:CHK@2hew...KAwI,o...21z91w' },
397     'name' => 'description.txt'
398     },
399     {
400     'info' => { 'format' => 'text/html' },
401     'redirect' => { 'target' => 'freenet:CHK@kME~...QAwI,3...dy4OkA' },
402     'name' => 'index.html'
403     },
404     {
405     'info' => { 'format' => 'text/html' },
406     'redirect' => { 'target' => 'freenet:SSK@0OhV...PAgM/fx/4' },
407     'name' => '.next'
408     },
409     {
410     'info' => { 'format' => 'text/html' },
411     'redirect' => { 'target' => 'freenet:CHK@kME~...QAwI,3...dy4OkA' }
412     }
413     ],
414     'raw' => 'Version
415     Revision=1
416     [... gekürzt...]
417     Info.Format=text/html
418     End
419     '
420     };
421    
422     Die eigentlichen Daten sind leer (null Byte gross), das Dokument enthät
423     also nur Metadaten! Man beachte vor allem die teilweise recht tiefe
424     Verschachtelung.
425    
426     Der Key C<< $metadata->{raw} >> enthält die Metadaten, wie sie vom
427     Freenet kamen. Dies ist notwendig, da man manchmal die Metadaten
428     zum Prüfen in der exakten Form benötigt, wie sie hochgeladen
429     wurden. Ansonsten kann man sie ignorieren.
430    
431     Der Key C<< $metadata->{document} >> enthält Informationen über das
432     Dokument oder in diesem Fall die Dokumente: Ein Hash pro Dokument. Schaut
433     man sich den Inhalt an (ein Array), so sieht man in jedem Hash ein
434     C<name>-Key, der den Namen des Subdokuments angibt (bis auf den letzten,
435     darauf komme ich gleich).
436    
437     Schaut man sich dir Freenet-URIs dieser Freesite an, so sieht man
438     derartige URIs:
439    
440     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3// # Hauptseite
441     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//activelink.png # Icon
442     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//description.txt # für Spider
443    
444     Wenn diese Dokumente mit C<client_get> anfordert, bekommt man immer obiges
445     Dokument. Freenet ignoriert nämlich alles, was hinter dem C<//> steht
446     und liefert daher immer das gleiche Dokument aus dem SSK-Bereich. (Darauf
447     verlassen soltle man sich nicht unbedingt, in Zukunft reagiert der
448     Freenet-Daemon vielleicht anders. Als Freenetter hat mans eben nicht
449     leicht).
450    
451     Der Teil hinter den beiden Slashes (C<//>) ist nun das Unterdokument, das
452     man über den Namen identifizieren kann:
453    
454     $activelink = grep $_->{name} eq "activelink.png",
455     @{ $metadata->{document} };
456    
457     Fehlt der C<name>-Eintrag, ist es der Eintrag mit dem leeren Namen, in
458     diesem Fall der erste Link, der nichts nach den C<//> stehen hat. Der
459     C<name>-Key I<kann> vorhanden und leer sein: Freenet überprüft die Daten
460     nicht, daher gibt es durchaus unterschiedliche Auslegungen für das genaue
461     Format.
462    
463     Die Metadaten sind etwas genauer in dem *hüstel* etwas veralteten
464     *hüstel* I<Freenet Explained>-Dokument beschrieben.
465    
466     =for latex
467     \begin{sloppypar}
468    
469     Möchte man nun das C<activelink.png> herunterladen muss man in C<<
470     $activelink->{redirect} >> nachsehen. Meistens verweist dies auf einen
471     C<CHK>-Schlüssel. Letztere sind sozusagen Allgemeingut: jeder kann
472     sie benutzen, und da sie nicht änderbar sind, kann sie auch niemand
473     fälschen, daher muss man sie nicht signieren.
474    
475     =for latex
476     \end{sloppypar}
477    
478     Auf C<< $activelink->{info}{format} >> sollte man sich übrigens nicht
479     verlassen.
480    
481     Und sollte kein C<redirect>-Key vorhanden sein, so ist das Dokument im
482     mitgelieferten Datenblock. Alles ganz logisch, einfach, und klar, wie man
483     sieht.
484    
485     Egal, der CHK-Key für C<activelink.png> liefert folgendes:
486    
487     'version' => { 'revision' => '1' },
488    
489     Und die Daten stellen ein PNG dar. Naja, nicht gerade üppig, die
490     Metadaten, aber man hätt's ja im Link auf dieses Dokument sehen
491     können. Als Freenetter hat mans nicht leicht.
492    
493     Aber mit diesen Erklärungen kann man schon einfache Freenet-Spider
494     bauen. Die wichtigsten Tools sind veraltete und spärliche Dokumente
495     wie I<Freenet Explained> und Tools wie C<Data::Dumper>. ``Veraltet'' ist
496     übrigens nicht immer schlecht, da viele Dokumente auch alt sind oder sich
497     eh' nicht exakt an den ``Standard'' halten. Mit der Zeit wird hier sicher
498     eine Besserung eintreten.
499    
500     =head3 Methoden des Freesite-Managements
501    
502     Oder: ``Wenn man Inhalte nicht mehr verändern kann, wie verändert man
503     sie?''
504    
505     Die offensichtliche Methode ist: ``Man macht sie veränderbar''. In
506     der Freenet-Gemeinde geistert seit langer Zeit der Mythos des
507     I<TUK>-Schlüssels, des I<Time Updatable Keys>. Diese Schlüssel tauchen
508     immer auf, wenn jemand über veränderbare Schlüssel spricht. Leider
509     weiss niemand, wie man sie implementieren soll, und ganz persönlich
510     fürchte ich, es wird niemals veränderbare Keys geben.
511    
512     =head4 ``Editions''
513    
514     Die nächstliegende Methode nutzt die Eigenschaft von SSKs (genauer:
515     redirects) aus, das man den Namen sofort generieren kann, den Inhalt aber
516     erst später einfügt.
517    
518     Dies nutzt man aus, indem man I<Editionen> herausgibt und
519     durchnummeriert. Jede Edition enthält einen Link auf die nächste. Bis
520     man die nächste Edition einfügt, wird der Link-Inhalt nicht gefunden.
521    
522     Üblicherweise benutzt man in HTML ein C<IMG>-Element mit Link auf ein
523     Bild aus der nächsten Edition. Sieht man das Bild, weiss man, die
524     nächste Edition existiert und kann sich hinklicken.
525    
526     I<Freenet Explained> benutzt diese Methode. Der Link auf Edition 4 lautet:
527    
528     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4// # Link-Ziel
529     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4//activelink.png # IMG-SRC
530    
531     Beides existierte nicht, als ich diesen Artikel schrieb (bzw. Freenet
532     konnte es nicht finden :). Wenn eine neue Edition herausgegeben werden
533     soll, fügt der Autor einfach einen neuen Datenblock mit vielen Redirects
534     unter obigem SSK-Key ein.
535    
536     Ein C<.next>-Eintrag ist ebenfalls vorhanden: manche Spider folgen diesem
537     und können auf diese Weise immer die aktuelle Edition anzeigen, sofern
538     sie häufig genug scannen.
539    
540     Editionen sind einfach für den Autor einer Freesite und erfordern
541     keine spezielle Unterstützung. In der Benutzung sind sie allerdings
542     umständlich und häufige Updates sind auch nicht effizient.
543    
544     Daher hat sich eine zweite Methode entwickelt, die gerade bei häufig
545     geänderten Inhalten besser funktioniert:
546    
547     =head4 ``Date Based Redirects''
548    
549     Freesites, die ``Date Based Redirects'' benutzen (kurz ``DBR-Sites'')
550     funktionieren ähnlich wie Editionen-basierte Freesites, nur wird statt
551     einer fortlaufenden Nummer die aktuelle Zeit benutzt.
552    
553     =for latex
554     \begin{sloppypar}
555    
556     Die Freesite I<The Freenet Help Index>
557     (C<SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/ FreenetHelp//>) benutzt diese
558     Methode. Folgende Metadaten wurden dazu hinterlegt:
559    
560     =for latex
561     \end{sloppypar}
562    
563     'version' => { 'revision' => '1' },
564     'document' => [
565     { 'date_redirect' => {
566     'increment' => '15180',
567     'target' => 'SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/FreenetHelp'
568     }
569     }
570     ],
571    
572     Statt einem einfachen C<redirect> gibt es jetzt einen
573     C<date_redirect>-Eintrag, und darin ein C<target> (dürfte bekannt sein)
574     und den Key C<increment>. Letzterer darf wie üblich fehlen, man sollte
575     dann C<86400> annehmen (ein Tag hat 86400 Sekunden). In diesem Beispiel
576     wird 86400 genommen (Hexadezimal 15180).
577    
578     Habe ich eigentlich schon erwähnt, daß jede Zahl im FCP-Protokoll oder
579     im Freenet hexadezimal kodiert wird? Also, praktisch immer, außer wenn
580     es mal nicht so ist. Und wozu man einen Offset addiert, ist mir auch
581     schleierhaft. In der freien Wildbahn habe ich sowieso noch keinen gesehen.
582    
583     Der Link in C<target> ist nicht vollständig (unter anderem fehlt
584     hinten ein C<//>). Der komplette Link wird generiert, ``indem die
585     aktuelle Zeit (POSIX-Zeit, üblicherweise das, was C<time> liefert),
586     auf C<increment>-Schritte gerundet und C<offset> addiert, als
587     Big-Endian-Hexadezimalzahl nach dem ersten Slash mit folgendem
588     Minuszeichen eingefügt wird.''
589    
590     Und jetzt in Perl - zum Verstehen:
591    
592     my $doc = $metadata->{document}[0]; # Oder [1] oder ...
593    
594     my $increment = (hex $doc->{increment}) || 86400;
595     my $offset = (hex $doc->{offset}) || 0;
596     my $target = $doc->{target} || die;
597    
598     my ($head, $tail) = split /\//, $target, 2;
599    
600     my $NOW = time;
601     my $time = $NOW - $NOW % $increment + $offset;
602    
603     my $result = sprintf "%s/%x-%s", $head, $time, $tail;
604    
605     Für ``jetzt'' (C<time() == 1084388445>) liefert der Algorithmus folgenden Link:
606    
607     SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/40a16900-FreenetHelp
608    
609     Und tatsächlich, unter diesem Key findet man wieder ein Dokument mit
610     vielen Redirects.
611    
612     DBRs haben den Vorteil, ``automatisch'' aktuell zu sein. Solange man nur
613     Redirects einfügt, ist die Belastung durch neue das Einfügen vieler
614     neuer Daten gering, Freenet kommt damit zurecht und löscht alte DBRs
615     bei Bedarf automatisch (ohne natürlich zu wissen, das sich in den
616     Datenblöcken DBRs befinden, denn die kann ja niemand dekodieren, der
617     nicht den Schlüssel besitzt).
618    
619     Der Nachteil ist, das man eine aktuelle Uhrzeit braucht um die Site zu
620     finden. Schlimmer noch: die Freesite muss regelmäßig neu eingefügt
621     werden, eben alle C<increment> Sekunden.
622    
623     DBR-Freesites, die nicht mehr maintained werden, verschwinden deshalb fast
624     sofort, solange man nicht einen Zeitpunkt kennt, zu dem sie noch (bzw.
625     schon!) existierte.
626    
627     All dieses I<sollte> eigentlich in einem Metadata-Modul stattfinden. So
628     weit bin ich aber nicht, da mein Hauptziel das effiziente Herunterladen
629     großer Dateien ist.
630    
631     Womit ich beim Thema wäre.
632    
633     =head2 Effizient herunterladen
634    
635     Transfers im Freenet zeichnen sich durch zwei Eigenschaften aus: hohe
636     Latenz und vergleichsweise hoher Durchsatz. Dies ist ungewöhnlich, ergibt
637     sich aber aus dem Suchverfahren: die Suche nach einem Datenblock kann sehr
638     lange dauern (sogar einige Male fehlschlagen bevor die Daten eintreffen),
639     sie sind jedoch einfach parallelisierbar.
640    
641     Wie in Java üblich, erreichte man dies früher durch massive Benutzung
642     von Threads. Der Freenet-Referenz-Daemon ist davon inzwischen abgekommen
643     (zu langsam, zu viel Speicher und zu hohe Komplexität), doch viele
644     Clients haben auch heute noch Voreinstellungen wie `Anzahl der Threads pro
645     Download'' und viele sind auch so implementiert.
646    
647     Da Perls aktuelle Thread-Implementierung ein Prozessmodell simuliert
648     ist der Overhead ebenfalls entsprechend hoch, und man erhält bei
649     Thread-Benutzung viele ihrer Nachteile, jedoch keine nennenswerten
650     Vorteil, außer das alles etwas langsamer läuft und man daher besser
651     zuschauen kann :)
652    
653     Für C<Net::FCP> (oder besser: für mich) kam es deshalb nicht in Frage,
654     Parallelisierung auf Thread-Basis zu implementieren. Das Problem auf
655     den Modul-Benutzer abzuschieben erschien mir auch nicht fair, daher
656     unterstützt C<Net::FCP> die Module L<Coro>, L<Event>, L<Glib> oder L<Tk>,
657     bzw. deren Event-Steuerung. Man hat also die Wahl, auf welcher Basis man sein
658     Programm aufbaut, und es geht auch ganz ohne Event-System (wie die obigen
659     einfachen Beispiele zeigen).
660    
661     =head3 Transaktionen
662    
663     C<Net::FCP> benutzt für jeden Request eine I<Transaktion>. Das ist ein
664     Objekt, das den Zustand und eintreffende Ergebnisse speichert.
665    
666     Jede Anfrage, (z.B. C<client_get>) wird intern in ein solches
667     Transaktionsobjekt verwandelt. Für jede ``einfache'' Methode wie
668     C<client_get> oder C<insert_private_key> gibt es eine entsprechende
669     Methode mit C<txn_>-Präfix, die statt des Resultats ein solches Objekt
670     liefert.
671    
672     Transaktionsobjekte besitzen einige Methoden, mit denen sie konfiguriert
673     werden können oder Ergebnisse abgefragt werden können. Die wichtigste
674     ist die C<result>-Methode, die auf das Ergebnis wartet und es
675     zurückliefert:
676    
677     # $fcp->client_get wird intern so implementiert:
678     my $txn = $fcp->txn_client_get (...);
679     return $txn->result;
680    
681     Es ist übrigens immer gefahrlos, Transaktionsobjekte zu erzeugen. Etwaige
682     Fehler bei der Durchführung werden immer erst beim Aufruf von C<result>
683     und immer als Perl-Exceptions gemeldet.
684    
685     Um also alle Freenet-Objekte im Array C<@download> gleichzeitig
686     anzufordern, reicht folgender Code:
687    
688     map $_->result,
689     map $_->txn_client_get ($_),
690     @download;
691    
692     Zuerst werden alle URIs auf eine entsprechende Transaktion gemapped, und
693     dann von allen Transaktionen das Resultat angefordert.
694    
695     Natürlich brauchen manche Zugriffe länger als andere. Wenn Transaktionen
696     früher beendet werden, sollte man gleich eine neue starten, um Freenet
697     am Laufen zu halten.
698    
699     Dazu benötigt man ein Signal, daß eine Transaktion beendet ist. Dies
700     macht C<Net::FCP> mit Hilfe eines Callbacks:
701    
702     my $txn = $fcp->txn_client_get (...)
703     ->cb (\&callback);
704    
705     Diesen setzt man mit Hilfe der C<cb>-Methode. Alle Methoden, die ein
706     Transaktionsobjekt konfigurieren, liefern das Transaktionsobjekt zurück,
707     man kann also Aufrufketten wie im Beispiel bilden.
708    
709     Der Callback wird aufgerufen, wenn der Request (erfolgreich oder nicht)
710     beendet wurde.
711    
712     Nun braucht man allerdings die Hauptschleife des jeweiligen Event-Systems,
713     da man ja nicht mehr auf ein bestimmtes Ergebis wartet, sondern auf ein
714     beliebiges.
715    
716     Dazu sollte man C<Net::FCP> auf ein bestimmtes Event-System zwingen. Das
717     folgende Beispiel tut dies und lädt die Kommandozeileargumente parallel
718     herunter:
719    
720     use Net::FCP qw(event=Event); # oder event=Glib, oder...
721    
722     my $fcp = new Net::FCP;
723     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
724    
725     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
726    
727     Die Transaktionsobjekte werden übrigens nicht gespeichert, denn sie
728     werden dem Callback übergeben, und nur dort werden sie gebraucht. Der
729     Callback könnte so aussehen:
730    
731     sub finished {
732     my ($txn) = @_;
733     my ($meta, $data) = @{ $txn->result };
734     # tue etwas
735     }
736    
737     Oder, mit Fehlerprüfung:
738    
739     sub finished {
740     my ($txn) = @_;
741     my ($meta, $data) = eval { @{ $txn->result } };
742     if ($@) {
743     warn "fehler: $@, wird einfach ignoriert :)"
744     } else {
745     warn "Lo and Behold! We got soemthing!";
746     # tue etwas
747     }
748     }
749    
750     =head3 Fortschritt ist nicht aufzuhalten
751    
752     Da Zugriffe recht lange dauern können, schickt Freenet regelmäig
753     Fortschrittsreports. Man kann sich das vorstellen wie C<warn> und
754     C<die>: C<warn> liefert wichtige Informationen, C<die> einen Fehler.
755    
756     Die Fortschrittsanzeigen werden normalerweise nicht von C<Net::FCP>
757     ausgegeben (wäre irgendwie kontraproduktiv in einem GUI-Programm), man
758     kann aber einen Callback installieren:
759    
760     my $fcp = Net::FCP progress => \&progress;
761    
762     Ein solcher Progress-Callback könnte so aussehen:
763    
764     sub progress_cb {
765     my ($self, $txn, $type, $attr) = @_;
766    
767     warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
768     }
769    
770     Möchte man Informationen für den Callback bereitstellen, so bietet sich
771     die C<userdata>-Methode von Transaktionsobjekten an.
772    
773     Hier ist ein kompletter parallelisierender Client mit Fortschrittsanzeige:
774    
775     my $fcp = Net::FCP progress => \&progress;
776    
777     Ein solcher Progress-Callback könnte so aussehen:
778    
779     use Net::FCP qw(event=Event); # oder event=Glib, oder...
780    
781     my $fcp = new Net::FCP progress => \&progress_cb;
782    
783     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
784    
785     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
786    
787     sub finished {
788     my ($txn) = @_;
789     my ($meta, $data) = eval { @{ $txn->result } };
790     if ($@) {
791     warn "$txn: fehler: $@, wird einfach ignoriert :)\n"
792     } else {
793     warn "$txn: Lo and Behold! We got something!\n";
794     # tue etwas
795     }
796     }
797    
798     sub progress_cb {
799     my ($self, $txn, $type, $attr) = @_;
800    
801     warn "$txn: progress<$txn,$type," . (join ":", %$attr) . ">\n";
802     }
803    
804     Startet man ihn mit einigen gültigen und ungültigen Freenet-URIs,
805     bekommt man vieleicht folgende Ausgabe:
806    
807     Net::FCP::Txn::ClientGet=HASH(0x81e0f64): ...
808     ... fehler: Net::FCP::Exception<<uri_error, ...
809     ... reason:Unspecified document name>>, wird einfach ignoriert :)
810     Net::FCP::Txn::ClientGet=HASH(0x81e7254): ...
811     ... fehler: Net::FCP::Exception<<uri_error,reason: ...
812     ... Unknown keytype>>, wird einfach ignoriert :)
813     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
814     ... progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c), ...
815     ... data_found,data_length:74:metadata_length:74>
816     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
817     ... progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c), ...
818     ... data,chunk:116:total:116:received:116>
819     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
820     ... Lo and Behold! We got something!
821     ... usw.
822    
823     Benutzt man das C<Coro>-Modul, so kann man Callback-basierte und Thread-
824     (bzw. Coroutinen-) basierte Programmierung mischen:
825    
826     use Net::FCP qw(event=Coro);
827    
828     my $fcp = new Net::FCP;
829    
830     # Achtung. Pseudocode :)
831     for my $url (@urls) {
832     async {
833     my ($meta, $data) = @{ $fcp->client_get ($url) };
834    
835     if ($meta ist ein splitfile) {
836     for (@alle_splitfile_blöcke) {
837     $fcp->txn_client_get (...)
838     ->cb (\&save_splitfile_block);
839     }
840     } else {
841     # direkt speichern
842     }
843     };
844     }
845    
846     =head3 Splitfiles
847    
848     Das Them Splitfiles möchte ich hier noch erwähnen, aber nicht
849     ausführlich besprechen.
850    
851     Da die Datenblöcke im Freenet (zur Zeit) nicht größer als 1MB sein
852     können, müssen größere Dateien (Filme z.B.) aufgeteilt werden. Wenn
853     man viele Datenblöcke für eine Datei braucht, erhöht sich die
854     Wahrscheinlichkeit dafür, daß ein Block mal fehlt oder nicht gefundne
855     werden kann, stark auf einen Wert, der das Herunterladen großer Dateien
856     unmöglich macht.
857    
858     Daher wird mit Fehlerkorrekturcodes die Anzahl der Blöcke um 50%
859     erhöht. Metadaten für ein Splitfile sehen so aus:
860    
861     'version' => { 'revision' => '1' },
862     'document' => [ {
863     'info' => {
864     'checksum' => '3bab309132f812cb2a281357b0dc1036920672e2',
865     'format' => 'video/mpeg',
866     'description' => 'Onion FEC v1.2 file inserted by Fuqid'
867     },
868     'split_file' => {
869     'algo_name' => 'OnionFEC_a_1_2',
870     'check_block_count' => '3f',
871     'block_count' => '7f',
872     'check_block' => {
873     '33' => 'freenet:CHK@eTNQ...3CBzTSsI~oPn6EUAwI,I4X...nyGoIRZBLg',
874     '32' => 'freenet:CHK@Ee1h...Kg3WZ90KXqwQZMUAwI,ppn...k1z3Sempqw',
875     '1e' => 'freenet:CHK@5gBB...fjO27kOtU0hZgkUAwI,Okf...wvtX~wfVNA',
876     [viele Zeilen fehlen hier :]
877     '3d' => 'freenet:CHK@iIT1...yOToFsyyytV-roUAwI,9u3...-eY7Onag1g',
878     '5' => 'freenet:CHK@EeVsn...CNBNVzvTTQ1l0UAwI,q6e...Oh-cc79WBA'
879     },
880     'block' => {
881     '33' => 'freenet:CHK@j522...h2ZK1D1q4UDnFgUAwI,Xwo...XFwI5f9iyw',
882     '32' => 'freenet:CHK@VU5v...y-Dnk8SCt9ZKbYUAwI,iX~...3Yk0rmpM9w',
883     '5d' => 'freenet:CHK@-wbX...uotK~uZbJp14zsUAwI,-bg...YvbwQeYtyA',
884     [viele Zeilen fehlen hier :]
885     '43' => 'freenet:CHK@zMXc...ee1cftvdK4~QukUAwI,o99...adXUXGjrlg',
886     '5' => 'freenet:CHK@1ZDiv...YTKQbDq1yCPWAUAwI,B9f...fOx~PTZkAA',
887     '3d' => 'freenet:CHK@ZGxa...qQIf6rbr-g8btMUAwI,f0x...mmxoJRzaLw'
888     },
889     'size' => '7e8e804'
890     }
891     } ],
892     }
893    
894     Die Datenblöcke und die zusätzlichen Korrekturblöcke sind getrennt
895     aufgeführt. Man braucht nur so viele Blöcke insgesamt, wie die Datei
896     groß ist. Den Rest kann man aus den heruntergeladenen erzeugen.
897    
898     Wie genau die Daten auf die Blöcker verteilt werden unterliegt einem
899     Algorithmus und ist nicht in den Metadaten gespeichert. Der Grund ist,
900     daß jedes Programm denselben Algorithmus benutzen muss, und Dateien daher
901     immer gleich auf die Blöcke verteilt werden, so daß Mehrfacheinfügen ins
902     Freenet keine Duplikate erstellt.
903    
904     Den genauen Algorithmus kann man in der Datei C<bin/fmd>, dem I<Freenet
905     Mass Downloader> in der Funktion C<state_splitfile> nachlesen. Der Code
906     ist weder besonders klar, noch besonders kurz, noch besonders hübsch. Auch
907     dieser Teil wird irgendwann einmal in ein Modul fließen, damit er anderen
908     zugänglicher wird.
909    
910     Interessant ist noch, daß man CHK-Inhalte, die aus dem Freenet
911     heruntergeladen werden, einfach auf Konsistenz prüfen kann: sie enthalten
912     schließlich den SHA1-Hash der Daten:
913    
914     use Net::FCP::Util;
915    
916     my ($meta, $data) = @{ $fcp->client_get ($uri) };
917    
918     # Extrahiere den Hash aus der URI
919     my $k1 = Net::FCP::Util::extract_chk_hash $uri;
920     # Berechne den Hash aus den Daten
921     my $k2 = Net::FCP::Util::generate_chk_hask "$meta->{raw}$data";
922    
923     # Idealerweie stimmen beide überein...
924     $k1 eq $k2 or die "Key/Content mismatch!";
925    
926     Zwar ist der Freenet-Daemon angeblich perfekt, aber früher passierte
927     es tatsächlich oft, daß man korrupte Daten geliefert bekam. Retryen
928     half. Man hat es eben nicht so leicht...
929    
930     Heute ist das zwar nicht mehr der Fall (...), aber prüfen kostet fast
931     nichts.
932    
933     =head2 Publizieren
934    
935     Das Freenet lebt von vielen Dingen: Den Entwicklern, den Benutzern die
936     ihr Interesse bekunden, den Regierungen, die die Meinungsäußerung
937     einschränken - aber vor allem dadurch, daß es sinnvolle Inhalte darin gibt.
938    
939     Ich bin mir nicht so sicher, I<ob> es so viele sinnvolle Inhalte gibt,
940     aber noch gab es ja auch keine 1.0-Release...
941    
942     Nun ja, solange man nichts ins Netz stellt, darf man auch nicht klagen.
943    
944     Das kann man ändern, indem man so langweilige Dinge wie den I<Freenet
945     Insertion Wizard> oder das beliebte I<Fuqid> benutzt (was ich noch nie
946     getan habe). Aber die benutzen kein Perl und sind damit langweilig.
947    
948     Mit C<Net::FCP> geht es erstmal sehr einfach:
949    
950     $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
951    
952     (Auch hier gibt es natürlich wieder die entsprechende C<txn_>-Variante).
953    
954     Einfügen von Inhalten geht also fast geanuso wie Auslesen. C<$uri> ist
955     eine Freenet-``URI'' (mit oder ohne C<freenet:>), wie man zu dieser kommt
956     gleich mehr.
957    
958     C<$metadata> kann entweder ein Freenet-Metadata-String sein oder ein Hash
959     der gleichen Form wie man ihn zurückgeliefert bekommt. C<$data> sind
960     natürlich die eigentlichen Daten und C<$htl> sind die Hops-To-Live, die
961     der Key-Insert haben soll. Höhere Werte (bis hin zu 25) speichern die
962     Daten dauerhafter, brauchen aber auch entsprechend länger.
963    
964     C<$removelocal>, auf C<1> gesetzt, sorgt dafür, daß der lokale
965     Netzknoten die Daten I<nicht> cached. Warum man das tun sollte, ist
966     einfach erklärt: wenn Freenet beim Einfügen die Daten schon im Netz
967     vorfindet, wird der Einfügevorgang abgebrochen. Möchte man die Daten
968     regelmäßig ``auffrischen'' dann sorgt diese Option dafür, daß die
969     Daten den Knoten auf jeden Fall verlassen und erhöhen so die Chance auf
970     Verbreitung.
971    
972     Nun die Frage wie man zu einer URI kommt. Möchte man einen CHK-Schlüssel
973     einfügen, so setzt man die URI einfach auf den String C<CHK@>. Nach dem
974     Einfügen erhält man die eigentliche URI als Resultat:
975    
976     my $attr = $fcp->client_put ('CHK@', "", "Daten\n", 20);
977     print "eingefügte URI: $attr->{uri}\n";
978    
979     Als Ergebnis dieses C<client_put>-Requests erhielt ich:
980    
981     freenet:CHK@MT~LuxKHH8fugJehkcgp239h7C4KAwI,378rJzHVZbsQbd7VGMHxSg
982    
983     Beim Einfügen ins Freenet kann es auch einen I<Key Collision>-Fehler
984     geben, und zwar genau dann, wenn Freenet die Daten schon findet, die man
985     einfügen wollte.
986    
987     C<Net::FCP> behandelt diesen Fehler allerdings genauso wie ein
988     erfolgreicher Einfügevorgang, lediglich im Hash, den es zurückliefert,
989     wird das C<key_collision>-Element auf C<1> gesetzt. Wenn die Daten schon
990     vorhanden sind, ist das Ziel schließlich auch erreicht.
991    
992     Häufig möchte man den C<CHK>-Schlüssel aber schon im voraus
993     Wissen. Dazu kann man entweder seinen freundlichen Freenet-Daemon fragen:
994    
995     my $key = $fcp->generate_chk ($metadata, $data);
996    
997     Oder man verwendet das C<Net::FCP::Key::CHK>-Modul, das schneller ist und
998     keinen Serverzugang benötigt:
999    
1000     use Net::FCP::Key;
1001    
1002     my $key = (new_from_data Net::FCP::Key::CHK $metadata, $data)->chk;
1003    
1004     =head3 SSK-Schlüssel generieren
1005    
1006     SSK-Schlüssel bestehen immer aus zwei Teilen, dem öffentlichen
1007     (C<public_key>) und dem privaten (C<private_key>), die zusammen ein Paar
1008     bilden. Neuerdings noch aus einem dritten, dem C<crypto_key>, der es
1009     Brute-Force-Attacken noch schwieriger machen soll.
1010    
1011     Solch ein Schlüsselpaar kann man mit einem Aufruf von
1012     C<generate_svk_pair> (auch hierfür gibt es eine C<txn_>-Variante)
1013     erzeugen. Das ``svk'' ist übrigens kein Schreibfehler: SSK-Schlüssel sind
1014     ein Spezialfall von SVK-Schlüsseln, die man aber als normalsterblicher
1015     nirgendwo direkt antrifft.
1016    
1017     my ($public, $private, $crypto) = @{ $fcp->generate_svk_pair };
1018    
1019     Daraus kann man beliebig viele SSK-Schlüssel erzeugen, indem man die drei
1020     Teile wie folgt zusammensetzt:
1021    
1022     # Öffentlicher Schlüssel:
1023    
1024     my $name = "..."; # beliebig
1025    
1026     my $get = "SSK\@${public}PAgM,$crypto/$name";
1027     my $put = "SSK\@$private,$crypto/$name";
1028    
1029     Den mit C<$put> bezeichneten Schlüssel muss man zum Einfügen
1030     verwenden. Mit dem mit C<$get> bezeichneten Schlüssel kann man diese
1031     Daten wieder holen. Letzterer kann bedenkenlos veröffentlich werden.
1032    
1033     Das SSK-Management kann vom C<Net::FCP::Key::SSK>-Modul übernommen
1034     werden.
1035    
1036     =head3 Eine Freesite erzeugen
1037    
1038     Um eine Freesite zu erzeugen, sollte man sich zuerst einen dauerhaften
1039     SSK-Schlüssel besorgen:
1040    
1041     # perl -MNet::FCP -MNet::FCP::Key::SSK \
1042     -e 'Net::FCP::Key::SSK->new_from_fcp (Net::FCP->new) \
1043     ->save ($ARGV[0])' mykey
1044    
1045     Das erzeugt einen neuen SSK-Schlüssel und speichert ihn gleich in der
1046     Datei C<mykey>.
1047    
1048     Dieser SSK-Schlüssel wird die Basis der Freesite.
1049    
1050     Für das Einfügen schreibt man sich am besten ein Skript, da man ides
1051     regelmäßig wiederholen sollte. Zuerst ein paar Definitionen:
1052    
1053     my $edition = 1;
1054     my $keyfile = "mykey";
1055     my $htl = 10;
1056    
1057     Das Skript legt eine editionenbasierte Freesite an und benutzt als Basis
1058     den eben erzeugten SSK-Schlüssel. Der Hops-To-Live-Wert von 10 ist etwas
1059     klein, aber so dauert das Einfügen vielleicht nur ein paar Minuten... In
1060     der Praxis sollte man 20-25 vorziehen, oder das Einfügen regelmäig
1061     wiederholen.
1062    
1063     Als nächstes benötigen wir ein paar Module:
1064    
1065     use Net::FCP qw(event=Event);
1066     use Net::FCP::Metadata;
1067     use Net::FCP::Key::SSK;
1068    
1069     Generell sollte man in einem Perl-Programm den verwendeten
1070     Event-Mechanismus fest angeben. In Freenet-Modulen sollte man dies dagegen
1071     nicht tun, um dem Benutzer die Wahl zu überlassen. Stattdessen sollte
1072     man in Modulen nur Transaktionen benutzen und diese dem Benutzer zur
1073     Verfügung stellen.
1074    
1075     Als nächstes werden ein paar notwendige Objekte erzeugt:
1076    
1077     my $fcp = new Net::FCP;
1078     my $ssk = new_from_file Net::FCP::Key::SSK $keyfile;
1079     my $manifest = new Net::FCP::Metadata;
1080    
1081     Die Metadaten für die Hauptseite (C<$manifest>) werden auch
1082     ``Manifest'' genannt, da man dort häufig nur Verweise auf die
1083     jeweiligen Unterseiten ablegt, also sowas wie der Grundstein einer
1084     Freesite. Das C<Net::FCP::Metadata>-Modul biette zur Zeit nur spärliche
1085     Funktionalität, für unsere Zwecke reicht das jedoch aus.
1086    
1087     Nun noch zwei Hilfsfuntionen. Zuerst C<add_key>:
1088    
1089     sub add_key {
1090     my ($name, $key, $meta, $data) = @_;
1091    
1092     # Asynchrones Einfügen
1093     $fcp->txn_client_put ($chk, $meta, $data, $htl, 1)
1094     ->cb (sub {
1095     eval {
1096     my $attr = $_[0]->result;
1097     printf "Insert of '%s' (%d bytes) ok.\n", $name, length $data;
1098     } or warn "Error while inserting '$name': $@";
1099     });
1100     }
1101    
1102     C<add_key> startet eine C<client_put>-Transaktion, ohne auf das Ende zu
1103     warten. Im Callback wird das Resultat angezeigt, oder, wenn ein Fehler
1104     Auftrat, eben dieser. Das Skript sollte so angelegt sein, das man es
1105     einfach nochmal starten kann, wenn einige Schlüssel nicht eingefügt
1106     werden konnten.
1107    
1108     Nun noch C<add_file>:
1109    
1110     # Hilfsfunktion zum Einfügen von Dateien
1111     sub add_file {
1112     my ($name, $path, $contenttype) = @_;
1113    
1114     # lies die Datei ein
1115     my $data = do {
1116     local $/;
1117     open my $fh, "<", $path
1118     or die "$path: $!";
1119     <$fh>
1120     };
1121    
1122     my $chk = $fcp->generate_chk ("", $data); # keine metadaten
1123     $manifest->add_redirect ($name => $chk, format => $contenttype);
1124     add_key $name, $chk, "", $data;
1125     }
1126    
1127     C<add_file> lädt eine externe Datei (< 1MB) und berechnet deren
1128     CHK-Schlüssel. Diesen Schlüssel fügt es als Verweis unter dem Namen
1129     C<$name> in das Manifest ein. Als letztes benutzt es C<add_key>, um die
1130     Datei einzufügen.
1131    
1132     Das Vorausberechnen des CHK-Schlüssels macht es möglich, das Manifest
1133     paralell zu den anderen Schlüsseln einzufügen. Würde man auf den
1134     Einfügevorgang warten, müsste man das Manifest am Ende getrennt
1135     hochladen.
1136    
1137     Nun ans Zusammenbauen. Zuerst werden (unsichtbare) Verweise auf die
1138     vorherige bzw. die nächste Edition eingefügt:
1139    
1140     $manifest->add_redirect (".prev" => $ssk->gen_pub ($edition - 1))
1141     if $edition > 1;
1142     $manifest->add_redirect (".next" => $ssk->gen_pub ($edition + 1));
1143    
1144     Dies nutzt zum einen die Eigenschaft von SSKs, einfach vorausberechnet
1145     werden zu können, als auch die Methode C<gen_pub>, die den öffentlichen
1146     SSK-Schlüssel mit optionalem Suffix generiert.
1147    
1148     Dadurch können Benutzer auch ``per Hand'' eine neuere oder ältere Edition
1149     suchen, indem sie einfach die Zahl (C<.../2//index.html>) vor dem C<//>
1150     ändern.
1151    
1152     Als nächstes folgt der eigentliche Inhalt der Freesite, eine HTML-Seite,
1153     ein Bild und der Quellcode des Skriptes, der sie generiert hat:
1154    
1155     add_file "activelink.png" => "logo.png", "image/png";
1156     add_file "" => "index.html", "text/html";
1157     add_file "index.html" => "index.html", "text/html";
1158     add_file "freesite" => "freesite", "text/plain";
1159    
1160     Die Datei C<index.html> wird zweimal hinzugefügt, einmal unter dem Namen
1161     ``'' und ein zweitesmal unter dem Namen ``C<index.html>''. Dadurch kann man
1162     erreichen, was viele HTTP-Server ebenfalls machen, nämlich eine URI, die
1163     auf ein Verzeichnis zeigt, auf eine Datei darin zu verweisen. Das die
1164     Datei zweimal eingefügt wird ist kein Problem, da der CHK-Schlüssel
1165     derselbe ist (und Mehrfacheinfügen kein Fehler).
1166    
1167     Nachdem die Dateien hinzugefügt (und vor allem ins Manifest eingetragen)
1168     wurden, kann man das Manifest einfügen:
1169    
1170     my $site = $ssk->gen_pub ($edition);
1171     add_key "manifest", $site, $manifest, "";
1172    
1173     Das Manifest wird. Damit man hinterher auch weiss, was man da eingefügt
1174     hat, sollte man den Hauptschlüssel auch ausgeben:
1175    
1176     print "site generated as $site//\n";
1177    
1178     Und da die ganzen Einfügevorgange ja nur gestartet wurden, muss man auf
1179     deren Ende warten:
1180    
1181     Event::loop;
1182    
1183     Solange C<Event> noch aktive Watcher sieht, bleibt es in der
1184     Event-Schleife. Wenn es keinen aktiven Watcher mehr gibt, kehrt C<loop>
1185     automatisch zurück und das Programm ist beendet.
1186    
1187     Die Ausgabe sieht dann z.B. so aus:
1188    
1189     site generated as SSK@wg...TBRylfnDlUBfjETgPAgM,Jr...UBDPfw1UO1g/1//
1190     Insert of 'freesite' (1688 bytes) ok.
1191     Insert of 'activelink.png' (1346 bytes) ok.
1192     Insert of 'index.html' (200 bytes) ok.
1193     Insert of '' (200 bytes) ok.
1194     Insert of 'manifest' (0 bytes) ok.
1195    
1196     Wenn man einen Fehler gemacht hat, kann man einfach einen neuen
1197     SSK-Schlüssel generieren. Das heißt, solange man seine Site nicht schon
1198     veröffentlicht hat.
1199    
1200     Die Alternative wäre z.B. ein täglicher Date-Based-Redirect, d.h.
1201     tägliches komplettes Neueinfügen. Da bügeln sich Fehler schnell von
1202     selbst aus, man muss sein Skript dann natürlich über Tage oder Monate
1203     regelmäßig starten.
1204    
1205     Eine offensichtliche Verbesserung wäre es, HTML-Dateien abzuändern, so
1206     daß man Links auf die nächste Revision nicht hardcodieren muss sondern
1207     etwa als C<%%NEXT%%//activelink.png> schreiben könnte.
1208    
1209     Nun ja, ich hoffe, es machen sich nun einige Leser auf, um eigene
1210     Freesites zu erstellen oder andere Anwendungen für das Freenet zu
1211     erstellen.
1212    
1213     =head2 Verweise
1214    
1215     =over 4
1216    
1217     =item L<http://www.freenetproject.org/>
1218    
1219     Die Hauptseite des Freenet-Projekts. Hier gibt es auch White-Papers, etwas
1220     Dokumentation und Mailinglisten(-archive).
1221    
1222     =item L<http://www.freenethelp.org/>
1223    
1224     Hier findet der gestresste Freenetter Hilfe beim Einrichten und Betrieb
1225     seines Knotens. Vielleicht.
1226    
1227     =back
1228    
1229    
1230    
1231