Zend_File_Transfer-Introduction.xml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 14978 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.file.transfer.introduction">
  5. <title>Zend_File_Transfer</title>
  6. <para>
  7. <classname>Zend_File_Transfer</classname> bietet exzessiven Support für Datei Uploads und Downloads. Es kommt
  8. mit eingebauten Prüfungen für Dateien und Funktionslitäten um Dateien mit Filtern zu verändern.
  9. Protokoll-Adapter erlauben <classname>Zend_File_Transfer</classname> die selbe API für Transportprotokolle wie
  10. HTTP, FTP, WEBDAV und andere zu verwenden.
  11. </para>
  12. <note>
  13. <title>Einschränkungen</title>
  14. <para>
  15. Die aktuelle Implementation von <classname>Zend_File_Transfer</classname>
  16. ist auf HTTP Post Uploads limitiert. Andere Adapter die Downloads und andere Protokolle
  17. unterstützen werden in zukünftigen Releases hinzugefügt. Aktuell sollte
  18. <classname>Zend_File_Transfer_Adapter_Http</classname> direkt verwendet werden. Sobald andere Adapter
  19. vorhanden sind, kann ein gemeinsames Interface verwendet werden.
  20. </para>
  21. </note>
  22. <note>
  23. <title>Formulare</title>
  24. <para>
  25. Wenn man <classname>Zend_Form</classname> verwendet sollte man die APIs die von <classname>Zend_Form</classname> zur
  26. Verfügung gestellt werden, und <classname>Zend_File_Transfer</classname> nicht direkt, verwenden. Der
  27. Dateitransfer Support von <classname>Zend_Form</classname> ist in <classname>Zend_File_Transfer</classname>
  28. implementiert, weshalb die Informationen in diesem Kapitel für fortgeschrittene Benutzer von
  29. <classname>Zend_Form</classname> interessant sind.
  30. </para>
  31. </note>
  32. <para>
  33. Die Verwendung von <classname>Zend_File_Transfer</classname> ist relativ einfach. Es besteht aus zwei Teilen.
  34. Dem HTTP Formular, wärend <classname>Zend_File_Transfer</classname> die hochgeladenen Dateien behandelt.
  35. Siehe das folgende Beispiel:
  36. </para>
  37. <example id="zend.file.transfer.introduction.example">
  38. <title>Einfaches Formular für File-Uploads</title>
  39. <para>
  40. Dieses Beispiel zeigt einen einfachen Dateiupload. Das erste Teil ist das Dateiformular.
  41. In unserem Beispiel gibt es nur eine Datei welche wir hochladen wollen.
  42. </para>
  43. <programlisting><![CDATA[
  44. <form enctype="multipart/form-data" action="/file/upload" method="POST">
  45. <input type="hidden" name="MAX_FILE_SIZE" value="100000" />
  46. Choose a file to upload: <input name="uploadedfile" type="file" />
  47. <br />
  48. <input type="submit" value="Upload File" />
  49. </form>
  50. ]]>
  51. </programlisting>
  52. <para>
  53. Der Bequemlichkeit halber kann
  54. <link linkend="zend.form.standardElements.file">Zend_Form_Element_File</link> verwendet werden
  55. statt das HTML manuell zu erstellen.
  56. </para>
  57. <para>
  58. Der nächste Schritt ist die Erstellung des Empfängers des Uploads. In unserem Beispiel ist der
  59. Empfänger bei <code>/file/upload</code> zu finden. Als nächstes erstellen wir also den
  60. <code>file</code> Controller mit der <code>upload</code> Aktion.
  61. </para>
  62. <programlisting role="php"><![CDATA[
  63. $adapter = new Zend_File_Transfer_Adapter_Http();
  64. $adapter->setDestination('C:\temp');
  65. if (!$adapter->receive()) {
  66. $messages = $adapter->getMessages();
  67. echo implode("\n", $messages);
  68. }
  69. ]]>
  70. </programlisting>
  71. <para>
  72. Dieses Codebeispiel demonstriert die einfachste Verwendung von <classname>Zend_File_Transfer</classname>.
  73. Ein lokales Ziel wird mit der <code>setDestination</code> Methode definiert, und anschließend die
  74. <code>receive()</code> Methode aufgerufen. Wenn irgendwelche Uploadfehler auftreten werden diese
  75. als Ausnahme zurückgegeben.
  76. </para>
  77. </example>
  78. <note>
  79. <title>Achtung</title>
  80. <para>
  81. Dieses Beispiel ist nur für die Demonstration der grundsätzlichen API von
  82. <classname>Zend_File_Transfer</classname>. Man sollte dieses Code Beispiel
  83. <emphasis role="strong">niemals</emphasis> in einer Produktivumgebung einsetzen da es
  84. massive Sicherheitslücken aufweisst. Man sollte immer Prüfungen verwenden um die Sicherheit
  85. zu erhöhen.
  86. </para>
  87. </note>
  88. <sect2 id="zend.file.transfer.introduction.adapters">
  89. <title>Von Zend_File_Transfer unterstützte Adapter</title>
  90. <para>
  91. <classname>Zend_File_Transfer</classname> wurde designt um verschiedenste Adapter und auch Richtungen zu
  92. unterstützen. Mit <classname>Zend_File_Transfer</classname> kann man Dateien Hochladen, Herunterladen und
  93. sogar Weiterleiten (Hochladen mit einem Adapter und Herunterladen mit einem anderen Adapter zur
  94. gleichen Zeit).
  95. </para>
  96. </sect2>
  97. <sect2 id="zend.file.transfer.introduction.options">
  98. <title>Optionen für Zend_File_Transfer</title>
  99. <para>
  100. <classname>Zend_File_Transfer</classname> und seine Adapter unterstützen verschiedene Optionen. Alle Optionen
  101. können gesetzt werden indem Sie entweder dem Constructor übergeben werden, oder durch Aufruf der
  102. <code>setOptions($options)</code>. <code>getOptions()</code> gibt die Optionen zurück die aktuell
  103. gesetzt sind. Nachfolgend ist eine Liste aller unterstützten Optionen:
  104. </para>
  105. <itemizedlist>
  106. <listitem>
  107. <para>
  108. <emphasis role="strong">ignoreNoFile</emphasis>: Wenn diese Option auf true gesetzt ist,
  109. ignorieren alle Prüfer Dateien die nicht vom Formular hochgeladen wurde. Der
  110. Standardwert ist false, was einen Fehler verursacht wenn die Datei nicht spezifiziert wurde.
  111. </para>
  112. </listitem>
  113. </itemizedlist>
  114. </sect2>
  115. <sect2 id="zend.file.transfer.introduction.checking">
  116. <title>Dateien prüfen</title>
  117. <para>
  118. <classname>Zend_File_Transfer</classname> hat verschiedene Methoden die auf verschiedenste Stati von
  119. spezifizierten Dateien prüfen. Diese sind nützlich wenn man Dateien bearbeiten will nachdem
  120. Sie empfangen wurden. Diese Methoden beinhalten:
  121. </para>
  122. <itemizedlist>
  123. <listitem>
  124. <para>
  125. <emphasis role="strong">isValid($files = null)</emphasis>: Diese Methode prüft ob die
  126. angegebene Datei gültig ist, basierend auf den Prüfungen welche dieser Datei
  127. angehängt sind. Wenn keine Dateien spezifiziert wurden, werden alle Dateien geprüft.
  128. Man kann <code>isValid()</code> aufrufen bevor <code>receive()</code> aufgerufen wird;
  129. in diesem Fall ruft <code>receive()</code> intern <code>isValid()</code> nicht mehr auf.
  130. </para>
  131. </listitem>
  132. <listitem>
  133. <para>
  134. <emphasis role="strong">isUploaded($files = null)</emphasis>: Diese Methode prüft ob die
  135. spezifizierte Datei vom Benutzer hochgeladen wurde. Das ist nützlich wenn man eine oder
  136. mehrere Dateien definiert hat. Wenn keine Dateien spezifiziert wurden, werden alle
  137. Dateien geprüft.
  138. </para>
  139. </listitem>
  140. <listitem>
  141. <para>
  142. <emphasis role="strong">isReceived($files = null)</emphasis>: Diese Methode prüft ob die
  143. spezifizierte Datei bereits empfangen wurde. Wenn keine Dateien angegeben wurden, werden
  144. alle Dateien geprüft.
  145. </para>
  146. </listitem>
  147. </itemizedlist>
  148. <example id="zend.file.transfer.introduction.checking.example">
  149. <title>Dateien prüfen</title>
  150. <programlisting role="php"><![CDATA[
  151. $upload = new Zend_File_Transfer();
  152. // Gibt alle bekannten internen Datei Informationen zurück
  153. $files = $upload->getFileInfo();
  154. foreach ($files as $file => $info) {
  155. // Datei hochgeladen ?
  156. if (!$upload->isUploaded($file)) {
  157. print "Warum hast Du die Datei nicht hochgeladen ?";
  158. continue;
  159. }
  160. // Prüfungen sind ok ?
  161. if (!$upload->isValid($file)) {
  162. print "Sorry, aber die Datei ist nicht das was wir wollten";
  163. continue;
  164. }
  165. }
  166. $upload->receive();
  167. ]]>
  168. </programlisting>
  169. </example>
  170. </sect2>
  171. <sect2 id="zend.file.transfer.introduction.informations">
  172. <title>Zusätzliche Dateiinformationen</title>
  173. <para>
  174. <classname>Zend_File_Transfer</classname> kann zusätzliche Informationen über Dateien zurückgeben.
  175. Die folgenden Methoden sind vorhanden:
  176. </para>
  177. <itemizedlist>
  178. <listitem>
  179. <para>
  180. <emphasis role="strong">getFileName($file = null, $path = true)</emphasis>: Diese Methode
  181. gibt den wirklichen Namen der übertragenen Datei zurück.
  182. </para>
  183. </listitem>
  184. <listitem>
  185. <para>
  186. <emphasis role="strong">getFileInfo($file = null)</emphasis>: Diese Methode gibt die
  187. internen Informationen für die angegebene übertragene Datei zurück.
  188. </para>
  189. </listitem>
  190. <listitem>
  191. <para>
  192. <emphasis role="strong">getFileSize($file = null)</emphasis>: Diese Methode gibt die
  193. echte Dateigröße für die angegebene Datei zurück.
  194. </para>
  195. </listitem>
  196. <listitem>
  197. <para>
  198. <emphasis role="strong">getHash($hash = 'crc32', $files = null)</emphasis>: Diese Methode
  199. gibt einen Hash des Inhalts einer angegebenen übertragenen Datei zurück.
  200. </para>
  201. </listitem>
  202. <listitem>
  203. <para>
  204. <emphasis role="strong">getMimeType($files = null)</emphasis>: Diese Methode gibt den
  205. Mimetyp der angegebenen übertragenen Datei zurück.
  206. </para>
  207. </listitem>
  208. </itemizedlist>
  209. <para>
  210. <code>getFileName()</code> akzeptiert den Namen des Elements als ersten Parameter. Wenn kein Name
  211. angegeben wird, werden alle bekannten Dateinamen in einem Array zurückgegeben. Wenn die Datei
  212. eine MultiDatei ist, wird auch ein Array zurückgegeben. Wenn nur eine einzelne Datei vorhanden ist
  213. wird nur ein String zurückgegeben.
  214. </para>
  215. <para>
  216. Standardmäßig werden Dateinamen mit dem kompletten Pfad zurückgegeben. Wenn man nur den Dateinamen
  217. ohne Pfad benötigt, kann der zweite Parameter <code>$path</code> gesetzt werden, welcher den
  218. Dateinamen ausschneidet wenn er auf false gesetzt wird.
  219. </para>
  220. <example id="zend.file.transfer.introduction.informations.example1">
  221. <title>Den Dateinamen bekommen</title>
  222. <programlisting role="php"><![CDATA[
  223. $upload = new Zend_File_Transfer();
  224. $upload->receive();
  225. // Gibt die Dateinamen aller Dateien zurück
  226. $names = $upload->getFileName();
  227. // Gibt den Dateinamen des Formularelements 'foo' zurück
  228. $names = $upload->getFileName('foo');
  229. ]]>
  230. </programlisting>
  231. </example>
  232. <note>
  233. <para>
  234. Es ist zu beachten das sich der Dateinamen ändern kann nachdem die Datei empfangen wurde
  235. (receive) weil alle Filter angewendet werden, sobald die Datei empfangen wurde.
  236. Deswegen sollte man <code>getFileName()</code> immer ausführen nachdem die Dateien
  237. empfangen wurden.
  238. </para>
  239. </note>
  240. <para>
  241. <code>getFileSize()</code> gibt standardmäßig die echte Dateigröße in SI Schreibweise zurück
  242. was bedeutet das man <code>2kB</code> statt <code>2048</code> erhält. Wenn man die reine
  243. Größe benötigt muß man die <code>useByteString</code> Option auf false setzen.
  244. </para>
  245. <example id="zend.file.transfer.introduction.informations.example.getfilesize">
  246. <title>Die Größe einer Datei erhalten</title>
  247. <programlisting role="php"><![CDATA[
  248. $upload = new Zend_File_Transfer();
  249. $upload->receive();
  250. // Gibt die Größen aller Dateien als Array zurück
  251. // wenn mehr als eine Datei hochgeladen wurde
  252. $size = $upload->getFileSize();
  253. // Wechsle die SI Schreibweise damit reine Nummern zurückgegeben werden
  254. $upload->setOption(array('useByteString' => false));
  255. $size = $upload->getFileSize();
  256. ]]>
  257. </programlisting>
  258. </example>
  259. <para>
  260. <code>getHash()</code> akzeptiert den Namen eines Hash Algorithmus als ersten Parameter. Für eine
  261. Liste bekannter Algorithmen kann in
  262. <ulink url="http://php.net/hash_algos">PHP's hash_algos Methode</ulink> gesehen werden. Wenn kein
  263. Algorithmus spezifiziert wird, wird <code>crc32</code> als Standardalgorithmus verwendet.
  264. </para>
  265. <example id="zend.file.transfer.introduction.informations.example2">
  266. <title>Den Hash einer Datei erhalten</title>
  267. <programlisting role="php"><![CDATA[
  268. $upload = new Zend_File_Transfer();
  269. $upload->receive();
  270. // Gibt die Hashes von allen Dateien als Array zurück
  271. // wenn mehr als eine Datei hochgeladen wurde
  272. $hash = $upload->getHash('md5');
  273. // Gibt den Has für das 'foo' Formularelement zurück
  274. $names = $upload->getHash('crc32', 'foo');
  275. ]]>
  276. </programlisting>
  277. </example>
  278. <note>
  279. <para>
  280. Es ist zu beachten das der zurückgegebene Wert ein Array ist, wenn die Datei oder der
  281. Formularname mehr als eine Datei enthält.
  282. </para>
  283. </note>
  284. <para>
  285. <code>getMimeType()</code> gibt den Mimetyp einer Datei zurück. Wenn mehr als eine Datei hochgeladen
  286. wurde wird ein Array zurückgegeben, andernfalls ein String.
  287. </para>
  288. <example id="zend.file.transfer.introduction.informations.getmimetype">
  289. <title>Den Mimetyp einer Datei bekommen</title>
  290. <programlisting role="php"><![CDATA[
  291. $upload = new Zend_File_Transfer();
  292. $upload->receive();
  293. $mime = $upload->getMimeType();
  294. // Gibt den Mimetyp des 'foo' Form Elements zurück
  295. $names = $upload->getMimeType('foo');
  296. ]]>
  297. </programlisting>
  298. </example>
  299. <note>
  300. <para>
  301. Beachte das diese Methode die fileinfo Erweiterung verwendet wenn Sie vorhanden ist. Wenn diese
  302. Erweiterung nicht gefunden werden kann wird die mimemagic Erweiterung verwendet. Wenn keine
  303. Erweiterung gefunden wird, dann wird der Mimetyp verwendet der vom Dateiserver wärend des
  304. Hochladens der Datei angegeben wurde.
  305. </para>
  306. </note>
  307. </sect2>
  308. <sect2 id="zend.file.transfer.introduction.uploadprogress">
  309. <title>Fortschritt für Datei Uploads</title>
  310. <para>
  311. <classname>Zend_File_Transfer</classname> kann den aktuellen Status eines gerade stattfindenden Datei Uploads
  312. erheben. Um dieses Feature zu verwenden muß man entweder die <code>APC</code> Erweiterung
  313. verwenden, die mit den meisten standardmäßigen PHP Installationen vorhanden ist, oder die
  314. <code>uploadprogress</code> Erweiterung. Beide Erweiterungen werden erkannt und automatisch
  315. verwendet. Um den Fortschritt zu erhalten muß man einige Voraussetzungen erfüllen.
  316. </para>
  317. <para>
  318. Erstens, muß man entweder <code>APC</code> oder <code>uploadprogress</code> aktiviert haben.
  319. Es ist zu beachten das dieses Feature von <code>APC</code> in der eigenen php.ini ausgeschaltet
  320. werden kann.
  321. </para>
  322. <para>
  323. Zweitens, muß man die richtigen unsichtbaren Felder im Formular hinzugefügt haben das die Dateien
  324. sendet. Wenn man <classname>Zend_Form_Element_File</classname> verwendet werden diese unsichtbaren Felder
  325. automatisch von <classname>Zend_Form</classname> hinzugefügt.
  326. </para>
  327. <para>
  328. Wenn die oberen zwei Punkte vorhanden sind dann ist man in der Lage den aktuellen Fortschritt des
  329. Datei uploads zu erhalten indem man die <code>getProgress</code> Methode verwendet. Aktuell gibt
  330. es 2 offizielle Wege um das hand zu haben.
  331. </para>
  332. <sect3 id="zend.file.transfer.introduction.uploadprogress.progressadapter">
  333. <title>Verwenden eines Progressbar Adapters</title>
  334. <para>
  335. Man kann einen bequemen <emphasis>Zend_ProgressBar</emphasis> verwenden um den aktuellen
  336. Fortschritt zu erhalten und kann Ihn dann auf einfachem Wege dem Benutzer zeigen.
  337. </para>
  338. <para>
  339. Um das zu ermöglichen, muß man den gewünschten <emphasis>Zend_ProgressBar_Adapter</emphasis>
  340. bei <code>getProgress()</code> hinzufügen wenn es das erste Mal aufgerufen wird. Für Details
  341. über den zu verwendenden Adapter, bitte im Kapitel
  342. <link linkend="zend.progressbar.adapters">Zend_ProgressBar Standard Adapters</link> nachsehen.
  343. </para>
  344. <example id="zend.file.transfer.introduction.uploadprogress.progressadapter.example1">
  345. <title>Verwenden eines Progressbar Adapters um den aktuellen Status zu erhalten</title>
  346. <programlisting role="php"><![CDATA[
  347. $adapter = new Zend_ProgressBar_Adapter_Console();
  348. $upload = Zend_File_Transfer_Adapter_Http::getProgress($adapter);
  349. $upload = null;
  350. while (!$upload['done']) {
  351. $upload = Zend_File_Transfer_Adapter_Http:getProgress($upload);
  352. }
  353. ]]>
  354. </programlisting>
  355. </example>
  356. <para>
  357. Die komplette Handhabung wird von <code>getProgress()</code> im Hintergrund durchgeführt.
  358. </para>
  359. </sect3>
  360. <sect3 id="zend.file.transfer.introduction.uploadprogress.manually">
  361. <title>getProgress() händisch verwenden</title>
  362. <para>
  363. Man kann mit <code>getProgress()</code> auch händisch arbeiten, also ohne ie Verwendung von
  364. <classname>Zend_ProgressBar</classname>.
  365. </para>
  366. <para>
  367. <code>getProgress()</code> muß ohne Einstellungen aufgerufen werden. Es gibt anschließend ein
  368. Array mit verschiedenen Schlüssel zurück. Sie unterscheiden sich, abhängig von der verwendeten
  369. PHP Extension. Aber die folgenden Schlüssel werden unabhängig von der Extension zurück gegeben:
  370. </para>
  371. <itemizedlist>
  372. <listitem>
  373. <para>
  374. <emphasis role="strong">id</emphasis>: Die ID dieses Uploads. Die ID identifiziert den
  375. Upload in der Extension. Sie wird automatisch geschrieben. Man sollte Sie nie ändern
  376. oder den Wert selbst setzen.
  377. </para>
  378. </listitem>
  379. <listitem>
  380. <para>
  381. <emphasis role="strong">total</emphasis>: Die komplette Größe der Datei die hochgeladen
  382. wird in Bytes als Integer.
  383. </para>
  384. </listitem>
  385. <listitem>
  386. <para>
  387. <emphasis role="strong">current</emphasis>: Die aktuelle hochgeladene Größe der
  388. Datei in Bytes als Integer.
  389. </para>
  390. </listitem>
  391. <listitem>
  392. <para>
  393. <emphasis role="strong">rate</emphasis>: Die durchschnittliche Geschwindigkeit des
  394. Uploads in Bytes pro Sekunde als Integer.
  395. </para>
  396. </listitem>
  397. <listitem>
  398. <para>
  399. <emphasis role="strong">done</emphasis>: Gibt true zurück wenn der Upload abgeschlossen
  400. wurde, andernfalls false.
  401. </para>
  402. </listitem>
  403. <listitem>
  404. <para>
  405. <emphasis role="strong">message</emphasis>: Die aktuelle Meldung. Entweder der
  406. Fortschritt als Text in der Form <emphasis role="strong">10kB / 200kB</emphasis>, oder
  407. eine hilfreiche Nachricht im Fall eines Problems. Probleme könnten sein, das kein
  408. Upload durchgeführt wird, das ein Fehler wärend des Empfangens der Daten, für den
  409. Fortschritt, aufgetreten ist, oder das der Upload abgebrochen wurde.
  410. </para>
  411. </listitem>
  412. <listitem>
  413. <para>
  414. <emphasis role="strong">progress</emphasis>: Dieser optionale Schlüssel nimmt eine
  415. Instanz von Zend_ProgressBar_Adapter oder Zend_ProgressBar, und erlaubt es, den aktuellen
  416. Status des Uploads, in einer Progressbar zu erhalten
  417. </para>
  418. </listitem>
  419. <listitem>
  420. <para>
  421. <emphasis role="strong">session</emphasis>: Dieser optionale Schlüssel nimmt den Namen
  422. eines Session Namespaces entgegen der in Zend_ProgressBar verwendet wird. Wenn dieser
  423. Schlüssel nicht angegeben wird, ist er standardmäßig
  424. <classname>Zend_File_Transfer_Adapter_Http_ProgressBar</classname>.
  425. </para>
  426. </listitem>
  427. </itemizedlist>
  428. <para>
  429. Alle anderen zurückgegebenen Schlüssel werden direkt von den Extensions übernommen und werden
  430. nicht geprüft.
  431. </para>
  432. <para>
  433. Das folgende Beispiel zeigt eine mögliche händische Verwendung:
  434. </para>
  435. <example id="zend.file.transfer.introduction.uploadprogress.manually.example1">
  436. <title>Händische Verwendung des Datei Fortschritts</title>
  437. <programlisting role="php"><![CDATA[
  438. $upload = Zend_File_Transfer_Adapter_Http::getProgress();
  439. while (!$upload['done']) {
  440. $upload = Zend_File_Transfer_Adapter_Http:getProgress($upload);
  441. print "\nAktueller Fortschritt:".$upload['message'];
  442. // Tu was zu tun ist
  443. }
  444. ]]>
  445. </programlisting>
  446. </example>
  447. </sect3>
  448. </sect2>
  449. </sect1>
  450. <!--
  451. vim:se ts=4 sw=4 tw=80 et:
  452. -->