pax_global_header00006660000000000000000000000064150657424150014523gustar00rootroot0000000000000052 comment=9241edd261a72331342eab742e92a13613556cd3 filius-2.9.4+ds/000077500000000000000000000000001506574241500134345ustar00rootroot00000000000000filius-2.9.4+ds/.editorconfig000066400000000000000000000004061506574241500161110ustar00rootroot00000000000000# EditorConfig is awesome: https://editorconfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] charset = utf-8 end_of_line = lf insert_final_newline = true [*.java] indent_style = space indent_size = 4 filius-2.9.4+ds/.gitignore000066400000000000000000000003021506574241500154170ustar00rootroot00000000000000#IntelliJ stuff /.idea/ #Generated by Java/Maven /target/ /.classpath /.project /.settings /bin/ /test.fls /test.pdf /test2.pdf #Special /.gitignore #Generated by Visual Studio Code /.vscode filius-2.9.4+ds/.settings/000077500000000000000000000000001506574241500153525ustar00rootroot00000000000000filius-2.9.4+ds/.settings/org.eclipse.core.resources.prefs000066400000000000000000000001751506574241500235700ustar00rootroot00000000000000eclipse.preferences.version=1 encoding//src/main/resources=UTF-8 encoding//src/test/resources=UTF-8 encoding/=UTF-8 filius-2.9.4+ds/.settings/org.eclipse.jdt.core.prefs000066400000000000000000000006421506574241500223360ustar00rootroot00000000000000eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=17 filius-2.9.4+ds/.settings/org.eclipse.m2e.core.prefs000066400000000000000000000001261506574241500222350ustar00rootroot00000000000000activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 filius-2.9.4+ds/Changelog.md000066400000000000000000000714741506574241500156620ustar00rootroot00000000000000# Changelog Filius ## [2.9.4] - 2025-07-20 ### Fixed * DHCP must work for WAN port on home router * Changes on linux app meta information ## [2.9.3] - 2025-06-02 ### Fixed * Fix layer visualization in French and English * Fix ping / traceroute with home router and NAT ### Changed * Rename menu entry to Settings (from Change Language) ## [2.9.2] - 2025-05-21 ### Fixed * Fix info dialog * Fix animated layer visualization (started from layer view context menu) ## [2.9.1] - 2025-05-18 ### Fixed * Show all contributors on info dialog * Fix initial window width ## [2.9.0] - 2025-05-18 ### Added * Support mapped ports with NAT * Experimental feature to visualize network traffic over the network layers (to be activated on settings dialog) ### Changed * Colors on network viewer changed ### Fixed * Use bind address for sockets in order to prevent processing ARP messages from wrong network by home router ## [2.8.0] - 2025-03-15 ### Added * Experimental feature for additional icon sets (select via settings) ### Changed * Handling of UDP rules by firewall ### Fixed * Overlapping widgets fixed * Remove firewall ui log "null" * Send ARP requests from host to unknown network in case of DHCP * Do not change pdu's in network traffic viewer after NAT manipulation * Ensure that no packets from previous simulation run are processed after simulation start ## [2.7.1] - 2024-11-30 ### Fixed * Start-up with Filius scenario as program argument fixed ## [2.7.0] - 2024-11-23 ### Added * New settings dialog to change the language ### Changed * Store changes automatically in personal profile when exiting the program * Closing the program without prompt to store changes ### Fixed * Tracerout starts first ping with TTL 1 * Fix DHCP issues that are caused by not sending ARP requests without proper IP configuration * Fix tooltip text: shortcut for info dialog ## [2.6.1] - 2024-08-23 ### Changed * Shortcut to open info dialog changed to Ctrl+I * New notebooks are added with label "Notebook" (previously "New Notebook") ### Fixed * Correctly update Network Info Panel (to open at lower right corner on the desktop) when IP address changesd ## [2.6.0] - 2024-05-09 ### Added * Allow broadcast ping with parameter -b (undocumented feature) ### Changed * Do not send ARP request as best guess via first NIC if address not applicable * Remove svg export * Use other file dialogs (might help with MacOS) ### Fixed * Shutdown resp. close sockets correctly when switching to design mode * Fix initialization of web server ui regarding vhost configuration ## [2.5.1] - 2023-10-23 ### Fixed * Ping resp. ICMP fixed ## [2.5.0] - 2023-10-22 ### Added * Switch / Wireless Access Point with SSID * Configure Wi-Fi connections with SSID * Switch with retention time for SAT entries * Manual for Linux ### Changed * Requires Java 17 (or higher) * Replacement of some icons ### Fixed * Issues on Linux * Log messages * Messages in network traffic viewer are immutable ## [2.4.1] - 2023-06-07 ### Fixed * Fix language selection ## [2.4.0] - 2023-05-29 ### Added * Switch is now also Wireless Access Point * Usage info for each command line tool, show with 'help ' ### Fixed * Decrement TTL only in forwarded packets * Contents of network info panel broader, info is visible * Deletion of connections possible even in scrolled state * Newly created rows are discarded if syntax is invalid ### Changed * Switch ports start now from 1 (instead of 0) * Command 'cls' now listed with command line help * Include OpenJDK 19.0.2 as built-in Java for Windows ## [2.3.1] - 2023-03-11 ### Changed * Use current launch4j plugin ## [2.3.0] - 2023-03-11 ### Added * Add command line synonym "cls" for "reset" ### Fixed * Visible area of desktop fixed (on some os not everything was visible / visible area was to small) * Under Debian/Ubuntu there must be an additional dependency for desktop file utils and Java dependency is updated ### Changed * ARP won't reply for ip addresses queries that ask for network address (e.g. 192.168.0.0) any more ## [2.2.0] - 2022-12-29 ### Added * Add DHCP support for home router ### Fixed * Fix TTL handling in gateway / home router in order to allow traceroute * Update tab titles in data exchange dialog if ip address is updated via DHCP * Reduce number of ARP retries in DHCP flow (for validation) to one retry ## [2.1.0] - 2022-10-11 ### Added * Add terminal command to reset / delete the entire ARP table (arp -d) * Possibility to show MAC address as component name * Row selection in data exchange dialog will be retained with new entries ### Fixed * Fix duplicate ARP request * Fix NAT functionality ## [2.0.1] - 2022-08-26 ### Fixed * Focus and scrolling in Terminal window * Correct calculation of desktop dialog window ## [2.0.0] - 2022-08-20 ### Added * Support recursive resolution of MX entries (DNS) * Add component Home Router with NAT by default (can be removed in filius.ini) ### Changed * Help panel integrated into main window (instead of dedicated dialog) * Command host now resolves type A and type MX resource records (address and mail) * Apps opened on desktop only in full screen (incompatible change for self-developed apps!) ### Fixed * Margin of help panel the same in all languages and modes ## [1.14.2] - 2022-06-16 ### Fixed * Locale not initialized correctly ## [1.14.1] - 2022-05-29 ### Fixed * Missing Language Selection during first start-up ## [1.14.0] - 2022-05-27 ### Added * Experimental feature NAT Gateway (enable in filius.ini with 'gateway=1' * Drop packets in simulation * Command line tool nslookup as alternative to host * Support parameter -d and -a for command line tool arp ### Changed * Consistent domain name validation according to RFC 1035/2181 * Apply first matching UDP rule in firewall ### Fixed * Concurrency in client while disconnecting * Wrong protocol shown in data exchange dialog * Unit tests using undefined locale * Data in exchange dialog won't be changed anymore (using copies of transferred data) * TCP retry and timeout * Error in case of empty file name * Non-fixable issue in case of invalid/incomplete firewall rule configuration * Fix content type when retrieving image with upper case extension ## [1.13.2] - 2022-01-29 ### Changed * Improved cut and paste resp. copy and paste function in file explorer ### Fixed * Status line of ping command was missing. * Wrong text in ipconfig output on command line fixed. * Multiple copy and past in file explorer lead to moving the same file instead of creating multiple copies. * Handle spaces in installation path (Linux) correctly. ## [1.13.1] - 2021-12-12 ### Changed * Rename label for DNS A records from 'domain name' to 'host/domain name' ### Fixed * Fix response for non-resolvable domain with recursive dns resolver * Fix http request processing with query params in URL ## [1.13.0] - 2021-12-11 ### Added * IP forwarding can now activated/deactivated (formerly always on). Default: Router activated, computer deactivated. * New command line tool tcpdump. * New command line tool arpsend (e.g. to simulate ARP spoofing). * Detect and show application layer protocol in data exchange. ### Changed * Firewall behavior changed with respect to sender and target address. * Simulation speed can now be adapted more fine grained. The new 90% correlates to the former 100%. * The last column in data exchange dialog is renamed from 'Description' to 'Description / details'. * ARP requests are only used as source for new ARP entries if they are directed to the node itself. * New initial screen for Terminal. * Application windows on desktop: support maximizable, set to maximized by default. ### Fixed * Some linux distributions could not be started directly with Filius files via desktop file managers. * URL encoding in case of html forms with method GET is fixed. ## [1.12.5] - 2021-09-01 ### Fixed * Templates for new applications work now with the new log framework ## [1.12.4] - 2021-05-01 ### Changed * Log can now be enabled for stdout ### Fixed * Z layer of components in design mode fixed. * Vhosts for web server will be persisted again. ## [1.12.3] - 2021-05-01 ### Changed * Use now SLF4J and Logback for logging ### Fixed * Error when reading old Filius files because of non-compliant version strings fixed * Fixed wrong data in data exchange detail section caused be shallow frame copies ## [1.12.2] - 2021-04-26 ### Fixed * Fix exception after one node was deleted ## [1.12.1] - 2021-04-25 ### Fixed * Fix command line arg handling (some combinations were not possible) * Fix spaces in filename when starting via command line under linux * Ensure that during visual alignment of components in design mode, the dragged component is always in foreground ## [1.12.0] - 2021-04-10 ### Added * Detailed license information added * Report contains now the forwarding table of network nodes and - if available - DNS server configuration * Provide an installer with bundled JRE for Windows ### Changed * IP addresses for router config as well as forwarding table entries are now validated after input * SEQ and ACK numbers are now initialized with trailing zeros and formatted as decimal numbers for better readability ### Fixed * Version comparison (only visible in log output) * Filius can know be installed in directories that contain '+' or '$' * SEQ number is now incremented after FIN reception * Help contents are now available from the start ## [1.11.0] - 2021-01-23 ### Added * The personal firewall supports now filtering of UDP traffic. * Build and Code Signing with AppVeyor and SignPath. ### Changed * The default behavior of the firewall is activated UDP traffic filtering, i.e., UDP packets (e.g. DNS) will be discarded. * Windows/dialogs are mostly resizable, e.g. application windows on the virtual desktops. * Added readme file to be shown at Gitlab. ### Fixed * When Filius was opened on a secondary monitor that is not available any more, the Filius window will be located onto a visible monitor when starting next time. * Within the terminal application the cd command could be used with a file. Now it is only available for directories. * Use URI encode and decode for URLs. * Do not allow domain names that start with digits. ## [1.10.4] - 2020-11-29 ### Fixed * There was an incompatibility within Report generation on Java 8. * It was not possible to generate a report of unsaved scenarios. * The contents of the help dialog were editable. ## [1.10.3] - 2020-08-16 ### Fixed * Manually configured DHCP settings for gateway and dns server were not persisted if they were equal to the os settings. ## [1.10.2] - 2020-07-29 ### Fixed * Fix bug when loading old project files with documentation elements ### Changed * Reduce corner radius of document elements * Use secured Filius homepage URL (https) ## [1.10.1] - 2020-07-18 ### Fixed * Silent Uninstall for Windows * Use secured Filius homepage URL (https) ### Changed * Default install folder under Windows is now C:\Program Files\Filius ## [1.10.0] - 2020-06-14 ### Fixed * Uebersetzung zur Simulationsgeschwindigkeit korrigiert * Falsche Nummerierung Abschnittsueberschriften im Report behoben * Fehlerbehebung zur Erstellung von Kabelverbindungen * Korrektur Rechtschreibung in franzoesischer Uebersetzung * Korrektur der kaputten Anzeige fuer Terminal-Anwendung * Fehlerbehebung im Einfachen Client: Immer auf eingehende Nachrichten warten (auch ohne Versand einer Nachricht) ### Changed * Abschnitt zum Nachrichtenaustausch im Report nur, wenn Inhalte vorliegen * Redaktionelle Ueberarbeitung der Hilfeseiten * Grundlegende Ueberarbeitung Dokumentationsmodus mit Formatierungsmoeglichkeiten ### Added * Möglichkeit, Reiter zum Nachrichtenaustausch zu schliessen * Wiederherstellen der angezeigten Fenster bei Wechsel zwischen Anwendungsmodi * Hilfeseite für Dokumentationsmodus hinzugefügt ## [1.9.0] - 2020-05-02 ### Fixed * Fehlerbehebung im Einfachen Client: Auf Serverantworten erst nach Versand einer Nachricht warten * Fehlerbehebung TCP-Server: Sockets beim anhalten des Servers gemaess Spezifikation zum Verbindungsabbau schliessen * Fehlerbehebung: Sockets im Zustand CLOSED immer aus Socket-Liste entfernen * Fehlerbehebung: Versand leerer Nachrichten ermoeglichen (war die Nachricht leer, wurde nichts gesendet) * Fehlerbehebung: Netstat zeigt jetzt auch serverseitig die Verbindungen an * Fehlerbehebung: Der Befehl cat fuer eine nicht vorhandene Datei fuehrte zu einem Fehler * Fehlerbehebung: TCP-Socket-Zeitueberschreitungen wurden nicht korrekt verarbeitet * Fehlerbehebung: Die Sequenznummern in TCP wurden falsch hochgezaehlt ### Changed * Verbesserung Source Address Table (SAT): Fenster mit SAT werden nicht mehr mehrfach geoeffnet und automatisch aktualisiert * Verbesserung: Netstat zeigt jetzt lokale und entfernte Adresse mit IP-Adresse und Port an ### Added * Verbesserung: Gnutella ermoeglicht jetzt das Zuruecksetzen der bekannten Peers * Export der Konfiguration und des Datenaustauschs als PDF-Datei ## [1.8.2] - 2020-04-21 * Fehlerbehebung Modem: Verklemmung, wenn beide verbundenen Modems in gleicher Filius-Instanz laufen ## [1.8.1] - 2020-02-01 * Korrekturen zur franzoesischen Typographie * Internationalisierung "Netzwerk" in Gnutella ## [1.8.0] - 2020-01-20 * Export aus Dokumentationsmodus als SVG moeglich (zusaetzlich zu PNG) * Hinweis/Tooltip zur Einstellung der Simulationsgeschwindigkeit * Fehlerbehebung zur Anzeige/Aktualisierung des Modemnamens * Verschiedene Fehler im Dokumentationsmodus behoben * Korrektur zum Verschieben von mehreren Elementen im Entwurfsmodus ## [1.7.4] - 2019-01-08 * Sprache Franzoesisch hinzugefuegt (Uebersetzung durch Patrice Treton) * Fehlerkorrekturen I18n * Software-Assistent wird in Vorgabeeinstellung immer angezeigt ## [1.7.3] - 2018-08-21 * Abhängigkeit zu Java 8 in Windows-Version korrigiert ## [1.7.2] - 2016-02-07 * Konfigurierbare Groesse der Arbeitsflaeche zum Aufbau der Rechnernetze (konfigurierbar in filius.ini) * Fehlerbehebung: Fenstergroesse fuer E-Mail Verfassen so angepasst, dass es auch unter Mac OS funktioniert * Fehlerbehebung: Anhalten einer Server-Anwendung gibt den Port frei, der fuer den Serversocket verwendet wurde ## [1.7.1] - 2016-02-03 * Fehlerbehebung: Sprachselektion bei erstmaligem Starten wieder aktiviert ## [1.7.0] - 2016-01-30 * Neues Windowsinstallationsprogramm * Systemvoraussetzung Java 8 * Aufrufparameter 'verbose' ergaenzt * Grundlegende Ueberarbeitung DHCP * DHCP unterstuetzt jetzt statisch zugewiesene IP-Adressen * Der Software-Assistent steht jetzt nicht mehr zwingend zur Verfuegung (abhaengig von JDK/JRE und Konfiguration) * Im Dateidialog zum Oeffnen eines neuen Projekts ist jetzt das zuletzt verwendete Verzeichnis vorselektiert * Webserver erlaubt jetzt auch unbekannte Dateiendungen (Versand als text/plain) und erkennt Endung .htm auch als HTML * Fehlerbehebung: Anwendung fuer Dateiaustausch (P2P) und Webserver funktioniert jetzt auch mit Leerzeichen in Dateinamen * Fehlerbehebung: Mehrere DHCP-Server können jetzt im gleichen Rechnernetz vorhanden sein * Fehlerbehebung: Selektion bei Löschen vorhandener Kabel korrigiert * Fehlerbehebung: Validierung E-Mail-Adressen korrigiert (Reg. Ausdruck angepasst) * Fehlerbehebung: DHCP funktioniert jetzt auch, wenn zwischendurch das Projekt gewechselt wurde * Fehlerbehebung: Wenn ein Projekt ohne Dateiendung gespeichert wird, werden jetzt auch die Textelemente mit gespeichert ## [1.6.1] * Fehlerbehebung: Fehlerhafte Bestimmung von IP-Adressen als Broadcast behoben * Fehlerbehebung: TTL wird jetzt nicht mehr fuer lokal adressierte Pakete dekrementiert * Verbesserung des Datenaustauschprotokolls als Textdatei ## [1.6] * Fehlerbehebung: Robustere Eingabe der Rechnerkonfiguration * Fehlerbehebung: Verbindungsherstellung/-trennung mit Modem stabiler realisiert * Ausgabe einer Fehlermeldung, wenn ein Broadcast-Ping ausgefuehrt werden soll * Fehlerbehebung: Knoten konnten u.U. nicht mehr selektiert werden, wenn sie durch Beschreibungselemente ueberlagert wurden * Fehlerbehebung: Ping-Fehler behoben * Konfiguration der Knoten als Tooltip in der Netzwerkansicht * Export des Nachrichtenaustauschs als einfache Textdatei * Maximale Anzahl von Verbindungen zu Switch (von 8) auf 24 erhoeht * Fehlerbehebung: TTL wird jetzt korrekt inkrementiert ## [1.5.4] * Fehlerbehebung: Inkompatibilität beim Oeffnen von Projektdateien, die mit aelterer Version erstellt wurden, behoben * Fehlerbehebung: Kabelwerkzeug wurde an falscher Position angezeigt, wenn Arbeitsbereich per Schieber verschoben wurde * Fehlerbehebung: Absender und Empfaenger einer Mail wurde nicht in Projektdatei gespeichert * Fehlerbehebung: ARP-Anfrage wurde mehrfach versendet * Fehlerbehebung: Dokumentationselemente wurden bei Oeffnen eines neuen Projekts (erstellt mit aelterer Version) nicht immer entfernt * Fehlerbehebung: Loeschen gesendeter Mails war nicht mehr moeglich * Fehlerbehebung: POP3-Server hat nicht immer geantwortet ## [1.5.3] - 2013-11-07 * Moeglichkeit, Textfelder und Strukturierungsfelder einzufuegen * Exportieren der Netzansicht als PNG moeglich * Bounce-Mails bei nicht erreichbaren Empfaengern * Reduzierung der Schnittstellen eines Vermittlungsrechners ist jetzt moeglich * IP-Adresse der Vermittlungsrechner-Schnittstellen werden jetzt als Reitertitel verwendet * Fehlerbehebung: leere Ordner-/Dateinamen möglich -> Datei-Explorer Fehlfunktion * Fehlerbehebung: Linux-Startskript ohne 'realpath' * Fehlerbehebung: mehrere E-Mail-Benutzerkonten in E-Mail-Anwendung möglich * Fehlerbehebung: einzelne Nachrichten wurden bei Wechsel zwischen Entwurfs- und Aktionsmodus wiederholt verarbeitet ## [1.5.2] - 2013-07-05 * Umbenennung "Terminal" zu "Befehlszeile"/"Command line" * Neue Befehlszeilenwerkzeuge: arp, cat * Synchronisation von Dateiinhalten mit Anzeige im Texteditor (insbesondere für E-Mail- und DNS-Server) * Validierung von DNS-Server-Konfigurationseingaben und Hinweise bei falscher Eingabe * DNS-Server-Konfiguration in Tabellen editierbar ## [1.5.1] - 2013-02-13 * Kleine Fehlerbehebungen ## [1.5] - 2013-02-03 * Firewall grundlegend ueberarbeitet (Firewall-Konfiguration in Projektdateien nicht kompatibel mit aelteren Versionen) * Neuer Nachrichtendialog (alter Nachrichtendialog in filius.ini aktivierbar) * Im Entwurfsmodus: erstellen mehrerer Kabelverbindungen mit einmaliger Selektion des Kabelwerkzeugs moeglich * Anzahl der Pings ueber Kommandozeile wieder auf vier reduziert (Anpassung ueber filius.ini moeglich) * Fehlerhafte Anzeige des Icons eines Vermittlungsrechners in Abhaengigkeit des Verbindungsstatus behoben ## [1.4.5.3] - 2012-11-25 * Fehler zu DNS-Server bei schnellem Wechsel Starten/Beenden * Fehler zu ICMP (ping/traceroute) behoben ## [1.4.5.2] - 2012-11-12 * Fehlerbehebung: Ping zu Vermittlungsrechner funktionierte nicht fuer alle Schnittstellen ## [1.4.5.1] - 2012-11-11 * Fehlerbehebung: Fortschrittsanzeige bei Mail-Abruf wurde nicht mehr ausgeblendet und Mails nicht vollstaendig angezeigt * Fehlerbehebung: Durch die rekursive DNS-Namensaufloesung muss die zeitliche Begrenzung des Resolvers auf ein vielfaches der maximalen Round-Trip-Time betragen ## [1.4.5] - 2012-11-04 * Domainserver unterstuetzen als Option rekursive Domainnamensaufloesung. * Re-Integration eines Forks (Dank an pyropeter) - erweitertes ICMP - im Terminal: Befehlshistorie, Traceroute, Abbruch mit +C - Vermittlungsrechner koennen jetzt automatisch konfiguriert werden (mit RIP als Routing-Protokoll) - Modem: automatischer Wiederaufbau einer unterbrochenen Verbindung * Der Webserver des Vermittlungsrechner ermöglicht die Abfrage der aktuellen Weiterleitungstabelle. * Vermittlungsrechner-Firewall: - kann jetzt alle Versuche eines TCP-Verbindungsaufbaus ablehnen. - Absender- und Empfaenger-Regeln gelten nur noch fuer TCP (nicht mehr fuer UPD) * Als Rechnername kann jetzt die IP-Adresse verwendet werden. ## [1.4.4] - 2012-03-13 * Domain Name System: Jetzt kann auch ein Resource Record fuer einen Wurzel-Name-Server konfiguriert werden. * Web-Server: Ueberarbeitung des Designs der Vorgabe-Webseite * Kleinere Anpassungen der Darstellungen (u.a. Umbenennung des "Echo-Client" zu "Einfacher Client") * Programmkonfiguration: - Grundlegende Einstellungen zur Anzeige und Ausfuehrung koennen jetzt als Aufrufparameter oder mit einer Ini-Datei konfiguriert werden. - RTT kann jetzt nicht mehr ueber Menue sondern nur noch als Aufrufparameter bzw. per Ini-Datei geaendert werden. * Fehlerbehebungen: - Nachrichten des POP3-Servers werden im Log-Fenster angezeigt - Flag fuer manuelle Einstellungen des DHCP-Servers wird persistiert - Schnittstellenverwaltung der Vermittlungsrechner bei angeschlossenem Switch fuehrte zum Programmabbruch - Anzeige des Kontextmenue fuer Kabel im Aktionsmodus wurde nicht unterdrueckt ## [1.4.3] - 2011-11-10 * Domain Name System: Das DNS erlaubt jetzt rekursive Abfragen zur Aufloesung eines Domainnamens. * Webserver: Die optionale Konfiguration der virtuellen Hosts kann jetzt ausgeblendet werden. * Firewall: Die Firewall der Vermittlungsrechner kann jetzt dezidiert fuer die Netzwerkschnittstellen aktiviert werden. * Kommandozeile: Hier steht jetzt zusaetzlich der Befehl netstat zur Anzeige aktiver Verbindungen zur Verfuegung. * In Textfeldern steht jetzt die Tastenfunktion +a zur Verfuegung. * Fehlerbehebung: - Netzwerkschnittstellenkonfiguration Vermittlungsrechner: Jetzt wird die richtige IP-Adresse eines verbundenen Vermittlungsrechners angezeigt. - Datenaustausch ueber Vermittlungsrechner: Wenn Standardgateway nicht gesetzt wurde, erfolgt jetzt korrekte Ausnahmebehandlung. ## [1.4.2] - 2011-09-15 * Fehler bei Aufruf mit Parameter "-wd" zur expliziten Auswahl des Arbeitsverzeichnisses behoben ## [1.4.1] - 2011-09-01 * Fehler bei Aenderung des Aufbaus behoben * TCP-Timeout-Fehlermeldung erfolgt nicht mehr im Konfigurationsmodus * Fehlerhafte Modemfunktionalität korrigiert ## [1.4] - 2011-08-07 * Fehlerbehebung: - Bildbetrachter: Anzeige von Bildern wieder möglich - E-Mail-Programm: Platzhaltergrafik für Nachrichten wird wieder angezeigt - Webserver: Anzeige leerer Eingabefelder in Oberfläche (statt "null") - DNS-Server: Synchronisation zwischen Oberfläche und Konfigurationstextdatei - Gnutella: Fehler bei Dateiübertragung behoben - E-Mail-Server: Robustere Verarbeitung von E-Mails durch SMTP-Server - Switch: Die Darstellung als Wolke kann auch wieder rückgängig gemacht werden - Programmstart: Fehler bei Prüfung der Schreibrechte auf Benutzerverzeichnis unter Windows behoben - Info-Dialog: Alle Angaben werden wieder vollständig angezeigt (unter Linux) - Nachrichtenansicht: Löschen der Tabelleninhalte korrigiert * Echo-Client: Jetzt können hierüber E-Mails versendet (SMTP) und abgerufen (POP3) werden. * Rechnerkonfiguration: Jetzt können Gateway- und DNS-Server-Adressen wieder entfernt werden. * Nachrichtenansicht: Jetzt steht eine Option "automatisches Scrollen" über Kontextmenü zur Verfügung und das Dialogfenster bleibt immer im Vordergrund. ## [1.3.1.4] - 2011-04-30 * Verwendung von Java Native Access zur korrekten Ermittlung von Benutzerverzeichnissen unter MS Windows (vgl. Java Bug #4787931: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4787931 * Kontrolle bei Programmstart, ob Arbeitspfad schreibbar ist. Schreibrechte sind notwendig für Dateioperationen wie Speichern/Laden von Szenarien) * bei fehlenden Schreibrechten wird sofort Dialog angezeigt zur manuellen Korrektur durch den Benutzer, d.h. Auswahl eines anderen Verzeichnisses ## [1.3.1.3] - 2011-01-30 * kleine Bugfixes * Problem bei Ping-Nachrichten (v.a. Modem betroffen) behoben * Darstellungsmöglichkeit von Switches als Netzwerkwolken (identische Funktionalität wie Switch, lediglich Erscheinung aus didaktischen Gründen verändert!) [Rücknahme: - fehlerhaftes Verhalten bei Problemen mit Log-Datei behoben] ## [1.3.1.2] - 2010-11-08 * kleinere Bugfixes * verbesserte Log-Daten-Aufzeichnung (aussagekräftigere Auflistung der relevanten hashCodes zur Verfolgung von Aktivitäten) * Platzieren der Log-Datei in Arbeitsverzeichnis (.filius) statt in Ort des Startens der Anwendung wegen Rechteproblemen bei Betriebssystem ## [1.3.1.1] - 2010-09-20 * Bugfix bei IP Adressevaluation; Zahlenbereich war begrenzt durch Fehler in Regulärem Ausdruck ## [1.3.1] - 2010-08-04 * E-Mail-System überarbeitet; bessere Darstellung im Mailprogramm * Antwortfunktion bei Mailprogramm korrigiert * Fehler bei Mailverwaltung im Server korrigiert, u.a. beim Löschen abgerufener Mails * Scrollpane eingefügt in Mailprogramm (Mailbetrachter) und TextEditor * variable Einstellungen für maximale RTT (NOTE: die Simulation wird dadurch nicht verlangsamt, sondern lediglich die Toleranz bzgl. Timeouts wird vergrößert; das ist sinnvoll für langsamere Systeme) * Erweiterung des Webservers um virtuelle Hosts * Bugfixes z.B. bei Graphikeinbindung * Startup-Skript für Mac OS X hinzugefügt; Tests auf Mac OS X erfolgreich ## [1.3.0.4] - 2010-07-07 * Hinzufügen eines Startup-Skripts für UNIX-basierte Systeme (Filius.sh) * Parameterunterstützung zur Verlegung des Arbeitsverzeichnisses ".filius": '-wd ' * Überprüfung der DNS-Einträge; falls URL leer wird Eintrag nicht übernommen/gespeichert * Fehlplatzierung bei Kabel behoben * Kontextmenü zum Löschen von Kabeln an engeren Kollisionsbereich gebunden * Kontextmenü zum Löschen von Kabeln nur angezeigt, falls keine Kabelvorschau aktiv ## [1.3.0.3] - 2010-04-19 * Fehler bei der direkten Bearbeitung von DNS-Einträgen in der Tabelle korrigiert * Daten werden nun getrennt nach A bzw. MX Einträgen verarbeitet (JTableEditable) * Pfadunstimmigkeiten bei Hilfe-Dateien (bei Aufruf der Jar-Datei aus anderem Verzeichnis) beseitigt ## [1.3.0.2] - 2010-04-15 * Bilder in JAR-Datei integriert * Korrektur der Pfadzuweisungen, um Dateien innerhalb der JAR-Datei zu finden * Fehler bei Suche von FLS-Datei als Parameter beseitigt * Fehler bei Pfadbestimmung korrigiert, damit Filius von beliebiger Stelle aus aufgerufen werden kann (betrifft JAR und EXE!) ## [1.3.0.1] - 2010-04-09 * Datei-Explorer importiert beliebige Dateien bis zu einer Größe von 150KB * bei fehlgeschlagener DHCP-Anfrage werden Zeroconf-Adressen zugewiesen (169.254.x.x) * ping-Befehl wurde in Befehlsliste bei Terminal aufgenommen * Überschrift für Tabellenausgabe bei route-Befehl ## [1.3.0] - 2010-03-31 * Terminal-Funktionen implementiert (move, copy, dir, help, ...) * Hilfeseite zur Auflistung aller unterstützter Terminal-Befehle * Ping-Befehl implementiert in Terminal * Tool zur Namensauflösung direkt über Terminal ('host') * Tastenkombinationen zur Steurung der Menüfunktionen (Strg+) + Auswahl Kabel (Alt-1) * konsequentes Loggen in Datei möglich für Fehlersuche; Debug-Ausgaben vereinheitlicht * DHCP kann DNS/Gateway abweichend von Server-Einstellungen vergeben * DNS-Einträge auch änderbar in DNS-Server (statt nur Löschen/Neuanlegen) * einzelne Kabel können entfernt werden * Fehler durch sich blockierende nebenläufige Prozesse beseitigt (Threads haben aufeinander gewartet; Race Conditions bei Prüfung von Queues) * deutliche Reduktion von Ausnahmefehlern (Exceptions oftmals für Testzwecke auf mögliche Probleme genutzt. Lässt sich jedoch durch vorherige Prüfung fast durchgängig vermeiden!) * Terminal-Funktionen waren nur angedeutet, jedoch nicht implementiert * Knoten können nun nicht mehr aus Arbeitsfläche herausgezogen werden (war vorher möglich; leider dann aber keine Möglichkeit mehr, diese in den Sichtbereich zurückzuholen) * manche Dialoge wurden zu Singleton umfunktioniert (nur noch ein Dialog) * Darstellung/Einsehen der MAC-Adressen einfacher möglich * MX Eintrag bei DNS-Server unterstützt nun auch IP-Adressen, statt nur URLs * Ethernet akzeptiert nun nur noch Pakete, wenn Kabel physisch mit betrachteter Netzwerkkarte verbunden ist (vorher: bei Vermittlungsrechner Pakete aller NICs verarbeitet) * warten auf DHCP Server bei Wechsel zu Simulationsmodus, erst dann DHCP Anfragen möglich * Überschriften in diversen Dialogen zur besseren Usability * fehlerhafte Pattern für IP-Adressen korrigiert * Sprache bei Buttons in Dialogen korrigiertfilius-2.9.4+ds/GPLv2.txt000066400000000000000000000432541506574241500150770ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. filius-2.9.4+ds/GPLv3.txt000066400000000000000000001045131506574241500150740ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . filius-2.9.4+ds/Readme.md000066400000000000000000000065261506574241500151640ustar00rootroot00000000000000# Filius - Simple Network Simulation Filius is a network simulator for educational purpose ## License Filius is provided under the terms of GPLv2 or GPLv3. ## Requirements Filius is a Java application. A Java Runtime Environment 8 or higher is required. Support of Java 8 is deprecated and will end soon. For most features, JRE will do. The feature to implement and run your own applications (by means of the Software Wizard) requires the Java Development Kit. ## OS Support Windows, Linux ## Available Languages German, French, English ## Configuration The personal configuration, i.e. selected language as well as the UI state when leaving the program, is stored within the personal home directory within a folder named ".filius". Global configurations can be modified in the install directory within "filius.ini". For more details see the documented parameters in this file. ## Installation The Windows installer is based on nsis. It supports the standard parameter: * /S - for silent install/uninstall * /D - to set the install directory (e.g. /D=Z:\Filius) ## Website https://www.lernsoftware-filius.de/ ## Code Signing This program uses free code signing provided by [SignPath.io](https://signpath.io?utm_source=foundation&utm_medium=gitlab&utm_campaign=filius), and a free code signing certificate by the [SignPath Foundation](https://signpath.org?utm_source=foundation&utm_medium=gitlab&utm_campaign=filius) # Contribute / Build Filius ## Development Environment Filius is developed with Eclipse using Maven as a build tool. Currently, the build works with Java up to version 13 since some unit tests cannot be executed with the newest Java versions. In other words: It is possible to develop with a newer version but there will be some tests that fail. ## Bundled JRE Some Filius distributions are shipped with JRE to remove the external dependency on a pre-installed Java Runtime Environment. But since Filius is not built as a Java module, the JRE cannot be built as part of the build process with Maven. The runtime is created in two steps: 1. Define required Java modules 2. Create the runtime ### Define Required Modules Use the jdeps tool that is shipped with OpenJDK: ``` jdeps -recursive --ignore-missing-deps --multi-release base --print-module-deps filius.jar lib\* ``` The output contains some warnings and the following required modules: ``` java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.sql,java.xml.crypto,jdk.unsupported ``` Additionally, the following runtime dependencies are required in order to use the Software Assistant for application compilation at runtime in Filius: - jdk.compiler - This module contains the compiler tool set. - jdk.zipfs - This module is required to read dependencies on built-in Filius classes from filius.jar. ### Build Runtime The bundled JRE (if available) is built with the following command: ``` jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,java.desktop,java.instrument,java.management,java.naming,java.sql,java.xml.crypto,jdk.unsupported,java.compiler,jdk.compiler,jdk.zipfs --output java-runtime ``` ## Generate Documentation Source code is documented with Javadoc. Generate the API documentation with Maven: ``` mvn javadoc:javadoc ``` The documentation will be stored in the default output directory (target) in 'site/apidocs' as HTML pages. filius-2.9.4+ds/appveyor.yml000066400000000000000000000016301506574241500160240ustar00rootroot00000000000000version: '{build}' skip_non_tags: true image: Visual Studio 2022 environment: JAVA_HOME: C:\Program Files\Java\jdk17 PATH: '%JAVA_HOME%\bin;%PATH%' JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8 build_script: - cmd: >- echo Source encoding defined with: JAVA_TOOL_OPTIONS=%JAVA_TOOL_OPTIONS% java -version mvn --version mvn clean site install -Plinux -Pwindows-with-jre artifacts: - path: target\Filius-Setup_with-JRE*.exe name: Windows Installer with Bundled JRE - path: target\*.zip name: Zip Archive - path: target\*.deb name: Debian Package deploy: - provider: Webhook name: release on: APPVEYOR_REPO_TAG: true url: https://app.signpath.io/API/v1/15baf9fe-557e-4a33-92a1-afc9246f41d2/Integrations/AppVeyor?ProjectSlug=filius&SigningPolicySlug=release-signing authorization: secure: eQKEcW+1eqx1UQf1eDHSwUgEAxy6XOcv/Nwh0UhwSzpt+C1Zpd2GYMovP5t03CGUPQwxt20ysfJVp04zX4Au8A== filius-2.9.4+ds/filius_eclipse_format.xml000066400000000000000000000743041506574241500205350ustar00rootroot00000000000000 filius-2.9.4+ds/pom.xml000066400000000000000000000441741506574241500147630ustar00rootroot00000000000000 4.0.0 filius filius 2.9.4 2007 20.07.2025 Stefan Freischlad UTF-8 4.10 4.11.0 1.4 1.6 5.12.1 2.0.2 17 17 2.4 2.5.2 Filius.exe 3.12.0 Lernsoftware Filius jar Filius Lernsoftware zu Rechnernetzen https://www.lernsoftware-filius.de GNU General Public License v3 (GPLv3) https://www.gnu.org/licenses/gpl-3.0.txt org.apache.xmlgraphics batik-svggen 1.17 xml-apis xml-apis-ext org.apache.xmlgraphics batik-dom 1.17 xml-apis xml-apis-ext xml-apis xml-apis org.apache.xmlgraphics batik-codec 1.17 xml-apis xml-apis-ext com.itextpdf itextpdf 5.5.13.1 junit junit ${junit.version} test org.mockito mockito-core ${mockito.version} test org.easytesting fest-assert ${fest.version} test org.htmlparser htmlparser ${htmlparser.version} org.apache.commons commons-lang3 ${commons-lang3.version} commons-io commons-io 2.8.0 org.springframework spring-web 6.0.12 commons-cli commons-cli 1.4 de.skuzzle semantic-version 2.1.0 org.slf4j slf4j-api 1.7.30 ch.qos.logback logback-classic 1.2.3 filius src/main/resources false **/Thumbs.db application.properties src/main/resources true application.properties maven-resources-plugin 2.7 copy-resources validate copy-resources ${project.build.directory} src/nsis true **/* src/info **/* ${basedir} *.md *.txt src/scripts **/* src/main/resources org.apache.maven.plugins maven-compiler-plugin ${mavenCompiler.version} ${java.version} ${java.version} org.apache.maven.plugins maven-jar-plugin ${mavenJar.version} filius.Main true lib/ . tmpl/** config/** hilfe/** img/** maven-dependency-plugin 3.8.1 copy-product-dependencies prepare-package copy-dependencies junit,mockito-all,fest-assert false ${project.build.directory}/lib org.apache.maven.plugins maven-site-plugin 3.9.1 org.apache.maven.plugins maven-javadoc-plugin 3.3.1 false false org.apache.maven.plugins maven-project-info-reports-plugin 3.1.1 dependencies licenses windows windows com.akathist.maven.plugins.launch4j launch4j-maven-plugin ${launch4j.version} filius-exe package launch4j gui target/${launch4j.starterName} Filius - Error target/filius.jar src/main/resources/gfx/filius32.ico filius.Main true . java-runtime true ${execJre.version} ${project.version}.0 txt file version ${project.name} ${publisher} ${project.version}.0 ${project.description} ${project.name} ${project.artifactId} ${launch4j.starterName} org.apache.maven.plugins maven-assembly-plugin 2.3 src/assembly/assembly.xml package single org.codehaus.mojo exec-maven-plugin 1.2 create installer install exec nsis/makensis target/filius.nsi windows-with-jre org.codehaus.mojo exec-maven-plugin 1.2 create installer with built-in JRE install exec nsis/makensis target/filius-with-jre.nsi linux unix maven-antrun-plugin 1.7 prepare-package run jdeb org.vafer 1.7 install jdeb /usr/share/filius ${project.build.directory}/${project.build.finalName}.jar file /usr/share/filius/filius.jar template /usr/share/filius /usr/share/doc/filius /usr/share/doc/filius /usr/share/filius/config /usr/share/filius/tmpl /usr/share/filius/tmpl/quelltext_vorlagen /usr/share/filius/img /usr/share/filius/hilfe /usr/share/filius/hilfe/gfx /usr/share/filius/lib ${project.basedir}/src/scripts/filius.sh file usr/share/filius/filius.sh perm 755 root root link /usr/bin/filius ../share/filius/filius.sh true ${project.basedir}/src/deb/filius.desktop file /usr/share/applications/filius.desktop ${project.basedir}/src/deb/application-filius-project.xml file /usr/share/mime/packages/application-filius-project.xml ${project.basedir}/src/deb/filius32.png file /usr/share/icons/hicolor/32x32/filius.png ${project.basedir}/src/deb/copyright file /usr/share/doc/filius/copyright ${project.build.directory}/changelog.gz file /usr/share/doc/filius/changelog.gz ${project.build.directory}/Changelog.md file /usr/share/filius/Changelog.md ${project.build.directory}/Readme.md file /usr/share/filius/Readme.md ${project.basedir}/src/info directory *.* perm 1 /usr/share/filius ${project.basedir}/src/main/resources/config directory *.* perm /usr/share/filius/config link /etc/filius /usr/share/filius/config true ${project.basedir}/src/main/resources/tmpl directory perm /usr/share/filius/tmpl ${project.basedir}/src/main/resources/img directory *.* perm /usr/share/filius/img ${project.basedir}/src/main/resources/hilfe directory *.* perm /usr/share/filius/hilfe ${project.basedir}/src/main/resources/hilfe/gfx directory *.* perm /usr/share/filius/hilfe/gfx ${project.build.directory}/lib directory *.jar perm /usr/share/filius/lib ${project.build.directory}/site directory perm /usr/share/doc/filius filius-2.9.4+ds/src/000077500000000000000000000000001506574241500142235ustar00rootroot00000000000000filius-2.9.4+ds/src/assembly/000077500000000000000000000000001506574241500160425ustar00rootroot00000000000000filius-2.9.4+ds/src/assembly/assembly.xml000066400000000000000000000042021506574241500204010ustar00rootroot00000000000000 ${version} zip false lib filius:filius:* target *.jar target Filius.exe target/site **/* src/scripts * *.md *.txt src/info * src/main/resources/config /config * src/main/resources/tmpl /tmpl * src/main/resources/tmpl/quelltext_vorlagen /tmpl/quelltext_vorlagen * src/main/resources/img /img * src/main/resources/hilfe /hilfe * */* filius-2.9.4+ds/src/deb/000077500000000000000000000000001506574241500147555ustar00rootroot00000000000000filius-2.9.4+ds/src/deb/application-filius-project.xml000066400000000000000000000005351506574241500227420ustar00rootroot00000000000000 Filius Project filius-2.9.4+ds/src/deb/control/000077500000000000000000000000001506574241500164355ustar00rootroot00000000000000filius-2.9.4+ds/src/deb/control/control000066400000000000000000000006311506574241500200400ustar00rootroot00000000000000Package: filius Version: [[version]] Section: misc Priority: optional Architecture: all Recommends: default-jdk Depends: default-jdk | default-jre, desktop-file-utils Maintainer: Stefan Freischlad Description: Lernsoftware zu Rechnernetzen Entdeckendes Lernen mit Filius durch Simulation von Rechnernetzen Distribution: production Homepage: https://www.lernsoftware-filius.defilius-2.9.4+ds/src/deb/control/postinst000066400000000000000000000001101506574241500202330ustar00rootroot00000000000000#! /bin/sh update-mime-database /usr/share/mime update-desktop-databasefilius-2.9.4+ds/src/deb/control/postrm000066400000000000000000000001101506574241500176740ustar00rootroot00000000000000#! /bin/sh update-mime-database /usr/share/mime update-desktop-databasefilius-2.9.4+ds/src/deb/copyright000066400000000000000000000007271506574241500167160ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Contact: Stefan Freischlad Source: https://gitlab.com/filius1/filius Copyright: 2007-2021 License: GPL-2 or GPL-3 Files: filius.jar Copyright: 2007-2021 License: GPL-2 or GPL-3 On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 resp. /usr/share/common-licenses/GPL-2 file.filius-2.9.4+ds/src/deb/filius.1000066400000000000000000000040611506574241500163330ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" (C) Copyright 2023 Andreas B. Mundt , .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH FILIUS "1" "July 2023" "" "User Commands" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME filius \- educational network simulator .SH SYNOPSIS .B filius .RI [ options ] .RI [ project.fls ] .SH DESCRIPTION This manual page documents briefly the .B filius program. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. .B filius is an educational program to simulate networking technologies like Ethernet, TCP/IP as well as various services and applications. It offers network components like desktops, servers, switches and routers. After assembling a network, the setup can be simulated and saved as a .I project.fls (zip archive) file. .SH OPTIONS The program offers the following options: .TP .B \-h, \-\-help Show a summary of options. .TP .B \-l Activate logging to the log file. .TP .B \-n Activate the native look and feel graphical user interface. .TP .B \-r Multiplication factor to adapt the round-trip-time of the simulated network: 1 (default) up to 5. .TP .B \-s Adapt UI for small screen resolutions (less than 1024x768). .TP .B \-v Activate verbose logging. .TP .B \-w, \-\-wd Working directory of the program. Defaults to .IR ~/.filius/ . .br .SH AUTHOR This manual page was written by Andreas B. Mundt for the Debian system (but may be used by others). filius-2.9.4+ds/src/deb/filius.desktop000066400000000000000000000007361506574241500176510ustar00rootroot00000000000000[Desktop Entry] Name=Filius Name[de]=Filius GenericName=Computer Network Simulator GenericName[de]=Simulator Rechnernetze Comment=Learn how Computer Networks are built Comment[de]=Lernsoftware zu Rechnernetzen Exec=/usr/share/filius/filius.sh %f Icon=filius Terminal=false Type=Application Categories=Education StartupNotify=false MimeType=application/filius-project Keywords=network;simulation;internet;school;education Keywords[de]=Netzwerk;Simulation;Internet;Schule;Bildung filius-2.9.4+ds/src/deb/filius32.png000066400000000000000000000124661506574241500171340ustar00rootroot00000000000000PNG  IHDRP8jsRGB pHYs  tIME 32IDATxݜyxU?{ss$$,DE ZڂRjPZLuQG[M;>2 Ԕ !-!!vϽg?$/ 뚠a7<4j!w1p_2dyB㫰t}<_Ab'4B}:*EyT=]ECBri@`܃0ui$  $#=Au\S"RSঀNsAQ$.;o09$lf682l{Aix %~LNd]LpvA{ɂvvh~ ^'k%Q9 dVC`n@, =pd $@|&}C )ujl=xఴ3t(TT9M$1Cs5 9 $XC+p7Us |#Fu\) TCp; Y;w˅jb5{` WA8=X5uq^~lNj:W(7O(Ge TC`oH.3aB(EUS4L}n, Rrnom,r`+7'הO}$7Qf1ŖZ Yq(APGCr!d@Aς0lme'ѐREOS!l!w<鉔BH. %xOP%)*8sj&8`BH)z)dv@Ph(Z}_ RolwZ) H{f;<@@TXq)9RrT[<$X x*pѳ%HL,߂_Q9*.}#@.$A/~ahyL GR=Gӫ뿈s4qis#@bX0c6 a^E{ H&RRkn.5J*mSԏ!ֽfRi $`t'4oCa%0PrT)j{G_IuyNx Z^* LLåiZ_?{?xSAO5@k'y_+ǣ; 8UB;}|_-R];/A{h69=v5H͂zSںh.hXnϢԴVO(T *;q#q1b h}JtD=aIC☋Q.=+tԾ5e/uBN* iW WJo&`lmNj IϫfWh68+R"о#ıN8ɓJCO3 }%N1K|x4k ![8tг!g&\+ k.a7:U5:bpt.񒾭 I/ηn~'sa&k@.XՓ>!U= Ʃ5h1簹— ]JYE?JgA.h#~`?(2L;n$@'sKm^:SGٻT ËuǰBuۤ$ %a?;6 "ȨSRz^FK@4C$(JCE&0n 8BD9u AKf5gU%yť8}$%/SD>t 0~'[Td/,y kTt^T Ip.o)ykn"\QHg|hYTNW4-ǠjPѓ;L[,ҟýy0RV5Y߅`W=4͇E3TE>'ʝAbx!|;P/3!D"Y>y?#Ї*d2(D 4{bST?*H+}ﯢ7vD̞YP0|LK~p2ԂVjՍ`w$!.0bOr%9|L:ir?7Phd$}~k УA+zKuM5M{ Pjx0-/9O ;z퇰 Hg"li2Π8(P9 I?턮62~ryTp8(prqAPZ$HJlU$>=Ը)N3P622@+FYȭ Y;g]/$n"7rL\|_ fCaZӵw@uaW;mio'=ZW¶0*u-`+VTn$ݚ?;ܹ^oz*bz?}Ӧ;܌g1]]q b>\XNoSƹg9q>TRf0Ö1 .Hjg| d֭$ϟ?²ƍk8TX… y ƄnҲ&O^o_Shxπ ";ؼ|菣㟡څq|>aVWۗ|HOO7Q&M*ڼyfO\ik׮}p֬Y4-\]]رcIJJN`$v~$p5 ]iVB3.Po= Np.X`&LJM%fei8m1cC 0o޼+V)iEEE:%%/֪BCkyC(6ϯgUQ43xj;ď@d p4eAZNʄ eYX @q &|n;{ ,Irrr83XEEeYX{7_v%TITZ|-(gU>1[󏰭DS s5윷j1'f8m#8PHm6eٲe]NYFFƀA൶ mcY+W6/Nj<l!aŋ7($w.㮆Sנ3nL$F*,4Mm۶|Æ a͙3߲s%%%):pX7M4Ybőo~<=kU2 j9l .pUfe˖Uƍ7Ҳ,,YҴut4c_4Hnn oݺ5eӦMrKز,l'ذhѢ^j>^P܂rLG{ ]tw5999!C [zugmme˖ׁJ;8pa{NNN ֑O~HOcQN[Rx1d>[d޳9g[UUsr<!m&{UWM'رc+G #Vul絵bmRttKL9Sw4 >5=w7uU7C # ^&ZGyNYKC:dzaçlJBI싩Vz\-" Jqњ;BIENDB`filius-2.9.4+ds/src/info/000077500000000000000000000000001506574241500151565ustar00rootroot00000000000000filius-2.9.4+ds/src/info/Einfuehrung_Filius.md000066400000000000000000001333261506574241500213020ustar00rootroot00000000000000# Skriptum zur Unterrichtsreihe „Netzwerke mit FILIUS“ > * © 2015-2023, Daniel Garmann ``````, > [Gymnasium Odenthal](http://www.gymnasium-odenthal.de), > Lizenz: [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) > * © 2023, Andreas B. Mundt ``````, Überarbeitung mit > kleineren Änderungen, Markdown ![Filius Starter](img/FiliusSplash.jpg)[^1] [^1]: Alle Bilder wurden dem Programm FILIUS entnommen. Die Rechte liegen bei den Entwicklern. **Inhaltsverzeichnis** - [Skriptum zur Unterrichtsreihe „Netzwerke mit FILIUS“](#skriptum-zur-unterrichtsreihe-netzwerke-mit-filius) - [Vorbemerkungen](#vorbemerkungen) - [Aufbau von FILIUS](#aufbau-von-filius) - [Der Entwurfsmodus](#der-entwurfsmodus) - [Netzwerkkomponenten](#netzwerkkomponenten) - [Rechner und Notebook](#rechner-und-notebook) - [Kabel](#kabel) - [Switch](#switch) - [Router](#router) - [Modem](#modem) - [Der Simulationsmodus](#der-simulationsmodus) - [Netzwerkanwendungen](#netzwerkanwendungen) - [Systemanwendungen](#systemanwendungen) - [Client-Anwendungen](#client-anwendungen) - [Server-Anwendungen](#server-anwendungen) - [Der Dokumentationsmodus](#der-dokumentationsmodus) - [Dokumentationsmöglichkeiten](#dokumentationsmöglichkeiten) - [Aufgaben: Aufbau und Simulation von Netzwerken](#aufgaben-aufbau-und-simulation-von-netzwerken) - [Direkte Vernetzung](#direkte-vernetzung) - [Vernetzung über einen Switch](#vernetzung-über-einen-switch) - [Verbinden von Netzwerken über einen Router](#verbinden-von-netzwerken-über-einen-router) - [Simulation des World Wide Web](#simulation-des-world-wide-web) - [eMailverkehr im Netzwerk](#emailverkehr-im-netzwerk) - [Ausblick](#ausblick) - [Virtuelle und reale Netzwerke](#virtuelle-und-reale-netzwerke) - [_Peer-To-Peer_ Dateiaustausch](#_peer-to-peer_-dateiaustausch) - [DHCP-Server einrichten](#dhcp-server-einrichten) - [Eigene Software erstellen und einbinden](#eigene-software-erstellen-und-einbinden) - [Routing über mehrere Netze (statisches Routing)](#routing-über-mehrere-netze-statisches-routing) - [Anhang A: IPv4-Adressen](#anhang-a-ipv4-adressen) - [Fußnoten](#footnotes) ## Vorbemerkungen FILIUS[^2] wurde initial an der Universität Siegen entwickelt, um Unterricht zu Netzwerken und dem Internet zu unterstützen. Zielgruppe sind Lernende in der Sekundarstufe allgemein bildender Schulen. Mit FILIUS werden Lernaktivitäten ermöglicht, die insbesondere entdeckendes Lernen unterstützen sollen. Die Entwickler von FILIUS geben an: [^2]: FILIUS bedeutet Freie Interaktive Lernumgebung für Internetworking der Universität Siegen. > FILIUS ist eine explorative Lernsoftware zum Thema > Internetworking. Sie dient dem Erlernen von Grundwissen zum Internets > und steht jedem frei zur Verfügung, der sich mit dem Thema Internet > näher befassen möchte, ist aber insbesondere für den Einsatz in > Schulen gedacht. Die Absicht der Entwickler dieser Software ist es, > möglichst viele Nutzer zu gewinnen. Die Etablierung nicht nur an > Schulen in Nordrhein-Westfalen, sondern in ganz Deutschland wäre der > denkbar größte Erfolg und Lohn für die Mühen, die das Entwickler-Team > auf sich genommen hat. Kurz gesagt: Jeder ist herzlich eingeladen die > Software herunter zu laden.[^3] [^3]: http://fameibl.de/content/projects/filius/download/Dokumentation2.pdf, Seite 2 Mit diesem Skript soll Ihnen als Lehrende/r eine Möglichkeit aufgezeigt werden, wie Sie das Programm im Unterricht einsetzen können. Die Erläuterungen werden stets durch Aufgaben ergänzt, deren jeweilige Musterlösung als Download verfügbar ist. ## Aufbau von FILIUS Nach dem ersten Start des Programms erhalten Sie die Möglichkeit, die Sprache einzustellen, welche dann dauerhaft[^4] gespeichert bleibt. Ab nun zeigt sich das Programm nach dem Start mit folgender Oberfläche: [^4]: Diese Einstellung wird im Benutzerverzeichnis im Ordner ```.filius``` gespeichert, der zum Zurücksetzen gelöscht werden kann. ![Filius Arbeitsfläche](img/workbench.png){width=100%} Das Programm unterscheidet zwischen drei verschiedenen Arbeitsmodi, dem Entwurfsmodus, dem Simulationsmodus und dem Dokumentationsmodus: ![Entwurf](img/hammer.png "Symbol Entwurfmodus") In den Entwurfsmodus wechseln Sie, indem Sie auf den Hammer ![Hammer](img/hammer.png "Symbol Entwurfmodus"){width=20px} in der oberen Symbolleiste klicken. Dieser Modus ermöglicht Ihnen den Aufbau oder die Veränderung eines Netzwerkes. FILIUS befindet sich nach dem Programmstart stets in diesem Modus. ![Simulation](img/greenarrow.png "Symbol Simulationsmodus") In den Simulationsmodus gelangen Sie, indem Sie auf den grünen Pfeil ![grüner Pfeil](img/greenarrow.png "Symbol Simulationsmodus"){width=20px} klicken. Dieser Modus erlaubt es Ihnen, das aufgebaute Netzwerk zu simulieren, unterschiedliche Netzwerkanwendungen auf den Rechnern zu installieren und auszuführen. ![Dokumentation](img/pencil.png "Symbol Dokumentationsmodus") Den Dokumentationsmodus erreichen Sie, indem Sie auf den Bleistift ![Bleistift](img/pencil.png "Symbol Dokumentationsmodus"){width=20px} klicken. Dieser Modus ermöglicht es, das Netzwerk durch Annotationen zu ergänzen und symbolische Gruppierungen im Netzwerk vorzunehmen. Bevor Sie nun konkrete Netzwerke aufbauen, werden hier zunächst allgemein die Bedienungsmodi und wesentlichen Komponenten von FILIUS und den simulierten Netzwerken vorgestellt. ## Der Entwurfsmodus In diesem Kapitel wird die Funktionsweise der in FILIUS verwendeten Komponenten allgemein erläutert. Im Anschluss daran werden Sie diese Komponenten nutzen, um schrittweise komplexer werdende Netzwerke aufzubauen und zu simulieren. ### Netzwerkkomponenten FILIUS bietet auf der linken Symbolleiste einen Katalog von Komponenten an, welche im Arbeitsbereich beliebig positioniert, vernetzt, verändert und auch wieder entfernt werden können. Alle Komponenten werden mit der linken Maustaste per Drag & Drop in den weißen Arbeitsbereich gezogen, Kabel werden mit jeweils einem Klick auf die zu vernetzenden Komponenten gelegt. Die Kabelverbindungen aktualisieren sich selbständig, wenn eine Komponente neu positioniert wird. Mithilfe der rechten Maustaste können Sowohl Kabel als auch Komponenten wieder entfernt werden. Im Folgenden soll nun zunächst die Funktion der einzelnen Komponenten allgemein erläutert werden. Eine Konkretisierung erfolgt dann im Kapitel zum Aufbau und Test von Netzwerken. #### Rechner und Notebook FILIUS bietet zwei verschiedene Arten von Computern an, die im virtuellen Netzwerk verbunden werden können. Beide Computerarten sind bezüglich ihrer Funktionalität identisch, allerdings bietet es sich an, eine logische Unterscheidung vorzunehmen. ![Notebook](img/notebook.png) Wählen Sie für Computer, welche die Funktion eines Clients übernehmen, stets ein Notebook ![Notebook](img/notebook.png){width=20}, und für Computer, die die Funktion eines Servers ausüben, stets den Rechner ![Rechner](img/server.png){width=20}. ![Rechner](img/server.png) Beide Computerarten werden mit der linken Maustaste auf den Arbeitsbereich gezogen. Die Netzwerk-Konfiguration eines Computers erhalten Sie mit Doppelklick auf das Computersymbol. In dem folgenden Fenster können Sie nun Einstellungen bezüglich des Rechnernamens, seiner IP-Adresse und seiner Subnetzmaske vornehmen. Die Standardkonfiguration eines Rechners sieht dabei wie folgt aus: ![Netzwerkkonfiguration](img/ifconfig.png) Wenn Ihnen bzw. Ihren Schülern der Aufbau einer IP-Adresse nicht geläufig ist, so lesen Sie bitte zunächst das Kapitel A zu IP-Adressen im Anhang. #### Kabel Computer können mithilfe von Kabeln ![Kabel](img/cable.png){width=20} verbunden werden. Nachdem Sie das Kabel gewählt haben, können Sie nun nacheinander zwei andere Komponenten auf dem Arbeitsbereich anklicken, welche daraufhin mit einem Kabel verbunden werden. #### Switch Sobald mehr als zwei Computer miteinander verbunden werden sollen, benötigen Sie einen zentralen Knotenpunkt. Die einfachste Art des Knotenpunkts bietet Ihnen FILIUS mit dem Switch ![Switch](img/switch.png){width=20}. Auch für dieses Komponente können Sie die Einstellungen mithilfe eines Doppelklicks anzeigen und ändern. Ein Switch "merkt" sich nach der ersten Netzwerkanfrage die an ihm angeschlossenen Computer und leitet Netzwerksignale entsprechend weiter. Ein Switch verbindet üblicherweise allerdings nur Computer eines einzelnen Netzwerksegments. #### Router Die Funktionalität des Routers (auch: Vermittlungsrechners) besteht darin, anhand einer Weiterleitungstabelle IP-Pakete über Netzwerkgrenzen hinweg zu vermitteln, d.h. weiterzuleiten. Möchten Sie IP-Pakete aus einem Netzwerk in ein anderes Netzwerk versenden, so benötigen Sie zur Verbindung der Netzwerke einen Router ![Router](img/router.png){width=20}. Damit dieser seine Arbeit richtig durchführen kann, müssen alle an ihn angeschlossenen Komponenten in unterschiedlichen Netzwerksegmenten liegen. Dies gilt auch für einzelne Rechner, die evtl. an ihn angeschlossen werden. Zu Anfang fragt FILIUS, wie viele Schnittstellen der Router bereitstellen soll. Die hier eingestellte Anzahl kann später in den Einstellungen des Vermittlungsrechners (Doppelklick auf den Router) unter der Registerkarte ```"Allgemein"``` korrigiert werden. ![](img/routerconf.png) In den Einstellungen finden Sie auch zu jeder Schnittstelle eine Registerkarte für die Einstellung der Netzwerkkarte des daran befindlichen Netzwerks. Tragen Sie hier die IP-Adresse ein, an die Netzwerksignale gesendet werden sollen (in der Regel die erste oder letzte verfügbare Adresse des Netzwerks). Ebenso muss dann an allen im Netzwerk befindlichen Computern ein Gateway eingetragen werden. Die genauen Einstellungen werden später bei einer entsprechenden Aufgabenstellung konkretisiert. #### Modem FILIUS bietet Ihnen die Möglichkeit, über ein real existierendes Netzwerk mehrere FILIUS-Programme miteinander zu vernetzen und damit Netzwerksignale im real existierenden Netzwerk zu versenden[^6]. Dafür müssen alle beteiligten FILIUS-Instanzen ein Modem ![Modem](img/modem.png){width=20} im jeweils eigenen virtuellen Netzwerk einbinden. In einem der beteiligten virtuellen Netzwerke muss in den Einstellungen das Modem als Empfänger eingestellt werden, indem das Häkchen vor ```"Auf eingehende Verbindungsanfrage warten"``` gesetzt wird und über den Button ```"Aktivieren"``` die Annahme der Verbindungen bestätigt wird. [^6]: Voraussetzung zur Nutzung dieser Funktionalität ist, dass entsprechende Verbindungen nicht durch lokale Firewalls unterbunden werden. ![](img/modemconf.png) Alle Modems der anderen beteiligten virtuellen Netzwerke können nun über die real existierende Netzwerk IP-Adresse des Empfänger-Modems eine reale Netzwerkverbindung aufbauen. Alle verbunden Modems zeigen den erfolgreichen Verbindungsaufbau durch einen kleinen grünen Punkt an. ## Der Simulationsmodus FILIUS erlaubt es Ihnen im Simulationsmodus, Software auf den einzelnen Computern zu installieren, zu deinstallieren und sie auszuführen. Bitte wechseln Sie dafür in diesen Modus, indem Sie auf den grünen Pfeil ![](img/greenarrow.png){width=20} klicken. Die Software ist vorwiegend dazu gedacht, Netzwerkaktivitäten auf unterschiedlichen Ebenen des Schichtenmodells sichtbar zu machen. Die Veranschaulichung der Netzwerkaktivität kann mithilfe des Reglers ![](img/simulationspeed.png){width=80} in der Geschwindigkeit variiert werden. Dazu gehört zum einen die Darstellung der Netzwerksignale an den Leitungen als auch die Darstellung der Signale im Schichtenmodell, welches man für jeden Rechner mithilfe der rechten Maustaste aufrufen kann. ![](img/packettrace.png) Doch bevor Sie eine Netzwerkaktivität beobachten können, ist es notwendig, Software zu installieren. Mithilfe eines Doppelklicks auf einen Computer oder über dessen Kontextmenü gelangen Sie auf den Desktop des jeweiligen Computers: ![](img/desktop.jpg) Über das Symbol zur Software-Installation ![](img/install.png){width=20} können Sie nun beliebige Software installieren ![](img/leftarrow.png){width=20} oder deinstallieren ![](img/rightarrow.png){width=20}. Im Folgenden sollen auch hier zunächst die Funktion der einzelnen Netzwerkanwendungen allgemein erläutert werden. Eine Konkretisierung erfolgt dann im Kapitel zum Aufbau und der Simulation von Netzwerken. ### Netzwerkanwendungen FILIUS unterscheiden drei Arten von Anwendungen: Client-Anwendungen, Server-Anwendungen und System-Anwendungen. Diese werden nun den jeweiligen Gruppen entsprechend kurz vorgestellt. #### Systemanwendungen ![Datei-Explorer](img/filebrowser.png) Diese Anwendung ermöglicht es Ihnen, real existierende Dateien von Ihrer Festplatte auf den virtuellen Computer zu übertragen. ![Befehlszeile](img/terminal.png) Über diese Anwendung haben Sie die Möglichkeit, Systembefehle über eine Kommandozeilen abzusetzen. Eine Liste der verfügbaren Befehle wird Ihnen nach dem Start des Terminals angezeigt. ![Text-Editor](img/editor.png) Mit Ihm können Sie einfache Textdateien z.B. ```HTML```-Dateien erstellen und bearbeiten. Natürlich können Sie auch zuvor mit dem Datei-Explorer importierte Dateien verändern. ![Firewall](img/firewall.png) Hier können Sie Ihre Firewall einrichten, indem Sie bestimmte Ports netzwerkintern oder netzwerkübergreifend sperren oder öffnen. ![Bildbetrachter](img/viewer.png) Dieses Programm erlaubt es Ihnen, ein Bild zu öffnen und zu betrachten. #### Client-Anwendungen ![E-Mail-Client](img/email.png) Diese Anwendung ermöglicht es Ihnen, Emails virtuell zu versenden und zu empfangen. ![Webbrowser](img/webbrowser.png) Mit ihm können Sie Webseiten betrachten. Der Browser kann mit den wichtigsten html-Befehlen umgehen. ![Netzwerk-Client](img/networkclient.png) Der Client dient dazu, mit einem Server in Kontakt zu treten. Diese Software ermöglicht einen einfachen Test, ob das Netzwerk funktioniert. ![Gnutella](img/gnutella.png) Mithilfe dieser ```Peer-To-Peer``-Anwendung können Sie Dateien in einem virtuellen Netzwerk teilen. #### Server-Anwendungen ![DNS-Server](img/DNSserver.png) Mit ihm können Sie die Auflösung symbolischen Rechnernamen in zugehörige IP-Adressen simulieren. ![eMail-Server](img/mailserver.png) Diese Anwendung erlaubt es Ihnen, mehrere eMail-Konten einzurichten, welche dann mit den eMail-Programmen genutzt werden können ![Webserver](img/webserver.png) Hiermit machen Sie einen Computer zu einem Webserver, sodass andere Rechner mithilfe des Webbrowsers auf die Seiteninhalte dieses Computers zugreifen können. Eine voreingestellte ```index.html``` finden Sie im virtuellen Verzeichnis ```root/webserver```. ![Echo-Server](img/echoserver.png) Einmal gestartet spiegelt der ```Echo-Server``` alle eingehenden Nachrichten an den jeweiligen Client zurück. ## Der Dokumentationsmodus FILIUS ermöglicht es, Netzwerkstrukturen zu dokumentieren und symbolisch zu gruppieren. Bitte wechseln Sie dafür in diesen Modus, indem Sie auf den Bleistift klicken ![](img/pencil.png){width=20}. ### Dokumentationsmöglichkeiten FILIUS bietet in diesem Modus zwei verschiedene Möglichkeiten, das virtuelle Netzwerk zu dokumentieren, welche hier kurz vorgestellt werden sollen. Im weiteren Verlauf dieses Skripts wird nicht weiter auf Dokumentationsmöglichkeiten eingegangen, dem Leser steht es aber selbstverständlich frei, eigene virtuelle Netzwerke sinnvoll zu dokumentieren. ![Textfeld erzeugen](img/textfield.png) Mithilfe von Textfeldern können ergänzende Informationen in das virtuelle Netzwerk eingefügt werden. Die Textfelder haben keinen Einfluss auf die Eigenschaften des Netzwerks im Entwurfsmodus oder auf die Netzwerkaktivitäten im Simulationsmodus. ![Gliederungsfläche erzeugen](img/groupfield.png) Das Gleiche gilt für die Gliederungsflächen. Sie dienen dazu, einzelne Teile des virtuellen Netzwerks symbolisch zu gruppieren. Die Flächen können an den Rändern auf die passende Größe gezogen werden und werden nach Verlassen des Dokumentationsmodus hinter die Komponenten gelegt. ![Exportieren](img/export.png) Mit der Schaltfläche Exportieren wird das virtuelle Netzwerk als Portable Network Graphic (PNG-Datei) gespeichert. ## Aufgaben: Aufbau und Simulation von Netzwerken Im Folgenden sollen nun Netzwerke mit wachsender Komplexität aufgebaut und getestet werden. Eine Verzahnung von Entwurfs- und Simulationsmodus ist dabei unumgänglich. Anhand des Symbols bei der Aufgabenstellung erkennen Sie, ob die jeweilige Aufgabe im Entwurfs- ![](img/hammer.png){width=20} oder im Simulationsmodus ![](img/greenarrow.png){width=20} durchzuführen ist. ### Direkte Vernetzung Die einfachste Art Daten zwischen zwei Rechnern auszutauschen besteht darin, die Rechner direkt mit einem Netzwerkkabel zu verbinden. Man spricht hier von einer Point-To-Point-Verbindung. In der Praxis können so zwei Rechner direkt mit einem sogenannten _crossover_-Kabel verbunden werden. > #### Aufgabe 1 > ![](img/hammer.png){width=20} Erstellen Sie ein Netzwerk mit zwei > verbundenen Computern. > > ![](img/crossover.png) > > Die Computer sollen die abgebildeten Namen sowie die IP-Adressen > ```192.168.0.10/24``` und ```192.168.0.11/24``` besitzen. Durch die > Subnetzmaske ```/24``` bzw. ```255.255.255.0``` stellen Sie sicher, > dass beide Computer im selben Netzwerk liegen. > > > Hinweis: Wenn Sie die Namen nicht manuell eingeben wollen, so > > können Sie durch ein Häkchen vor ```"IP-Adresse als Name > > verwenden"``` den Namen automatisch setzen lassen. > > #### Aufgabe 2a > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Rechner > ```0.10``` eine Befehlszeile. Starten Sie die Befehlszeile und testen > Sie die Verbindung zum Rechner ```0.11``` mit dem Befehl ```ping > 192.168.0.11```. Beobachten Sie die Netzwerkaktivität, indem Sie sich > den Datenaustausch von Rechner ```0.10``` anzeigen lassen. > > ![](img/ping.png) > > An dem Fenster erkennt man, dass der Computer viermal ein ICMP-Paket > an den anderen Computer schickt (```ping```). Der Computer wartet > nun jedes Mal ab, ob eine Antwort (```pong```) von dem anderen > Computer zurück kommt. Diesen Datenaustausch kann man auch in dem > Datenaustausch-Fenster erkennen, wobei die ersten beiden Zeilen des > Datenaustauschs zum Adress Resolution Protocol gehören, welches die > physikalische Adresse des Rechners ermittelt. Die nächsten acht > Zeilen stehen jeweils paarweise für einen ```ping```-```pong``` > Austausch und gehören zum Internet Control Message Protocol (ICMP), > welches Bestandteil des Internetprotokolls IPv4 ist: > > ![](img/pingtrace.png) > > Man erkennt auch, dass die Netzwerkaktivität maximal auf der > Vermittlungsschicht abläuft. Höher liegende Schichten werden für > diesen Verbindungstest noch nicht benötigt. Durch Klick auf eine > einzelne Zeile des Datenaustauschs werden Informationen der darunter > liegenden Schichten sowie konkrete Informationen zu den einzelnen > verwendeten Schichten angezeigt. > > #### Aufgabe 2b > ![](img/greenarrow.png){width=20} Testen Sie auch andere Befehle auf der > Befehlszeile, wie z.B. die Befehle ```ipconfig```, oder ```host > localhost``` oder ```dir```. Der Sinn des ```host```-Befehls wird zu > einem späteren Zeitpunkt im Zusammenhang mit einem DNS-Server > evtl. deutlicher. ### Vernetzung über einen Switch Sobald Sie in einem Netzwerk mehr als zwei Rechner verbinden wollen, benötigen Sie einen Switch, an den Sie beliebig viele Rechner anschließen können. Der Switch merkt sich im Simulationsmodus alle MAC-Adressen der an ihn angeschlossenen Netzwerkschnittstellen, soass er nach der ersten Netzwerkaktivität stets die Datenpakete an den zugehörigen Port (Anschlussbuchse) weiterleiten kann. Dieses Komponente nutzen wir nun, um drei Rechner miteinander zu verbinden. > #### Aufgabe 3 > ![](img/hammer.png){width=20} Erweitern Sie nun das Netzwerk um einen > dritten Computer, einen Server, mit dem abgebildeten Namen und der > IP-Adresse ```192.168.0.12/24```, welcher damit im gleichen Netzwerk > liegt. Achten Sie darauf, dass Sie von nun an für Computer mit der > Funktion eines Servers das Rechner-Symbol > ![](img/server.png){width=20} wählen. Verbinden Sie alle Computer > mit einem Switch wie abgebildet: > > ![](img/switch1.png) > > #### Aufgabe 4 > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Server > ```0.12``` einen ```Echo-Server``` und starten diesen auf dem > voreingestellten Port ```55555```. Installieren Sie auf einem Client > einen ```Einfachen Client``` und verbinden Sie diesen mit dem > Server. Senden Sie vom Client einige Textnachrichten und beobachten > Sie den Effekt. Schauen Sie sich auch die Netzwerkaktivität im > Datenaustausch-Fenster des Clients an. > > ![](img/chat1.png){width=49%} > ![](img/chat2.png){width=49%} > > ![](img/chat3.png){width=62%} > ![](img/chat4.png){width=37%} > > Sie können am Datenaustausch beobachten, dass bereits bei der > Anmeldung die Transportschicht eine Rolle in der Netzwerkaktivität > spielt. Die Verbindung des Clients mit dem Server durchläuft also > schon drei Schichten. Die ersten beiden Zeilen des Datenaustauschs > gehören wieder zur Ermittlung der Zuordnung MAC-IP-Adresse (_Address > Resolution Protocol, ARP_). > > Sobald nun eine Nachricht geschickt wird, tritt die vierte > Schicht, die Anwendungsschicht, in Aktion. Die Anwendung, also der > ```Einfache Client```, benötigt zur Kommunikation zunächst die > Anwendungsschicht, dann die Transportschicht, dann die > Vermittlungsschicht und schließlich die Netzzugangsschicht. Dies > erkennen Sie, wenn Sie auf die erste dunkelblau hinterlegte Zeile > klicken und Ihnen das rechts abgebildete Schichtenmodell angezeigt > wird. Bei zukünftigen Aufgaben im Simulationsmodus sollten Sie immer Mal wieder den Datenaustausch im Datenaustausch-Fenster anschauen, um zu verstehen, welche Informationen die jeweilige Anwendung tatsächlich über das Netz schickt. Sie werden z.B. bei der Versendung von eMails erkennen, welche Datenflut über das Netzwerk verschickt werden muss. ### Verbinden von Netzwerken über einen Router Doch bevor Sie neue Anwendungen installieren und testen sollen, erweitern Sie Ihr bestehendes Netzwerk mit drei Rechnern um ein weiteres Netzwerk mit drei zusätzlichen Rechnern. > #### Aufgabe 5 > ![](img/hammer.png){width=20} Erstellen Sie neben dem bereits vorhandenen > Netzwerk ein weiteres Netzwerk mit drei Rechnern wie abgebildet. Die > neuen Rechner sollen sich in einem logisch anderen Netzwerk > befinden. Wählen Sie dafür die IP-Adressen ```192.168.1.10/24``` bis > ```192.168.1.12/24```. Verbinden Sie anschließend die beiden Netzwerke > mit einem Vermittlungsrechner, welcher die Netzwerkkarten mit den > Adressen ```192.168.0.1/24``` und ```192.168.1.1/24``` besitzt. > > ![](img/router2LANs.png) > > ![](img/greenarrow.png){width=20} Prüfen Sie anschließend in der > Befehlszeile mit einem ```ping```-Befehl die Verbindung von Rechner > ```0.10``` zum Rechner ```1.10```. > > Wenn Sie alles so gemacht haben, wie oben beschrieben, so zeigt Ihnen > FILIUS die folgende Fehlermeldung an: > > ![](img/nogateway.png) Das Problem liegt darin, dass die Verbindungsanfrage das Netz verlassen müsste. Allerdings ist bei den einzelnen Rechnern noch kein Gateway eingestellt, welches bestimmt, wohin Pakete geschickt werden, die nicht an Adressen im eigenen Netzwerk adressiert sind. > #### Aufgabe 6 > ![](img/hammer.png){width=20} Der Vermittlungsrechner hat eine > Netzwerkkarte ```192.168.0.1```. Diese Adresse wählen Sie als Gateway > der drei linken Computer. Entsprechend stellen Sie das Gateway > ```192.168.1.1``` für die drei rechten Computer ein. > > ![](img/gateway.png) > > ![](img/greenarrow.png){width=20} Testen Sie anschließend die gleiche > Verbindungsanfrage, welche nun ohne Fehler funktionieren sollte. Sie erkennen am Protokoll (Datenaustausch-Fenster), dass die erste Verbindungsanfrage wesentlich länger dauert, als die folgenden drei Anfragen. Dies liegt daran, dass die Weiterleitungstabellen der beiden Switche zu Anfang noch leer sind und bei der ersten Anfrage befüllt werden. > #### Aufgabe 7 > ![](img/greenarrow.png){width=20} Testen Sie die Netzwerkverbindung > auch mit dem ```Einfachen Client``` und > ```Echo-Server```. Installieren Sie dazu auf dem Rechner ```1.10``` > einen ```Einfachen Client``` und verbinden Sie ihn mit dem Server > ```0.12```. ### Simulation des World Wide Web Eine der wichtigsten Aufgaben des heutigen Internets ist zweifelsohne das World Wide Web. Mithilfe von FILIUS können Sie die grundlegenden Abläufe bei der Kommunikation zwischen einem Webbrowser und einem irgendwo anders befindlichen Webserver simulieren und analysieren. Unser bisher aufgebautes Netz ist für diesen Zweck bereits ausreichend, der Server ```0.12``` soll dabei die Funktion des Webservers übernehmen, der Rechner ```1.10``` übernimmt die Funktion des Webbrowsers. Doch zunächst kümmern wir uns um den Webserver. > #### Aufgabe 8 > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Server > ```0.12``` einen Webserver und einen Texteditor. Starten Sie den > Texteditor und öffnen Sie hiermit die Datei ```index.html``` aus dem > virtuellen Verzeichnis ```root/webserver```. Passen Sie den > ```html```-Code so an, dass eine Seite mit Ihren Informationen > angezeigt wird. Erstellen Sie auch eine neue Seite ```kontakt.html```, > welche von der Startseite verlinkt werden soll. > > ![](img/webdoc1.png) > > #### Aufgabe 9 > ![](img/greenarrow.png){width=20} Starten Sie die Anwendung "Webserver" > mit einem Doppelklick. Starten Sie dann den virtuellen Webserver über > den Button ```"Starten"``` (linkes Bild). > > Installieren Sie dann auf dem Rechner ```1.10``` einen Webbrowser. > Starten Sie den Browser und bauen Sie eine Verbindung zum > Webserver auf, indem Sie die URL ```http://192.168.0.12``` in die > Adressleiste des Webbrowsers eingeben (rechtes Bild). > > ![](img/webdoc2.png){width=49%} > ![](img/webdoc3.png){width=49%} Allerdings entspricht diese Art der Kommunikation in dem Browser noch nicht der gewohnten Art, Webseiten anderer Server aufzurufen. Sie sind es gewohnt, eine Webseite über eine URL aufzurufen, die nicht die IP-Adresse des Webservers enthält. Die Zuordnung zwischen Rechnername und zugehöriger IP-Adresse übernimmt ein _Domain Name System Server_, kurz DNS-Server, der nun von Ihnen eingerichtet werden soll. > #### Aufgabe 10 > ![](img/hammer.png){width=20} Erstellen Sie einen neuen Server mit > der IP-Adresse ```192.168.2.10/24``` und dem Gateway > ```192.168.2.1```. Ändern Sie die Anzahl der Schnittstellen am > Vermittlungsrechner auf drei ab, indem Sie auf der Registerkarte > ```"Allgemein"``` des Vermittlungsrechners den Button > ```"Verbindungen verwalten"``` anklicken. Ergänzen Sie die auf der > neuen Registerkarte zur dritten Netzwerkkarte die Einstellungen: > IP-Adresse ```192.168.2.1/24``` und damit Netzmaske > ```255.255.255.0```. Verbinden Sie anschließend den neuen Server mit > dem Vermittlungsrechner. > > ![](img/DNSrouter2LANs.png) > > Damit nun alle Rechner die Auflösung der IP-Adresse über den > DNS-Server nutzen können, muss in den Einstellungen eines jeden > Rechners die IP-Adresse des DNS-Servers eingetragen werden. > > #### Aufgabe 11 > ![](img/hammer.png){width=20} Tragen Sie bei jedem Rechner in den Einstellungen die > DNS-Server Adresse ```192.168.2.10``` ein. Dies entspricht dem gerade > erstellten Domain Name Server. > > ![](img/DNSconf.png) > > Nun müssen Sie dem Webserver noch eine passende URL geben und im DNS-Server eintragen. > #### Aufgabe 12a > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Server > ```2.10``` die Anwendung ```DNS-Server``` und starten Sie diese > Anwendung direkt mit einem Doppelklick. > Tragen Sie in die Eingabefelder den Rechnernamen (Domainnamen) > ```www.filius.de``` und die zugehörige IP-Adresse ```192.168.0.12``` > ein und bestätigen Sie die Eingaben mit dem Button > ```"Hinzufügen"```. Starten Sie abschließend den DNS-Server mit dem > Button ```"Starten"``` (linkes Bild). > Testen Sie die Verbindung von Ihrem Webbrowser nun mit der URL > ```http://www.filius.de``` (rechtes Bild). > > ![](img/DNS1.png){width=49%} > ![](img/DNS2.png){width=49%} > > Wenn Ihnen der Browser "Server konnte nicht erreicht werden!" anzeigt, so liegt es in > der Regel daran, dass Sie entweder den DNS-Server nicht gestartet haben oder > dass Sie eine URL aufgerufen haben, deren in der URL enthaltener > Rechnernamen noch nicht in der Tabelle des DNS-Servers eingetragen wurde. > > #### Aufgabe 12b > ![](img/greenarrow.png){width=20} Zu Anfang haben Sie die Befehlszeile und einige > Befehle kennen gelernt. Testen Sie nun noch einmal den > ```host```-Befehl, nun allerdings mit dem Rechnernamen ```www.filius.de```. Sie > werden sehen, dass der DNS-Server nun seine Dienste tut und die > richtige IP-Adresse ermittelt: > > ![](img/DNScli.png) ### eMailverkehr im Netzwerk FILIUS erlaubt es Ihnen, den eMailverkehr zwischen verschiedenen eMail-Servern zu simulieren. Zunächst werden Sie einen einzelnen eMail-Server einrichten, der von einem eMail-Programm verwendet wird. Später werden Sie mehrere miteinander vernetzte eMail-Server einrichten. > #### Aufgabe 13 > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Server ```0.12```, > auf dem zuvor Ihr Web-Server lief, nun die Anwendung "eMail-Server" > und starten Sie diese Anwendung direkt mit einem Doppelklick. > Richten Sie ein neues Konto mit dem Benutzernamen ```bob``` und dem > Passwort ```bob``` ein (linkes Bild). Kontrollieren Sie Ihre > Eingaben in der Konten-Liste (rechtes Bild). > Starten Sie anschließend den eMail-Server über den Button Starten. > > ![](img/email1.png){width=49%} > ![](img/email2.png){width=49%} Als nächstes muss der DNS-Server so eingerichtet werden, dass er die Maildomain auflösen kann. > #### Aufgabe 14 > ![](img/greenarrow.png){width=20} Starten Sie die Anwendung "DNS-Server" > auf dem Server ```2.10```. Fügen Sie einen neuen Mailaustausch mit der > Maildomain ```filius.de``` und dem Domainnamen des Mailservers > ```www.filius.de``` ein. Starten Sie den Server erneut. > > ![](img/email3.png) Nun muss nur noch das eMail-Programm installiert und eingerichtet werden. > #### Aufgabe 15 > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Rechner > ```0.10```, auf dem Sie zu Anfang die Befehlszeile installiert hatten, > nun die Anwendung ```"E-mail Programm"``` und starten Sie diese. > Klicken Sie auf den Button ```"Konto einrichten"``` und tragen Sie die > folgenden Informationen Ihres eMail-Servers ein (linkes Bild): > Name: ```bob``` > eMail-Adresse: ```bob@filius.de``` > POP3-Server: ```www.filius.de``` > POP3-Port: ```110``` > SMTP-Server: ```www.filius.de``` > SMTP-Port: ```25``` > Benutzername: ```bob``` > Passwort: ```bob``` > Schreiben Sie anschließend an ```bob@filius.de``` (also an sich selbst) > eine eMail und rufen Sie diese auch wieder ab (rechtes Bild). > > ![](img/email4.png){width=49%} > ![](img/email5.png){width=49%} > > #### Aufgabe 16 > ![](img/greenarrow.png){width=20} Ergänzen Sie nun den eMail-Server um > eine weitere eMail-Adresse ```bert@filius.de```. Richten Sie ein > eMail-Programm auf dem Rechner ```0.11``` entsprechend ein, dass > ```bob``` und ```bert``` sich gegenseitig Nachrichten schicken können. Zuletzt werden Sie nun einen zweiten eMail-Server einrichten, welcher sich im Netz auf der rechten Seite (```192.168.1.0```) befinden soll. > #### Aufgabe 17 > ![](img/greenarrow.png){width=20} Ergänzen Sie das rechte Netz um einen > Server ```1.13```, richten Sie hierauf einen Mailserver mit der > Maildomain ```filia.com``` ein. Fügen Sie abschließend ein Konto > ```alice@filia.com``` hinzu. > > ![](img/servDNSrouter2LANs.png) > > Erweitern Sie die Mailaustausch-Tabelle des DNS-Servers, dass > auch diese neue Maildomain erkannt wird. > Richten Sie auf dem Rechner ```1.10``` ein eMail-Programm für > ```alice@filia.com``` ein. Testen Sie den eMail-Versand zwischen > ```bob@filius.de``` und ```alice@filia.com```. ## Ausblick Nachdem Sie nun die grundlegenden Funktionen von FILIUS kennen gelernt haben, soll nun noch ein kleiner Ausblick zeigen, welche Möglichkeiten FILIUS weiterhin bietet. Inwieweit diese Erweiterungen im Unterricht integriert werden können, ist von der Lerngruppe und den Systemvoraussetzungen abhängig. ### Virtuelle und reale Netzwerke Bereits bei den Komponenten wurde Ihnen das Modem als Möglichkeit vorgestellt, das virtuelle Netzwerk zu verlassen und Netzwerksignale über das reale Netzwerk zu versenden. Voraussetzung zur Nutzung dieser Funktionalität ist, dass entsprechende Verbindungen nicht durch lokale Firewalls unterbunden werden. Veranschaulichen Sie sich die Funktionsweise an folgendem kleinen Beispiel, in dem lediglich ein Rechner über das Modem das virtuelle Netzwerk verlassen kann. ![](img/modem1.png) > #### Aufgabe 18 > ![](img/hammer.png){width=20} Erstellen Sie die abgebildete > Netzwerkstruktur mit unterschiedlichen Rechner-IPs auf zwei > verschiedenen realen Computern (nennen wir sie der Einfachheit > halber Computer A und Computer B), welche sich in einem realen > Netzwerk befinden. Stellen Sie am Computer A das Modem so ein, dass > es die Verbindung annehmen soll (Häkchen vor ```"Auf eingehende > Verbindungsanfrage warten"``` und klicken Sie auf den Button > ```"Aktivieren"```. > Stellen Sie dann am Computer B das Modem so ein, dass es sich mit > Computer A verbindet. Tragen Sie dazu die reale IP des Computers > A in das Feld IP-Adresse ein und drücken Sie danach auf den Button > ```"Verbinden"```. Beide Modems leuchten bei erfolgreicher > Verbindungsaufnahme grün. > > ![](img/modem2.png) Ab jetzt können Sie auf beiden Computern A und B die virtuellen Rechner für den Netzwerkbetrieb einrichten. Z.B. könnten Sie einen ```Echo-Server``` auf dem virtuellen Rechner auf Computer A installieren, wogegen auf dem virtuellen Rechner auf Computer B ein ```Einfacher Client``` installiert wird. Nachdem Sie den ```Echo-Server``` gestartet haben, sollte sich der virtuelle Rechner auf Computer B mit dem ```Echo-Server``` verbinden können. > #### Aufgabe 19 > ![](img/greenarrow.png){width=20} Installieren Sie ```Echo-Server``` > und den ```Einfachen Client``` auf den virtuellen Rechnern der > beiden Computer A und B. Starten Sie den ```Echo-Server``` und > testen Sie den ```Einfachen Client```. Testen Sie auch andere > Anwendungen auf den virtuellen Rechnern. Schauen Sie sich auch den > Datenaustausch auf den Rechnern an. ### _Peer-To-Peer_ Dateiaustausch FILIUS bietet Ihnen die Möglichkeit, mehrere Rechner einem _Peer-To-Peer_-Netzwerk hinzuzufügen und über dieses Netzwerk Dateien mithilfe der Anwendung _Gnutella_ auszutauschen. ![](img/p2p.png) Alle Rechner in einem solchen Netzwerk haben sowohl Client- als auch Server-Funktionalität. Im folgenden Netzwerk wird jedoch für alle vernetzten Computer das Laptop-Symbol verwendet. > #### Aufgabe 20 > ![](img/hammer.png){width=20} Erstellen Sie die abgebildete > Netzwerkstruktur, indem Sie drei Laptops mithilfe eines Switchs als > _Peer-To-Peer_-Netzwerk verbinden. > > ![](img/greenarrow.png){width=20} Installieren Sie anschließend auf > allen drei Computern die Anwendung "Gnutella" und zusätzlich auf dem > Rechner 0.10 einen Datei-Explorer. > Starten Sie den Datei-Explorer und kopieren Sie mithilfe der rechten > Maustaste die Datei ```index.html``` aus dem Verzeichnis > ```webserver``` in das Verzeichnis ```peer2peer```. > > Starten Sie nun auf dem Rechner ```0.12``` die Anwendung > ```"Gnutella"``` und treten Sie dem Netz von Rechner ```0.10``` bei > (IP-Adresse ```192.168.0.10```). Die Liste der verbundenen Nachbarn > sollte sich selbstständig aktualisieren (linkes Bild). > Nun können Sie im Netz nach der Datei ```index.html``` suchen und > gefundene Datei herunterladen (rechtes Bild). > > ![](img/p2p1.png){width=49%} > ![](img/p2p2.png){width=49%} ### DHCP-Server einrichten In großen Netzwerken ist es günstig, die IP-Adress-Vergabe der Computer in diesem Netzwerk automatisch durchzuführen; ein DHCP-Server übernimmt diese Aufgabe. FILIUS besitzt ebenfalls die Möglichkeit, DHCP-Server einzurichten und deren Funktion zu simulieren. Im folgenden Ergänzen Sie nun das Netzwerk der letzten Aufgabe um einen DHCP-Server: ![](img/DHCP.png) > #### Aufgabe 21a > ![](img/hammer.png){width=20} Erweitern Sie das Netz um den > DHCP-Server und benennen Sie alle vernetzten Rechner um, so dass > deutlich wird, dass die IP-Vergabe ab jetzt automatisch erfolgt. > Geben Sie dem DHCP-Server die angegebene IP-Adresse > ```10.0.0.10/24``` und richten Sie dann über den Button den Server > als DHCP-Server ein. > > ![](img/DHCPconf1.png) > > Nun zeigt Ihnen FILIUS einen Dialog, in dem Sie die Einstellungen für > den DHCPServer vornehmen können. Übernehmen Sie den Adressbereich von > ```10.0.0.100``` bis ```10.0.0.200``` und aktivieren Sie DHCP mit > einem Häkchen vor ```"DHCP aktivieren"```. > > ![](img/DHCPconf2.png) > #### Aufgabe 21b > ![](img/hammer.png){width=20} Setzen Sie nun für alle Rechner in den > Einstellungen das Häkchen hinter ```"DHCP zur Konfiguration > verwenden"```. Sie erkennen, dass zuvor gewählten Einstellungen > schattiert dargestellt werden und nicht mehr änderbar sind. > > ![](img/greenarrow.png){width=20} Sobald Sie nun in den > Simulationsmodus wechseln, erhalten alle Clients vom DHCP-Server eine > IP-Adresse zugeteilt. Sie erkennen die Netzwerkaktivität wieder an den > grün blinkenden Leitungen. (Sollten die Leitungen nicht grün blinken, > so beenden Sie FILIUS und starten es neu. Der Switch hat sonst noch > die Einstellungen vom letzten Simulationsmodus.) > Starten Sie nun die Rechner und installieren Sie jeweils eine > Befehlszeile. Starten Sie diese und prüfen Sie mithilfe des > Kommandos ```ipconfig```, welche IP-Adresse dem Rechner vom > DHCP-Server zugewiesen wurde. ### Eigene Software erstellen und einbinden FILIUS bietet Ihnen die Möglichkeit, eigene Software, welche auf den virtuellen Rechner installiert und gestartet werden kann, zu erstellen und einzubinden. ![](img/software.png) Über das Symbol ![](img/software.png){width=20} gelangen Sie zu einem Assistenten, der Ihnen die Integration eigener Software, z.B. eines selbst erstellten Chat-Servers und Chat-Clients ermöglicht. FILIUS stellt Ihnen dafür ein Programmraster zur Verfügung, welches Sie dann Ihren Vorstellungen entsprechend anpassen können. ![](img/softwareassist.png) ### Routing über mehrere Netze (statisches Routing) Im Kapitel zur Verbindung zweier Netzwerke über einen Vermittlungsrechner haben wir uns darauf beschränkt, alle Netzwerke über einen einzelnen Vermittlungsrechner zu vernetzen. Die Realität sieht allerdings anders aus, denn üblicherweise sind viele Vermittlungsrechner in Form eines vermaschten Netzes miteinander verbunden, d.h. der Weg eines Datenpakets ist nicht unbedingt eindeutig bestimmt. Betrachten wir zunächst folgendes Beispiel: ![](img/staticrouting1.png) > #### Aufgabe 22a > ![](img/hammer.png){width=20} Erstellen Sie das oben abgebildete > Netzwerk. Richten Sie für den Router I so ein, dass er mit Router II > im Netzwerk ```2.0.0.0/24``` verbunden ist und mit Router III im > Netzwerk ```3.0.0.0/24``` verbunden ist. Geben Sie dem Router I > immer die erste Adresse im Netzwerk. Richten Sie dann den Router II > so ein, dass er mit Router III im Netzwerk ```1.0.0.0/24``` > verbunden ist. Geben Sie dem Router II immer die zweite Adresse im > Netzwerk. Geben Sie abschließend dem Router III immer die dritte > Adresse im Netzwerk. Insgesamt ergibt sich dann folgendes Bild: ![](img/staticrouting2.png) Nun fehlen noch die Einstellungen für die Weiterleitungen. Richten Sie zunächst die Gateways für die Rechner der einzelnen Netze ein: > #### Aufgabe 22b > ![](img/hammer.png){width=20} Richten Sie für den Rechner ```0.10``` > aus dem LAN A das Gateway ```192.168.0.1``` ein, d.h. der Router I > wird als Gateway verwendet. Richten Sie analog für den Rechner > ```1.10``` aus dem LAN B das Gateway ```192.168.1.1``` ein und für > den Rechner ```2.10``` aus dem LAN C das Gateway ```192.168.2.1```. FILIUS besitzt die Möglichkeit, die Weiterleitung im Netzwerk zwischen verschiedenen Routern selbstständig zu übernehmen. Dafür muss in den einzelnen Vermittlungsrechnern das Häkchen hinter ```"Automatisches Routing"``` gesetzt werden und danach übernehmen die Vermittlungsrechner das automatische Routing über das _Routing Information Protocol (RIP)_. Für ein besseres Verständnis, wie das Routing tatsächlich abläuft, besitzt FILIUS aber immer noch die Möglichkeit, die Weiterleitung im Netzwerk manuell einzurichten. Jeder Router besitzt seine eigene Weiterleitungstabelle, welche angibt, wie eingehende Pakete weitergeleitet werden. Folgende Tabellen ergeben sich für das oben dargestellte Netzwerk mit drei Routern: ##### Router I Ziel Netzmaske Next Hop Interface -------------- ------------------- ------------------- ------------------- 192.168.1.0 255.255.255.0 2.0.0.2 2.0.0.1 192.168.2.0 255.255.255.0 3.0.0.3 3.0.0.1 -------------- ------------------- ------------------- ------------------- ##### Router II Ziel Netzmaske Next Hop Interface -------------- ------------------- ------------------- ------------------- 192.168.0.0 255.255.255.0 2.0.0.1 2.0.0.2 192.168.2.0 255.255.255.0 1.0.0.3 3.0.0.2 -------------- ------------------- ------------------- ------------------- ##### Router III Ziel Netzmaske Next Hop Interface -------------- ------------------- ------------------- ------------------- 192.168.0.0 255.255.255.0 3.0.0.1 3.0.0.3 192.168.1.0 255.255.255.0 1.0.0.2 1.0.0.3 -------------- ------------------- ------------------- ------------------- > #### Aufgabe 22c > ![](img/hammer.png){width=20} Richten Sie die Weiterleitungstabellen > für die drei Router entsprechend der oben angegebenen Tabellen > ein. Entfernen Sie zur besseren Übersicht das Häkchen vor dem Punkt > ```"Alle Einträge anzeigen"```. > > ![](img/greenarrow.png){width=20} Installieren Sie auf dem Server > ```2.10``` einen ```Echo-Server``` und auf dem Rechner ```0.10``` einen > ```Einfachen Client```. Verbinden Sie den Client mit dem Server und > beobachten Sie die Netzwerkaktivität. > > ![](img/hammer.png){width=20} Ändern Sie die Weiterleitungstabellen so > ab, dass Nachrichten vom LAN A in das LAN B über den Router des LAN C > verschickt werden und beobachten Sie die Netzwerkaktivität erneut. # Anhang A: IPv4-Adressen IP-Adressen in der _Internet-Protocol Version 4 (IPv4)_ bestehen aus 32 Bit, aufgeteilt in 4 Blöcke mit je 8 Bit. Damit sind IP-Adressen von ```0.0.0.0``` bis ```255.255.255.255``` möglich. Zum besseren Verständnis des Routing-Prozesses ist eine Darstellung der Dezimalzahlen im Binärsystem hilfreich. > #### Aufgabe A1 > Wiederholen Sie die Umrechung zwischen dem Binär- und dem > Dezimalsystem. Wandeln anschließend in das jeweils andere > Stellenwertsystem um. > > a. ) ``` 1101 1110₂``` > a. ) ``` 0011 1111₂``` > a. ) ``` 1111 1101₂``` > a. ) ``` 0101 1010₂``` > a. ) ``` 96₁₀``` > a. ) ``` 254₁₀``` > a. ) ``` 17₁₀``` > a. ) ``` 127₁₀``` Eine Subnetzmaske ist in IPv4 ebenfalls eine 32-Bit-Zahl, welche eine IP-Adresse in Netzwerkteil und Geräteteil trennt. Durch _UND_-Verknüpfung der IP mit der Subnetzmaske erhält man den Netzwerkteil. Durch _UND_-Verknüpfung mit der invertierten Subnetzmaske erhält man den Geräteteil. Die kleinste Adresse des Netzwerks beschreibt das Netzwerk selbst, die größte Adresse ist für Broadcasts reserviert. > #### Beispiel: > IP-Adresse: ```192.145.96.201 = 11000000.10010001.01100000.11001001``` > > Subnetzmaske: ```255.255.255.240 = 11111111.11111111.11111111.11110000``` > > _UND_-Verknüpfung ergibt den Netzwerkteil: ```192.145.96.192 = 11000000.10010001.01100000.11000000``` > > _UND_-Verknüpfung mit der invertierten Subnetzmaske ergibt den Geräteteil: > ```0.0.0.9 = 00000000.00000000.00000000.00001001``` > > Die größte Adresse im Netzwerk (alle Gerätebits ```= 1```) ist für Broadcasts reserviert. > > Broadcast: ```192.145.96.207 = 11000000.10010001.01100000.11001111``` > > Nutzbarer Adressbereich im Netzwerk: > ```192.145.96.193 = 11000000.10010001.01100000.11000001``` > bis ```192.145.96.206 = 11000000.10010001.01100000.11001110``` > > Oft wird als Default-Gateway die erste oder letzte nutzbare Adresse > verwendet, hier also ```192.145.96.193``` oder ```192.145.96.206```. > > Der Netzwerkteil (alle Gerätebits ```=0```) ist gleichzeitig die > Netz(werk)adresse. > > #### Aufgabe A2 > Vervollständigen Sie die folgende Tabelle! > > -------------------------------------------------------------------------------------------- > IP Subnetzmaske Netz-ID Geräteteil erste Broadcast Anzahl > nutzbare Adresse nutzbarer > Adresse Adressen > --------------- ---------------- --------- ------------ ----------- ----------- ------------ > 192.168.213.15 255.255.255.192 > > 172.16.5.254 255.255.255.0 > > 172.254.13.8 255.255.248.0 > > 10.38.133.5 255.255.0.0 > > 10.0.0.15 255.0.0.0 > -------------------------------------------------------------------------- > > #### Aufgabe A3 > Eine Nachricht wird im Netzwerk mit der Subnetzmaske > ```255.255.248.0``` von einem Rechner mit der IP-Adresse > ```192.168.203.15``` an einen Rechner mit der IP-Adresse > ```192.168.200.65``` geschickt. Bleibt die Nachricht im selben > Netzwerk-Segment oder muss sie über ein Gateway geroutet werden? filius-2.9.4+ds/src/info/Introduction_Filius.md000066400000000000000000001227471506574241500215110ustar00rootroot00000000000000# Introduction to the world of FILIUS > * © 2015-2023, Daniel Garmann ``````, > [Gymnasium Odenthal](http://www.gymnasium-odenthal.de), > Lizenz: [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) > * © 2015, Lukas Herbert ``````, translation to english > * © 2023, Andreas B. Mundt ``````, markdown, proofreading > > If you are a native English speaker: Help on improving the document is appreciated! ![Filius Starter](img/FiliusSplash.jpg)[^1] [^1]: All pictures are taken from the FILIUS program. **Table of Contents** - [Introduction to the world of FILIUS](#introduction-to-the-world-of-filius) - [Preliminary Remark](#preliminary-remark) - [GUI of FILIUS](#gui-of-filius) - [The Design Mode](#the-design-mode) - [Network Components](#network-components) - [Server and Notebook](#server-and-notebook) - [Cable](#cable) - [Switch](#switch) - [Router](#router) - [Modem](#modem) - [The Simulation Mode](#the-simulation-mode) - [Network Applications in FILIUS](#network-applications-in-filius) - [System Applications](#system-applications) - [Client Applications](#client-applications) - [Server Applications](#server-applications) - [The Documentation Mode](#the-documentation-mode) - [Ways of Documentation](#ways-of-documentation) - [Exercises: Design and Simulate Networks](#exercises-design-and-simulate-networks) - [Direct Link](#direct-link) - [Connecting Computers Using a Switch](#connecting-computers-using-a-switch) - [Connecting Networks Using a Router](#connecting-networks-using-a-router) - [Simulation of the World Wide Web](#simulation-of-the-world-wide-web) - [E-Mail Services in the Network](#e-mail-services-in-the-network) - [Outlook](#outlook) - [Virtual and Physical Networks](#virtual-and-physical-networks) - [File Sharing in a _Peer-To-Peer_ Network](#file-sharing-in-a-peer-to-peer-network) - [Running a DHCP-Server](#running-a-dhcp-server) - [Creating and Implementing own Software](#creating-and-implementing-own-software) - [Static Routing](#static-routing) - [Appendix A: IPv4-Addresses](#appendix-a-ipv4-addresses) ## Preliminary Remark FILIUS[^2] was initially developed by the University of Siegen in Germany, to provide a tool to enhance computer science lessons on networks. The main target group are students of secondary schools but with its wide range of applications it can be interesting for learners of any age. The software especially promotes explorative learning and is very helpful to teach students about the internet and its various applications. This document is intended to show the possible ways of using FILIUS in the classroom. The different descriptions are always followed by example exercises with corresponding solutions available for download on the FILIUS website. [^2]: FILIUS stands for **F**ree **I**nteractive **L**earning Environment for **I**nternetworking of the **U**niversity of **S**iegen. ## GUI of FILIUS After the initial launch of the program you are prompted with the option to choose the program language, which will be permanently saved.[^4] [^4]: This configuration preference can be found in the user directory in the folder ```.filius```, which can be deleted for a reset. From now on the program will appear in the following look: ![Filius Work Bench](img/workbench.png){width=100%} The program decides between three different modes of work, the design mode, the simulation mode and the documentation mode: ![Design](img/hammer.png "symbol design mode") To change to the design mode, simply click the hammer ![hammer](img/hammer.png "symbol simulation mode"){width=20px} symbol in the toolbar on top. This mode is used to construct a network or apply changes to it. After the start of the program, FILIUS will always be in design mode. ![Simulate](img/greenarrow.png "symbol simulation mode") To start the simulation mode, click the green arrow ![Green Arrow](img/greenarrow.png "symbol simulation mode"){width=20px} in the toolbar. This mode is used for testing the network you built, installing different network applications on the computers and running these. ![Document](img/pencil.png "symbol documentation mode") To switch to the documentation mode, select the pencil ![Documentation](img/pencil.png "symbol documentation mode"){width=20px} in the toolbar on top. This mode provides the option to add your own annotations to the network and group certain parts of the network for better understanding. Before starting to build the first network, we want to explore these modi in more detail. ## The Design Mode Before setting up your first network, this chapter will make you familiar with the way the different components work in FILIUS. After that, we will use the various components to build and test networks, starting with some easy examples and increasing the complexity step by step. ### Network Components Filius provides a range of different components in the toolbar on the left hand side. These can be positioned, connected, modified and deleted in the workspace. To create a new component, use drag and drop to move the component anywhere in the white workspace. To connect two devices with a cable, select the cable in the toolbar and click on the components you want to link. Connections remain even when components are repositioned. Using a right click, you can delete any cable or component again. Now, we will learn about the basic functions of the different components and a deeper explanation will follow in the chapter on designing and testing networks in FILIUS. #### Server and Notebook In the toolbar on the left hand side, FILIUS provides two different types of computers to be used in our virtual networks. Both computers are identical in the way they work and provide the same function but it makes sense to keep a logical distinction between the two. ![Notebook](img/notebook.png) For a computer serving the task of a client, it is advised to use a Notebook ![Notebook](img/notebook.png){width=20}, whereas for a computer serving the task of a server, we will use a regular computer ![Server](img/server.png){width=20}. ![Server](img/server.png) The components can be added by dragging them to the workspace, using the left mouse button. The configuration of a computer can be seen by using a double-click on the image or right click and select configure. The picture displayed underneath will appear and provide you the option to modify the name of the computer, its IP address, its net mask and other settings. The standard configuration of a computer is displayed in the following picture. ![Network Configuration Widget](img/ifconfig.png) If you and your students are not familiar with the function of an IP address or subnet mask, please read the appendix A for a short description first. #### Cable Two computers can be connected using a cable ![Kabel](img/cable.png){width=20}. After selecting the cable from the toolbar, you can first click on one and then the other component that you want to connect in your workspace, to establish a link between the two. #### Switch If you want to connect more than two computers with each other, you need a central hub. The easiest way of doing so is using a switch ![Switch](img/switch.png){width=20}. For a switch, the configuration can also be modified by double-clicking on the icon. A switch remembers all connected components after the first request is sent in the network and redirects signals to their recipients. However, a switch can only connect computers of a single network. #### Router To send a network signal from one network to another one we need a router ![Router](img/router.png){width=20} to connect the two. In order for the router to work properly, all connected components have to lie in different networks. This also applies to single computers that may be directly connected to it. After picking a router from the side bar, FILIUS asks how many NICs (Network Internet Cards) it should have, that means how many interfaces to networks it should provide. This configuration can later be changed in the configuration panel of the router, selecting the ```"Manage Connections"``` option. ![](img/routerconf.png) In the preferences panel you can also find a dedicated tab for each interface with the configuration of the NIC and its network. Modify the IP address according to the connected network, so that packages can be routed correctly (often the first or last usable IP address of the network). In addition, a gateway has to be added to every computer in the network. The exact preferences will be explained in detail in a later example. #### Modem FILIUS also provides the option to link several FILIUS-Programs running on different computers in a physically existing network, such as in a classroom. It is important to ensure that your firewall allows such connections. To use this features, all FLIIUS-Programs need to include a modem ![Modem](img/modem.png){width=20} in their virtual network. The modem in one of the virtual networks needs to be set as recipient, which can be done by ticking the box that says ```"Wait for incoming connection request"```, and then then clicking the button ```"Activate"``` to start accepting incoming requests. ![](img/modemconf.png) All modems of the other virtual networks can now establish a physically existing network connection by using the real network IP of the receiving modem. All modems will indicate a successful connection with a small green dot. ## The Simulation Mode Simulation mode is used to install software to the computers, remove it or start it. To change to simulation mode click the green arrow in the top hand toolbar ![](img/greenarrow.png){width=20}. FILIUS’ main goal is to display network activity on the different layers of the OSI model to the user and thereby enhance the understanding of the functionality of networks. To modify the speed in which the simulation will be displayed, use the control panel on top ![](img/simulationspeed.png){width=80}. This will adjust the speed of the display of signals in cables as well as in the OSI layers model, which can be seen by right clicking on any computer. ![](img/packettrace.png) But before you will be able to see any network activity, it is necessary to install software to the computers. Using a double click or right click on a computer will provide you with the desktop the selected device: ![](img/desktop.jpg) By clicking on the symbol for software installation ![](img/install.png){width=20}, you can install ![](img/leftarrow.png){width=20} and uninstall ![](img/rightarrow.png){width=20} software on that computer. Following is a brief description of the functions of the different pieces of software. ### Network Applications in FILIUS FILIUS has three different types of applications: client applications, server applications and system applications. These will be briefly explained according to their class. #### System Applications ![File Explorer](img/filebrowser.png) File Explorer: This applications enables you to copy actual existing files from your computer’s hard drive on to a virtual computer in FILUS. ![CLI](img/terminal.png) The command line let’s you manage the computer by using certain commands that will be displayed on startup of the terminal. ![Text Editor](img/editor.png) Using the text editor, you can create simple text files, for example HTML, and modify them. You can also modify files that you previously imported using the file explorer. ![Firewall](img/firewall.png) The firewall can be used to open or close certain ports within the network. ![Image Viewer](img/viewer.png) With this program you can open and view images. #### Client Applications ![E-Mail Client](img/email.png) This piece of software lets you virtually send and receive E-mails. ![Webbrowser](img/webbrowser.png) The web browser enables you to view websites. It can handle all basic HTML. ![Generic Client](img/networkclient.png) The (network) client is used to connect to a server. It can be used to test whether the network is properly connected. ![Gnutella](img/gnutella.png) Gnutella is a ```peer-to-peer``` application which allows you to share files within a virtual network. #### Server Applications ![DNS Server](img/DNSserver.png) The DNS server is used to simulate the translation of symbolic host names into their according IP Addresses. ![E-Mail Server](img/mailserver.png) Using an Email server, you can create different E-mail accounts which can then be used by the E-mail program on a client computer. ![Webserver](img/webserver.png) This application transforms a computer into a web server, that enables clients to view a website stored on the server, using the web browser. The default website is defined by the file ```index.html``` in the virtual directory ```root/webserver```. ![Echo Server](img/echoserver.png) Once started, the echo server replies to all requests of clients by sending back the received message. ## The Documentation Mode FILIUS allows the user to document network structures and group them symbolically. To start documentation mode click on the icon displaying a pencil ![](img/pencil.png){width=20}. ### Ways of Documentation FILIUS provides two different ways of documentation in this mode, which will be briefly explained. From here on, the script will not focus on using the documentation mode any more but it is up to the user to meaningfully label or group their virtual networks. ![Textfield](img/textfield.png) Using text fields, the user can add information regarding the virtual network. Text fields have no influence on the behaviour of the network in design mode or on the network activity in simulation mode. ![Structure Field](img/groupfield.png) The same is true for structure fields. They can be used to symbolically group single components of a network in a meaningful way. The field can be dragged to the right size and will appear in the background upon exiting the documentation mode. ![Export](img/export.png) By using the export button, your virtual network can be saved as a portable network graphic (PNG file). ## Exercises: Design and Simulate Networks In the following chapter, we will design and test different networks, starting with easy examples and rising in complexity. Therefore, a constant switching between design and simulation mode is necessary. The symbol on the left of each exercise indicates whether it is to be done in design ![](img/hammer.png){width=20} or simulation ![](img/greenarrow.png){width=20} mode. ### Direct Link The simplest way of connecting to computers is through a direct link using only a network cable. This is called a ```peer-to-peer``` connection. With this type of connection, computers can be connected using a crossover cable to enable the transfer of data between the two. > #### Exercise 1 > ![](img/hammer.png){width=20} Create a simple network with two > linked computers, serving as clients. > > ![](img/crossover.png) > > Configure the computers with the names and the IP addresses > ```192.168.0.10/24``` and ```192.168.0.11/24```. > > Note: If you don’t want to manually name each computers, use the > > option ```“Use IP address as Name”``` to let FILIUS automatically > > set the computer’s name as its IP address. > #### Exercise 2a > ![](img/greenarrow.png){width=20} Select the computer with IP > address ending with ```0.10``` and install the command line on > it. Start the command line and test the connection to computer > ```0.11``` using the command ```ping 192.168.0.11```. Inspect the > network activity by displaying the exchanged data of computer > ```0.10``` in the ISI/OSI model. > > ![](img/ping.png) > > The command line shows that the computer sends four ICMP requests > (```ping```) to the other computer and each time, waits for a > response (```pong```). This data exchange can also be displayed in > the data exchange window. Here, the first two lines belong to the > address resolution protocol, which is used to find out the physical > address of the other computer. The following eight lines stand for > the exchange of the ```ping```-```pong``` packets, where always two > lines form a pair. They belong to the _Internet Control Message > Protocol (ICMPv4)_, which is part of the internet protocol IPv4. > > ![](img/pingtrace.png) > > We can also observe that the network activity reaches only to the > internet layer. The higher and more complex layers of the OSI model > are not yet needed. By selecting one of the lines in the data > exchange window, it is possible to view information on the lower > layers of the OSI model as well as more detailed information on the > layers in use. > #### Exercise 2b > ![](img/greenarrow.png){width=20} Also try out other commands using > the command line, such as ```ipconfig```, ```host localhost``` or > ```dir```. The use of the ```host``` command will become clear in a > later example including a DNS Server. ### Connecting Computers Using a Switch If you want to connect more than two computers to a network in FILIUS, it is necessary to use a switch, to which any desired number of computers can be connected. After the first use in simulation mode, a switch remembers the MAC addresses according to the IP addresses of all computers so that it can forward data packages faster. We will now use this component to connect three computers with one another. > #### Exercise 3 > ![](img/hammer.png){width=20} Now, expand your network with a third > computers, a server, with the name displayed below and the IP > address ```192.168.0.12/24```. Remember to use the symbol > ![](img/server.png){width=20} for the purpose of a server. Then > connect all three computers using a switch as shown in the picture: > > ![](img/switch1.png) > #### Exercise 4 > ![](img/greenarrow.png){width=20} Next, open the desktop of server > ```0.12```, install an ```Echo Server``` to it and start it on the > preset port ```55555```. Use one of the notebooks to install a > ```Generic Client``` and connect it to the server. Try sending some > text messages from the client to the server and observe the > outcome. Also take a look at the network activity in the data > exchange window of the notebook. > > ![](img/chat1.png){width=49%} > ![](img/chat2.png){width=49%} > > ![](img/chat3.png){width=62%} > ![](img/chat4.png){width=37%} > > The data exchange window shows that in this example, the transport > layer is needed for the first time. Establishing the connection > between client and server already uses three layers in the ISO/OSI > model. The first two lines are again used to connect the server's IP > address to the corresponding MAC address (_Address > Resolution Protocol, ARP_). > > As soon as you send a message from client to server, the fourth > layer, called application layer, comes into play. The application, > which is the ```Generic Client```, first of all uses the application > layer, then the transport layer, the internet layer and finally the > network layer. All this information will be displayed upon clicking > on the first dark blue line in the data exchange model, which will > display the image on the right. In the following exercises, keep in mind to have a look at the data exchange window from time to time to get an idea what kind of information is being transmitted within the network. You will for example notice the huge amount of data that has to be transmitted while sending E-mails. ### Connecting Networks Using a Router Before installing and testing more software on the computers, we want to expand our existing network with three more computers. > #### Exercise 5 > ![](img/hammer.png){width=20} Create a second network with three new > computers as displayed below. We want the three new computers to be > in a logically different network for which we will use the IP > addresses from ```192.168.1.10/24``` up to > ```192.168.1.12/24```. Finally, connect the two networks using a > router and configure the two network interface cards with the IP > addresses ```192.168.0.1/24``` and ```192.168.1.1/24```. > > ![](img/router2LANs.png) > > ![](img/greenarrow.png){width=20} Finally, test the connection > between computers ```0.10``` and ```1.10``` using the ```ping``` > command. > > If you did everything as described above, FILIUS will display the > following message in the command prompt: > > ![](img/nogateway.png) The reason for this response is, that the message would have to leave the local network. However, we haven’t yet configured a gateway for the different computers, which would determine where messages that have to leave the network are forwarded to. > #### Exercise 6 > ![](img/hammer.png){width=20} The router has a network interface > card with the address ```192.168.0.1``` which you will configure as > a gateway for the three computers on the left hand side. Set the > gateway for the three notebooks on the right hand side to > ```192.168.1.1``` accordingly. > > ![](img/gateway.png) > > ![](img/greenarrow.png){width=20} Now try the same connection again > and it should work properly. When taking a look at the data exchange window, you can observe that the first request takes a lot longer then the following three. This is because the routing table of the two switches is empty at the beginning and is then created after the first request. > #### Exercise 7 > ![](img/greenarrow.png){width=20} Now try and test your network with > a ```Generic Client``` and an ```Echo Server```. Use Notebook > ```1.10``` to install a ```Generic Client``` and connect it to > server ```0.12```. ### Simulation of the World Wide Web The most important task of the internet today is surely the world wide web. Using FILIUS, you can simulate and analyze the basic processes involved in the communication between a web browser and a remote web server. The network we have created in exercise 6 is sufficient for this task. We will use server ```0.12``` as our web server and notebook ```1.10``` as our client and web browser. But let’s first set up the web server. > #### Exercise 8 > ![](img/greenarrow.png){width=20} Use Server ```0.12``` to install a > web server and a text editor. Take the text editor to open the file > ```index.html``` which can be found in the virtual directory > ```root/webserver```. Now you can modify the ```html```-file in > order to show the information you like. Also create a new page with > the name ```contact.html``` which you will link to from the first > side. > > ![](img/webdoc1.png) > #### Exercise 9 > ![](img/greenarrow.png){width=20} On the desktop of your web server, > start the application "webserver" using a double click. Then start > the virtual web server clicking the button Start (left > picture). After that, switch to notebook ```1.10``` to install a web > browser. Start the browser and try to establish a connection to the > web server by typing the URL ```http://192.168.0.12``` into the > address field of your web browser (right picture). > > ![](img/webdoc2.png){width=49%} > ![](img/webdoc3.png){width=49%} We established a connection but this is not the way we usually communicate with other web servers. Normally, we contact a website by typing the URL containing the hostname and not the IP address of the web server. The resolution of hostname and the corresponding IP address is done by a _domain name system_ server, also called DNS server, which we will now configure. > #### Exercise 10 > ![](img/hammer.png){width=20} Create a new server with the IP > address ```192.168.2.10/24``` and the gateway set to > ```192.168.2.1```. Change the number of interfaces of your router to > three by navigating to the ```"General"``` tab of its configuration > and select the button ```"Manage Connections"```. Now move to the > tab of the new network interface card and set the IP address to > ```192.168.2.1``` and the subnet mask to > ```255.255.255.0```. Finally connect the new server to the router > with a cable. > > ![](img/DNSrouter2LANs.png) > > To enable all computers to use the service of the DNS server, we > need to add the IP address of the DNS server to the configuration of > every Notebook. > #### Exercise 11 > ![](img/hammer.png){width=20} Add the IP address of the DNS server > ```192.168.2.10``` to the configuration of every Notebook. > > ![](img/DNSconf.png) > > Lastly, we need to give our web server a suitable URL and add it to > the DNS server’s reference table so that we can reach it through its > name. > #### Exercise 12a > ![](img/greenarrow.png){width=20} Select server ```2.10```, install > the application ```DNS server``` and start it with a double > click. As domain name type in ```www.filius.com``` and below the IP > address ```192.168.0.12```, belonging to our server. Then click the > Add button to add the entry to the DNS server’s reference > table. Finally start the DNS server by clicking the ```"Start"``` > button (left picture) and test the connection by using your web > browser on a notebook and searching for the URL > ```http://www.filius.com``` (right picture). > > ![](img/DNS1.png){width=49%} > ![](img/DNS2.png){width=49%} > > If the web browser returns the message ```"Server does not > exist!"```, the reason is most likely that you haven’t yet started > the DNS server or you asked for a URL that you haven’t yet added to > the reference table of your DNS server. > #### Exercise 12b > ![](img/greenarrow.png){width=20} At the beginning of our tutorial, > we learned about the ```host``` command in the terminal. Now try > again to use the ```host``` command with the URL > ```www.filius.com```. Now you will see that the DNS server does its > job and returns the IP of the web server. > > ![](img/DNScli.png) ### E-Mail Services in the Network FILIUS provides the feature of simulating the work of different Email servers and the way they interact. At first, we will configure a single E-mail server and use a Notebook with an E-mail Program installed to use the Email service. Later we will install several E-mail servers that are linked to each other. > #### Exercise 13 > ![](img/greenarrow.png){width=20} Select Server ```0.12``` to > install the application “Email server” and start it by using a > double click. Create a new account with username ```bob``` and > password ```bob``` (left image). See how your new account appears in > the account list. Then start the server using the start button. > > ![](img/email1.png){width=49%} > ![](img/email2.png){width=49%} Next, we need to set up our DNS server to make it accept the new mail domain. > #### Exercise 14 > ![](img/greenarrow.png){width=20} Start the application "DNS server" > on server ```2.10```. Add a new mail exchange with the mail domain > ```filius.com``` and the mail server domain name > ```www.filius.com```. Then restart the server. > > ![](img/email3.png) Finally we need to install the Email program to one of our notebooks and configure it. > #### Exercise 15 > ![](img/greenarrow.png){width=20} Select notebook ```0.10``` to > install the new application ```"E-mail program"``` and launch > it. Click on the button ```"Account"``` to create a new account and > configure it with the following information of your E-mail server > (left picture): > > Name: ```bob``` > > E-Mail-Address: ```bob@filius.de``` > > POP3-Server: ```www.filius.de``` > > POP3-Port: ```110``` > > SMTP-Server: ```www.filius.de``` > > SMTP-Port: ```25``` > > Username: ```bob``` > > Password: ```bob``` > > After that, send an E-mail to ```bob@filius.com``` (that means to > yourself) and then retrieve your mails (right picture). > > ![](img/email4.png){width=49%} > ![](img/email5.png){width=49%} > #### Exercise 16 > ![](img/greenarrow.png){width=20} Next, add another E-mail address > to the E-mail server with the name ```bert@filius.com```. Also set > up an E-mail program on notebook ```0.11``` so that ```bob``` and > ```bert``` can send E-mails to each other. Lastly, we will set up a second E-mail server, which we want to add to the right side of our network (```192.168.1.0```). > #### Exercise 17 > ![](img/greenarrow.png){width=20} Add another server with the name > server ```1.13``` to the right side of your network and install a > mail server with the mail domain ```filia.com```. Finally add an > account with the name ```alice@filia.com``` to the server. > > ![](img/servDNSrouter2LANs.png) > > Also extend the mail exchange table of your DNS server, so that the > new mail domain will be excepted. > > Take notebook ```1.10``` to install an E-mail program and configure > it for the account ```alice@filia.com```. > > Lastly, try sending E-mail between the two > accounts ```bob@filius.com``` and ```alice@filia.com```. ## Outlook If you followed the tutorial up to here, you have mastered all the basic functions of FILIUS. From here on, I will give a little prospect of further possibilities of FILIUS. In how far these can be integrated into your teaching plans depends on the strength of your students and the configuration of your classroom network. ### Virtual and Physical Networks In the chapter on the components of FILIUS we already talked about the modem as providing the possibility to leave the virtual network and send signals over a physically existing network. A premise for this is, of course, a physically existing network of at least two computers running FILIUS, and a local firewall that is configured not to block FILIUS’ signals. You can get an impression of this setup through the following example, where only one notebook is able to communicate with the physically existing network via a modem. ![](img/modem1.png) > #### Exercise 18 > ![](img/hammer.png){width=20} Create the network structure displayed > below on two (physically) different computers (let’s call them > Computer A and Computer B) with different computer IPs that are part > of a physical network. On computer A, set the modem to accept > incoming requests, ticking the box ```"Wait for incoming connection > request"```, and click the button ```"Activate"```. On Computer B, > configure the modem so that it connects to Computer A. For this > purpose, insert the physical IP address of Computer A in the field > named IP address and press the button ```"Connect"```. (If you want > to try this on one computer, simply write ```localhost``` into the > IP address field and choose any port.) Both modems will show a green > light if connected successfully. > > ![](img/modem2.png) If connecting the two modems worked, you can now set up the two virtual notebooks for network communication. You can for example install an ```Echo Server``` on the virtual notebook on Computer A and a ```Generic Client``` on the virtual notebook on Computer B. After starting the ```Echo Server```, the virtual notebook on Computer B should be able to communicate with it over the physical network. > #### Exercise 19 > ![](img/greenarrow.png){width=20} Install an ```Echo Server``` and a > ```Generic Client``` to the virtual notebooks on Computers A and > B. Then start the ```Echo Server``` and connect and test the ```Generic > Client```. Now you can also test other applications on the virtual > computers. Also have a look at the data exchange between the both. ### File Sharing in a _Peer-To-Peer_ Network FILIUS offers the option to connect several computers to a peer-to-peer network and share files throughout the network using the application Gnutella. ![](img/p2p.png) All computers in such kind of network serve the function of a client and can connect to all other computers of the network, which is why we use a notebook for this exercise. > #### Exercise 20 > ![](img/hammer.png){width=20} Create the network as displayed above, > connecting three laptops using a switch to a peer-to-peer network. > > ![](img/greenarrow.png){width=20} Then, install the application > "Gnutella" to all three computers and additionally a "File explorer" > to Notebook ```0.10```. Start the file explorer and copy the file > ```index.html``` from the directory ```webserver``` to the directory > ```peer2peer```, using a right click. > > Next, launch the application “Gnutella” on Notebook ```0.12``` and > join the network of notebook ```0.10``` (IP address > ```192.168.0.10/24```). The list of connected neighbours should > refresh automatically (left picture). > > Now you can search the peer-to-peer network for files named > ```index.html``` and download them (right picture). > > ![](img/p2p1.png){width=49%} > ![](img/p2p2.png){width=49%} ### Running a DHCP-Server Especially in large networks it is practical to set up a DHCP server which takes over the function of assigning IP addresses to the computers automatically. FILIUS provides the possibility to simulate just that. In the following exercise we will implement a DHCP server to our network of the previous exercise. ![](img/DHCP.png) > #### Exercise 21a > ![](img/hammer.png){width=20} Implement a DHCP server into your > existing network and rename all notebooks in order to suggest that > the IP address assignment now works automatically. Configure the new > server with the IP address ```10.0.0.10/24``` and then set it up as > a DHCP server by clicking the button as shown in the picture below. > > ![](img/DHCPconf1.png) > > Now, a dialogue box will pop up that asks you to enter the DHCP > server settings. Select the IP address range from ```10.0.0.100``` > to ```10.0.0.200``` and activate DHCP by ticking the box ```"Active > DHCP"```. > > ![](img/DHCPconf2.png) > #### Exercise 21b > ![](img/hammer.png){width=20} Next, for each computer, tick the box > ```"Use DHCP for configuration"``` to use the DHCP server for IP > address assignment. You will notice that you will not be able to > manually change the configurations anymore. > > ![](img/greenarrow.png){width=20} As soon as you switch to > simulation mode, the clients will be assigned an IP address by the > DHCP server. All wires will blink for a short time to set up the new > configurations. (If this does not happen, FILIUS might still have > old settings stored to the switch and you will have to restart the > application.) > > Now start each computer and install the teminal. Then test which IP > address the computers have been assigned by the DHCP server by using > the ```ipconfig``` command. ### Creating and Implementing own Software FILIUS even provides the option to create your own software and install it to the virtual computers. ![](img/software.png) With the magic wand symbol ![](img/software.png){width=20} you can reach a 3 step assistant which enables you to integrate your own software, for example a chat server and client application. FILIUS provides some code outlines in Java in the second step. ![](img/softwareassist.png) ### Static Routing In the chapter on connecting two networks using a router we restricted the traffic to flow through only one router. In reality, things look a little bit different, because the internet is a vast web of uncountable switches and numerous possible ways for a data package to go. It can never be determine which way it will take. Let’s have a look at the following, still simple, example: ![](img/staticrouting1.png) > #### Exercise 22a > ![](img/hammer.png){width=20} Create the network displayed > above. Configure router I so that it is connected to Router II in > network ```2.0.0.0/24``` and to Router III in network > ```3.0.0.0/24```. Configure Router I with the first addresses of > each network. > > Then, configure Router II to be connected to Route III in network > ```1.0.0.0/24``` and with the second IP address of each network. > Finally, configure Router III with the third IP address of each > network. All together the new network will look like this: ![](img/staticrouting2.png) Now we are still missing the settings for packet forwarding. First of all, configure the gateways for the computers of the different networks: > #### Exercise 22b > ![](img/hammer.png){width=20} Set the gateway of Notebook ```0.10``` > in Lan A to ```192.168.0.1```, so that Router I will be used as its > gateway. Then, set the gateway of Notebook ```1.10``` from Lan B to > ```192.168.1.1``` and the gateway of notebook ```2.10``` from Lan C > to ```192.168.2.1``` accordingly. FILIUS routers are able to run routing automatically. For using this option simply tick the box ```"Automatic Routing"``` in the configuration of each router and the routing information protocol (RIP) will do the job of finding the shortest way within the network. For a better understanding of how routing actually works, it is still possible to manually configure the routing in FILIUS. Every router has its own forwarding table which describes how incoming data packets are to be forwarded. The following three tables are the result of the network shown above: ##### Router I Ziel Netzmaske Next Hop Interface -------------- ------------------- ------------------- ------------------- 192.168.1.0 255.255.255.0 2.0.0.2 2.0.0.1 192.168.2.0 255.255.255.0 3.0.0.3 3.0.0.1 -------------- ------------------- ------------------- ------------------- ##### Router II Ziel Netzmaske Next Hop Interface -------------- ------------------- ------------------- ------------------- 192.168.0.0 255.255.255.0 2.0.0.1 2.0.0.2 192.168.2.0 255.255.255.0 1.0.0.3 3.0.0.2 -------------- ------------------- ------------------- ------------------- ##### Router III Ziel Netzmaske Next Hop Interface -------------- ------------------- ------------------- ------------------- 192.168.0.0 255.255.255.0 3.0.0.1 3.0.0.3 192.168.1.0 255.255.255.0 1.0.0.2 1.0.0.3 -------------- ------------------- ------------------- ------------------- > #### Exercise 22c > ![](img/hammer.png){width=20} Configure the forwarding tables of the > three routers according to the representations given above. Unselect > the box saying ```"Show all entries"``` for a clearer view. > > ![](img/greenarrow.png){width=20} Install an ```Echo Server``` to > Server ```2.10``` and a ```Generic Client``` to laptop > ```0.10```. Connect server and client and see how the packages are > forwarded. It is easier to observe when you lower the speed down to > about 50%. > > ![](img/hammer.png){width=20} Now modify the forwarding table so > that messages sent from LAN A to LAN B are rerouted to run through > LAN C as well. Observe the network activity. # Appendix A: IPv4-Addresses IPv4 addresses are comprised of 32 Bits, split into 4 blocks of 8 Bit each. This gives a total range of IP addresses from ```0.0.0.0``` to ```255.255.255.255```. For a better understanding of the process of routing it is helpful to translate the numbers from decimal system to binary system. > #### Exercise A1 > Practice the translation between binary and decimal > system. Translate your result back to the other system to check > yourself. > > a. ) ``` 1101 1110₂``` > a. ) ``` 0011 1111₂``` > a. ) ``` 1111 1101₂``` > a. ) ``` 0101 1010₂``` > a. ) ``` 96₁₀``` > a. ) ``` 254₁₀``` > a. ) ``` 17₁₀``` > a. ) ``` 127₁₀``` A subnet mask in IPv4 is another 32 bit number that splits the IP address into a network prefix and the host identifier. Through AND operations between IP address and subnet mask, the network prefix can be extracted. Through AND operations between IP address and the inverted subnet mask, the host identifier will show. The smallest address of the network is used to reference the network itself and the highest IP is reserved for broadcasting. > #### Example: > IP Address: ```192.145.96.201 = 11000000.10010001.01100000.11001001``` > > Subnet Mask: ```255.255.255.240 = 11111111.11111111.11111111.11110000``` > > _AND_-Operation reveals the network prefix: ```192.145.96.192 = 11000000.10010001.01100000.11000000``` > > _AND_-Operation with the inverted subnet mask reveals the host identifier: > ```0.0.0.9 = 00000000.00000000.00000000.00001001``` > > The highest IP address is reserved for broadcasting (all host bits ```= 1```). > > Broadcast: ```192.145.96.207 = 11000000.10010001.01100000.11001111``` > > Available range of addresses within the network: > ```192.145.96.193 = 11000000.10010001.01100000.11000001``` > up to ```192.145.96.206 = 11000000.10010001.01100000.11001110``` > > Often, the first or the last usable IP address is used as the > default gateway, here: ```192.145.96.193``` or ```192.145.96.206```. > > The network prefix is also used as the network address (all host > bits ```= 0```). > #### Exercise A2 > Complete the following table. > > ------------------------------------------------------------------------------------------------- > IP Address Subnet Mask Net-ID Host First Broadcast Number > Identifier usable Address of usable > Address Addresses > --------------- ---------------- -------- ----------------- ----------- ------------ ------------ > 192.168.213.15 255.255.255.192 > > 172.16.5.254 255.255.255.0 > > 172.254.13.8 255.255.248.0 > > 10.38.133.5 255.255.0.0 > > 10.0.0.15 255.0.0.0 > ------------------------------------------------------------------------------------------------- > #### Exercise A3 > A message is sent from a computer holding the IP address > ```192.168.203.15``` in the network with the subnet mask > ```255.255.248.0```. The target is a computer with the IP address > ```192.168.200.65```. Does the message remain within the network > segment or is routing necessary? filius-2.9.4+ds/src/info/img/000077500000000000000000000000001506574241500157325ustar00rootroot00000000000000filius-2.9.4+ds/src/info/img/DHCP.png000066400000000000000000001032561506574241500171650ustar00rootroot00000000000000PNG  IHDRO D40sBIT|d IDATxyx}_s%iu[7m1`cHKi$!:_h88%7n Ł@b@9 o˺C1;,_ny|g~s?뺎 6l8!#=6l?N" xp:#=AhٴiMcm>}1I&AC4EӴ>pdma` Ӄ ޻\.n,&O6lL2NF}ߋM6lt]'ŀޤi=,˸n$I c2#cǎ DN8UU$IƢ 7,FÁgL6meƸ@:&dzYpg_Wp)W~I:::he۶mL63fp8Fpv6lMJ$!J IGu2 rƄ+?J1Oӯ}D,$駟ϲc2̰uK1glǾ=itwwN2 xxۇ16!χ_)Bt!+D3)Op˿~zzzx7ͶmkIHq-[JkkK4X,F,;K [txٴiO7O:(Ҵl3kuja֨ɾπ1JƈJJCuER{.]۰q|4`0H$u^DN<_.d]ܘދ<5вd*"QHՌqPLK$IA`޼y\|۰qDa`fLLT*u&A~N'+A@W<1Nݴ25!,57Q,*~]Ig 6M ӋL&ܹs+ QD"A{{azͱB4dH3ZXVdeݴ<5+d3ʡ}YDU z 4bdijVxY`f^-[e.Dm82 $ `5L&h Ͻ{[d$T ]Wr4us\tA3tni*lV3PAɠiugcW¨EӦM+9VJWWPhLf͸{b\iy d%UC3Y]E24V|3k4ѴAJ"WAIDu5;d*= ݰs'_Dm  ]]]G@vd22̘9.Jw Z)S NDO!Mx=E"DLْSBw<^7?yB֌?{y,{シ;#l@ @{{vX97!tJlj`0O~"+$n( $#C$fוCɼgЬʼnAJ cWUcj{*))aٲe̛7odqH$BKKqW挆id2TU={G:_`'N M'AMEQF$etQΒ.7Ⱥ8&5*κ[9KrAqYڣy/akLoz?0?<\p^z-sqEIƄ˞xc̓7h( bAD0% $'H>FW%t DjZB6iA,:Li%n4`Wcy׹KlqR4TjT'r܌$Yw]0HTDG4 5\weGv4TvDd#Id5edܕ4%'9AZR TGZNt9Ig`RAjjjF!EG(U`&F4yڵ :˅$HH$-*"S c<cژƧ4ʕ'YvYF嘕& H8\FHuB2wipee˼ H|پٞAƢ Y2sIb}ؖ޽{ xy". c<<@OHf:zVdָQIhTEו eԵ BکHRS̕8u-Vf.K/"d)ic0o>V\QWWwN'N|TU5M;̵Eq\Oc<8=+1t-]44ݰIMԴt((ò462e(A AIS@]vtr!]HfTh.@GG{/\s _~1?:芢3&5 ƤT={" BO'Г1dD7Z-mHQT =7T=;>Ȏ% t%g't؟IgRqJJTC't#8\ q,ohz槊g4d G?)x cQcK{_o"<%$dV A䬨F$f:\ e=N!qm]K'6c/JFLOA.o7Z1O' S|rdYFeŋt:(D!m /GM!H.(u0❀ѣ * Yo!"(>꺀gQD0K?ɺ*!{. }&}ic8y饗裏W?SsiA@BƏ$AF fL(HI]ӌE%E 4 Aj4U ]TDxA2Q3JhI͸19)Ol1<\ve̜9=SH$ofǎl-lolGDPֺB/MCbM*u].'!Y*f ZzA̎F_ȠA 96"#P(O?,̚5zjkk)//驍) LӄB!$:SNe\zN6o~7uhCwMAײrl%dH!Xp@_9Y%]̮ʩIK>*#f2(<}{6'&L@WW }gΝݻ׊:j***hӄFdV]ɑ-̶CSN+#dJB.j.jVWtgú7m1Xjw}7TjrJ`Jb:w?1oHkI&wWX7SybjX]] )y9YllsivCMzYu0hЫiz8g>6S>{Ny*)B0$L"2T^x_@ B$K6m[!"---|~&ӊVZY<F@ȺF?O y]zubYԡNL05!㦛nGmcoWx<~3vկt)}}nPJ$DQb$I8N<> &PTTtR% X,fݻ{@ 餠P(|l߾N;e˖1u^ĉO>/5vT5[֢qf6X(YS5c$j4CbU]]=lƩ<~sύt#ٻgٷopEQp8twwFn"BOYgń FLj$I"vIR<l߾Vp\!"PSH$/:u*/϶P])--eR:k&;wwgNQtFr¡~rŇzfSV/H9C,dbE6W_}5{.>Q{~4M/?J2D*]F#f]jg2(---444h"~bDvUU d2q8lݺW^yj Z` Ceb]]]$IN'^ .^@$aV6l[e. a)?l #2Haf[ km>bi5Vζ]#1asn~yٴiLfX;wsFIRaoo(//Q]Zzᾨtww q:ٸq#1pvq\hF<piNh4(_zzzxx<~r0W3wr!R ;e<9^m&!WQ\Tgdڴi̛7oq 3fs9옩S2|(mmmkMΘ1׋i&W_}!0i?Nl۶ӧhrlXSuP])..v-)R[[@P(墮R<Pp8m5h(۶m7$PSS"2%E\<´T"o6MH,aI +i$$T&/f_QfR;6͛g˔l *L|hlls9gy睇!Nc,@nd p߳zrdj0*rsH\NflǾ{9p@EEE,Zoo̟?AY0,rظ*uww(..dO^PL&IRd2B!IR|>pGG%_8N\.Ճt:Mww7Tʲv-ZĢEV<4w={;Y^>!V`v7BʸΛ1`W<P?rUϸꪫtށ1$MIeC/vzK5556qVlܸݻw|rN:כ8q"yyy S8RTP(D&vt:Ilܸm۶Y֦d׮]J~~>#I@:&MPCaa!pN\.P<*++bB! Lb-+֌lٲ?ŋSUUeY,sV$~vZ9[?Cou+jbNՑzY)K/ @l vs.z . :yF"q8x<DQd۶mE1m4N'x`̚䱏_:*+V)Yz1+795J{>O>aΜ9,^xȴӧOW^xH$Het]穧bHdcߏx<&MD~~>(Ass3,3a$I.|>Zv1cPvZZZE*<LB(  p8$N|3fk_SLb>.Y lyo Os@WsTj/1:G?9O{^r}$qiZuD0` XJ^~eUUPPٳy:N&M$Ituu'p8fĉȲLAAv"PRRbGm'L@*ۚb֬Yihkkv3qD$Ip%ܳg/BYYW^y%s̱eee,_W^7M{P^jgFW%ME4h'//.˴1?Jmmm꫼SYY(̟?մkTIu('Yuf'w;袋ˣ$IӒ)e2QXXپ};۷oN;sRUUE `֭b1;<&OL]]TD$\.LN^/gq>`0Hkk+@"&MDUUnll׿5?яؼyJ~/ehZћ t=tw؈wGIbK|F|2v7;B*Ayޓ>@1J܌(jUA8s}>/2nI0===8N)..FE+1:TTTziooGUU FQ+)d&"#SPP`e0ֽM P*b IDATӬxi8&Hi,]ٳg[fӞ={Vގ5Mc檯sWlٲ."VPEE(DSS,S[[KQQ,,˖X]]͔)SHRtvvH&TUUQ^^NAAU ">|t:vo{3Soy0CJr39ŕW^iac0O?>UU+$-ܹs'oaPP\\l5d2<Y G(EEEoq:;;ٱcD /~7s&`;H71lQ׀)++c޼y466Z:"@ ~;Bа,yF"Ǒ  uO?e˖QRRBcc厛Yii9=U=X P(D,c̘1x |cilTN^xnV>#)Sj*Z;B(' R\$ R) qݖڟ|ɴ:EQL2˗3sþ /DF2.N9罹 )m? j퍍XP]]M&v4Mb?w\=\+馛It:(tvvSO" .Z"m8zzzزe /"۶mCUU,Y_~„Dعs'w殮儥Jt'"ivҠd2455 ǍqSO=E{{;tL&C&AUUjkk+-[xVմ>].Tʲrn7imm%L1c. EQhjj"*yyy9xUy!f⢋. A&4STVVZlٲg}֒a p ̟?&QGs=͛.B$qgssNW]uIU!RNQɳ6;z6yBW1-D(<ݻT*E:)bo ,X@AAA/%Qv^uᰵnJn{N'DQ)..Fu˩D48@kk#5eJ]t'NDu/EQ>dY숭vs=ǫjqײd;6j07[o{nkYX`K.~+wYᢓőSQ~pAk\ yj$I|ߧnx2fȚY&:ǴBso7wޱ,t:8qQQK, /;F fB(PG}}=T*EKK p8XgYYLùKCC[^PP%JJd2x#A^~e}Y:;;u](>sx lb~? .+`ʔ)}$O@/^iv|ҥKYd?b<ôJb(Ӵr;Z~azzz8Yf 6loz„ L>I&QQQqDKδ<_|E>cc;Q H4nkeܹV4a6H$DQ+3n GU7fƍb&Lk嬳\G4%t:q:|>|>߰i}^ٳYbqu6F[nf֭!m[t)^{-'N<?L:qw2)UUyٰa%[|:O5I4HWb,\1Oe1*3Dp8L2}i9%D"A"?$Xq͆&OQ@yy9+V{۶m;1:(qKVd55M{n݋墾 /ZRc  zjy5p76T<I:;c ۷ 6悛^)2 TIE7ٳG6yBhkkc͚5l۶ Ϛ5k]j0{n֬Yþ}$noqLX뭭DQz뭇x]ٖ)'||UUWʊ+Ƅe5TH$]ctRVX1b'M6[qcն+ҫ7,RϟҥKjS 6yxWI\r%\ԃ~嗖trJ.䒑֠ H[ok ZcAmm-z)-Ƕm۸뮻D"444p=׺7)SjժQ(o&|`pYXX__hѢ{gTU &KJJ~cDJ~!k׮Ȧ/% Ñ {C5|L>6n6} _eY׵[qV1}C56"gw__2ؾc8;s7c}w#NrJnĉbs7o{!N3{loaclcx<rɭ/m,'2v<0st:Yf g}6mmmXN p{?ˬ^t:͒%KXfM6N#Bw?RnifaժU!#<œ9s̶mwôiND}Q~TUo;<l Flv3sIݻ}t:{e:ht}>/mlT HO7Yr%ׯ?ȣ>ʪUD"JdYfҤITWWp]vF+d28pDQj@K0 /7u4GlMr}w2Vn~ iꄑы믿%K+o|r+!TSSܹsDd۷uVbXx(IVXgɣ>:طn):(ymy0NٶmsPw֚ຮSTTĴi(455qenM].3f L~VX}ʕ+rcåklK}4s4&O$IZ|+=n7SNpovك(#2uuu:LSSwOߟ$zD\,a"D#.U:ѱ6S>^z%-[FKK [.`ɜwy"H$'|®]40V]]ٳ[$9s0i$Z[[ۿ[9{Ay#16TȽfsoiu6V!ܮlG6N}w}7oacTWW#'ԤR){\5:u ɜZZZ@mm-wu]ws)DDsϵry(SXXi:*Bkk+kbʔ)466h}3ɥyZ`y0`~FwwOu\.'Op@줩 e****tH$b>])--|@qjN8*lիπCYmIb„ :oww7+!4qD)J~+`^+??3fPQQal8^i_455zۄ 0uYXXH}}j5r6t:fʔ)v:M6z_W+ppARWW#Pӽ9Cd2/*$I&L8p@{{0Iv?bv:̟!ynv]6ra)D"",++l@btvvFd&~.))P׹eY&??ׯ5Egfup='<6Ly vY&p8 rTUUp8NZf8MqE Wg>|>R/'9ذa–*HG4&OlXPUNkuՂݜN'&MbҤI"p8̞={,Qϧ{uz_^UW]PY'|(D?~cw5cm>T7tYBr(R\\D"+?%I(--hf8 vuKf$I/^%.d߾Zj"%%%\r"xwٳg\uU\zS~ BuûiVA0"H^\\Lii進f8 :,\]x>< s"+IYy϶?^AUU,YW_c?ml<:Y=1AMӬczzzF= & =4`0H(*LȲ(f++++R\\ ud@'"xO?oum8^y $-[p9琗Guu5$I븣՞tR^^>`뭻dNn+֙ ˲eKfN: O?"U?in1\z^/z+ ,\m0a)momm% 2ejjj(//h4 5џn[މ"H  8zzzH$5̙ùk=$ӟ0u|EL\+waǎu]ҥK 66y0S2 _|̜9|\.XdMk*B0MAhhh`9Hƒ{:-D%9opYgqXjF%p8̇~Hee%ոndY&NN{g QciD""aWH$ү>i$r)@MX<Mhoo#SQQ-œ9sNٰsFr>f8ضmxAeۍ:.wF455uH@577[]r`t%%%,[kGy?ntZle}Bݼ+*1osLjǕ[غ4Mq㪹lBas~vC?uuu˗/pqqײcbaQɇ$I$oOx#@CްTjl \yѣKlڴIt7*))a$I^y0lD"agggN0(***Ne͛sz0۶m#nŋ3iҤ Zťx^u;v /w^n[n#zj^xAs}>477ubᰨ׋Vq&Mߙ;wElذ?Or|_gΜ9n'&gk+h>~_,Eqh_|ԩS羯kP￟|b݂`۶mb EQ[ۘ9s덺4Bctuo|{VjjjXd k׮eٲe;>Xd [l᷿-7(**:oŋrʳ΋-S2{l֮]<̙3ظq#> w}7?Hmj8|\%X,ڵkY~=P q~CImذm۶+fg@'L@ss3^{-ʼy>'N#7nEqm1y>C# xAi^~e֬YC,`w}£477o>ǛoIoo/>apUWtR-[6l.iz-^}O䈿os5׌ٹ)s Au^~e{9ҥK:uÇYv-oϘ1{9syK#ظq#k֬ɓ'tR>p8̺ux饗Di|fQzj1{wr.΍t:y . s"f֭[r}͝)3Hlذk 5c.Ui.//|5n{9os!?WW>x;Yf > uuucd6m4;龎p=QÇj*89sInw12u7p5װd:?FQ]yꩧxuF~4 :ׯ穧믿e˖QWW7g5y]vj*Z[[Qo~{n+QX,?ڵkxwy'K.ue9J<<䓢 4yd~0iҤ>5P5kְ~zt]%Kh"4A, mXz5(׾5.]W. 4Bca-[&?>V:سg 1p[n9Oޙ>Fꦛnb͚5,Y?ϽO?}Ascv>.U)Ow^}QBUUU P9sz6pfcy6p(yqG<Z￟B֭[ǽ˻;ҧvF455%HEŘ7*D"+Xjt<裣fه3бfw}7WfΜ9B!xVZ% h4[o /d2#xfcx检Yl7nb Jʹ<ӍX /Yb˖-n:>N:iڷywO$F|+3}իW3yd:::XlO?%:pk +uid8t6mСC$>ۡǘ7$M63R6^U?Gl4o\Kc4iWFQ|d$>$*LJ(A c<Cd2:u۷JF윇c4TXn+V HpBx >Ͻߑs={r3{Cӿ;K߻k<߽c￟˗xw0b`R_/C "K9_߭zw><@gg''N jy*۷W_}ȼu`?0uL= id0/f2S4 UU}Vbݺuw}ƒpB xG8pd/~skAkoa`Y0 0gAtouܽJ&HEEE;|#}Å;w:l4Ճ4!ҟ`#hI$I:::x<ONbhvQa˖-q66)xTh^dU^^$zNf>xmK|EQ*ٰ=Lcgi`Nna١;3zY8{_s9d2i̘1 t]s6>|˗AUU<3f8q_W䍨t+ử_^02z#/]׍P#Jx5kSL u|^Y9~Gٴi~˗M] ;rG}t~w6mb֭:SW0 [-zdXlA=ÄJ< (Μ9s1455wӖOuR|w,BE`O>9<~ܢ%ʪ-)(Y5(E[/ѻb${(_D0cGa(u$I^{5~c6l衇x<iX$'!F:<3vؑ=kv3ii T _(ːJd2L:~bcÆ \t:… yի9]4}I9rtʩ- t-fg=ɕepeQ d⇋/Qa{[[wXWY;w nN^!" )]Y#c 킴RoAOO LsrB1ydO֭[9t̞=s}qQdˑ'eUձCrT;;5hHg{ܐe:QLLSenk7?rD4]3{ll8x \ywyNv)FD~SRl=ZÀHc'w\Yҗ`:@!cǎѱ1i<]pa/| p!n @ߓdQIIR-edO+:d#8ᠨϲ:(CVehT5Y_Itcۇ444 <Ƥtƍnj3x9u  ظq#">.g3VRO#˔$[dQ|" Ɏ.yrgII{3( dLD!d&{طo;v9 ؅O믿-[pĉ =ʾ}0H iȓ̦,Φ Ai0 $Sf )-Љ EEze;0S8+xUҘ1[8 CNss gFyy9ӦMcʹM7t޽oNss3;Ix=eӠ~6YZ:6Hi )>Țz|IYNM Y)ё8Ч24'~֭[d2Ccc[oВ.H19c&ax#އ'Ø>tC#ۏB$_W+{1o!W؞mS1{B4wxWx衇@<ϫ{>x>uhG}æMXryȑ#I,clL1]JFmadd`,H,aXi2ɾB$oϖ9V),z& 5^lhW^y~xy>tuu_c+IFͬ-'vFmPө[ުۚvZMSN1> ***XbHJco>(|xU@@Rge;j.{@]3O49zg,}uKSB] 2x I !ovH:${#bA'ylBքlM'''"J)5g+`.>;~ رc?Oxꩧ(//1IHH IVN ){l3R4/;:djk݃t+nDQ4aAU!m]eGr9QafUj`#1e<]p١( ˗/`,_+W }ss3x f< z%IoY~*^VI}%ײ=k钒j#MCpzR*H~$I\k3_jdSQQ$LzdPlX#Jfz8DĢ2%EĢUF*(f*a&fz1R1k[3:5~$oW)@<+_ y h~JΧ{R|F؅~/r/K1M XܢY$hӔaD#HD-IDl ])G8f:a=1Ss&ink딥K~AǏKaCCےLرcկxM.L?HhKO .^ApZ+^ՙgfl)u)%(e)(m-}9stq c?DuVX9sXf FiJH E{n$xAg膣c>Bt=s; |ݳigM3=i3)L=eoKSMXbŐ3aýFL 4s"-`S`NQȮ gTe|V,7힡e2ds9gBI7b$1c -[ʕ+YbO>$Ē@Z64A2m6UOA"oWt( Nj42F /XX!CG{Z}y kݣ:+A֭Li***!Ϙ1]y۷M6|rdY3hbf14i9|V} hYf4p64(cZ#i"a ݒ04mOkEU5,01a<8ͮ]"<=zƏ׳:#i6.ڲ"3kq84'bs`G`9&LL k8ءfIBu ;=ݘ09 Cg|Pl VQς´ 곖1t$ô!ݐ >ٻ7M۹iٰvgiH.+`"!Kuq=ϳ ]p1thllp8LLOYptEF~֑t,HiLXH2d%+uiZd$gf;bMS?#a2Hvg4LAq-ᘌ Ç+)r Mzjр>рM[2) ŝhɕXS33@l:MwU״ 8K=cC_0Sʔ۔-DYUv;}evX4N[(Ny9 /:xvY3sജDCu$0|z5JaC x^@@7lREObʥCQTMYcylj4gjEl*C VK8L/|\qG?D.t]?dÆ7ؽ7ÇMADm!Jh# FC Y]szY:' M;dC=u?P r뭷r= =a#KrA eG{ŝCN ):dXu3dd1uIKe je$9KHFM׺!|@:d2I64MMԩSٵk!;gv@ab:uI'!3{Cӳu Q'MU) `ʠș Y+;\4g)S:u*1۶bW.\'t]'DžNɲ_ϵ^GCf'k,`Y$8<ȶٺ&I'`[%2&1ec@L ;/+`c%/`.> at]G$=o~Əŋ0 $IB$JOܹM~^}]Z0K4K`U$9d{/E,wc5uXr BװcGİ8aWKb;徍Yl'NdҤI]8,Pd2dY֯_ϦMC.r0eʔA^MM Ǐk#Zۏw[$bv՜\:gYWj"&Di0,N}}Ǽ*N8?Oss3?ywcP={g9rY…?2?k׮駟ɓ르 4M#JÇ~O<O<}PeUVV27WOD/@/GȚ٩,>+I6i#pHm$եtdLSL$L1MK*_?@ id2"I6%*SLnk^%_ocۉn ֔d{V$te]ZXhv<#JCȥEyP&wGydX!]u`0H&Mػw/:Noo/]]]dY$IUU裏d2㡨 ̙iL$0'Or2^"ٕ?bS j6@g?? AZۂcV..mI$!((( kqI(**d줻[ȊPE~D"A<GUU|>UUUqp Ǵ$ n(ȵd!N/.$G蝤z=/;w:/+`bha&Dh4(!2vb޽ d0 ***$h4J[[x@ @yy9iD"7z)**br-|A:&It'awob"]?pz,a$N D|W}ٽ,^|+vGx~`R)JKK twwe8v%%%ё,!2T2$I[}fȲLii)dD"E̙ ())"$qaw@+ϙb;(-(9$ͪ7(I&/G~~ +pČ+#`"`2d2޽EQDL2$J1ad2D*D" zx<^/X ]),,$NDHRIYY ,`©qRg`رݬ\{3 NAHpEA6'NN%߆8"KdP(,iMMMkb1ڨ~x%N7nLfx_.uO>eY B|> 򗿤T*ȑ#ܹ'N0~xL_N}}=2qDSYYI:H$B[[@ӧS[[+b&L@]]@t:M47G駟UМTUrS)M!CH^΄6@sLRֺah)g1Jl^2 >C:fÆ ߿_xhNt]FP`0ȩS0Mj:ީiz Z?q2 eee/6zuqmQWW7 k-)L@əud<{dj̝;%K w1Fᤩ|,8p_|L&CWW~BEWWDS]]aq;F4SQQ! Ȳ.oEEEI0M(**bԩ,X+#X0usrrR}M'rS{.Bn<]]=$ BaxPUIx饗ضm 9 'RPP,tuuۋ7RhooI줾z{{줿Y7n~L&C[[~?>^qf69iַɓXWW;v̈́2(gUu$^?YB;=lp~؁3)$HV+EL{n6l@(SUd(--EG88Ԡ*H#Gdg$JX^ 4---DQ|>(B"  NK<UUU,^ٳgb2ȕsr9aDV\9r 8b4MbpxPw#gjdn9_x6oތ(B $RQQAyy9D"1$ QXX(Bmm-B$se!2x'Oyx^x<\i6% vM&6@$Q[z MMM}f(S_ :O_]4OW)`a*(xљtyk.6l@SSi fJ__X C}}=eeeȲL__ݘIqq1ZDqqf&0'N SXXHuuEtvvkuG,i3]#H`ǏgѢE\x8v/g0 AtѸCf<]秀] 8<3 ^Ms:EEEHD$a>z~?tvvd(//gҤI"'N4ME!"Y%IFhkk#( ~Ar"h4J 1e/nNȑ#!oǦW;#[\q˗/?a„3~|$I"ɐJ;͵6| FƗ/鲔dhhhYiiiUUill`0aYH<z&OL*[УꨮD"A8FeJJJ(((.,x=χ|;zN*ѝuocJ *aH$d2iA[[0n8R`v8\vehFkk+p*0SVV&jdOcc#@@<(TUUQPP 8ON}}=bf@ee%]vp8ѣGEseAKKH/ Ƌ30NS\\LII sWh"^/-pSţx?;HRttt~Сs$ 訬_~P0\S^^NMM ,ގNfpfp'Oׇeԩb~{kk+---hF D4E644p( UѩT*eڴi0r+D"AGGPH ;4[roƍl޼H$ɓ;+?G0hT,z&"2傋 BzX]]MqqIp~?Oogg'aPUU%7p3g43a„ FG.2 ---_C g-3WSS/iQ uFnO|wǎ_^E!h-J>F 㴷L&)))aڴix^:@ hnnfӧOg޼yCttI?UYU) a"Gx$iir)~_o>lv|MvލiX$JYYcTJG2KpA4hnn.H4eN&b 'A}}=|>OWU&M$Jl]שdԩ\}DZ|;K:oadhP$l޼Y0Fal۶ IE4A:T^%I11h'f:tp8Lgg'L2qƉBm:fɂ sw2sL^tPzillR8_Si+;i_?~T*E:f1K_]s%iG*L93jkkEG2ѹiSD͛GMMX|l6+CUUܞ.a Bb:0Iؼy3۷oZi&r󥥥,\oA:}vcѨBÂA,%%%L4IֆiNEEh]ש"9s&3f4M3h$owUr<v1E{Eg SXݺu+۶m# r-%c[o~UرW^yE8*NKIPO0+++,J3hҤI3a/*EEE/ 4G +`.β6HD8)$>x 8O'G藦irm1|" t~虪Kם&RYY]wEMMh+~?^W,@9pQp"NߑHd2)W"~~m`\$@7o7g^^}9虮뢣S|-..ofܹb!H$"f:M}>E//Fd #W.\ /N[9V5IDATT*%9rD,x-zṘ1gcʜI"`¤i bR;Լ^Xܩ#FtaW.\ / N:p}_27nsW_}JFd2<JJJ>Q 4k}ֺ]~3H>n;W\җ… b1hE]=lc@F7nh&jm~qY.y88p1;w.=w)**b…?fƌD":;; B455ZSS#_j51l!I4447n@l6K__===aɤfA:s&:}{ncp]׹yYx1iN(رcZFuu5&Md8龎ꙟiE!Du]v>,yκLFZc:͹p1ڑL& b18̾sVu)<]4 Re4i`܌&̅+`.NjiFā(qh$Fґ!A^O]~$ H&q'Vp?DZ8]rg% { 1c]v]aT\vbxa̒Jd2Qgp ˲,8/yϞ=ttt]꫙1c7?1ꌧW.\ ?fI23t]?w8z(Ǐ2i$nF//F̅+`.lD"A,ӦPXX(~;W^y /JeL㪪R\\L}}=]]]466R[[Kmm-}bz)--=t1u<ǡ &0a&N磦FP|>'NooywC56&<ϳzL&'NysUWj ..ZbdR}L…%g<]pb(Nv… k<]pO.\ h˷q#ǽ:K… 7lw… cf:={ ~?~..τ5]^g͚ŬYFt}Η  ?GLyӎ:\p\~|gC:T%7-Y{>߻б|wtqi\cu|ma \2Ás| 8[}cI/x:v ъm.$,cq3}O.\8ȍN/䎟kl(Y93ȋ7\Ryϲϳ]!pbmw… @^R.EJh˷q#g,_yp`T\p"_O.\Ӆ ..t… k<]pRFh,ǽ::|\ .\ /rC*IENDB`filius-2.9.4+ds/src/info/img/DHCPconf1.png000066400000000000000000000410201506574241500201020ustar00rootroot00000000000000PNG  IHDR*3lsRGB pHYs+AIDATx^ixUǫzd}fa/>W-MvEPF6E@`+Ȁl I] ap\}sNUuuwu;Iw8էy?9]ϝ;' D _WL`LpuT%c:H~}0el2;7o6i… nA@ l=@ @ءc   I+:QD,{)"vlBX: 篤I{PM3d ݁Xg3ȯ0Gw   V'eA@@L@V0<0!pb7|xԴ&^: "_!f!"zx/̋ 1W^j#WI2$NgS_iTcE8I)mFyjH5Dny5WzI4%DkB;y;Cz2II>o7MQjȬ}TN}Ŀ#*s/,zt#GN[0]᧔bs5 0;yLx]gﴏpetEgcF@7$͗I?w=kW(Mu|vn_͞CpڵL+1} Rk%SFMQuOgMMJwWswOoP?,Ӕp6K+ƭ[~[%;VtvTJkg}_Ɣ0S,ׯU2*s6I^7+ awL`G:..Uf3UK3W9r$IRW#*AS>he!A^2-g]s2ԏX3iZ-sjRq΄j/?u1d\)1RyCfҦuOT+.ZLH$/_{q_6h~i mbZ;{} YeYn6ͽEZ\ hui6[hKGi>JX*Ӝ*˱}gjMZ@*Q^tY?cBxXHCIS~rhܷg؇WVU7$FUiRZZzΙ9L7D~;H*jEN ̃XUJKM}Ց.69K:/[ꚬ4y.9}b?vRD]䮽Z}xe9y$73㍫RRbڲ/_09IUidSb __AA|*R$b8KC[ydBd$q/]\6[w_"/}Z@5HęG&>}ɖ -ܫ֍HDzm~ytˠG5R9 $r`CՏ\pZU&nݨo۷} ΂aفQeᐺuBGU/ {~%)>]еz~iuVgBsm\\ľZMT6EiKӀFCAL@v+R8ƴf($[ݩ:Oꋫ :HHyd(AG'8(E5c[ D '"&Ii;C~JR\͠e9鯪,ShT'.I.d^qNQhU0@PSȰ/!u5,@YKdwq=c"H]-tAJf{uеkR!ھفTzW?Tx`@ nq1M[ϔl)JLbu:7 }Jlvb'Xˣðа#J⣡AC'iU\ȏM_TkU]pF $:$vbGI5~ .tӌ{jiTֻ;ޘTfSE!eV:nF57c|-aFUsohv;Av=߯Mߒ 5ZM8X<бzۥ:1.KbHja֥Qt )uau^7Ҁys8/|qIf9?sڎ׎y⪑Ӷ8*s}U9U c[6fkִ¾1[ŵ "\ˮRY# Y3ڷEˌޭ<`SXK3wk-'˙-k*qQGt}T?|{Pz@ XF]}Ld>\>/!D5w!e~4z 7R^5Sk ^o#! }c>a k1=B2Q5͘Þ_ ܇b;Aڞ*D#RW_4b2YdpXT2}72[g8@<o OA@'GL+ȯFg    9   a%V @@@dle$ؼ?[-8YlSGA@@@ 3@@@J+@~a@X @~7:[Y-8 6;1yt!m_A@@#eӤI+FMNA_    VV @@@(V 1@o3@${),8 ~?<k bU0up@,>b&@X @~7:/+ȯFg    (X@Ͽ{ zK[> A  WXq38pD?sfdR u@ D B6B~E9 05!@1\j#W 0Q'tK.:,G&>2^!@+@ l{|kיOwUYge6esÑ6i+meYK}&V}LLo.8Oi61k|[<ِy8#Ѷ^vyzqO37w|[uؿ5"07]L_u  G{1M6ee}4(9w:>鋩(+|4 1w=J}GD*cbþuV!eCڋ Jޑ@<+9(ԬW''_{#ۆ)ETMȯ8  PW?\xtt-9ߍ^fKtO=3wRAǟ,Or.;%STRʲ X)N:;>,Ͷ0TԂWƹ Ld墜YqVƒs퓇|vwMQs+.9&íx@$ZJSҭvʈm?@PcOK{n(t]MdT"05U@"md)4/Vi?Rw['2ܥ[HW/ cٓv)cتb5ۂS.&\% ?x47JLL$߸N$O[NNNiw}e\ `HQ%H ({,ѫ?X+cMJovo?=G {7j*kt4ܤϷNV_BD{?hRFIf]jX+VZ%v|65Y9c!c96_qeLT%믧إ6(SB! (jYg;-[:HOi'fnBJ2S" ^D}J}˛QZC*Ι?uOBaGn]k|njQVq.Ѹ~NƿKY[l|%5z5H.uj4MEgӧR信Ay61xMAv}..'Pc' Fǽ~+w~2[rj.ypeM |JHxt&XQn'=>yW3܉.FbXR:sKK}nbJ*XeB{ilzw( >ӭ~j[sC$IQGwkǘq[i4y;/6~J}5\>P1j a&p+("n>6&:rR*TygȐ!ռ2{sΙOڎ3##NS=>Vu dS>ߦ~`QgcB y6訝U9* zb9v.Ac9'hՎg5Lon/ҧ)…/"&)*:/?_mp;Ci*]xjPgr3s0)p(+4&Ӱ{7pYw= J\yl@B-K2=\qOһ~׿X%;^RnY˟D: a cnнn1tɘEs Lw:OdnS_;{8*ѱ_' CJb"Bwlvv(ݲk!;jNIb@ 1]0t-OqCzW@-f>˅rX{ЩS:lW=#Rݳ)[?~n'(SڜgQ9u$ \Ԩm 3:']6o2([>n{r;ZCmhE s:u?v~}4=Dh:Mwwnn}sv3ŗ|]o[4lU VJ5͘x5 L~rO 6~W|{dnadwr35p#~5-?u~I^rc$HAy$GQ{hNydmcul*{ ڸQC#KWVdvz0  v~cR>Cȯg  $`)`@8gW"Dd#ޯH M@,>x [Kh`ꐣABz<@ !d5u0+F>Ԟ\rq:g\;&0Ykҳ>K;g&peq'yWϿ'+)7\w8@lxY@|%_wv GŤC|}V~hCul~:;dJkϯgh݉ X! P,&BvW婼b)1mXXdtV\8y蒿%M:~q.N޽9iפ Kw5k |SmHS3>V4}z7o@tt ?.+Mmh%d]>ʈ@ ;tj6vor۝f'˝U9ɬ /hydPªQG?S!(\(׿~-Y5W)۩@.I--tV_xB| 쎍RJZ8_V,ʔ5KޢkZ;ay۴6[hKG$ڽTZx,<YO 7h@nW;|I\fIԪNROl=KF\#./V_=1rX$f?KSh\|X3YhZaL=2:u<"OsnFPy״Sv6'۳F~[i]̦?ȡT}̜‡1;zSZeT" M/Nf|ۨ ŷ^b47዆m˖39~ͱuPC;B).bM\b/W(&!foz??7ꔕUNgRlnVޯǤ w:sa@Gy+B|jٿA @ D[[R())^^Თ9c3:$!ŎY-L7C^dw79fʝѳp͈9'XJ2lӫt0//+"uOOV{.NWe7)_o⸺rԿ2T?X~Gٽ(y,=>;IXȻguay>PF .[*Ou@qю%g>ߛ;+"Rh6mQ ZyZܩ٧LՔ۽!J MRb#ǂKWô(=ڪ|jAq  G hu}1&)AH~5Cg:^Yîw8J %i\DK)/Hj٩c?Վ֌K&fhsʣ=Vu(_0Otm`huIv#R˸؀Ziw7 9t%!G|\2 "pV91kzGPa_j{WVwk-a D @thnWߡ5Ġ^j oUZBNiB|V†k N0 b{AlG=׻ e\L?wb~U}QrlW7J :DQ3"ڬĉbY1΀Z(|Tf JKQ_wD7tsR9C/ j}zVO;iy\$i=-Nbz_<՞,ޕ _˲wζy'Җ{QH>GQ踯DL-&͖4qZfg࿁t=.hd0bXSLP/ePm&f/( QB+i7ي˽{ᙤo{DƷ#uBɪJ'd|{MӟȸlZ۹I.b}q?ޢJr:N֝qebQݲ엇|LVz:\|9Aֵ nM1-;/@b3f꿘PV!iԄ>c=' +Ly}jrd Z( $S,q_(D* n)7_cw }eY|VgKڶ%#n⭓: ّiK9Dc<}Y;ųwGwצŃrJ%bc,])/=5fwAVȴGZtlO5l%hVZGpȧ ey;KpY_*48WČ@@@PwnQeYAEG{Nۏ9i%_QS?to?;Cc)嗇?=JۤJA>?B=ө=-kkh+al@hNRɸ"R+;I::1asK,PͶ]%? }s1}oPW.++0BP>SAa L&myyyú#]dfFcp)tggE1P7Āa@ dk2#YWd x׵kmz  k   H+G1D0ȯ   d쥈a!O7/ C{_n(|$!{A ! \ i5q&RhLeD+וE$7 + 7\ kGTpdX XĬzo%c+k3 ܠX[8#浮9vl5=4lb7wtj u1 u\o_!C+ '﵍Y#Hc0@Ű,RTޤ-_F"'<_w썯 Y'C@xE~zmY_mݰ{+;iq_)teRlX:H_quZ|N@Lv1e , *)S±j5p_kYȟ>S>Tz`~H:V<ɇѰPHCbC(nh @T\ :Da 'u_͚I붝h,RP}Ucfq8%NΧcIf  V>ATXdRkbXHv<8@QKG[~WW܋4U:rV=\3,WZ-L][50ot&CSL:ĂǾuЋ$|ԣxԬKj<tHA)\Za=g&y|b2}#7.$HT% Xv`Yf>}9JBqJ鳎S8l@$2{ZS:]QL횢 4 ġ=AePjݷ`0.R"",l_4,4DҊb ҔCZ{/SH *ueחR9Gإ:Ng94b*惖_Y 4 xNɻ9}Y_{/8s֗؊ngͥڟkD^Wi#5;jKmː-&;SzQ"iX'b4ojCO@7lj<.?'1B2?_(ѯio҅)] 7~2tdڬQGKkRCS6FgGN偏>v~1:uY\\,%uk!]=WA[_ڟ;ʷm8p9Y~,R-&p}l/-sxq[}BR>];Mqeզ|3!k ʱ<4-wF>8+$G6a@a7,ԐE_+Oe ߊ#}1(j%$@륛Z95(mf}pӰؚ kICV|Ыh͋^\SC$ݻ+(شJٓtG%se8]!ERRM'DG=gn6zݴ41Sc[HVaR\R"&M$iel+/' YƋ4;|tlUUytF]Om+?A@}]mhcf܆?:db@ Eݐ" &@@@W )l2RDĻ?؊_!! !# ~}!A@ | k   D @@@J+KaI0-eq7ub?`(<~a&@X @~7:,>Zqљ Vg[1xlZX|4qO_VM4bA_    VV @@@_V 1mX뮻LFˊ⨛<7o҆E20}1@L@?5 E d63ֈD<F@ @@@"Wr_Q>@@@ @~Eڈ(' c+kQx.߄wS,O΃ fWXq3 +nt   _    V.tfE=sq}?/3 "iҸ ǎ?~Þ3:X9y [Q#35qEaq3&2ynnnaI\ZJ%RdLsfKߋyA40e50@w    ?WIj4ѳSJsVHc6()ġt8=?$X&fY3> ZJGs$,)aG ҩM^Au󏒶3p%<< MVꇝ %eKaiH+x\qi](k&uM9crR$J+ee-s^&oWj[a/q8zuOS@'̃@  <H"'%.β;wN[{Ta]222L=HKʥ StR^R,tѽr޲{FɁBw7R_~.4(E~uFG=})9Bɭ7OWPJ:GYscDYNl"ٰHans))}fhpfM2٫eA@@@ ٯN#6wX/۴%m?XnKUV3e/fK2=wB.[8xFRL5I* y D)E~Qi/Kcjsn0tɲSpԛ2dLeޒΝOϽiY?n|nة[/-Vl +I]B}t5ҩ@@@g`Oց)-B\V7_)c5:_I֭[QHCeY229 ]ej#xA@@,B RWuLy㤿B8K70y?NQ±r=G,>r1}lG(&I$PFt萚ڡÈ L9sgg+#6nA%pC@DV(Z&M j͵&zr<"-[ϻl!Gl8uC& :#qPsYMQU{ҵl]z%cWbGQIә;g}PjҲ/F墦>He%>;ԩ #K@DP~_thI)$J.g/=2a䤮=.S.>ҳk+J}z )HR\%s(RxP]͓yk}] E~<)24,)<\-cfҚ(%vj#Ơq<#(%)V[ErDMA8j2D,ٯ"X)nE(\\, o߾$i~Aގ]zJ\jmf|`*OrSJZuH-HMRXkl%\&GzvjnU;h   E=xYSweɌX?D9 >*I@REc-]yH[kG NI#HI[QⲲˤ(H@G&`,N[Jۙzt)ul@J&*Bֱ SN5wʕ+ZZ$O THYlKR]򫱕h˪4i$VEJ   KM~*i:&ﳐ0{ *\*MhxǷpB[H}hǕR cQdG:_G5p> =-#ާ5RKS DQ k?26B`4i#! u ?:Txh     Z@@~@ g    P2ցYX[:xV[lsU n"WT '|_?F@@@ @~Ep"'cA@@Q5󧅫>0T){C+dZ67"5yt&[䗵т@T?&MX1rlr~|>B S@@@J[ÊizEFIENDB`filius-2.9.4+ds/src/info/img/DHCPconf2.png000066400000000000000000000571271506574241500201220ustar00rootroot00000000000000PNG  IHDRr~sRGB^IDATx^} `\U&&1 Tt,Hh`-uej$"Եغ >@4qZ˘&ЂH 6;;s3s'.ι}s~wϽ77CG0zo{qA!@Bu:S,X0k֬3f_CؑF&} +V\5Aa,g>kN!ez"l #,;vP\bϴpmz%@AK=d((3/]d$=;epqև!oʳ#]woٺxȸ%%%%M/ihc_ʡcoG}7Xs: p~.IcT1y#K͎f:TL9 0 j;Ba'd!Al]5TVѐ8zuz& SrjJvv%聱yt9]zXyT*Ր5h5Mrrkq1t%tlmK ^)>@vw&YT+;\V~Óؼb=Oy8fvbX6u RB1{1Q\V/};?~œrUW+oŁ޺>>[GƎN@~}I1v (qϚo:FI;VE9H-d% ZjAML=a9OTh^Y uO2&qZ52TyqՄYٔU ME^O<+ EYj0fU8R4,m1'YR{KnٚsWRRq#4~;>6:6>6; 1v, 1C8NOclC=P 5cc]hdI`;2k. =;:: S&} %fI~Mf0≖}2tSPV ֝x+@|}pʲPȟ1)xMA^DV/-d4u:ǍіTXҀ c+aBU841\IE Sm#VXh&(2БbC\DL@6jckE~9 %1 1NNrh^#)Isur(ƒC;r wcl3y*9,1>~Xرc`C1IGr+ez E֯ZA0H"W8M>vܴho=~#&֭ᩙ8.\rQ &"fP2#<:Wӥ&fن^lqbsnЅ`VrS#w,} u9bMl}/c͵s>ܙ"3q[Bl(?{×㣯:M?ЏoxϞ'p 1j%Ĥ$aYjPVE2M2'>`%IF숝 (ARA j3#1=$;(׼ +qqhE?lYK*%'i²X&*gSa ZV>ppGP^46( k-rP:R4ㅕVuۆן3R$JȆ1akzMx|5Xz#)g'# lpv;Ϻu[zw*JO=clx30悮 јr9};$,\"\h9FeA! N(Ƞ-bpG{Z1*+2+K(.tП.i(6X踁ǝ_Vv̶~.1K8CYy İV7M9ARdbpz-"8#3Ii;bKX٘jTN.oR+Qj %2Lzq"t-U'i)f8ri9cGǎuᣯ>f{o{nr!'aMD6>RP;|ï;C} $ѥM%3>(5f$#JϪF^n1& pgF qd`TRq < R􊱰ȻqFO-d|!02:v:8qčXD CH,Q&TC{؀f&IJ2&l}*4gt uI6mCsǨG2;11UoHiH-rV#&3X &;%q4VBRt%uM91GݶZ0ѱQaQ+X8A د(yE:+aCp4 z;;qءc  .$2ZIV3(ˆ;eagwР̆ddhuFҺ>tN-4"z~?EaP<3Hm,id8\99bUZ<<ރkn'6IЊG2T4W}!fI4fyٿ6 QNRj`Yiläkg/fBB%36s sk'qpa:#3{ B.w cB'8>4HF1O+^ӊqt!wJb}/.g6<տP_ֵ?vyȁϜ!ٷy~K7o; +?seT{}VkOMRBtz5Ҫ3_qcw,^;2cȩ]^}kԕVJ"q"mTR(d,(ސ8 sJ\.>t! &ȇ(@萟ō;5!9C"أ^јY8lAKZ9bL愩)S u*JBQƳPWNXu_;˞E:!BH0 ,uIbV&k{nj{xOLn\/_|NgÕ`ŐW|%׷=UKWVG@yQ-8ah8gƖ1%2?8'Z8m_BnK^z{|ş~n+=w_!g["F" n:e>#r% dÓӴl]|w?c8.6BXSTb4\Հc x(9-!/?pV1#@E2Q>?6i.I3)l[ɠhQ(*("5 MmS01fl]h9kA$6/*plT} æAmRѕ.gvdUAyBv-c v/r[F@Iz˘R ii)!`1.np)YIs뾿Ko9'ɁzמNAܰBcl~o7~+6_]~`g꬟*[+J#PDāDVJљO.թUD\%@fN{kGX 1VI6FU@Is\b_!zHX6ܤ#6#I2v6ۄQ sIbr RxհI&-pQY,XD qصeG(h\&F*#\vNWܬyфthHt8wp5ʋy@79 $%I&.|J{՗O_~acʦ19K0腡׵?|9cxes]}xןs}^]rJ݇]U z.9WؿiHQg5gX;辧ƻKO)bXnh.l~jf49Ee|geY?buY3u-L cW+Z w`gOΕgL!EYoKurԋd$ng9H ͛ޗg7U5_8VHgm{ʛոgX_'7#}W~oGoG~#IgߧQrs޿ۑ[\mm7~4Ѽo?v|ȵgO>~dlÐ/?y /v]@Ã} ᑃo~{ɂg7V:-eMɫ߿?i8c啕ɥN ;TC\w_aAXB9I Pش9y(oe\ZCf#fY10, `ֲ^md?j9#ـ]!kvn]Q'aUlWZѴaetla͉oʴ'Oi'Nvw4iӌb"pҡ/vؖ³" ꄚY]i p,:?=d`f}MkI-4y螟Ys|SXXiM};[ ?3 ",a"}?/G?ⳍg~er䢷\+bOc?v>O^ 3~s=Ua VCLąO|O kKW>x3 /8몥Z}F[\1rύ_usz/ٜ|9XݨbSbhWYUJX̀kl捜~J9QP ,I`9QS,PLleVhNZ76"S($2UrIŪ@ 2.؂Rvt e fu@v]S'O={铦O2sY3`Ϝ1yƬIggΚ>phx &z~⛾' "Pz}u!Z}/n1lL$sZHD $-xV[p=+,17P3oV CuõMHErJZp+ Vb.*x_  ̯&4) ZMg SeDMSttZtq$/n^BfA5oxq\:!҆j:c8qZG̞ubhfќSg͞;ЌYSf̚:s|ٓgϞY)^\g̜-B:plLRH]Eg7MhҮM}1ŌWpϼu-&8WCqqdٍm5 4h4u`߶;{G,p0k.]i|)sr) 3Y_򋏾(/֖ *Ч!5x\s>4%񘱢I:rFw?+&_p%m, QȚt, 1mdIDER:E\qNv}Xٮ3?KҌXV-t$S2j &pkJTt)u 1e;G8( 6)Y#`D5MxPWudl8SY /oY9qiӘ4c'̜1u̩g)ә?' XRv8cxY3N C+ٳ]hdX5;tJsdk嗟xGg\>.#Ospr͸(EanE=uY10GOs?͟YQFkϕƥW h6g.[r/'S>hEM}Q)W6<|Nqb&B%RWlu+ύ;;o" I&'Vzs]$B!CcXmbQsF4L1,ͩT5TgXJk0;H$!_Rt`'&ZsT#} h2x¿39E 1tVrFK/\\~/1֯?m̳S|oɢ K.Y5 Ϋs~|hs5,쐟c@mP'꿠ڂ]hdIx>VRo5bbT[saEɤU #~hdxS.')KuuʢA ׶D=b=$_y8d&jIx/%T.$2#3.5ʆU(zL hcB>rHloKY dnf2ߪI"YQfBKUrT납SH`[\4\,iF<zC-^-D!ISS~7_{([q5_d"(>]{eHc]&rUT2J| 0{|)Kp^=gUc8 RL1:hc^\:Q$\u+[rUNff|}6a?g%sK.ū۔i.jx|&6x뿜@S(Ұ ;YJep4o嬝ܲGcD)%p&1YlrIv7H6>MLQXi N~+\l6$kOZ( (A$b0(bBVb@D"Mplfsb Q sFiYM.(dKEKiϦG$bxe$)N /!*H24ݝ+qM`e~2/vQKku5. pwXL&HYlCY1FL"Hg'E"bD|tkP)D0"#kк6S,XO&ˌ$EMPRE$d4~A/L_1;>{+M'&j"NB9-"p1rЮ71nF"P$R^1ITJyѰ ptV(wO8ϜdS> ;ÆL;5T H"uoLDv$5fff8x1 ^3Ky &dT*6-h LWX[X%aU1*_}a`}dB<,~*bZX|ĤZs Q) $>H+yj5nR~aHJ%:1ҖsS wcbj'hM i IzZƏSDAfӰwyjNMUFj8+7ram3YI,)]X IVH%iS=I ~.\4[ G"Z[gzGoBI0C䯢`-:e\.p@CMŦ$.䰛{h)WaE&9U-2̈݉Ú" $F a$~MQ,Օ\1:xXUa>XIX&fffY,?1)YUm7JJ,bXk=xROOg& M"|V",{7Hx#Ҹ,~`XdgrEAtTQqL*1m6Y wV~nWfZu6-qqBU%fw1^$+6brK%fU5>]3\VV'1a&{N4"цuEi)ɜ;_)휛恘45e>I$dbP8n%9x(ˮ?"cbKx;mҳ:"@ b0))Sς&27%Ne;h#1&wsqv̾Zx OdL~8f?)Uc?,'A}6֜h78QفLXAnq>/jT#&DH[=5 cd58 d=Yuډy~'B"~ւʣ*3l~5 r)UfjR\UQW@®=/A_uM^sWE5[,Ɲ`V7s-/kU@R_iYK(2N69'k;X&6p'νIȆ1pӹBOt ȜY^梷P`n1cI#%ڌRzIU7 .詵䚘^rtT&nqɐ,S[M /9 w}'^3 mxj. ţk8$?$LN,•_nF#벥"Reu2*(BBos@e% Jƫ{=msItI\2x*)u߸tS].z5 9˭/OEo%m2Z5Ž>8~]E"܉cw <`%UHPhv>暨Hv|AbRU[ 8oPe2K:_s޽{o(Ӥ ^)+t06]01cx9 s@BuH fWe(Z͍U`<-Y2`bImt pЪ̓@-@4Dj[i>_)F{:P Ӹv,,mVjº+n"d J9Zk[yQi Vb{? qO5s_p*Ʈ|77,\Wo(gz%$rV9E{._<?Anw$>;n1 q{Ϲv-A Z4[KOUcsD`!88k |@UĖY׶]+Jl(G|8E-Ek17s㶩#&SRUF~nV$rU# )d ]NI6NVKn lS;Z 뒖v?9m%Q"g$ V ǒ7xRԙS>K *KXǒ/Srn9XR*%epPӄ![X(-IZBq" ?&:@ -ɀjՉ5&q$uP̈́!@(.Fۛ(DC R(I{`5oVoC Iϋ *wa[)߂/6f%_ vN#'񁒻q(IHoR[D#'r=gd>ny IHBYVw!dM_@r;IJB TsJ+OY#$x9,#|B@7kLOM9&hFQx;%r]5VIo9'Nُ@TMM>ؑڑ!sod{^qsI9fy}{Q8x}>󝝕 Y-^Gf(778dDSz7i)%#J)虏e[xd:{$d%Yqv;D$@*,܈q@ ~!yCJRd2 [fH꺺]nXelUu۰!zr4O3Cϋ ԚE4lz(„@ UKnٗbc6QQhq-:h0w+Wo1z+VmZfXi4v]v6l`s^ĶPOZd.MO+"T..Q%Ll 7nY]9yΦEYM<ʊ`*`Vb44nb[7 U^AQTtI /e$[[ۜ5 =NRUHj)Ȣ߇e|Ӹ HA*@PhhhAxSXZVapt>?rMZeyO{ csυ^a8b5æZaVO#[cIUW 4J˫"aRL}kkc;zkb\SQ (.apEMG0."ՃX[\2ĥo3ܴ{3q^Kuby9;pXO\+J13 "ۧ%AIQ̯X`k@nacJ| ,"&0q`?KND0TBMJlkut!ӵHī{=m!})u߸t].z5 8;%kyK k/ynp 28 b)KH,;p AMЊ"ʁx-$=fa#=i暨H |V[l=׿V<ڴ 0H;Bu+W髬施{P8<$!@:GBۿ DJB!~.)u]I~Bzk p;n(?{؍6B ҅>X6AI"(]@ -(I+"&,!zu^d%W:n̑Ï^j rF1$#! ++YVʄt&!@⍑ڋ##r hD@ft^cIRf{Z' $<쁒{3tHsLH}=D["pۋ"<p?i;3wMJ\e5;UbG()YcM?I>FIrFI6FuKOS@" bI&6_D6!!F,~n_GfX]IQJr!  WK BT/J 5 )!@<⦛>&ݶTmmٱWR[.+CZ}5A;P*!8UsRVI"n.tj۵`L FcWOOO{ ٰaQlc{](k۴ Nuw+-+[WBC;[[0e6B=F}RS$]lYe\q|)% .^8u鼍UKE50VctV+B8.)FTU1.Էi0ZpY"nYM}}v3Q#;YGROUQ)en]3D†&s<2xZ`jvfq+Y\k~sVCշ*&HM@_bq 3OmlgZRcN nn l1~[(-H^<=X*rpM4ζznAzyb?;o-n8h(3C܇s]q,\yf߻z3DPGwOm쪛T(*HD\1+@2 Z͚(϶zQTPs46,(;)㖆Wo/ZOoA 0|5qP7L"eP?[8k` FԶhEcx6hvY2,&FZ[ʷԁw5kݚw3nO>?cZ|#=wݟ% IH.0 J$dwBpC@R/VЂI*!6l.$!BB __/wn'A rD| 7@zRhK*@N!ey~C䮻)dHXB_tQ]5kVIOd(%o b! n[I+ϯkg$h%%\cI B %$w"XIjJeI")%=I;Z|EJ4tB 8ikBI:EB@} %!~ܗJR,)=A-x9޽ Y v@wj !^N-;"B yK%wRɰ{~j76' %]a<4t @$IʩlC}$J?7X?5w͆J4Jb&JDI߂()Y)8@aCIj]iS`; @ަ'n h$@6 cI|-29pxd:vE{.۱+#B +䠤nG;6t[{zzB=-kR8kCCg00PU!M9FI'$hs`UcRfn۴=L5RZ~_kͭ+f^TxWGl`r|XNVVHBpUt,/`8Juܥ 1^螺 xN(ΜiC)3YCm  /`w",@Rw6]Q; "I-A/2jB?d:=ԝ&/͜kQR;wot[qC9ˇ]rGIxB R([ukwu"P$ Ba'e[[zZl@Vb['$eZE PO_f'!Q>s2 VUj-:͖Ge 5̱5`}ڴjPW4\KAb>҉ S2/M@πi4%e1y8r1sLN ,@A7*V>>S_eGFfQR}}2u&!oX؈+ĩ$8fơ;,ΛW XIŞlgtgШ]\X14JVwU奆R64R,z(Åz;zͨd"4L 7B "I,Y:y~z }4X "ˈ]*uScm jWmol~hle%qL+ʘ!0ʊέ.`/u_Qٮz"baq mZnrhW vi#r)f i5RoiT(#e{['X'kppP%A U&d>5w=<۪=>IZZzD/rmfZMق;Θ3j A[n+di 2kf !y`}pA'fy*=fٝ[ΦYMj.a2@IQXA"N+JeLAvWfz9nmaR _HWEQ7ug U ଩><MB㖡^f;nޯ),E| gqSDc'R]w^ImnDK@r JJT !@$&) J B 1JJ ?*MIE()pRe!aK G*MIAH@r( @JPð|XiHRIBH%D NI72-C#(}<!L%Xm@򲧬}87%)F"jʍ=)o&nSUVS:Ϻ5GR2Lû:~!'ye%!~MXEYx˽!%dlr2w"ɳMJ]YYi 퇙wr,*Woȳ{hlCCgcf_/O^HyB`)wZjk#@m (4O` `nX@J$rΪ߳Tc]:3-7a]]l&Jxdږ>׊fR5Kd#ԻId['.B@"[PW ow*/`4Vo.܆D[pku%V9_i4vA@a*-J'ۄbѰ{Z-OׅB60JFwxl-XA?̮d#ƣ)-fIe2tMF6#ߪjT4GwYȴ&&n^ n^%bIƱ* huSzz/-FHURD(xA/u#wi#RJ]Gǣ)f7# mI LM[Iiֺbf3cI<]`ҍ[YʍKlqb@RM]+ x-NL*c{%LF3jp|c׺jIO} 07byjöeuul:N]+ޡpDJb.FHe 06GI#<8tpj~ErmPbfʖ,2;ahׅyQaiZgA-)*p{+Y9NԔ,d'cC2zz dY3Sjb亁2`dɀ!)@ah@F[LMŒpMS L,#iM`(Vױ= T*͍US?Ø}m{~ 68m'67ќ,idIEXKFo?{^f 9X2صqY̘NdG YDѤUvVRz̳%-=Uᣴaqcw/i @ڠ/~OIT%ihtMmWwiӛ[q0TO񈜰[~(Ҋl揸F@oP,)C@G@0M B@ `BIM@^"` oז}LJ9iI$rrHTB pu܈IB WpmNr@! cI6zm0iDz, N>[Nu K8+-[/>@ hRHN@"x$Hyˤ!3hľ$vIPB/,m!@d RIdġ Bn I{B  Jʲ!qF(' ,(l I{B @JlY-$!PT=OzYQRVv E*ܿݳѧH.u7ќ LTk@"iTki"B鸥iMgB$ !@ ĒO_EE5uuu5G%^F,X0P*ugNz.V mQN)B %ָs_p!@11\{KcUO˒ J[sUPn(eueO#" ak ;׫64ty jkv49L77^:oqtʀ7;r6+.׷iy2h2;G̔9洌UX)ZHϭwg6MhP㆝iխYy1%~h'z1<"IA ttw%8~cp{蔖WBC-T? UQ%fFXil+nCmm18olP .uh+JV=p,R@ٵ Fcv6hV^beo/2cG<ޮՕv֐ YiW^|!D'J* #8,BHW]j g|`TĨl6j|̠ڴld(7誱AÖJ'"z*GwX@M5)K}Lm|D@}$}ʍ`W}'8D+0g<Ƙ0fWYR7x[Ha@:`MKZnb[Mv'1O h:v[৫%O@QVB GȀ6v<;uO}(VXQ0eX1iS֡٢Z!`:maD 70Ӹ]cDD7Y'^(?!d;_kpܸåj/Za; ^ 0bzUepC \/ݔaS 0j`vROKKF>+E@} ik%QKOW2.곻rUQS][wI ˚Z[D|I\7U v YyAjc{Uyl#gMEG=q{Z<Ļf3ns$!!DzNI9)'d!E&K%  D҈  %-Ν,BJʛ$E|@()zt JRIB o Jʛ$E|@()zt JRIB opy mmM%=~N2,ӏ9H qt ac]לP<꧗jo[V@#xko`Wֆsgl C= k~æOz'#$`".Jų~eT} (8~5Mz NIƱܶ;n^81s9iNda6-]6ޠV(g~u;?=B[pxm_5>zӽX k"A,(h,$(ɗbD ǭD5v0wU㮜WbzBJSIqEH/%`׸+?㡸*A@Tq`8y-7߹UjdףC;/ƹVް⽡{;vU9fN1T=!@xGK'=tUKkAQpi p+CƣlqѢ!z㒥> uKx'/|M?Ip ^tU;~8z<>VJ=!@čwKML).Ee~}܃͗bz7?g|P**@)EIk%/ȜRlrBDIÍJ@JH%A'I@bRRNNJup҄!f%xN5a)*% JJ ?*MIE@|ZsJӒt4'!dzA}ǭm!Qs;tPRހH@DIqK~8;YbS=!8c$J!R)xT!mDvR C)zMO}IFj-TIGBYJM G*E)A()%R!RSE‘2@MrJ"NJT!@LEI@EF@XRzB ? %@xT FJ6T!@xF$PQFBH=DIǘZ %y2@ JJ=!@xF(3T RQR1B3DI!zJ3/ݿzݓ֩B(P;oaG/~/>khO<mΌL:eudX;\*k/Ao>~B :b,6Z̄aLpvd/⦏ m Rb(lhXJ\uf>37 ^(FbwTbgUsx׏8 H#mINam0 bdQ*e "aBቑ.M\ɑd["$%69xK#9,#*54]S;s@ *i@2m/!"O:bS-eS>hΜ9ٳgG@≱b<z1AFPՃIiª˾/9: ҡ:gOiŀM9Pc]}3NIbQp1ЯT떞aAfkfy7c_gg/<22~+/DiV3b`!BÖ cW碏GwՒZAQCwc^Kujzmq9z$# HG=J`V@E[ejkW?=d).TME454%R)˯{}N3N|F5Fbx%a[S7ꒌ=^ҵffF`M5RIup7͔-^q38{.̜npTFqA3θ妿&Kn3N?KM+3Ozfxw<]{gf 4oUUkꪫ;[ۛZ;T77V9c41iP$}jZNȢ|yJ(Ȉ`,]Yk򲒀c𘴪8檩@J_}Tȏ#Tza:qQCWw+U>әPWmzU;ELab-m EYн:r>{*M**:NM wFD FxѢfSːfx?U,0^'2F%rE=wu%|KӰA0g$KO=nkcKIˊuԫov5QAy.l3UER*rd:xba  5!+Qe٣V ^'n,)$cg/Q~d Nc%w=F}Sȍlp i+0q.7kAJԊ u,]Xt<7:)%iJAq}"tvfږi-L[kg[k5..;oܕECAfKU(wwts_{eK:W-kڇy̪F11<Pق71 -tF Z# #d@# 8RVvkRޕ(KDB^Lƈ*&ˊՈX/gzBMy_FkS?Jy% p㵗60Jj#֠G@ڀ3OY9b(:(%0XB^ 9,1CnLgL8];3)ζ|[{G[+ 씀ݖW.O xWUn  ꯠ))+У+z(l׆@,XۢC6˕~Mm&xpǤBEh(rpS/WY>NIYfGFל˶Z afB)mg3[J/tg`ZHܻkk#9s#V@XM^Ո9/QYK"0UѩG% udBp5w ;;u"25n\\B2d_"ȤwQ̍jus'zF80H'0K,n=ۮkU}zgO81i6SW}g_\;wij왰goGsë^YPKn{3 f)?nFn W2P(-*0g ЉFV:\B/CxC{q3YYW )6Hc)Cu1Z"VO 1 H~)WV'O_w`CS t NٸYG)EhS'\(Ң#u5`y+Ou۹chwtYbVK[gK[ܮ" wgj>ccq=ս8BøE(wE1<ְU@SlokT__ѻ&& xg&gY_ncë 㼗ۭ1 F ,ykioNS7/fJ&6 5yZw%&>`u@t ġ8lcAt\C!ȍo%4EӒlGʌ{U!PTԥ^n魲9- 'Źuؓ܌pKHM$Y;7lLl @;#Pt1hIhG4fhhp Ɋ5Ej`byꂴ 1| r Ԯo<tNwc5%zb׈9|)>y[IQlem2a5!e;/xFZ )Kh{ vv g{kg6XٳQwIj_ehQ*TP[9i? hro^AtDg24#S7_L7//9?g[2|ntԖjkC!٢oAJO8i;WrΝ^Mt2n_6r׋PJ,9oگ^c"f3 3/?dNg/s!tG 36~#_b.i}v!])$9s5|vӾ"#E .zǏ)e3=x椷>ftOcQ}| Ik6mc<']HAdR-;&{uY Idώbf-X3kB&⭻e֫kγY`[7~C+,e@NFki"٪H,ƜK/2dLaé2 2T|ICW\?Y0P`w|(q-JDS}~W_X;iCGv6SUUէOj"77;N:_r2Cd{}G)g.k/'^,>(eKf:vjUw!~>[dzom9P-/Sm pUƪ6ov|H7Αטv[~EO=o=V)n#2Ӗ)hoʼn?}}wܽuد_޽{ |PA]`%; =v':r;?v?0#Ǐ}ۣoD+H\׼;B7 jYwG*/8RJߗ gtYGv%ͬPfkXxz1A3O驱NdY\Z5sf h] .j9bƟ*DV-;?mqO?e>{ǯ9cρZ W~83qX!Gj/9|CV5`b+jsg3x܍##bܝ~ɟ_EhE^@kUvQz `I14RBN냮&$ qt(Sd)8 1UFq+ *X"_ @oێ@%a|!k̡ҋ&DkI,ه\_rٲ\iID-U+-͖ʖe/)˔ʔsjQmvO_%GQ>h@Tl^Z). ^nj轨0栙8IW_}ŬY"^ɜ0C#LlŮ7|gf,x_Z>;sg}2`泿>qb%|9wf[8k3"ߩY}_v>՟0mi+rbً8?io=~ N`SL&)&:WqUόI|-Oi($-_˷OH._>yIo}{f&s3ǏO3W O` I[>p©Z%on#v8a]NDk6D"~Bl]D6+ v"y ,:\ܹx>ЖӅfaiK8A}c-̥0bfJ]k mQzj{< ~s> ZAC}@YEMT(-=mtΎ>ٜBNf + * ^\iyAyYTيy+S.28'翅S\_Z蚲UN4/+lyiLp,2fJņ.-8F}TvutM%%%d_~DΜ L>*Zi<]^Y!zEc!z9Gs*gۏP>b<1 ;Kߙ͎Ћ z̿5ed?~Įɹz_"{z>}@(uRO^y;!1z>i(JGr6` ~yxL͋_]7Oa-Jܣ|z߽}m'JD8(53bKh}:tiz': Ȑz]2:*dpFcϤ衆nϤ?N9ǯՀO>(54N^Y^RBRj4k >B.#ˬԙi7[g5v,(*(`X{Y,,S >}E\oy~mNq %W%+rF4;[R+-SU-7W^V+ ڽ=[^WWwg|;v,/..rf|$;UvkeX-#?h9bC:K{^RX|r8Son#~G5䕻"^W5 +3QAY3bݻI>Z<i欀K9p?gϘy^}6!o$9`-joM೒o~MoD|fcI*l}7q\{Ճ<2w52{w1R AKz!_Rk-6Em)e7R7ʠXdUܺš}K_.[j|].x[>RC'ՌF?Yd,|IGO8`i" eJX?%E%_sSUEE%,gu;C_EmUMq'Vd#h)*),.-W}wJ o"q ӻW\? U+\Ŋ<{ "qG۫^)ڇϮ?裢ٝw9sLhUVx}~]W[M=<'ʿ @O9A<U|73a@0.y,{S]Dfہ՛O2ݮ~PKlq0/#etMijmMuHlMڿO|;N~l'=vQ[iЉq1ȅMJT_%#[R,l3;y8`~G?ėp5k?zb?/~s=U7l;7·w7 u?'9s< y@$. W9N"k * f`QoH ( k.٥haV־D:M ®+K$0MGuM; Wi²ͤb,^No׬); 73h@^%%>*vYnViLE#E"stu2~ KD\\ѻwqEEieEqEeqʒޕŲD"Ǖ-*V.(>~W|I#)x),UK-8f[^Y;iQb{vM\h*S ;ņmq\zXM!-woq>ROT_&=k]q0b̳G >2UlƮyo6/ee/X G3[w労<?Z #;{{»\ފyg7Wpn7^ʩcT}\Ď3|&s#6˼>ۀnkZ.VM/l4{5K _8AY9l_q9s`җUAlgxLo}"(wn!Yb"`1]?$SY6fOoÂYa~,Y֕+W_lo`\ d=8dFC m\s8\azny#s 8;9jsFQ0>+q3I *1CډP_}7O OZC Nxy/iFoU)%[P]S0#FY]a2~Bhߠ fa$~e_͜")}]xV⎐y^ٳC i #{EE]y5A'T\D}8Pd̬7gl_yaxc4tzwoΥ7u0y R󴗟pO9 a){wD@PI|| GZhdMZp v(V#Cc'N A(+BPS=ѠMY|2Ndh p֍S[*62TAʕeJp`z%S1 ~HNFhO8FhA~aM`bZ6Zw|ZՊ:?Un̟qgZu_ЃB?^XIb῍Ё&yL:,r'.]gG>H^{Mz\k\@J\c6LfeyA-[VYkb(eMMMU N/@N(!)/g 8+"f<?0.5ȍ[c$ aK&j n(跅uެ)ZG-WKq)U;s=`A۫` Kmkʞe.a @T@e: FU\R!W Hyω3! $*ՒNã9gI!K19Կ=Vjc kGAv +#ԕ&XBaԖ{0k?J^c"(0V5q;P58Kq&c t؀0Z̘6%>h1)X> #OݍS":fBG2h?WK06v \w5w'!3@53wГWfx;J%r]hWA:5S Ƌ ƣzpA檈#1y!3crG_c6ظ 쭨dDttV oYk ~;*G;{ A8$aN^B a1:QKsOc{b5hi) AcA)tb6ɀ˧Γu! o|"hb$&ж,a|2Ol4M30ohiM gP\틒 x`2U- wSba1DZ-ܑiay[hb0z۸EĉY+̒%M [FW; ՜ȝu>]gEY$/0;ǐ"ukg_n]>`XG8##(eK@TfF4p r0<=0đF0#sj\ ŪdC/ĭyrÂq9ŋnWxXf4ضxlSxoߋs?w+N!K\,61aOLL3tAR%Zk .ⳁLڇn"kiKz\u%YwRSjK Uoli-~0+y3B`X5C@-Pt`EǷ.c,pƀ^*Uͪ&CJ Q=#)$@uBsGh{ڟכ;cr߬,* 2.ɘTϷZϹ:UPtd#bLϴm+A3].q[[xCAF1@{X.$)6I|\Z$\S)zX"o\geW4@@^* NtLt [O(^׵D3~:|888dڝ4خ@5ws}~"f#Q9kE:h[Qݻ'7^# HG=^{:倃 σ{wT/Z-UwM^Ѱn$SB+Ӗ3TƍBc[c+v ƥ zEa<"_KVC/Mu?~c( 4n쌻$eFݕ5؍`(Dʝ(@<֚ͨ'&z•c4IEZ+Y~B"#.0 > {<% -l'? s@lDVri% QXP' J \Vr4j*?-Rr0%[H:z4㙍XC8T'm&%1O]肽{a. `:A.rvp{ z{V7'ph *'(W\hCڻ#UƬ Z<=`{d<4℥7m9An{2+֓ISi1Kɘ A4aqt`ApQ@4Ԝe (]yIɯ[w!=LDDx'uѥQ9ê} AhD mЅL_gffuN\!v=HG h89w*f 9(#r4.%$‚vfP,  ~& ᮖ9wfb q:tmzۣp"$%#+|(γ,j mND` {# L ` dQڪV Z>5ft*!LЗ L`}'j{&ǦN6Gp HBauA tWDH?HS{0mq6ZZ4fݏ ݭPѠYB?8KD|Pnc:YYiD% ćr>= 1jȒӷ$hYM-p5:ZYxAA 7¤[}DNLAIj:PGͫn[7aDԤjۜcR81DrYiX]p÷{#׈MEP?+'#GSovfIFNxUc5ttgFFi e&$͡ 3EeT\vXJ8r[7{`3ؾ|JcAG!5K=$ XKÏ6oʥ~-ge xf9oGHcWa Gx #pKѵ^sE6({7L˦l-,[w]ǐ5^ﰇc8$a}Y&F,ȫ\`atFEAEdkĨ$^Jim@VE/ኛI161EX|f'ٰ9Ք0̲%n̖ 9J+ ɐ&ظ1{Ʉ+;顈qv+qn 191}TT35qIs'`0;ׅHzݺ mxh*1^T豁J2pSfWk) Wpe, x )Cl55XhzlUy c5@V,,MxftHX44iA$mzc YVVlB "e)%)Vhtr:) 8`VPá6֐pE}3j̆[3EV4iIճu3O [SѮ+ Epihlc53^:b''գMM/܊! gbi`c6ϹďgVF:^lGrfO/(!G.nf1+跽W4;n4gmOG13aPɿ+ h-G k0awܚ2@t&|_'!>5W ZiWHMp8xS/j5; ^ GZ"-go `HMJU6iOKr+WgI>2D1](4#$XBEe4YtR1Bf<2 q0X5#K;NILZ&[DBc8 [@|ignE4|Msv#Y6el`Droc^Pm0Y.`!>Mjĝ?*su8׼y̵a$ 厁%fBP´{Ib 6ޡ,(9ɭ-p5,˗X1 nO`)S+FW'bK:R)$!RȆ+b"N a=X#>tKZ?v=4&;3Af8q EҜr 2+NNݮuߢ8\1КUF6E2˼ eƭRF46t03鑍šb؛jێnjaZ*}{t3#w)%9ù$L 8M2K|DHC^͌M~Lrw 9N iFRc!QH^tvnx:;]Pp}g#i4w8EAR?i{dT ʭ!e]賾SPwr+-qNH)Hw{ti!8fZ8R>|MθЋru+C 9iẮZ%Aq ڂu֣0viȪ/BIX0A/c(ClQ>3]a蛙CׂwO֔1Y*^-!GdR(9spܨy&XyFhW#օN 0@ORSSmEWGFH" ]b;ZAK[fSWK |qtd^H_'fgTL6 ;0^|^Ƴ')~Js؈KFAf/F( οKhutIhm?ͺ4%L](Z?yl 9\bwӔS4Rb9α]'8$_y؂TyzPet Izؘ3}Ҙ]# 1^[漎( WDYJG5UaS=սKá\AqXθ5"MtF#xI!RRUw'AD]'| j+[:R"!kT;10#> VJu] ʇ #-ZG.OƘa"- |,'B,6ye 20sE259 ^)c0evK91'3 FYp;"E;:aWrzݪ:E Vٮʣ>3íLE*,k"s0ؓRm5cBO"f5.ʧfͭ je{dϞ~4CЋYQQl:pͷurC@"of E,sw)/eauFZ42 ,r !jE6 Ph ]4xecaҘİG&d64N]kLu04_R+{,@á8Ë(4'1sv(R|R@@1V'F I[ 6TooB]PJ4-=N؅ݻ%CmHA@db4&4F#G GEaE*])La:"tOd0(U7y;&nc/MQ)] 7|=&QǃP*Sk#!}H<6a#:.{*ˬ$IFgZt ` +i 6̀fO(xm']?ij e^ϕzNiq3g~2>D- #@! N0P-OǴ}`~栵x>, 8Sπ0#[c2gЗ+tƝ5$m.M=[f4CM"i'`TWUpjYПnt~-MvGDIݍ$)hܦ#C8z"nr4L9wjR~Rekrt|SÆ=G9zYxqX~++PuĦ# E Tt1'|gvlwtsZ<{ޚm_}ոvmǼr M>WlNʃKjX /%7tHVKiƥe(D r 2ԇf d5dYKCUJ3}KF(ߡ_X;:$S`3jwm~I}fbjǷݹ}P|U3n(T1Ƹ5!qD8|&514 T6 IxƜ=!!= ASF@߅@ճhuWt@*,@aM,l}m \Johh5cZdd25%^) w+)BP}*s[;73q|%/Ӂ#WPQ Mflhj^MjSB)3CtDB6]>w%X 4gW&+>zvjm݋ 񧾱eނk_vm-MM<OIy-\"VI%ء2H v@z :" 2<,"Ƒ+$ft#JqEV0~ʥ#mΊKr&S_9}ɒ[_iQޚ]ݚ{ͥfxт*^_meNx8H!`* 5@^92oI T1]29B= -r`1ᇄ7 QM} za5!1 -ƸNm p9h Muć{\; /6H^o8LfSTKfӱ&&4a=Da=˿rԲV7r$]<<Ϙk-ּqgum5:b>,ݷ5r2TG2·B_ź7`f`zϑ;r̃nxD_tStHi*%j{R2>zK@TBg}Ҋ 6v&.]gt90jVСͩ]_|\: eMآuLIYD'U>76,6 ^mnԬ(hiɮXUadk1)I#gp& c)؃G </RZ+RB]i}WBdSa99tHVHٳ,Ix~2s" 0tHDh~I72e=Rcwǖ( ""~[4KJKQ XoADOxu-Ь8i9̍Dvi=8{렦-p,c% !#wA`00NCE h^' '4[5Lk0C`M^ؽV.lj IC(̝WnTI:t`DgQ/C&nxrVѤ:dJ t瀠EK /ن)TT)с]QZ\Fn;[oiqoQWqqCv?d>eZ8߶[ygwpոn9=|g,pkj|(1l(Du.ܘT&ạ6R}eSD\R9L߾rW$tA)2˕E~ L3~W&8K\cƣs:Ÿ_V[mzFLmNPNא ڠ /׆o] f̝> @:B̲wBm:fX'g#b54K햞,V)ցK~bDRM!b93L0``3ٶVS,\E N5+|ËkIQkZ5o_b/7٨Ң^}>̴97 lyˊv4Mcv[pI6dL Q\lٰ6Ӡ>>ŖH)4nn$Q MrwyJ7>aX)@.L_[]2ѤW»IGè/{wۖiil-a,H@uga]gQ]ga}GnmQlK{0X߆~|x/pU݃ HIaAISۚYbJܡiyQ{NͻJ35fn2d+f .mYӷlȚKWnֲ76!ڊa;DC_:6} pzf\si r(Z٭-:"`-dHuns/esm쵫{ut(k5e׊/Umڒ_ Y?lklhg;:G->o<.W}e}'Tn+8Ccá'iH>&Ba"Ҧ P\ 93ESheWZ;^ m̀CAƶDSbJYKqw03#6:4G& spêҮP !hrB/20<!p#~3Yvř$fI/ԏNkom7'7k5Iz?.irP'n&;KO(,Su"P4bUhsW۸hjP{{8aa\_Vj-8,nYX{i}|ٿrdAAoϳrn\ Xk@>:<5VÄb#jcSPn -Ԉ.D&e)qȩK-,_gEDZ1vإSlӺ%-5Tbk[7a"oV$hi |cEwvoscpcu' y A&@[VŸ+ qs{g=f XTnl‡k"3=yJغ-!ELj =.U(qrgN.E1:ĉ΋]B6ҵjF5O" 2Z"Ef=8 ~)'F5a񯃔",`,vr ']k6`y^B_]<{OEɋE/|nG+I5-[Ӵ_kbb \ HVmQG@D:Ė[Q?Wt'8e 2s$ JXG|r.cVΒ>J 5~w\:mRSCa;@@LK8-Mks_ߺ _n+΢R}W|xc]%'=}7_qUri 7EvXTMN|mf)Vr \eK X f cBBb#r("ym#$suHp8Mؽ߁5Foľ1gDc $g|0~8OLg6 i®\X\LUyN^#9;1H`6M5tOQy_RE.W8v j+~cc ŋ?fwt54(-*+*Lje:7rw#QJ)@7Vقβ UMƮ|력ϞXY!_ X|%͐=/lX]&|%r営־1di6~UFՎj߸1Ww>#~E_STs-7Md-0Ɩpb(78CfBYo(FyDUN_'/_݉ԍנ{MΧҌ48UI&CS CQ Wx8F"bIV=z UAb9/v@:Tj(w-EN,O]c2_c.C-b[|ts[셫>˰! <0!\VL^dȱPa&_z X ]5T(fE\A+g4[+<壛*~=m͛ n{fm}M6๥u ug7U| v[|7t6{~"fʴ&Lƭʔ\)jKbB=gfi"&D`ȁ%\:n3N ˦g(7!kf=qŇCQVMpqnA5&ۭ|QI;6r:!3!KH&]xR$53ʑH(GO: F-<R4msjȦ$K0_4l`&j~ _6mL(Pپ [Ng|/9`x1g&?Lf^a`e2bb/ȅa/deȒ -!%B`U皊NSaY]AZ7W((\1sÆmb{{sSnvM/񼹹)x>zWMMnmoGmÿzsmK2ZGkgQ]9)i&V;Ś ZKw[ c#[rfA2H!Ye>_8ը쭵5ed4lf=#zH2s[8,ӀeYw8CaG'fBcqIr0$YE8u{QcRI`ҙ)Q墭Ÿf׷ >#% 8Q#<2#E!@/)U?H\pʏUvHd0bc].ϛGZhnDYL 7&.YcP EotV4Pvhg^G#S$?yvHbEko2;k;KVv]]ﵲIMzTVԯWQª¾#{*ު_Ѥ 5(:L8rC[x!IB,Ycr#P+C|ZhR(PכOn.,v9J{.`.gF*^-2Z/,S^3[[$M 8=p7ź8w]-:7o\[T1\vSܰpx5 z HvBP@Ky.Oxo &r&uxjMv_IP'.e$raL,CA 50xHwcʛkmvx°utRke*5¢A֡7*8.׽wxeed~,_Qۑ)hDhD9ޠbDyIȪ;Zso/圱nޑ/t;xhoUnp1S!02hqg*|mTl}DGϥii*&W+l͇;'7ߒoo {Wz/YP[jQT2o9%'='-͝N5"\x?KK&`M؄&W1I! C..YYmrL%ةiڭ"  <)eGNOJXa--jۤBjỎYG&;=_'1H?y|!PxNJ #)E8؝6~%*p@f&vԚ6T2D' #bD牷g]`eqq!+P㏸ 7EbἛdMZ#^bxCmk▸Ř;ZK_ΎY8-֖mmͨ3Ny]Px{j͏evt[ٞgGMUHȜt.`|͌-=vlE?TʐANF ":a Q=˺U}P->:RZ7Em%!zluޒx^H&%?3 2`IQ08X+`"NqeqɷR2qQ;m{`W*Zo'ТFkۇM+~RY&65=L?*Y;khKP(eo$Fye%e[/`b[CEL.;~֟ڎF!d IZ`/;n@DfU]lծ/om(pB͖=FEْ )" 1$WퟂeHj)t ]4h pXFR#5D0<0uxvK$TǖqPB3̈\ jP6Y?4[  WQa۳Y" ✳"L '*/* 䜝NыtUh6+i?ߴ(ce4bMpqhd%oe ǡʄϞE^>ݏ'm&c'KVălVR>u_+uVʤ!оɈ!~pzȨDZ=SpGGh?!AD..[q&`hq)^-)KNc3$8 kNbL;GmMxpIYEkAIuWs@Z2}  eG1"<1qP&|n0S4LLHxɥ4e5PqTd5p~ne{&avHa:׽|a٪ĖЇ,M{וlޭijn1Ȓf ~uA!Ū)"ŎVbynd!#dOLdΛ : ?(O$":d3o7ε;Ry!a%_̙9̨-l_AVv+ouo@z 3p}.8`6p #P%a{hHl8 :,d(1LR3v-|k[6/ݐRXi*% +.FltΙyiw{[ʛ?-2" EE^TXҺfH ſ %:"; !RMCi5ӅEc=጑:E1 ٣)-XR3 E_:01*qUU$BR4uwYίJCܮLd/U?: Д|o Ğ]9qf.4f!:6(]rRDz!@a8a( r z6ƴK| r[{~Gf bb[K(j*l};O7. ea^gFmrU`bI*gt0kA?A}Գgq|ζi1׼rV5/6͕C; dܯd}HǒS~ww~,_q+joX<聣nik A 8]K&)Rn ' ]ZL p'Q>bDdSyUkT k~}Qxо)(+* l?> +Z.q:?H 1<^gxk 8t!Ţ,.8mкjPlVV<^)5/}K׳eea5}\?lA@w4ګ?Xڸi56[k>LAf_2Q:>AM7" wp# 2:kV|)/$)@o\&!푮6n]zӴ5* LuM .t#-8(`qa)uqv3 q \xq[Okmm5b Gn$ YEEBlCh 4Cr+.7 6[TNEPKʑ=G+O`I+t`=jnM?wtƳ7]8ͭ s}U0_ ]9k xg)]\.'!4>f1"vIh]pמc2L Y[8hHD\Ct`G"ܡ98d]%Ro EzR519t}mA W}h\pk>ڹ2W6p;K/*꽪rԢLQjHlA::ͷ_2;xgdhk.mo.y/ǽSV͒kEZF^gKXry;T6bys3SvvJ!Urژ }ԭ^/>9`z5Pfh4E?hlaymCMh2"`jwj$dAS"c[_ J&0 Bd,i$]&cKЙQ\d}Ѐbpqp8L"X8Yv@$M֦WN*1~Km/O¥c\ޯY+c]h`vq4( &dԵ V"Uߨ+wh*{=}GO唻p L12ˆ0!X1. P$=JIʱ.RW1Dw2 m:n+ޚtIqDb@h7 +$[Z+-;46K}?%qxv W\4j* `l?b4E%,F^h +6W|1ZjZ:|4KCpG*}_`S;]RL2$P HoiL*]D|]otvС~U.n+V7q8^CbHH]{tMrR:oO /ZZoI -8+Wr0ӫLBu[[Gڵ "ɮ]c0Y"FkA}MF;r$tD`A@% )S hUId bB /5c* iOj^&~e2W>⨿-]ߊeN\Y-ߚzӏ|Mˆx&}}^ťC dI:/_=hŒA i#E9 "%̍jX j:2N_d\MH~&xi7RCG˜\c$SJ lJXfUn1T1/,p!蕀AHS[-== Rׁ\͉QT2")e%'{=}E2 '^qIVZ5kw (f {YMl?T%V[[ Gl@4WVVз\V]԰j򕫖͚Қk5]#PO_Ôð E 947aG%Zh.i4(| ?VA1'k˽tl}XAYá[ηҸdगݧu 750 S0{ϒYV׷]'ݹy?{_ Y+J u$Df  ƍ֡IK-@"Ii/D<+9M"=;>e3I 82ol8`x&NR Pt'VEݥ>v9ul(O7 ]$R6 %L7q-#D_T%mUfF:}E XajE2WYo" ]QQP^-̜͟S>=x>P[߰ZgB\!fVAvd^ܦ?E,%5r2bNyz"@Rv V=T\f$m%((nNx#ɻeuGUj~ip[flf5,I+~~riܧ_3.|77 %B+t(X4B3kvfR Mi@ VLЕD۳N5Ӓt ⶤGGц2(,#1Xx$W)+':nWH:Ws {ȤF/ke.Mhc6138be,츮M:|TAs|gPLh|^[ÜTG.[$RgQUT-U%hOԏ'UVUT/Yk--z T'̧u2-نI,E-ĥ9.oT)-%5,[Pn~R?t4qIymb5ԴpcxHYe|MEYXuƬW)d,?x{5?'M坭# )QsMfP&YfDMh hS#10%\6% *nQ8x6:6 '}%_-nbhoM Oz1CL8bD߮ շՔHrt.1Ku`^g7OFַּ]n+;fUlZ٦m_wdw쬭_ݸp\a{MVW7glajgc 륎KyL㵊NP;]/1DyؔC{\/}?E$%&΅4B+Ms^'T݅\|tz9ED顔t1Q,W[q:ryuKE(!f%)aE):[[;)f/P57u򳥥S/:{*.?PT,%fxeA5iN`P|j&#E[0-~₽>2=!J y V֏9AŜ1,n5/-~M]3йE+:ɢkOH!S_r}6;/yߦlF3WwUm{hFlFgۜ4nJ?Kk'm˸6п(8?+IЬ[<{(X-+%S \Y`[C,ʇ9<'Ξ/ּʵnHZh|έJlY2_ܸ :rg_ࣺ_9V]+e.^Hk늊nxzeF*#>ϫ(_ 8Tn0l㝇d؞OG>Ry (WNT $~PٖěC*P.Bq~ ?KKu@.R@.Vq4WX޺sjˣwP55"9;[M0!ܷ-n;JT֋U: t=b6(%._-kg#^h5o,MNyYvXnlio딯 {D\\V{ Z!~Kc=>l\Qe#:]kAqؿfҌloX#š"VI~ZhS( gkt!hkBIYIS|2-Ix֣b]]r[ t5k^9dZ}F>Bh+,z*M֣Kkk+"o䓟 V.UWwKˢ0㎾[mw?rmwuϽz'﴾dˇֱ$K/Aau3bRp _|]C l2 ZRk΁TX~!S[g kVCNdV&HѼk^]]+j`VavN (+JcoMxB\94-!Z]yW~0r{U&rye3ՕE|!_ʨsv\:憟1:WgM|\P gvwDwpgLxt07v;C .Bޔؘ-~Y)\yIxD1Ƴh(XX̰|!U+XCG}xԌT^ԊAj(['R4 pEsާ1 =lqW_G|/O 4Lrk~z}`>>Q:˟|}y@1Gpmxwyp}kiyn{guU`g;70k$b^N jlmOb(T+kjqkDCb7͈6]P{]H3(5w­~j&]?j4ѯ4/YMkI;E~%1X%~>1{Qٳ?4됅65Oz;:V7ٲ~VM{c%Թ_TT9OFO8?VA`ͻe7< |_.'oy躥ԋ|rƥh=}U}V[/$|:2*3^'@WlłiRäӻjK_USjի)l 6ev.)2ƙp,LdLR?.Ql{5z @TzGzaBH.X /JƌL}G&"+t4oz_chngxyֱyp=rЖӿ«my|syo/j"]?ccL{W ;s7/>籿o;˟_+T厹rA$å*HRBZ9wp gSw\N6 KaIn e΃-m9hsYNւ[U|@Ēף#A/ Qѷ:nˮ8^6_l;DO$Lu]xI.o{DDNDZ⹢TCt.=ћc8x0g L:I3*( ?ߘ0n-0!Z R8^Zp]-eLH_ `)&T,#ddNKDś&/"8RP̥a2Ӛ^q40fBr}cW| 5CGg?;KO=uܹ77/̴ɻ['{UD8jjn2ek(voz>>tʃVy v=M h;2^5yJ@^A`"䲲zn}Y`P7&{p셌r9GnnWȯ,0 HG,ā7!* , D׿2 ;aٽ/~_R`e_>PjBe_cu$ ʙ*Ss?_n~ rbe Xu|0` v~>/nwO⋿|%wC_qÅ_|щnr2+]GI iW=q %vL#zZ |`[9AА5>co=V.׷K6|cHV.Yb3KD_mH0?,h%ҋϰ!uBK~TB"+rQZ;-ȘNePs#ˇzq]w]MD6]u &o{s:;v-4y DC!TYZW:U*,]In(Y=xh@kzM1 P8j |` 'h$?攥iA+Pc |\C\Z_UqmD|ȇw5•%[<6lgEXdQ 2C3w[~yoϛPͭo@Oߛ_i3׵}5MYxWO&Yi.('d0Fjڵ0{#Qy*|Zx5keCTHd[nkiOIAbV3u04T N{!f1X0msՄQ !,P_Q'1ۨ t#H2]қ_@#+пY*kM@z"l+gݾLfƭ&l %b4 fZ-q5<(@kڪhqy]|$b`1A>ZV`s pքʀ% UJGכm6][w?^dw,\E/:* ?;{q5,}'sotvhi;+{ᮊΡ3iIN]"AѬ c]| q grx2N1FtNKWu@bg5PA h;~,abHŵ(FmaäG( '!HDN'P8^'3Ds@=pЀ1kkL"760˕mc-iАRB۞G|5Rc1>=qYa_JN9CB X^Xѹ}i5t:0ѯ/̗kjVfAA?[ +݅3"r$ߊUx.kۥ1Y`ZQd5K,BFEŋH2-]4˼%  4m7eBq|wKkUlf Wj~,6#;A0/K.пf(Ǩk,hK 1[8W0z^M} t6(CȐ[R[OL~o=W=ϡ:˗.,鍊M{ {シӬog\h8{_ѣǜqo[o7~7"5F92L/oXʦlU"}ӯJ) 6К=SUC7zޑ& }@$.Oؕ:f)ebq 1fy(L Dw9~)CLYA4פ"kvzd09? Qxҋ Ѡ0 jz'P(8 #‚aHjy$FQQѾj/~rsnKܸ[ Z7!o~7!I0y^'Ҷ6z9oȊp.Uc ZZ_T}3ZM6? zso~{ϼf^XO'}럮?vkHG Y|lToTRk>P )%A5&wG6D-quc>l/=5} Ĥ..X!KkWm1S^|gRwVY*.x*#2'&TbP);译:ݯ_ՎN?~'I0Qjv, + z-w>(LarXb6-)$](~ԡlA-Ȇ: K}AG"4kObԴlWC+:xl"-.(sq;n[x +{!in>@sٰB@ ,GSf^4eoh wF}|gCW&y xvIENJ焥 m\y"HtF0i/ rVÏgS ?3[pDLF$ٰSL`wyNL:E)mK Y5rrr~JlRBF|Yu' AU@#Ɖ+s~͚'01"Wj[qvЛBIwhP Ax8{65OlK.GjĠCf֦-7$NJD Hjd؈aԖߒwjįCV ڰb!;LzNf_|q:m@kn^ꫧ[Q(6mtBPb{ᆱנ0/Hy+2mcW<#CYܒ #L)&p^`СrS5Gk3L _mڼ ]5*By RRĂ(Go]wc `щ [ Nߐ#BSy?1KV߳)9:O~=XG4Y)h`櫶xx nA+2~J52T|PTk7}e~X˪Mgk3TbNnl'Soo.ـeC’uֳ}MA 4IXd3|IVع))a*fN .P+{ M*'ZH;HڒVk:ץMHEa+v~_n0ׁOSMۃ3RC&MTi3?qJLQ!oVธlE`&)LE3HVXKN53;RϮ(SPACͤ%YNCwg]oJ  YlSGe3Y,۶m}۠f7Ϙie;U;ڿ xj?3foO' @0fk+e҄`d:sY;kO ╙͓f0bAsrG1keڜ0,%I=gIvɕ "ы hGJc"^ gIUy>Z_K< I A g-99w*4Ǟ5~$R7tSpk70BY{Lg\T5թôX!`cqv>pVzۯ/ztQ1lbR/ - OlyTPkj%UGՑloO\R[_9k񚺵mϫ[Uu;,~H^aeg*q+f|]smnȩjYFhXM0#Z\0R;pEPS5\K0 `q$П,|[;[ӗz+99vE_ b֒yfo$e^|G_߾#),^`r}cSPP*7Oՙښ|̜ՙ5#7w+3ltcKg_6:x&G2OD,Yˣ_YY{i_' -lE/ubpOc+wHZ1d5JL; b"C64Nы݄a Ϯ5Nn G_p+j zL1ZzؚŊ,9H$=JLhP$2K5{d4x5d2-pIcI~w}/ FQrse.5FUN#: 0)T4$< c"*/W]ja~hݼnF$!I?g}07X`qن@iW#ݳ% P)` G di u8H!"N4&tMp [ o3EV@xۈgNs0!`I(9d~4`CihQVeXfcZhrKx3bq`泽 ˚N1=/K1هw|-7Õ% m^0 mVDY_\<(9AyeA,nZynt.8j]3 C1Ccv ]@&`8з\X-V"4#EǫqL(`)ޣ_'̬IY db100e7d@0c}LJKȀbQ]!:dGt ֡(MΖ]4/˴b0'c@f8tMtPHCI"LΗF s 2|:12 ڑ1![0)B%4|ߔ0a{=ac:eSY/0R2Oԁy-6Meƌ0 Xi{]G=$if#zDG>RH8I$jMF+52TT/sL:,M}jtudzbxXx9'oPjKĬYc׷fXs&m21A?\${? ǨZ%:Μ tE)d>aXGj>?ȤX!Y"nW -H(X5w]&CjPb2 A/N;b[4!]pe'ԄQ%*!f-ɻ§q?63AW `\K1.7 n t< MܟS`!*w:.ͣX]4 y,vtIq4~ޭ_XA0@x* +D9/Y6ī86֊VHho/&ʭcj] h5K&ssA`rTٸd_\b*[8&łKZb#mHm5N@ˤf񃄯MDo\aTC\iokEX-@;ޗWDXš-|DYʰZfF٭>L6jȀ-V[0$V:dy^ɢPd>Af3y!m+ E8/zH*fw <D`ϵa5LdX?{`'5Oº.ׅ) ŏǡ !Bt >NŸlsk9)3dBY(:m;c lBs/4ܿC^]zvdQa3Jn5?m50pDqvj (:}H5l~So>fMVu+C6ףW_q!{>|'m_#ko"ɍmsN#G\&C#GeͼǿHx#…P>hHu[ 6F^ .$ga!9vkcY.jDвq4QAt(9ibЭM YFʚPB8oܬo8e;D!̢v?_u;=qN_5:z:l)𻪋 9ni!PL`.-dMvי_n%@^|(x徧kَGOa:,c]Eh؁̏RaXef=l3ss7,vM;\cа#֡8C7vϼnXSJggFϾeMV4yGXvoK0-!ԐtmqKfZlHpfhHBd.426B@b@fX|R8$FQܚLC:"J!N2Ĩ"%:.BJ 37N-7-[O83?\{nS?e~_LY6jO" x箲|Ex@v*q G2QS$bCraY!NNJV̓iVP_fRF)>I EȶvdġEo,IpBCF/&R38AFh0F 2Td ADkBm$̆̓3R`v@D? ! <`Z;ZW2Gw&29~nG2^wգV\'1 S}23t#QT&ffMr)gsgO͌=t{ވCkp }N_=)׳;:tCjO=gփf=Pn;{Zf3uU_pڋ}.>uĤ[\}N0 >, !R䳧g՗\ʳW^th"iԿ~==̜smbלv7,ͷ)id\hl˵k;HZZ?[/og&_mz19ޖ̡ξ{L=P322nOe{Oa9?i >?|W.^rJ+7y]Q?U9VsŏhuU?zҥά؁(Xr,'QؔQڌjإ/&K!WX$ 3BB.U6DU!2']@?ɨ|o"^=]: `vpEgd!!6SP*\l]ܗG-Ї_jO:v~?𥸞Y " ao6 7vѬgͬz܂'\s7NM6 @Y,ØڧߩzoN&3) A)>YN/XbBO8>E3&~዇o'(L#&75Ce%Cv)Ta:Bx&ž7dyL$itp)baz .X]t"`XOk[r5_[{X1gǔ+uTuvaYg„c2O(Lf򙴒̇KU;X6;S\fҐ}3 3d'L4& *sB4T+u|rGʞi~K4`QRq sJ.I2btHz+Y4<&iŹv޲A&3;qpOy$-ƈC"k -aT=9u;2Jv"Ir=#\S@`C)#bDuլz&vg~N2ĕjOIWP*Ϲ?D-V PG/{zYTNTYŠߊs.د>A*k72NوJ%;ژɤȹĨFYN1A 6"riE!;<0NGNsyhޜ@2išA631H/QQNu[X!>3‰X#Jh.m!X󒍌D޴ LfeM%MDAQI"z8DcZvte}viYyL4g"./E4OV4&)V}.r'mѤ~ϼ3kڱי%r-/~O?NN@_[`m{gwדdƫ5!I?~%pLfYmG/߫ylsz`!]Q"^M•Lfx1B>Kq9OPL9 GY&I,tL# YK];+SSBTn4XӞPjtPo;$c?Z;E\ F yJH8'=`wŞxVG cVl:bGKg0n~8'?-2i-[͌m@%(fڪM~i2㹁GL)X$2ba!⾩%>֮4|$**GH&2h8U|nw-<eC /l 04;}:HIddj%reW?O˙0ex;  +\b=k')fV\,^}6S6RԫL> :zwvf(YT;K^g[rw|R,60L}{Lˇ:z͝p/gi<\P$ԹNӎ'.Zʱo"C; ό 'l[V1M[8+CF뉓ͬyfZwqtZ!_AdF@>) 댐=lƍ,~|:q.3cA8mU}B TU=-_~_9p"\& uf.Ntvb }$wNWojx@f[n}Ö-ZV[wS~)ufo-`~5-#7Ue#L,>>y.=S9pG[' Uv b'`L:%'ƥCdÐYw0 Si^Ȩ1&{e bXiff9Y; 0N `BXN_v .߹?`cO] ]6?}w֤^Ʈ{D*@ُIwE,+S+3bx&3񷤠>õߙg߽wfXHs&߬ƙ[4MN=H+tp37=sVXK{;O5ǎyOlDo-v\߾&"j{kn53}Aovڣoj֝2jCs ~&͎o 7X +]+?% odn.{H ydwLDAt"RCnj9r55`x 3vf^uNؿw?li.tĻk^(6ݒOd255/Q]:g=5c{gu2yxX'ѳedm>{M,ι;HdدPV ~ݤX_ta@Dr6D'u@i|#DϾD#<J`|9(=[Fy>UP*г\jXPBG1x~R+; j۱$Nܽxѭd$يXC`STTHwf[w(C5 o l[8.KZ P +BPBӗ@'hN,:Z H:QKcׂ~4'|L"?!HH/;!萫0Uziu`tT.(P`D`BR}q>X.G]BLc%&2l#\QW[Ϻj|Tg Q0 #iu|p5*8Ǥ1%@1ۅ^%AeM$0΄x*-[]!2(k;?뱮6l5h* r`.~p?rk jeT6/vhoM22G$/dאRs2HYͦHX' y\ﺜgG9@\}Q`4M8ytʇssˑ0\24j{RV{3f{dM_ UDO#.m9ۦnK^k:QJ}C-dW{[':}11a|PL/P ;Scy@R("eb2ռ081è&]*g52Ra=&֑FOz\ȇLu[ `Hb&XlF(sE]T_Q0(@$:l"G(ȦcfK"ftUO DEWpGj;$Ď4RDOA6˰j273c`.$S ,O~j|b 70Q!nܓB}=(S!M'Idxry"*\T4hXa-42-kIU1b5#D^&MM>ݠ&Nfڳ &kX]ԎYBlR!DL,˲MzI&(U*!D3.Dcw+hh>U g9@BgO*iطZRa3ƃȅIGgwjc[0dL:_hv!7|B"u$7p.U9pbPV3|.*}a ,1 :yI,J' cNAƉ<$Ä`\Y+MlQ45MRsǜ<Ƣ"_\ukԱGO^NȒvxPwQ$ K߷[q'e·DMbukގg5wW_л:MU' ;>K<`P{ aKPY8ꁨ ca& /[*t'4hr"_%5 m@6{עc̣ %F[RjgQ1|t8wb<F70>vDrt|kR!8fk@Tf& h<|90 D:WJO'ĉ 6M']GL^Ӫ[/.YGcmјDb﹡-v!pARAw`\Odҭ@ ٝwySCI:>Gx,:xx!$B/&汄"z*(739[-FRyP;|DC_)fD@2D(_CbYIڟ$L.'sԊD%!Xg/q45P`#ŴnD?XĹL ,cF]#3|JI\e67#| )H̆gpdP1oH Qf\G3ad;R=__mQ9JhCVI˜%1 uFD8^> ǥif5D˪ ʾy0"꧉ :z9bOёm Y=x:$09B5xd3t!]eՅh5qEK%tw)"FGk&t.b>SB_dF7 cd?FJ´?C0fPh4Ȃ'B8זa\z,F\51*Ф83Nc\h?tp80d2j Ju)ĸ!P@Hf0k^PmK^gPÓ*#3K^וUԘѤ!g[4ER6H)8S"4 ^1J߂ic'*,eDp9_.?*} oAJ1T@z$# tOם?͋lk^fTy"۪zG7a^/PV.Vu` sM?\wZnಭ(yΩbF{⟧gjm+SꠧZz$# #߭O3GP5|;#F );t%Y6|rHV۾K|WeMXӞ< $;;QE& VuP^~3s޲yK>3 sV޶o # :6egʭ[~?3 43ф]nŅ9s?RE\t_tŇRzR㚾/nz⫂+M G=6c誳ݠ4m3[ vouo>mևfîmqo'q_y 'le_^Q`NHo.xW6<& Ɲoφ۲|ܠK2w=if(U\WQCI-Ӻ>Vla>rBIŅ@G=6ck+)*MZ$cv͡Owuկ 7]xU;V2hnbJsӼA̴w]\yVmQ*òg( sΧ[~6C6Z^W~C-Ym37yW]7V%B`t==r豁&@fL-S.bwdE5̫^uCG]w=qyݗs˜[^e19) 04T <<{x""{<\G)˪[ڿzeRq;=߆ ]L!U,ږ+#?Γ~~{g׽s|ʿw}w}wT6]}Yݑ2ȿv< tw$i$ݼ/V\{#@НM>[SN c|Mk\^MNſ^+ԇ_I;luշ^[X^Y~}`~;5~UHϭz(L eg&/6o? { v%+/V_\,WYS[Ƃeo;eɚ哖sի*j{yW_ݙoo?:[C1oo]R[gx9;X$P]̾?~R4O􏡾U:'u. (\ȡzl`A&x`uïojKE*b)=μ@wf!|%/NЃ|;yu7l߯'o<ѱ-WG=kLC;mf<{ӆ^w.[lkDI?kV/xmR۲m9_'uf>ok'xK9o%s}77J(obK7.~,hܬᒝ{I7gNo:M<ʂ:P>'_v Ѹ3_7:c>ulYq@ᤪA:U==r豁&؀m{ͷ]/t/^%X½w"_efQ`3u>w|WN?)n^K3 p1}0jۓVhGδ2׶nXKZ_sq/K8u3c933{<%/!V0\-[,̮G=6cp|&9|#863o&o:g~X 6gGdCOE TA&h-+ils4 +Ntnn4L՗Y:pIǽR_ې[/ȿsܧ|~͏~|9Om**s~=?:)1#TL彮}عP\!}{滷~pYJ&"پd[z3O&"ur+m&؅;j/tG,{VNL3ق8z{c=6M`u~;A .kĀr)I֑]~P"H2qq}Vx%;/\ w_=`G'umz#~qUXqަi.6YӜ7g^&T^_Ygh>;y?8&{}OW@#uϩ.X͝'9c|yΌ}TlY XޗVU\Yٲ8:ڶfm#Pҷѕdo=+1}zjsc4 =!eo.:ӽz/ז6߻;aX &oMLsz?N_=/v ]vǷz箞tUaSk~Ԡs{ÏwGsByX IW|G^}/{ڳ.I\~@Tpvۯ~O |@ێIu Y#K#Itܑp%;G~+] ^瞰z>, SB9*^C 9S41YPB$J ԡsĘhKSUeZɬ7j5}kuwZs*3| SMze|}nQwn=>1rd>kF*PGOX ,_U#L {!{tj` -ݏwM'yv5$3+ ![Z&(˯{b!՘J v"4h 1< cJ_'[X sf,M'(a*B2 lѰOƫJQ8'cJl Fk3g=_twp={;+o]yW}А .Ҳ[N_xaʔ[Vwn=.^w 7}e?e߃yOOhk&W?G7r,_SH'-p%M'zv^-aeSwX~FZ5 ӊasGd\7 uP;a`FɅjyO2P&ZnVs<ޡ=g7h BS!H,":ӝnoȲ/L)Ǧ|Fst)9p]zȹ>"(GٟɅUDUO-ZN\_T]:1a <³.{qܱlƅDr!E @Ϛ I^PX5V9+#. bY`H:lbH2uSKBI}dtV!HU'4KϭBuѝF!ВC?p -$vmiaY.?>UW*|ysdcgn͜*X =_,Y @aaΖepY&sْqb(X)B ";=;=omJgn͜*Va!Np$ TSf0 >+*;$@sFV\Ȑ׳| &LɃS[cA[>jCF5ė'粙#5cYSs"ح ۻ{t=SʦӨщHp+ 1bnEEf|;R[E[]9+/Lħ 񀓛VT5s,0ݚY3r 4'(,!#c Ģ% VįY6T)jlث Xff*?G[SĐ]7tِf&)K(g1ȲȲm^Y/+--[|]T]v:heUUU񌃈KYs,K,,^~ma:[wkmU"/Yr>S@C5m'T-!z2IlTЊ_ezxC>m)7M~sַicƪ<)54nkV(6Qjɷװ[<쐲cvƟgO*g+\#ry/;k)7n)ˮ!9 =65EyK/~&NeS^$ɚ`V52QXk ڦ9&Ma0ؕݩh h& Ȍ4Yۻ zŮqbxf U ԛ;Wܠ X`DV: TCtG[,On_0Ɔ!B-/ Eqw0V"Q.FDu#²T]2KtWn]8[<9[#5ż򥏥RƌJmYstzjFg~ٛK/3n]$r^}A,~z|j {Ё8,RMM4pKU;snoSJf; B%*Ǝ6x'*A9nddM :fĽyxG}$uޝU~@ gVU\\28jXN*@-*'ZJUW/@ "YWvW39.hlPƳRǀ9%xwY[ӎC.װյVfnX0鸊ʚs~2<^Tۉ?*\R"M aG kNsٔ=M2š@[榯#3V ֲ\ub QP" HKKo\:}XcMkc4*+sfU<%Mܷlax=خXj 47ج4KMqQ(5%0LW7Z5g=?x1/DvNP; PGC Vdցry!lQ^.1З?4lQ`0lEͫxo֡ΝNf{y}XNXs?dnq帓>WTAwzpCͼQv:5٥xN mk oiJE9% T*' f.O3C9r֭mP\l*Iax 3k C2 yİ̱Tx6i9iF?1.uە {YrvNYKI1Vݚ4vIJ'"!Z6Q1&66SSD/Ydh2z01exo_#s 1 J*2ZTj?&b (,}ɺw҉A✞rvxfBV-Ώ;-,\hVƌ 6 LV|S"N] ,sUP`+0Z*7]Y r droCrѥ* ˕,$֕JvcݚCjG XBBnrY`g]t Rtsr+.4Bqdr[s fk?ZR.첧ɒ{2j|IgV0S|pmʛhIs*s65/ 7Bv|f; 'e$:,vezA4LUfgVM^%:媍Z N8]U/5T{?8NJxӮT}f1%s,5ZE+5,BhjR&8Z":%#,@8MGmT/蟺`n"K֤gg~BqMuj'v?u ~>l-&2EҐ?9~YzI o[zʹ-<ù ?T k`@%C-bm z/,s0^Pe8y̠$ШZ| 6 |c8w9GvQUH9iKR" y`4bn 3gTѭLFXǧdN,ml@f𺯳 o`͚ش.q ukeb{k{s\3Oo)dF\x?=8~V˙b^'z} -#V*|n 0W謆tRZ ;Vo0I'.qn+u=Ei,H;fV䯥bJ7ߨ5?tѪs|r8c7Ց4B >`5X F<ʙ=K8 Olo , g/,CX&5y%MPjLӓD '0~5{)rf؃{V :+.r?3k+ɑ Gf?Uc!&4Q6+b ʿQb{SzSÈFNbqSװ6~VY:h`߈E9 B"Jψ"c]bHv]\_g͢gǛӳ(ww~8c; GN]۩Ʉ!P`\zPUAZbc@NcYuJl4vxu^znwsPG!Krzgi`G t֡1>Da{/SZs։:ԩ5S DYlz'ч BC4e E~}.gs*KGfA9$%̈́3c9%»‚Ѥiþun1d\ xׅ\{n=Nz!@t| 2 j1o.p9$jL.N ^yЊ(^Ƶ^{O*!@t/.m%[cr(ԃlmЭ 8'!@@{G9ԓі(N{קx|l <Ax(Q>bLN}~!@tr,#A,ZUSbg|7#3S=~5 )pvV31t'NC# ciintƍNZs]]S^^5KbnGݫ{ܹ;gg`vIDATxp?2dA0|Sre'А'76ni=L4(bscN[^no=aJ M+{t[y>JZG9b/ɪNUzɥZ2Ԗ/CX:_EĶ7?WAMݿّuySʚ^5v5*H_:hW_r7lj>:o55M=^}uQGrݻwb~mGt; ln wO^JAYYveV]M٘楐6[6X(]N/O)CϐFXϹ? B=7}|6Kԇ6A3 6͛x7n݊?m/|7opuօ=;bo#ôq鄌 Չ<4CUjq3:K<(4,`o^eǎO> Ô#={jn7{7]c{}_M{MwZm?7vƍ_S;cO|ho?Cq-毾"Lze ' fVGqSiVeΙrň=᧎c caO4"r|O2q#zf!gnŧ;|acO>9?/yݺキλͻ,}ίxi/D{Ҿ=GY߲Ӣ]rFg)r'R@b[R&K`izy"`wiM֖*"BS5;]o_Wsm-EE[m6-#yoݺ͟oi7?AoNc{ zi}y9y%/xvhs)8"F4h~ϯ{~?NfEt0r[Z5۟m7\aV߅jt Ӝ/[̃="J8Յ]-ɊtYst/}s[>jԷoVtz!{-X0λF㧛kv8m_ۑ,9*z_7{ }wtM{ˏNRho$I͠^K C l-b1.uu2Gͦ6\z:9.Q!0?YՔܭφlБO,8vnؽoխWm/S|[VyNnzmLVkc1lmrN.*oNфV2{e1b*9 j^uu{UTT]![1,rO^12]_򇝩_#A& HG!ګT(][y2%Q G}a'>[UݞOT6kZ/A>k^LGK..`^sI6S/p} ngxi؋nWӕ6n/Jܴ_[Ǽ]:_]ܰ/~q}TzkӦn\8hhqW˥[̇\$h~'ǃ_ۏ+ /qZg ЪheUUUc7#N_g% ~-N ֞"(lcWX7xc~{:cԡ:̛'v+bټ 3/_?z{rCm:llT϶;e;wk.*ڼ%Քܔ`͉-PgK:\I)N}e[c:h[> M:h;~4,їTۄKe>nZ<)2O`qrW Wm-c8Gx5/*:{c [p֢+D㚺M+(>paDf(d&g$ɚYPZ[e^[[(yGcRĜJ+'1J/*,h;Gkriibg Cў;WUܳB90neqznٔ) #Ϻl64íz];u|\ vmnuQQjjj`ZBv\]߅$X\+GdcJ[ؖevhuh6q>&jwD":B̉6c1=Dɷ||z>:=YM&ᬌAGU컵{=zogsMY#'ucsg;u8A콧^hH^<1CO V\=~|?"Qk7aݿ6!q5?51Ooczg@Y];,JB,.jjl=S.˾nj 4K9* oO[p҆`Z[PZr~L0|&Wί#F~LeNY*hͼS7N^Qۅ!^1/UVlƄO]bBn9< MN 9 v:Ğrکcf a`/ô!":dZu5_0}͙x0KtD"HSQ72D=+Zr :'n 0ZԐuDR50Pl:h&qAp.8Y(` J G!!c)'( ۮy}5foę[P۷zKG&o`1KwOwϾ_ [7}2rE5N9׾ۣ{=\w58K/ġ*ƨQƫ#1e#h ?j Qx ܭ8;4{5eɷwkK*84P7[&6^ vhbf920bs_W3` ?/hq43 kQ'F!J8t`L?tL CC'U6`'|"NjrR\R*Kp5 &wz6"vWb\=S (CsCјKUBC;H@zlX]ֱ]h6eW9RmGͩ&jo9A_<% b\򛴏I)]nvWtjKu'{lavEg{-떜{[>'_Ǩwpnn0';ѡVii%ÉA{ ^{" ٵxh |G_WAU:(-o"[>l2jySxJg$74D˧'f-[Nbfc#[ xQ(nJexpŃ_ZY VH+)ܢ \X\h;-85p ֳn0~f#$ÝTo9eW5Ǒ]ύ=x4HD*4q%R[')xH옴&!)r"InL{ wTxvp2X,כ?@D^wo>S~] soxGTb}c{:̊CzU5um~޻澓_yU7'߹m#/8y`r>l|+LTj~'27mш?Mjox4L^fQ+N(OKպ,hM>%yd|ăR^j[ċɊ3'N~G'+me3Ϳc)+q;y <| F7'&;\XJKvL\p44 e?w \k%\2[-` A[Bpwt60Eج.gp?A^M:'OмLغ{֫WNmݭ{v;8#]V{/C;p'n! .KIPV%gY)@D~`UW.ͦ)-{J 8Ls!,fw-"f0Z3(d LjUO%[D ڛFy=8gO%t'la{z =.u2wGOhhy{֛3 V5}zc@3.8(7п,=nY!`|,6-}Btvfg`fOk76&Z*>t CӆuӼȏe[Y˼PCϟ<~ ?qsϞ7k3=7}xm}M0C:{nUQΥ&bJS O (>{w>Oy2=+݄ι/`% o<"$W<HPA¿VZEL:π ]|Uܠ_9}6yoڃ=v5 E;uwtSSo }8{4YTtcw8xh>:}2n3>k>m7U/0_˜? O{ٛ/jUϜ>mpLt`ZM^:~-wsh9d $߇ P/g 9w_̎eY)3;{{!݉ΰWZZKmz}ǦK/4+|Zܘ}rvAe!@ Da,5. 挾԰ۯᆾѤzY7O"Q??pɆ׀cC9F#Tr Cb- l ]ؓz9+rBw\Vjw0uwD6>DŎ\&Curh)hK;rQ9ZbO{za {߳<:-AҳWb)ևm!88uY{wyj^GD 򁻭ۿRA[^$8\[w)qk=1 J`Y~PEnU+^Ϊ#rw\Vjwf9Sʁc7L|P}K K#ɠ\{p5񌞥^n ,mz;-;yBYNX0Oqރk[[^(*X#@S{ c}vkLm֌Ŏ!.ZGaqjM in #4V8n ~峧ll;ɞJ|e/F0[.-M7[o׿j&;^8}Ef'gsT>pߠe`_#B7gYb>+]5܍Uf{tvugeC'}bj ۭy[N8cN۸}k߾c߼c`@W++'W|D_? 8Ď ta/;o|y1߽n744t5:t{;t~^½SM ;"xNL %)t!h?$lc?䛟$ٶ*Ͼ{6mzh [Ik}hNcgeyŝUo6>]UzKC6b-zwpWz+SD2ڠ%1ḱWr!e.Rm*I8 )z=ҳ@S|*'Vg, @ϱQ&B :^}񹧸~䀡|YWX0iu"XѩDI(Lau)%V(K(#W,"EOCzB4V2W.'=OX@Qs:0 :o3oעsˠך7iS^L%WK"kUk:B  6Ux)gkDF\S̭sWbh7}(?!@!bE֋2W{AZbƦv5l\-Lsg[LY ȺvV)fm Qw4ab| 1*M! %*+K``UjF)]Z۵@}k*&u6*l 6Za8V(4l3gRhX㫋8|BC6SEm$l>gt~=_XLZk._ڳl0E"`Ϝy3f"|%e[@G`Y#5=3/Ҿxi "ɀU%a&fZ8j)4t QN"?nZs[9ҵӏj !@@!ϵCp*g 93 bP &Xѐ,ʶZRE!@߱$ho#GL1}Ip>QDo«fkʌdl; :^pᤫY:0!@3DwڸTqI3 P𞴳uUD IW.7phMR%_+#lwX%ؙIt;!@c\O0Grzs Po69+xTXut +M?-cwzrnI@쁐ngJYY]P-$:cz\r $!@?&)64%hҡ:'Et m;u2Κm[ gu;KWbMԒQWpYXh=4` 5*QZ9i4pD񌉊,CqIi"(*>u^ͼׂ=tc·/IQKv@ @$B .z9)sX>XѤK6ȭůkҕ7j#[Ʊk+ln74E~ b+]qvnYKI1X$?{L&̬A:QAB Ч6f&b-iR`Pwb;dL%d要-"8sмe4  PrO_+]:FyJMrR)rcuw=ψY=e#BT*M=Clks;/Y<{C{b$uM#LVVA`'8r֕(!@@@3X'`k:*I[;:;52er8B2DLօ& 8gcrpUs ,b۬ [rjqQ&N&L_F9|rtS!r9tA=*B!`#l%U[SBW7N[Pϕ-krU~В5!`yiW:t30_Z&7V\Y5+߬YUtd*[(?!Ezݭ:7V=c煒%W1+/D)λ) ^s%^ty?|d}5,*Ws{_jYqA#ٰ"I!І0iQk6:ۖ~'i1mnhqv7q9?Ť'1!ʬ/{m3T5!@@D `TDUV[/4CMNVtUF!FҥRAX(Km -fp#Ƽ6ƫYvbd,]ξ5{K㯣g\֞IOkt=_|)޻[ T :*֞v\14/!{ r׹.`SB)x 9d/Ye-zZ/Qס_:{l|^FaѳKvаzkk~BhD=m8+ଜB  dtA~qa"4 +_a3s;Ԥ!@C#~np]r+.yVnx7o u F)r52Fr4~ [j a&!8(ն+ig^H3u!O- B !C \~Vx 5 !B:Cb1 -dT*rX<2TDR[%TpPWzO p2QhF1ylR&(z&F=(#!@@pwkREPljxfb9LoKfxc')&vߢ@,b =H!@@LqGHOVVGVAK]LEmR$lгu`:fJ7f@2;x6}XɁPYtgAE4!@!`Qaw?/&NHP0ߦ,QP FeL3;y!)]UA!/O5{@hvR*tv!P$@'D`2jcUzV{)WC][:?Y^[$=#F3 y;}AM"BZL_—gd4yݔrYrDNgē3SVi,zٔO m 5e9<;'B:XkMwR4_vVW`1MPLh`UYRs5{@]Bu:ZJ!OT54l2EM\ܘವ:L5AE633a"7`JJwKc'B (o#_zN[QQ1w+斕wrOt=S2zɤ1yY5N>V7}Mw$o.EkTN@R;Ch*=B 5joe*ZqZhXZ֚1Zsn6.>]gڮl텷+qzy$Gn߸gpXlV*KͿ88-_ZWZZZtv=RH14"jgtc /Z]$9L+K\ tU+'egE^A9HC-%:q !*Lk߳f5el*"sr56dJIvhLq G(pa:C -䌞TY0p:yK/~FW 5`7kcKeS0S[OQV0#ŵO7Ia`EuaZa=.|o.YXcϟ\3"9L9;Żxi&|8.#&Zֿ)k)7a#PXH!@hz!}3I^:XLߎe,|Ih|5"u&˅LN,?qwړ k*sj|Y-f;' }DUpbq^9n9 &luά71q'*i>nkC]z iNbxSxo90Sn7[0-|E x0&\?vn .|LWLH]@x !@X*NJezWbUʕ+նS3j&%(@C $Wfӆq\Y<.Q:P O-sщY梢1c++Ǝ[u>zlE2Y#p1Z,$z (1+1 m(s&]xaxrtn_l,&0;oWPB!*)3ksnd)DiI10pPB34 d~@5aSoèz.bʼ?XP'hl,74|ؑꠢ m4[5lN޵lj8Rw 5hG`+3ę"x%^^nMUe2֞pɪυ]F0>Dh_V`z֡a*뻌;ai`gNOjְqM]hIcD,e),`,,%)V*wt82u֞Dx/vpqkCK(jXvzx̹-⮀5y8YΌR$7a`쪵gi.GnQk-QQb!;&4 !«C5cc: Z()V-&`?ٰ83cT (sp*VY FuyymY{V^x΁*`q D_܇o(dmaN|GŇy]nM (a0.W!bB)]Ω.ƓY @8BgF>jYЁH1ۙy~]vf bNrJ %=w^(èEU]~z%E*mSy#\}3U˦|/b=oR\|jPJWor8F p_kw#hL!@mZ&0UYu)W[jG-qTielYILabHsn]S̈́!@FNb˭UdW6B"r@nQrۇ!%BeWIXhiXvm`V/#=V fqHz3ͿW?Ѵ5n·֞/>]/|$ 9kK}K_32  rT)\ZقN]fY-{eQ^lfDqNfmu6s=϶֞#֞5=gD|gמ#B  #z6azfz6T5!@C~np]r+.yVn[ fƄ΀2a1҄U -]l +XQksQzFV>ҹ9^Y=Cttg%]!@wȁNTͲbP- F2};hϫ5WL;ۜ# 5-G@@&6"Lhқ4gڛ) E܉c 3BܓR>!=L4t7|3%5 B YT2 jSgSj3jhRǬ("ucKc#1-zv[!@!EO8p:Գ O!\!2"28T" kQ)GiF&7lֻ(Fnhom*ztK_N!9mu+Q-YűТ*vlu;- 0KJ >O[pEoIJ&ypRv )?jV=?FZ@ ʎ Cr#ӕ9Ց\f)nRWPFjc⿢X{YDϹXG" COτXFޅ2!G D]iftRd=wĎJookp6l-sGq!6KۨwSs;xf.m$ص.X=5vi`G|Dϭu+Bwojj m.,Wۧw9g!žhkIQ0^v SL!a]`螤0BۄXF ܵ]=L@( 3 A.'l$huUNUJe;O;L%3zH)Eax"wnhCcI~umXdZd-+:3̕C<`K]cVnmNe~FAz{ѤGMy˓3F2jYI f^6sȁo_MJA1?=gWvO* qI_13t79Kt.tRSUʪNY?S)6WL)L1RT5+.Z b-MO d޿$ㆫ%Ȫ]9;=ۅˉԘap" tATS<eiG mq893aa75N{y}X;s'YyaTrf6̲թA'A9@d#KR=tkdAM!2{"¼ދ8!rngƨpvҹ-K fa #4ˠbV3wݐ,9j,ioS]sj;f67$RC&殇p@ٴ7DlmɆ??vc>L&j>?摿CW<+v3P{os.=0W<ѐ[.nJ̇?t)N8x߉n_ddSuvw{"qS;ƴ:D[^,dl˪ OB֠W|v1s,Yܔ# zf|CR'8@V׿d x٥>!%q< _x`R:J5Wy걕JCz*5l^VȺK&Oߵ?ҭ~Yy}@-1Ha>pdCuCw $ɷ5j$ew8HvzVЉh "ZȪ0,&"Zɦa<&8WO 3;32)cjó_UQͰ#+{,]l.*"|HCΕo dWpb'MwOTKJK!=1XGsgWę5쎘.=t|2fZm 7[5x8=OSPڪ3'`?FPHVN:T8(Ѱv4\V3b+͔LpDOڪRx'YQUK $Aj “4"G>%عZ|;k9S*{X~ccvG,Rn"5LmcHN:̉/ )Zd"^:^˺ 618{`MY@H-8=1yɕTC=Q f/- 1/u# frfMBѝUTTÂ|i|Iytp.Tor߲ĚV1g24vgiپ3kfcs|c5ڎE*CD%3p\P^3,<Ē QhrɛH]KQ|cIA/sYn9x{4gg,ys_suVY3#~3!:4fV>=1P*Ă\kbY!g9Hit!E{da|lw#Qf֞QFsa Ap7-,u?z;lo(-U?OB1!Nv3g:됢vƪBvpb^ %˛W2%^۬|z[Ƀ^-_wEډBy UdN-Uo %z'B Mt;[dޔestAA B ە !@!JWbD[12,f=ȵ=E%Ї 0tkx W~nS.C_tռe+7=z6fg]SgQZ{'bkZ`YOϖ-5fz#>#sg@ !@!yIVdBL|3baLa1Ge#B0s,Z!̪U8vzK A2 B 4w=TFȇ!@WB̓_ev{[,U5g@AX4{h kD9==0\-Z#K&2xվ,_-F@fBJ`a&Dt^z4M \JV?Hu_V߳WZ5{,26KTh AOi5.zU7E%Q_.Kb}rDco9=&捗sA3.z$uo7o<ən87yvU>Qچ(.@F͆Dðt-=nfr"Oi9~zMcd2xRͺ-t0Ɗw/FFqaȆumdtp $-?6e0`K&wMf%Kaׯu9 gͲ%o&-gձ_%ā%ukAYDWmiA8 PB P %'2 #??;9ꤓr:l8bxBpL*__}DLk]~X#õK_,OؚqMǞo~ML`H41\bU 9ߧW %5MjǎE+[vp{V(<|J;='8gO& jA">6(?̓~2/G!vD-UNjU j2Y:Hg2q_/ȎErdX. !fgLcfYYXZZxpVa롍Ƀb.\&,k]%ay|a sg0O:sO*f$K"痂Fyְ sq+܁?za۷meb]K-dʬ2l\\v}T%.lavgC/\#o Dȣe Ԛ`Xt# y /f%Yyp).^:[eZdЎڸƜ\E)^CS$O(1HT-WUg>:~'P @^ q jVL>xNdXp9ţ`[ÒW_8-ݏ,{CLs&+])sWE9VrB|eT >L23|\gcga Օb@̒ ɛj&mp[~6k\46ëJ9U׾Y:- nƌ}e_~⋮l.8^t}%0#B b"0[8D.> L1ԋvnc5fVMw^Ʈr-<)͌ie}Qbz] {W͚=F#GdugF!@䄀`/Fn+آ. RvU"ھfJa]{2s;x@!@/ÑuPp0&̖u"Үc_p =b "!@!`!zwg.@!@-@_v+Inb3]XkQ@$.z)ƶ"rn;-=%B D:Sb/:̮\ⶇZ nSx$#wBB Bh4˺ne.ZC̶%* "'*+Yfs(!%36{jcӧӡyF@!q'г/fS]`Zӳ6dO ,3 fE)&J-890NWg*C])Ftj<!@mū,L;Dt*?x,eS;wT9_ 7ݠe',}Mںk~B @ %{6 B!x$,Bb=ڜT1~bcזu%gO1lͳA K6=}A!BT'y.U-I$K4 %ؙ̾J:s 'Yx sJR B#B, Sđ2 ֪i"S1ŵa)+Ern; BhMȱ,Z=IKs8hp(VNi}5?6;mXJ,=SD7J%@!Ђ)0} ۝B-A$ Bc jS*\_ʸu,WVe.TGKE5=gĢct)iI!p_3r~*OţfEkՅ_;5cGz3^!lsgҎ׳1!@@gAce␮1au-Y0( #[̋r&Cs#Y5qn֝!@l-2  X؆-vD̋Wd%Y%3k6ۜE "M=wAK:!%wfY  j@SYMCI! @~ZkV*6nƞm˒* җClwwX= t >O!EGKI1:9'vۇhhj&Q0>D$ë#_J{.a-|/* w>rnӭB!p6h:~*lEcLpue=9ƈ jK] ȺJsV+yЊ~ɽF)@@BSaֈ H:0 @۟<ݭV\ *eܶJ7-~K]ꎠ!о舤',:ss!@@'@8Qcb|!כ crڜ |чp/l cۻmey=waMM B {o9CWq6h}Ll/Nbjx,{YE##L)>GN0eY9Ed=wK!9,"Dvf;"T)P_J0cjQ52Qv]m;ysQ9ҩU!@t4fn7|L; lb|Ϊ.clj-]x]Dm!@t: Ci'I3_mi".yYIE4;nj-Wnjm%1ۘJ7ƩE!@t40HB%IZbr_ 9T48kMzaӫ6ӦM38uxMTN:S~s4B \uy3cO .Pb-oVº6ey] pd/; '"UTKȖ^G%sKt C*.Y/5 Gȑ#5/fP6B B@ E=y$,/ fș)p&u^[f@3ձz9Yqi8tgj!@@^mNe,f$.8mLߍwZik?EbFPnAs6ζ?(?!@!=s[o͎FR/A% fX0Ӷf['X0Fg^<->̭ii^/M BhE Y ׉Pm٤裾Sﺰbzykϟ\Ea3ФBxBziXF0n7KhXJdth"61t̢nQ O,OV_[Ys)0B EYN܌wq֛5[[\l<؈\<7+z&n}ڌ̅'ѡa[ϭqT!@@B^4Eq^]ÄI]S  n6'D7Ӷ!rמEh TB  bwK(5ܬc-&3 6~oIҒUXK+mU9O[Q'B1`rN>5,Nα$C%EB  ֳF-`KYZҒG cJC%swcKB-RYYB8WQA̗XV1锢l - ;z.=&Fm^A!ny,CvמF&0e:c&מ-^KOg[qfb_8%e*i:<-)ef̵gi=4wtn!B<{}GQ:55sk?y ,{[2 qb%k/!]=ϥޥ \Vq>MBU% DЌCԢٱ_։DžU޾".²-겨^ETy[Z^MF*j 428\\p7l5^іv 8x t8;i7QX)yyO:@̗{D1}oׇc 1] 6AgA 2XJ"Ѐl/s54NJ?aƒq}.UF7xv]1)NN;x3ըGܘ.OGkQ6+nVKΆ0z|$>[&Y&NINL2RyZhH<N-dMJǡ|g|zGxWEArz"]e_@d縛jb|CbeAR]ܚ*F p3y*d#Sz̩'7U> uA~Qn3}Uk94ٙR3N&g/:yHCO*^ *~9&(1JegqeiA H P?EѠ5MFWeaQ&Rx]Aq tC,=+ک:S!/oRW| (wB w u/wZ3Js<ʰ;=5§ehLZɴ3p!LY @APVJWʚ!\jKeyx O(%uRfJ-ٜ"fFLWT@eRT_LVH=yyqE7V, j4UmRe9jH@bjL)R| 6.FVC 2@Ai!+DaA6"p;RuQ@okHKQv=eMQB4cC6nP4ba;9檭 Q[ݭU`fjWcfޑ x~ٕa=lM>dj+T%ՖKKP}a9T{TL7z('F6-bYZ56Zl/(۫V=x B/!@!\pٕ1%iI&!@!@dwn#EE)+!@!@ f9㦡Q!@g C!@@[#z'uP!@?}߾} |֭Ç0aB|!4+xK]v9r$=RNB B!J>N:(sGΚ5kӦM9\_ΰ9:!@!xGb&=O,zFK B Ό-[6H)`=C@cU@%H!@!бj?6lܸ1!_d=[X(!@@!4 M{ܵgaT B uYZ:C -j"!@@'mLҭ[7\TRҳ!ˆ-n$ Bhw裏>/K/¿^1cvm@j^3h=U`Ke B  =_|1ܹs{ֳc@gW_}q,ɿ֢cI(y,('*R; B <ΰ-@ؕW^YVV\r%NVœӲ0E!@tyD]w݅O+_~9w!F!@FXO>__|4W?cDtkӳo֊2!@]݇z#?~|޽p|V hYFs(RB .@yy\ZZ:l0*xV OÆ/5XEs<(!@![6zULH!@@g8Vr95*ڳ>u;AT Bkԩ:R?}7駱mv,$cӧ4wZlY B rA@K.|wvu%Q1bT@eB BP`r)xPB B 'h9'ب!@!R0+}cY-6%B b# CÖDh޻mG[2H!@ 0bA:o%_(ߟJ}o٣{DhQz eE+ WE277`qµ wB++}uqu{TB3{2~=t@d箊wwѤ'5ؓyhka>b) &1 B>d |Qh>': +;JWj?\=%>[ҟlɆ-:_1h}ŗ/7SǠDOR&zJGy+k g PD]5-'yLbx%6hD05<4 ֨p q)p4]Էo1$}!e`x *O0LƮ bqV>z_7HϸXhg+_@S} e@-?dnϮZ> IN/*(0b ;(>vئGê%{o/ٹ3ЃqvT{Q2RܚFLZ!q̃*UђV3"1ou;X=OP}C!QCd)6i0SVd#͜ }wHteD3oVzb=!? 8a,`SdfW CM߀GZBoKy ]w5v7O<=G+,9_,LŘ82xӓ\YIB,]ufs na҅8y<7lGvR'Ulɕә]aYODza? |37Z.OĽ&ao5,307Zʈ@G-`RvEMW]I`թdK o߭bB޽n}>C2⻛қ6u*grxg\A3YTHvdr[H~bWwhsŭ"E|z ZLBTSqA#?I,-Ys E u|Lʎ}Kz|ggHtGN݃s矯^zݓS([D_{ni!Ι{]BJ&Dc;:Egؗ3V>5BUs.Vz1zaz߆|A[Q^/^BIlOEF멣9jkhAini P֟f ZiRY .٢-hXJ*@" \;;Y˽p3sw4s 믽=ܛ@^>믿&l: 4i<,Xߛngs6a7íHMVkAV l&4I(W'.*JQYit KpCgGI1o6է/M.:GM;㑹=w~S>!1{k=U;F>3Y@}B "]|Q$i\re ?雧d__xc{i3/|W,_~;gO%lfJcS$ZЋDlzoWstE"Y" TCGs֣I/Y =D&L.SE΄.2]]\=C MqKF֗BMS@T-WW0nQ] B2T ҵ֙HD4 PzR iX5BةGئ? &6q"/ci(H2{',O |Jh>f 9`G%3Pj,E+yzJQ}+Ejx/BTx2GCwZWPe(j. 9o`6Vɀ-LU:$W]ҭzŧ;bzNi#M2Oe 11ST]Z^לWO2}&if4';\ԺS:eY3NSh6ðX[JΑΒ([:̋hG p,= ޑZMp0%ɯ:E$IdUh}O*$I,ۡ [%Yqg-j*hYbEVlYEww*) TM\KE E$~Ҫ_Q`Zk =o-?da.\ش{?)r;ME35rSm51fF< rA_bJ0=,-ŷRVʑTÚmK}v<`eP"!o{x%yEi*3mT* ٱU:C?et*n[L3LSk}WwӚ F+19Oej[N/v;eB2#^;$} .V'UD9i偑&C'k_BzfUĻc~色zl=)=v(Y@Hm2{E`2[\Ǐ?ne?[o1S [OW izyW ZwlfYrI>w0OUmٖʒJ+djܸ}?ݱe~aqe,Ҕ-RmVJAm#Nŷyq-7Lُvg^2|{LlӲ" 5K6O7iҺ(V1`NCr8KYѠ`[Y'`Xwl?4Eep+c=YD8:Q58-bcCr>mf%{?/&oe` $ S19R<BNIpѬЁ= n.X&s6iSӾ*3KoI4Ysܬca #k$xś4}3ҥ4Bz$*a6_XH<0,TV'iy ɢy*Y"G6S30JTlZYCCt" ~vɛ:qU26|kNоO=]v% zhm?F/~}~‘|kx@G-Ň鯲[n-[Rq{ Y%Kݖ #V)υ0[oOow[zxn*' }zkG34TL’lN{ m2Y[6>TڼK"NB.͹fnf}aUYQv`aDUfdq Ʉ-lq;\efTGjMۥ`?[Zb ͖-fVv~ZV5odz摎L!/ַxYvIMX d`;۠NfcLZ=oQ+1TϺ6%e5qb)hKG Q.J^TaᑕP=wy1qW׏{r|3f3Ð{yIռZ5рjrԴ 9֧)z--ᄏwͻX|V|Y)>--*OR%a^%$YQ2/2ww\˗.]|[+myR]Kvx`e`倛U*Hb/"D\u\| 11Ϣcn>E}ͲܬViPWU/4zKK5tk~r<[f\),mYS8چ13-TKl,WyZBQ9DLSDrMYʕ*CmWcf ^Z8mv 99,ѧR4jZhjm/G$FQn >B/&ICpԞ|`l >-Ojߨ8m/;xe}1쮆iP/8aCr`ٮ!Kpl&3mi\sQެ (eXU,-c:1i y"%D.j1*$ -"FKܢK؀fVM%ƪV"JbyW[V,UGL,$K.3C~l"DJM VWKrt&.,JS!-ٱ~.|~sS:4eQ3yY&V?ňo44\'9.%T|g M0+L檹q6qxZnM)p*;ʟV>wsWTGڛu0hmHRijYKoѴv)uGSv~GgN=P7x-]QJGX|:B;$#Ԩ#!jeZJIAςx#dP=ePf :7NI-MNʶ8ZSгTx w?O7msf3MTOM_a;z ~:_=oo1Bքz&g,czObC<;g_b 7+x7ls^= ccrJp۸(*k2]ˆ7 ^kij{]xjgd}w\ߝ?>ӍnE9kB|o,zE_wvzH JN@,C]SKo83==/ ,jbqsΞ^;6дCeq\ӿ/fϕ_>dܛO>w_z\r__^#ls܍ F5Kn>O]K/8m͸;3?/y빵lKLl_55/u'璥39^t;*1`F-UFjqT;j;Φ׼;xKGӃeĒbi J a{4b<`i8.1ZvTV|PcJ05jCZRbiLM2heu51EkyJRaQDdҕeLЂzmsy Ve[~"l>/=x6<&-$sMj+כ#-SJ}SM7gEqEO(*!/ۘjGcCwgy?Zݷ .Mh(1Jw99ײwC/r7b8D3sQes~;DØs~҅wn4c63oFN60U.8ͧԕ"j/<3)~=}9x~FJ8-~ yK޸~=ۿK{-y2z6rSGF~;A׌W6YS />:.;t{{۳Ld(_Y:kiܨ~3uYͿSw6.2PX"^Q¶cōvH2XLyU6V*EmM53L927ué@7Ĩ1 m1;iX\OD)BdLňzI6G~:82]3A :.#wHwt\(֣E:ry=9zo|y&b>g"]~r?GLK2I Q&֓%d:> {[?vGW<}o˂Zs;Q۩\٣Gç?&n{ yj/?uߴA䞟q^˾ 﫷>O=~<. _yOmk&NaLӣ`'nS>7Ă 4LPn]nMv+\G1[l`5j6./z:Y,g֛ z/)i1F*?gm{ȌsYajuz ?l7"̍6?({Tԋry]Q3orXnHX8GҨع 64Tr,mp3Wq1[#n?Bą WoCVeg zSCُ vx[/G=4wz%Qv| %iIƢ S-$Zn]ECm|ũJ+U&K@G3ѹ :#ij#-+PLM$sޡ gǑ@NDde֫gs^ـ^zg}{7ݣ}zoݿ&'B~}zF?9K?Ekb$j"zkӯā͢SbֿWs75n[u˽:o}˟gsS٪wެ-}>y &5l]w:`x{'cxz)pי~uTaZYbO'E|lF?O?ߥcv<~_^v>F1qC% 5cvS CUP˒W4WQ6z%mNu|KvTgޜ(H<]_+2Ce\͠mQamF4и|VpI#_ZIq-@(w_m2 jZ]CElj)9*\Վ*֊Ցkׁr(Q;XH}_6;{צC+O%I=pp=>}#SZyd/]sS= `o߁M}4c@l`_q5 C~ wi߹.} B(L2_H޿+jз~ܬW_!UwZzk[_`ԩ?|q^c-lԫA?w YwyǤ,G y17fcN:Yꬻ{_O2F<^x_1a.~e{Q;NczV`x[-6$M)6nW|zӭz}Em^3#{m,N? {k^x[x |Ь2#&`Qqrѻuq^iSu"̳*VpDʹ1{=UGkOz+.JÛUŬ\88rU3ȕn~aozәD$!6]DfB`3$Mut@cck(u O+^ ϒ+V}zݣGC^^z8y]{xH=լ =UsgFw[YoCUH(WOI={nݻ码=k^<:z s:=ĵ:M[̓%#xz+%w 91x`m`&3y̭uUeP^wDɾg,2V k~'7hu]w 6xG9c޹ϭkmGoZQ?]"A#Uf&\{m!A,^}7_znOy{$ў:%xz=֕w]W|鱎[R2`,, rXEK.._]l}p{/}?@z>.fyMjsskR'.kzGɾOtB9Z3&>ϝ2L߮C1ȆwI :/ M'ô%g>s̐o;켏|@/bP7'6zFhҿ~vȤ*Ζ{}Gpν{vܰlO TſߦGTO6 c~ /ͼ] UON̦F1mZ擱VOiBh39iwM2i-dm!+&c )ЎP.Jごu~zZcM B28|ڕ*A r@#ijk}n,l >2YĽRonqxhGM"_ך) gpY0+)ʒBW$S)8נ;h`zg2D\BǕ-"4/+~*|J@y|~]wov;G~xW=glkuI'u뮻&護#{y}dQ[wu_p'p ƈUcM#{=?Ϳx ;Or;j+H>W"_Yvoپ/]7}Zl[g߿pw__{^/rnz7J*wΛ6XoqV7fi%!?vy|oW>'j3痮w/~gOIN!}>RΚ'=?~Ɉ}{.qwO-'4N1~eX[Nd/Fo(BRH.[բ1$hjefTSSOo^˸QMe,F51ICQ)Ei&@ZeHNELʟh˔>o=gKt44BuXW9=!ZdfG췶"l?gM^]T GT& +M}Y~̙Zf%@+>=zx̳=6tT7gIǝ6u귾חWbOfI 8l/]7 uPJW֞Ҕ)R]Q i{s3G+=+/=[k}-}WJ!bHExHҳ]+O3xӷo_&O=== +:k&;H%wP M^$<#~;xTF |X|OM8 #}cSdG esPj3[S(LrQJuG:}*O^'O5ZRg-Se߻^c4̣mZu!N_ne5'e/=GJOH@IuX1úU7cՕuŽ9=Y4jGEkdø\M $چIZH7sJ4s5ŬO5t}ƒD -C5N]-e( ~ﯙ+@-ǡn~qMiJV ,GxnӘ~ƻWy5S݈Hƽkyٷ^-w\ߥa~fZy40X.~Xwƹ'D`qn4;bhOUxCEv\# }OHOy(> JYms<ˍlƝ ERT71!X=X6(Zܶ!.i6Zܞ%G2!Q"vaN{vƜvg3:7jt]ͨLϹglS$Nj ϟrnB٨YMju4vJ"*mFQ(j4#&7$=_?T@>~DJ2G΍dQɋ f:KhيKϪAuv[ª /ife:[mSۥ!Sutؑ+9Q)1r*W9;Z[R:cӑګb)}Js }%I9}#bF9bz]R]-QEē 3˒U̘l恖ǽG2m,C/EӚO(J]ճ3۩s=h0WH S@|yaۣ7 IŶڎ)EFUk7 /=,sr&Kݶ&ijۻ!|+C4,J_it3eݣԒɛ5jUY&p`h\S*ðܣfozйH1rP]ZRСۚ\D7K^eX\q븖ιնG!ї%=).K,`2D'YI6=NC7#I]K4Q $eoY11 :y!k,- NF׻\\~_Q~4P\~H5I6m'Jv?$mbMV39CM12q4B[>AZ 1$~KMlxqJlQȞ=#6sn6#r3#dJdNC} dce&4%jf'\(EY=11}w'ќ_FF,ƩT"*7!$rَߪ.'3pZ:%Z>'*X'AY-DGCyZՊ̔ГGQ*4BPǯb(z̆1\Ԃ=$Y5thnC Jɑeak'z{C˩E4G ٽmkLYmR.Es:ׅ=pewxIn{P:%j]QtdQ<@(*z ]S ?5ny5kڦY K iAWHAxo{W!=Sa2r}ta,MS.9,[GFG4<:ÿ޶[Gl$ SWxcLPyQ ~z EG7FuhoHOI 9 N,7KѶ:)[ʔx; Ղ[TCX6McøE;J҄5(6ndZa V cNQ& Gl+2H#aзŷ_JfVB8fįKToDSJaExiK zfdXzxqʲEI9MEZ]c^ C $q:%VOIǪfY Qrf9x3 ;ejDrVw$UT.^ݩj[Z:\[DuꪞOԹT'.d6Kəy㔩 &Q8_Q^- 3\CaEʴd%>`3gӐ(CCj@k%;0t>q|R n?+M~gbQ v4= 2YWUI"I$$9M laP VEˍF*?O[^cRI:9{ 铃\UhP')u$:]azѝAUcxS>҉D|rEDMיc= P묵X%W-˭N6mjKDec=z ezJF6t5%{_rFB7).~%ZN`z'˺K޵_0*TCi=E)+D&'f$XlR*jr֣i (mPꪜI `n$gfIz>%{O<[~|v6L;}4o;DY I;9 i]ll2VmZ2D 4G)(=%}u]zc kH*Sx'pXdO {24ytuxa/#ik#Q_9bzke6w2zNh)^CRPUc_V aϴ.k`^S QQL2j(m@#oM0Xy 6t4о\c4iU1?G%/XwHէau"1W*%;-^{Gk*Wpeoˍ=R@4^'<迯k3ȥ!{7Ozɣt@ *!p ;9lRaV%/]5MRXF~VXDfcY;=ob+H! lx)WUg)eԏW^'+"nJ n&ߐ+ Th@ } +V7wP1mV tp=9IW&q|zv @M=wSJv-&фұ:Sĕ QuOa%Vygbh\{>¨UZ^yuU!7/2wm7wZQ3ϫKSu8t>~{.mm͚LR1ԋWKCYUz "去0ߜ|Y7E|_Le*睗II ׉]9f1S; >b.N6fGB톮ӇF*֬P=k5XrӬ6ZX=ַL˟zX%"ln>k_wWGw>>@#ګJڬuN\ w<{юӘ >tw:ǜ?~̅;=_}π>?:w-?s6I\ɿw+ZV0BwxӫH|eKiQvС?ٷ(slt{W~2Y--0BYVPO]_ TN^1fxvX{~y\V~ySi?ۼwVn[+Zڼb~͂kOvTA|]5 nCTI%ثDL꫓ Pc7|JXe4{ӀZ̓aNAoCL, ڳ79zgEm{f+==7oӽ|W{-_{g_^zXg~Tss'/I#֭vݯ"|>z_Y͒;נvA>BIXvfnFg& ɁkGvΙ|6~>iҼ*5K'~+kבvs\)k߿gnKrznDu؋]UV^]R^fzwWbP6fX+i6ٵ@ؔL[_]<7ݳ߾}V~wzw?ɯ}`-ݺϫX$_R6-:{WZrOY/&\9;Q(+O_oe~7U8kUȶ+tO]Zw[D<8t>H+.qWz띕U[qδP)Hrդ:NbwfBkmx F)Bɭ6K/5~< GS AŊF3M'xӞPv8OM`h.q-ـ=7 qS7>c~/-ZzCwKRE< ?ˎSq癣{~ۇSc} kukrw}&i? "^;6O lJ-E>?WΗnyW9d,7/z{tw>>@!#8so.&BDĊGĞ;؅>fcgPAfGiyNOܲn-+Ebog#~Y!~d"-N8~a-wB9:O;F[L)Vz-yuF/߲j{>z-We=x;L.g~:|Yкb3l=|V&|?ڱ˭_%4]eei]%h2 mҖOONM/˞Z`RPPu߫un2!RJwE"8t> nlt n͏IߨLqZk"uVLe.TX|yFӯl(_{V ]^"suOdL%UHN}qΙ3hqqI3n<R!Ҟ{;m ;~~?`١k}їeG_gwO&k2W~˧6?gܤ[tJ;sЅR#zݚ>|ߏo~43|}s@rɏLCְ n߃*}/8k"+i f_&:s'_}iޯ:GLco{Cḭ.;ϧmDYOdeKL۬K>ɣñ$?ܛt ߁@W`u!{L6m;dpR;qS]f>t.?O23W>,2w;XYՕcF-†.?% A(I%~[Fqb7s1:dԞ_ZRӞWGn~Zxf&7}紐̾Ǟi~]K,;Mn:|;_f8 F5<{j}Dſ?:EBmw]Z\UODI~?%-yvAk떢IJ4['!w>>@0q[p޵ш:D$z7lLiFğtƔ"m;<80ІYk.Ofu\qYPU/dp˾ZImF+3K~YXң7뾵ns8-͙'\yeƗO6o*%46_!ѧ}:D|?vRڳ?=?{0s(I CJ$_(0QNs D uu w>>@r򸽅,nY_0j;y~yܾ+[7"tqsISnnCǚŻ$i;r?%zHC6hSBR1/\3.IW!ۤ9twLӞ[Kc,Z֣)[w^l Aҽ=onk⴫6Y1CV^u6lm8ҫx'7vBQ;ݦѶW[۷6OQ4-(I8f# ^8t>"qn%Sd%t|Ȕ\ܞgn vP#T-HW̡h8dz4*Ga[mxsoOO|Q{Jw`eL7{ߕ'[CgJggkV]\BGQ!Uj*.[*_(47KM_LDVa)g{鵏qwǕNixxWh(^dޕ쌚MT''*5ڡ-ga}ߺUZ@.Ǘ;mpͳ7}M^_k{sLaNZxeBG]!UU(S~e/?k:u)³@;AG>GrE&}D07%t J $!}4# U|=G)@${)8Qln6],;tKˬ^Os+1>mC!@_ UtG~wVrD輄 OPan&7n nغ;\;  @ p͌y=?z[ -gH^K,}M9$C7~#\{Nҳ($ygz @X}Oⓢ~cՕpc`[Scέr=ˤc-KܤMLW[mmZӰ\lR5+Co@Ec _sޠ.kVS)m’z;> ه}- >MU0E1Ǯb+G0SP^b}/ װ5iacIyhg"=O>u|Kdk뭷A|WX`kJ_~ ux`YV--(z^%Xe$])V_Z *^=׸ad{v:зKss+ ܺk#/6u#YO5⬣e@C`f!p96J.@*iK6ViJ"j pF \{ ճi*%™bl"ok)_cW&/"A;C_!6d>$#G1$@{"vj϶--W4WCzzOMSߛ,굏I4ȼ82-`k% %9oS!h랽@tYNzN릁&,Fg-L]œrm]yʱ_N9OLa#@zl^kj2r=Ⱦ#X/XtW'cz7vD@tVqWؒ %`᩟e^t4Xz/+7_HWjSf_w> LMg-~BʑtG$}?EZF-ڲT=d ZU \+NW_}F9Jgm`l3Ol98&]g~鼤WecYNI5 yיT͝cDGQW=2xe,M?ǴˏUos<.xiwzAJUV|9߈Y(~ ]'Y$/|m7[#Z>Z=|W[rhOʄ->O|Ȣי'=}㳛Ԩ@V^  h@{^aYuK߳ur?se_?ipAYzM_;JZ]b7\X3qgyۋl3HVfG9{-5˝Z#wcmN[{./㿬o#5hyGl!`eeܱP 'x㭂͵ջ[V-HGs>$j8mtÿ|A$@!P/2+ں#>+IZUʸݲ?C}'v?6 ,xDwϝ PʜJ>E U$ RN ykWնL,m|uAo[m#$wc*t(Bq|:WBDc%!7f;zq@˪Zת͎8滦|iOLV.v{fg~s/\kUTbrsG0eUӶi_˲<1bven5&7yiüdvr&W=<-LB_6IKk|-圑 'J+l{H<ekO_~}/^,Y'u}/ȶeީ7lS/*ůWn&~xZ^Y^Zܮ|1ij -J\BG`DPO?/j#.Jwbr6L+#K.ɤ-ٗ\rSo!ZmǖޔRxm *x^{:9hD@ *u^Y"gn٦2ɷ5M%U{"tAbThVV9h-:W3橘;ozd q[ϊ*`XE7JmE&qPv sbrӬ7öئuqA▋u*݊UJ,gd>= LO~4aleفz@]rP &" |G1|w7ZkF5y1?3\F3j{~BRB>笒L_*oC':s3"C1{)G̪͝Ŷvǹ<$|Bβ= 9XgYPV%Z*VvPx9_YFCC#lVǥ.@tU]'f,ЊU]U;(&s'<.ْXe|ܽ V1  h]tKROݾT|T/Zګ*$Z,-co9LWJӍ v!%kUbr'=.G V_Y]wT$@vo}fst6׬KE XTYNr!y'El^WwA%Ir)<=[9@!T@ 5{tLT)m6Ms_$mc:O-v A 鹽4W?{naђH@HI ͍ID*cRZyҚ9W$E?8?(V#;Xxn3zYiz1[oNmnX7Vu iiMsLĹ^vip`ÚMTN,Ni;wϧ@tVy+#XQ9YNdk^X*ܗEFc:fc+8:W~ч%j}7N7`N9i;|v( !)N FU*8oKUޑnoRI^SbnT/T?`_,,j;촋煊7Uq{_:@Վۧ״\际Qx!ߪ$O^Qɬ¢?w33Wjo<k+Ꜫ)wjisGeNJZqJq$jYJcI>֎z Dbhՙ[iXU&sl4Mtw*uKD0挷l8"wȓR~d'FJk e#X;Xkn}ʰGςq ~)@Տ@j߳eN >W|\|ExmITK;V,%UjUڲ$n4koc=Ѐ\p4ۡe6.Ef\ͽZKN$/l#͘))3$<ެc.U<+maWlh60/O隭&|OvȄX Eߺܻv.C3B;I M_~qߙrgfj Y`#Q@D fN~o5nR)r\@jK0?fhعy  Xmq6n/3Ul-MMrYS5YLaX@`5"|AYv&HE.r&MӴPDfM%ɹL}14=}X{^E @.fD,;(T]9rjXA#J3S!df瓠 Xm$TLFc)0nk:6$(4c&mm&!維jk @(b#9dSBGͦx紶3Cҵ]fĦgJ}ı>rJ]$8x%=Җz$2%5l5wJޥ(n/o^ёD'#64K)34e=[px6o,4r& R8a D-rȭ0N\rbw_k;ꚧ䄷vdMI F86]zcgx%W'D0g)r7s$us #ۍUSUzi7c75(̘oԵWL37_<6g-3i؄+ > (؋kW7pEe Îb[n޺>(B RRY֐HJUYJ9I Q'\Ǝ׌6k1Q߸pA<̫#5OZ(1Af͸b"+Ϻ>;f0:à5H3NlP!$.5VW5Ģx7jsxNݱ$$O75*#0f/M³n[ }D =9%v^nC?D;BR}@Ͻ:ݬ^Z.b #Pe [ ;]V/gz\ZyV곥gE:TFyTHl߽pCbKlZ.z?QLaYFԷLOS͋W\סi>,IH&OFVuu24T/u&JЩwn=/fxډ3̞ɓ:d^)P/KMd֓a-~'`N-ϝv.[4eQ=56}Rǚz,ՏI7͹Jܫ3wޒ5O@n@@ݲUI.W*v]'幝Qޥ-hVUlٱTQ5wjvi%cl3jYeʴ~oK>"a >I)VuWp[VIO0bpOX5@rt_|ÏRD!/9p9s iˋ褑<#M9EKɡz֞l #yx].!̚2N~OT?/pYOݝ9bX z@ 1}zH4-G>"g(%% BB F¥<-r N iLokf0ʒ<Ἅ #9h/<飵FX4G86>c8q6 7WvlC3N<[mYb,YHz>۲ճ2?Z|cFrOHm 96!P)q&c5ćKq|~^8􋪴oI,}gM@#1h',;r²!#h]4Pp'mmBtz`۽,_oVb@[Ȳi'NvivC6@>X#;1zg/P$+*:h% T@`u Jv+s>]u9i2`(p"qNdSLr3M~5Xe# ug8P"*i%: a vƔ^~u昉%I:)= ns֔Iǝ{tS,Gr!iXMk^Jo/Ve}zy8˶rޤ]E6Sh:<]ES5:7<͔eo tQ $ΔٜRzIEmmZŊr*lPWvz+2m_{5.練<)f~nx|}8>H'*8Ƒ\8>)M(SM_[ Q{?dq3 +֐1kLs+u҇_W%gp:%ŸF^zgsط:ĸS֢FU@` B'-gyV*<7;>K9>W}_XHf<  6O ñ$kPO@BɰR.F2*'VDeζ3ӞךVFE@`MC ֏Stf)%)+RW+I;89+eb{ XTX{rTN˲*ʥyTܕ Z^ @1 K5%S2Um{r԰UTU"-<7GG9z@Ձ@} s($}ʕۉwGqL܀mO,Y2@$(u΢4a miVU"8"}u3&k&HXfj&"G V+E*2i9+ʡ>j"Rۻ-rH1wgiɩ6F*Aba4ϕZUkAn€6nX2mӹg6j1s6 YZlWkKKk*}֘gAK^[Uy-~u嫺{GA@`"ɧኻJ|*(:%^ז?ڊ^_!Xժg/zewV X=k*4D[O$g=գUc^.$Nbt U|+ݕR\/շ,mʸ'];>@  UnV,>ӮydUW/IG,NqwF*m:3Ƞ!@!4fIEXÔB\iUCwrM;t]9Х Dm+׃ EMUl[ִ{e/'v>؅[uߣgޠ_!@ o*'F9 aZrّ&y @t8wi38<'@!OzoLnb|p>NƣZg{:ÕUSlڳgt #֊J<Ӷ%7^ON<2ў7@t5cIbϯ%קc=Z2]N:p,Z<@n@8^rÎ7.ۍXF3N7 .8ɻT I){e_N6;(-^T6l+6KPB @@MrvIW׌[̫%RŬKۧsC'lf}I@]qy2e(^UH.1uP@ V#Plνk %),BBwp"JpPIbLf;Z{{Zr@Fwo8/{QvJ /'_i:i_WKE,ākXW륐 HЭW1Ki8&lI瞃MY˝UgjPs* :N$lyk)$Z<$@>r:V T3s;(J@/M Fo4`F%%@ V:#+)%۝gb~IYٕL߁OY;ؽ:8su4@ !PzHekFUVU6-kQw }0BI $}Z-Ie"N,xZu*mEi%Ε" >mJ]D-1pVN$_&& j+=g1ҥzggϘ{]vUwPQP =`q}O5)+L+(nnU,T"bvNìFSZ/Q8r(Pɻ1BTF]_Dl FqUG0멳"0? -NeWMN"su;v;ofzi_)[ym5F%6܏GQ^8bjbbcVRrqX,/^(_7 olS]w'jI_dNe=!Ȅ)5w'\=7…kT> %Ik9bU;n쫹6w-McjWXwaO/tQKo_åsͷ|a\aS TP-N~lWzrWiRֻ`~F\ܽ KSϚ)A*$o.mBX-[MmM թUL+bt+;2^DYP=K:4Kŋ~!Kݗ*-͕ -]:'FyspECgYQ4.x+N3źzqR$n6n V{Iﰆ?JUo<$/U/OjKEd3N^vJGQ6† Z@dwNf6t*"AK9nY5[EfV967dǮچsڵimw3g]On#lY38ߐE#7rw<'sqb]Y|hp=v.si] kL@|ԘO̺ܝ,s.~%R)bԶW\ؾi;z.渻wj3yVQ/Ir휊xڞ,e&S=f&*#[AA?ZF0w\ԐkG_ ͟":OK>Rrpl\dM&jt*"!tڢSfم_mǾoq=".vZ1UyL}Sǿk<©<)Ocwユ?6' 2s\Cl?K M|μ.j]v8={<#@b*LBB~#24`wί7tSٓUGύ#@ |e8ME`0sS )2' bU'WVPۥk! hlxoR@p ٮeEj|"X*(=?!)@o9BKjT fw.{ܞ}yݤuWs}2mcىʼ8ݯ)? :RyGxrmP;s W;(,HtQW*U5=W @(yw;ý":i;1voI l" tB֧gZX dAݐ3@TC 8xmYBGfz ycU|J7'qO%,Xf@պb @"`lIu3eכڴԶ+[cVsb]혰lcf»xə3=\:S!k h#2~ٳS^].oo]8[S(%piwjr'>@ .D~_>b2mJnYVߵ˳k̟+/UY.7 i@ҫ @d]Tdvѡ;[FMI;vrGפc<+&=?!-@yK2v>W}c8< VSfvn6'\q=z@@\hӉ-Fi5s'pV|ȉe ,tg+nC{~,@^v$suen%ak*]ۘKubTO$4s`2o"1y+z  B w܊2^U h s{uu1u#}.&Y65f8ꦞu`Q^`XkFEUxFтq^@ gml]e:mkDCГʋIz*c E-,KX%Lyn22וɚn:' U4qr=ۉ U=-q78-Mw03/J; 6VywMNj znN@qRme@h[чG ۟Yaw"@vBiW^FF".lևoeSOJjI|ChԷ @4BM9lx)pis?`i/N<ȬX9MH q둟URj9~|7RHa]sU@Ӈ @;b4?_S݊ljgc;z$q|!Z <|h󦦭 ;SMj}G +WB$˕r^r$s}؆2no7kJ:?ʖfbv@c'pX}' \<݄ă̭@[u.h< q9n\sx{fj 2lSS\i͸XPӄkqyܘ.lm(&y*8U'Y8k<a9o5|YSJt )8F/p"-ЗB'fhD/.T,ĩR3376(! |Vz^--xU=略i2pSbպ9s\X٧?FHcj{lƁt9/edn?M* qDSQf\5ahv–+n~rϝt)J8MtewUO<׍UW. L0JߚulŸ?sTp< CIhxJjf-vKU6!^>²{ģose+!*cPQ+,޳\h)_H@fv]j/u)cFg 2S5& :2kM ee*ʌ?n*k6d΍CAG.f8mA N SxM϶\U9ܕT 1(ETgrxitb1TRϘ͖ Ɇ-k*(ؔ'4:mg8sYp5e۝KKڇk~ОBe"+);nGhy" R'Opus p&$[Jf| 5geQ&~癗y4F֤;642+ٱ3+ \znj̙sד[(۬go"N9i;|vX_xu_k]ELYrb L}Sǿk<)ƍ?J|lդir0f,8^Y/pi Kp5S0OljKegO|μ.ej]vvvw쐴yRv;6? @ ø͗"mEjL:kb/K{*euS@U:ސ# zn W2m$;@tsܒ3_o|v<ͣݩemVTozXݱbʷsOvluCG=w @{!6T1/05,^-'k]O>M3aNgkО۫w! hmmYLoĭJ~ښqœڭ\{1/3M2ik?ך1DSsέ*zn3!@t?/9+6?,HeҧUl' K3Y! @; Xr-@1lj`/Dl:t&qc-m } =CB@ &hns3s3͜X;p Yg.G)׭c4 ;XN#znSBb h'6V1]Yz3geKv9y .)uS$9;qI|7蹝z@vFZjvcUۗs 5͍̚ۉ>F16L܇P0:d[Y`m(vc G ?gY2NA>qV9z!@3kI._w6/K+u\*Tя&(^ Xw3S^yq8fr㒖$蹝@HtK'AzU>ԅZN>f|Pɭ%́).#g =݋ +yK1;ZZ.i{Ŕb7lKɖ OBnK @{"N%s1IJlu<}.fl:s1(,Ak: Qp3Qh@4%fh5-k-: LOO<ϏX,u]Chr(H@DewN'?/,2esJvƴa4f.sgfp*Pn? O6ְA{nkBz h+kXxfbЙ},H+ʙMRRst6~v5hkAz HUVs^t>9Wo;0|7bݭkH:,o  8o|bxxr`r5٬(TnltfO iƂ=W89Jn_<+VX!F ;=żP@$[neC1ӭ˩dWv/Czצ-2yWDPXޫaǤ֍Ux@">q7lm&t.Ue3K5Ĉ# ih]B0 @wA00Nbvȑ t|`sskϴA#t-e德WR&^! Hu?c /J8pY֗EpDZ>h8f"V[9ƵsflUٰl- v_Ee=:+/I]"2@t=JJ#|lI_M<< &S_+;bX# =kf:2Cn^FG  IҮVҡ(qYGx+ZyhzY t䛯UgQڳ,ʎM5/٥}"Y ~52p5ى3'J B.RM_$¨@8V ;TwO0;قtO5)fVrkţSvڳt~vl!k6\غd'dSejK`lR6l>Qݥ~T=\,Ogwܯ-Ղ |HFF*}FkuwbwK(xΓt n؊ǎ?p_3֩(0c!IDmgysy-0wFnIw씱p Ӆn]9=a6VS^7C6)eܽ8#)sF]@<;Xa6$4PRټ=7 ˮTjj %L-EH٧(;j|g߳B.' zYnk Pr;{燸C%j12+āN[vENF{CQÚ̃N/%z,K-טK^`(iք8l#iX]٭Z)ӞwsN5cfTs"M2vL;|e%r M[m9szubm37` 4yu ;&=K62@.ۧM>uke:G96,Rj/,wB"Pmd@MVbYRV;RI_[ׅʜ}s~˚Zg]F6n'zڣ=;K GPqU~ LF 9J XKXqV)4Pg+\UB %qYne"Sv:yõ%wŞn&m`׳M͜. $>heWіڤ"w[`=/1%ߝLķ^֌Ե&^]fjeZ} &_ X;pLEtYKI?mKҚ9%~Z3[JBNv&&Υ)Р絳V@5 GlL$Mc |isӚq1_يs|P:7i[]D7ͱf 4y  6"~gY+O 9y7 #@in>z'@ԁ-֒_ lz3\90eF RnCz`6{ R@|ȸohsQ@C /*QuIܘ$MY]֢ͻ*itbnջnU9Ɂ [O=os4d  8ՒtU9p5LL4Z>pN uYVɶoۥh; ѓfs=q@ڎM(FO(96_WFk3r0 4v@О;![ hŽ <C +H T0/ -?;@.@FNNSʫN&JeiN5WXv4jIBwAIH[!׃Kȥgz5Z3`3àɥhW}ߔͭ@3->C{n@֌C:;0Wp}V֌]s s߻.-[Ulz6hmV  nF;swr3/oj \ ߳yAMX*.m|Z zn.l@M+v*& D0] znSoBb hEڢ)|õ>`ĘΝ48FgU0[pH.3{s:MFoJԺmc@CN&+;g+CۊtėsݢĮh?˅1u)õ lh; s. !@*'ChtFM:q:+MVna*v~s'm#81lH|`noz2XPB" Kvr;WVyNݾfl7S}QZ],fu+:'mhѐ'@4X el ^d"`v5k5h=33Sk 54\347SVL-2ܝ.b`wHh"jjx9sI@ύ$@vC=228Z^&le'X ׌ogg |QhNiXAֹ@4;N.6۽V l`_5R#^vr6VlFfÕSuulИ @ 6I;K1VvJf#t^]JnmT'~lfgִ ؝6o&z.st3d @b2 ܜܬCO@ϝ ( }:JdY,OX@3Eӧmڔ+\ /5ey[=fzƃg'0rjVnst Rm([֌- M?wn'cs|6p( [eZ`UЌNq޵:7"vQ:1׌ÕoКCs~- "p3 ]MD@ @GjZ!eeR˝˙)9=m^k mZI#y? xN^Z:Ϙ왝YY q)-F c;3;'r5Cz^ @xב+;3$pcK%'Z-&BD+ڼ-pe*_ӧ @x*@ k=9vQ!CsWҜz$Ж=#PskO1@ϝ)!@N熞@ 04?S:&-F3V߃k}o29Yn%O?&i~ VО;L U8d)S6#oiMގ7Sl`ό*SɱA>jrgxV@}^H@_Faujgz8ו+_)] Z%i5=;7k .5;G뵤 kr^d>{pCSdYn[iZB'hrKg/,qm@@ =#ǻp LCt.]V[[˽+N[ر%Fbydnэ\8r = uЮ]㴜+mP.7ebS-i>(jxnw Iih(ρfK>ˉ|Jn0V^W ۸s}8Pq @" I+dܝǖ|-rYp`z}]91p|Zng󃼝'g0tvP@z @#Vd}Mj͘/Re"S&/Wqfc-lӂ*LH++)>X{^ MWCǼKTS^HҮWH07% :eScTvͅ=jƒC 7`͘8튱bkN .ס-sv6XM8gAPD D@gQCJr2:`hN5.9p$èt69:< zOj]Eb 9n)ՊoF.t|o g\@Sfڤ=K{n<@.@1 z34st0RY(ezx$Tmۭ4#Yba; KV}DieӡG:i#Xsl'v{1֧E$Ԡg<@ ph;psNI})rCbsfrA"vw=ccUW蒐 =ghݾK @#Ba@- lP廆Eڭou?I۱rV C LС0fL-i";gr+w"UK4q  :o]9:S]VXπ?U^2zWUSMrksr׃wXblE, zS@@$"`jYB]I V2 #5i5Ųz֡stLFYBG @ `(2h o,*QX(uHP׉>U@ jB c|3SFA1@  @ˌ eZ*eYj9'Z o:z$#gC0n71R @#P~8:Dl]ǩnL-ā #sDD (|B+_K&bfϓnR/T-a|@OLfʸaN#Czi2z BeeDО@t= Q(#_ ,K2) lႱR6YP- gط;3\ @F'rc>$LV`n;uS4ҡ=7R @;# smC**i+VI=+  :Sbjl@ݏ?@=  @D@P@/=w>J e+a>=D:z @G"`tS6dGyٌ3^z H3vSudC@ P@ZMaހnGl?KMC{@ԑ5@x z" @g"Ω+^(9T XUT@zV@tNw1vT)J;!O "s97Z X:{)SV ?w_58T 庽-1f{.% *!LR녕P7q1=w9@`u Pʒm"y-5$a@ Zў/2@@@mWmW9fSf\T4]@  #}u6 FLs9b]4|(BkP9. @xĬkDyRx]9Oj@#,ҳ|uw@G2r yItӱz( !$J+\譖H",zfSmyPly"Y0qќ @v.C19M Tpŵehč@Տ@)Cy⇤X@LSOĪl:y%sa^]% Dj|u۠EP@4@5$](6_J]2_ˋjK:g:H@;Y^e[Tj9gʟR@{n @=i,oswA|ByCBZ=At > @ kʈYX\X.oY)IQǺxzr@{oA孇@ 8RB&3)IUlæT K"Qu\g_r( =)z Evi^Ԝ"@MZ<;(kD$./s镐 НfN" Ъ0`?M3^?>PZX.@a@݁]˺m,k5V\li( >Y%y5&@t3'SU#}sD&ekݾ\)a&qUJB_n˾rKZ :BiޞD@(  67vW޺pah}eC .O5u-!\RIU$g Ǝ߸\jjzB2pQv0o]E趰w7T0@n"&V,<[Z54o#soL\5Ϝ*wBg;n$@.@믾셒β]("lT*BZs9l&2o* mہ"z >@tl/˲fSh2TZnLF5Fi})WhX!E64s Vz Y @B &z PjC]vpr\\Қ-jA9QE'dזmU kҞ>\m=w~9Bd$XFȖ? z.h*2#gkl+Wt|"5Q@])0CfR4l(.'Tr"[W֖f9sL)b3|o:8N犳s  Эຩ"vq/7[$js`2mWjJfj3evLi:6ki@ݪ@Ǔ)A+rVi>qq8` %Σ*owbm~nUUٝbmOfoQ>xW=[. aݲ@a.ּ-/tl_5e-~O(kjDmWC ϫGzN@ft6)$@h:1nsgS3e5:a۰)<*|r@ƔoЫ7ux\}egGyը@`F[&n@bUgY6Ԇt8ҹoUY*#<ٮk]/P z^{4@p^SjZŃ,|&F=@ '@EY[YL/mYmcZl]+ݷ섖9%װ"lUc7Og0ڭ!7@`-@@*-_k=ԫ:߆0:9PyOeX'd{k؜*_C=U@%$QTgGvDÚMtõNaVEsyLo$!wvPY1'֞kIF5kCWؖS,ٱ ,a8M7\֠ Q@ҖgSz$@ P6=7,hѺ̔jStVWs^]mV*S ~)Z4a=@˩RyTAMSiF\^0YVgy`@Z<@n@:ۥ<4EC= BS!aN-=#,*Ks?/rF.e O  T תVԤ5sYM*~eeJ,ŴY3\]UT:I4 QtYĵ@ &#`g\"uՎhy~9_XOV+oV \Zhf$Hw6=ɽ RifXSt&qnfqZ?mZۖs)3SɁ5w*G0c{'0n@DD/z5+C̒01+֊ݭ 2>\r tRg0E&)Jjʁ U@9xMkv5Yfn1Yʻ[e`Ts=yӹ@^NV@(Ėrg3H6nל I`M^k3slK3YGd:KS tw;@=Jz70ĴZNae\w0T_L~ΚqcR)!2{RY-k$}Ϡ( r_ 'Zukp1l ڜ2;5+q\#mgE @{""Gڕ`떄o[B*y\ E:=XAѹ=Ͱ+ہ݁3|ڳ!/ Il3VpLlOtѧeTeCXZF廆8ѵqs?05?CT ڃ@u: X2[:}Q,@# @-6Sۨ|kr=+?s}TakyiEQζPvK5YՔ3 Q2&>*neP} `tG6؋#:aiPŝŌùVK—Sr\ֶm7_N3v>wp/D@ @i4GbJ`sKjjk)(ĩ&gǵ"9GL;gۨXn<2@ \˙kyzpmTZU .FeۺvƪPQY3\祧QH q;L hh XbbAK3I}^4@ 8i_FݴڭEE[q[Fim[WNpӫΈmr~gLlR̭J>sw55o@n@fl'77[68L{ji`fٛFR_u'@v@om6jo՗En1Ӓڞ, TQ[gQWrs@k z@Ճ@UǤKWOV[,qH18UĆ7r( bB.78dycD$:g_uU,'zfjhYE:1Zd#st* @#lz64S3i}:2S;n9p)6xm]=#U$-A\kfmI鏥g@ :@ S ƓKٸvjqRslm`_R츙1?̄+In6D̵gs(@ X0 y.],9pQľsu%vm_3q4Ik{/XLi`n^@!uU`qH>Vi8DG=7ԅ ѥp-jV NhU*SݩOEZYuY={T7=  B@~7̜4mF "jlFwOxYa^if;.snMnI:@{ƣLOξ: +z㧭GǾ^.xQV@WNN^ٍ@`DS }~a52yˆs_FUs{ @t7mP96iۤ7ۄZɍ=_*i Vk5mevj"<Yn/@T<>[8vU7$מS= ezvKKzL5gKjU>zVҸme->=_rwmv@ qh6W,5kkv|Y*h[%mڮ^DlYer*IkJum߹JY4u4t{X>ksZmgNjfι>: Y4#'2槷[ơ =t\XaƺYX,Hi[V ޭ/y!Gn/Z( < s'MdR UP떱bmnێ.XBs#5"yDTer` wWvevHƣL 4f+9=_hcz3PS# O?"zDs澫Cͦ )4TQHM4BvbtcapѨLVWwg:4LWV!3xś2I$cY'18Xg+S87bt q!>f;ٔڧ=zu}!LlUdvFHNj;/BZdfW^j^Ct_>Mq ׵I\__:$] >9 47d"n3?eGf*[}y86\~1`2Vq)XhO&d+t0p Zrt2y)UW0v̙Bݑ6%Bld38Tp:'>+ڬ"<͗U,T+*lt;7N'+=Gg]Y+Ѓ_.i-Rڽ.Y'&4,Pj(L3f5RQL-R[b$3Hgf0g%V)g38aH|ģMڨ<~nr519lutmWZlV "L fLȬ3mn5:bMaN% t,L)fts2xQ@9ٲZG7h8  %;^]\7BP%g>~tiz )XAln͵$nf%k3m$%i> 0M(Mw`F.RY!N@Y$>橱JCa?̌-jt`oYk~56  >X(y !I{hXnD@ JH30@ :EЗ;P:@6{'^>q:3ςk @_?[bʕ+wyۯ:np b Eq?{ww}]3kv X~ߴ# ZyVzt[Bz k +VXDH; &@fae˖,@KB=@ NiCaR_{+РvDY ڈgAoGҞ統+N@ ~]sQѣG{gt5!r@ "tMwy?R jcIf'XuY8]E&@ 6#p7 5SN|yKĂ_}s=7 %Y,h,v9>'z믿~acU!>@/vJGyi3:@ܯ_osժU xto@nn^{շo_Ap~VЧ{2@I3;7 @7G`}?V[1vUgP3N$i=$@nИߣ>'z*iܭE@bӳ8VrGn 䍵@C !}8q/qJK/W^/},zRUnKR/@1p,Ic!@N@ 4nÅE מB  @5 ɭ@# 8Fl6˃tshP} G>>yГ/,ͺ#GݻgJnlJƤhO['PA]$z% K"ەɭ3O:auCgޝνVeY{ B۠B _|'!_}5 U_6H4 o£{RCW(UF$F&v.ȺN Ӊ\*{uʗ?5=o&-veYT8 ;;* $,rb;E5ieu],~ 4ӫX7a^uqDAU)r'鹈`(P`JpRݏcE⿦f?A#MA7dСBubδg3Zݶe-肬XO\Σhhգԏ}jOmu<n4DPMGvfі! 7޸y-̛g$:Q`D VU)5[1 UjƪuSSCծ.#3 > rAA,lVlA8q -̒ fOdϜ{zC-ҦBI:wsݔ~&KBꤢz6lD}]~G['ˆm rhʮn9##wZVԥfu[`4-*Ƭ<ξs2ԨU=7s5DjAmZa( .HjQ Fbqu9,Y 5fWc46 6a7>)ݺ˨ݲET=E֢NuRT0_ȣBZRWGŶ-`VA-hyV$-o gU\Gye{TL}&fʔ [o>}seӮ||%֛|銹#@ ^ t«M7l !¶IENDB`filius-2.9.4+ds/src/info/img/DNScli.png000066400000000000000000000063241506574241500175610ustar00rootroot00000000000000PNG  IHDRZMh;sRGB pHYs.> yIDATx^]n9nH6o`@.s:;| 800&ѧ $ǹ07AzBd"Mʒ-`Ԫ.WdnVNgc ^o^vC`r:gE!R-~)Rډ+< RL!ls]&oc[Uݴpl+j\HZŪHS-4Z!}'=$/4qОOOQ&Չd1Zͧf9]Lfs۷f0I5)ϱwT-1J6&ە5)uN?gb`y܆8;KBs :T4wHc`ux \IH.8Q;, &EMiV9,6Lt!w'vƮIu'j@,t%jOvvv.E~@6_SΪ]j&Y(=ZW-EB@ΞKҮVhd2+㩻űv;!2AhãIQ񡪒DbQG}\~`vv xrAk"hɤY賘Yv5nZ}!M|(Id&CT~;<؜~x||AJL*Fb^fGtCT:?OYvUJϤ.Rv-|;i!- \C$&t3fO"#Z-FKu;u!fq),[`4UTuq@`t ?/ 6+d|M'A`3B*x d 6+ LA`3B*x d zL۳Z ~Z+ Ul}Ԑ93nW-3~t>$ejyVen?DgJ{yh̓TUHrѦ2;R++hR5@~4aRg;w{Bce3Õ4 y]k8QI>rh07Bƒ.Sndicu?٤{+T[Tva]@~O c=]NpvFP9դ0ctQ0ܚ'7'f7sqL=P˵N;oOJ -)&-Lʖ6$6[etZq Ɋł,Weuqwp17_m8d nT:d :q-n:^S@eݕrj~sťa? {eӀ?jR贺'y+1'9d vek+4ջ{AWe! ȉZ\BC./#~gHy&>-V`ǐ!!k8z9@l΋hP @? ;"t0$ H@`  0(0 A?C+ H L t @? ;"t0$ H@`  0(0 A?C+ H L t @? ;"t0$ H@`  0(0 A?C+ L]o 0 lO//%ޖ 0LA= l>]\wWwl._w JX^<hT[oy/Dոlux@/\ˋ^| ݫ7~3wu'j_U胋ӱ\0]S>/ v rLGCU%2<ӟs0M* |@`UhD-wM>5L?NYͼ8[)sG- v@d!ЕI%O:dA *_"ta@` &lMH@n^ H$pt#tЍ8+Ann1x% @M Ѝ@Rw p `u[@rl)H[Xx-D`L_~wZQL:bXNLmYhk0{j610A>ǁBnw|f7iOp+I v؊Z Nc-zKq {)hdU)D}7ZH'ApR 0"4P8b.1LQ8\#QH!N i27*K4w~`qKvh(i ÉI}I0"Cw eRg9("$qY&m4r`O֎$*4+!J [C]σM'[h|t ]ͫz쯌lKU Gi"@lk|`4;PɈQg)Շz#/{%9]eCe";;rS@i4ԭ&JGCSH4ߐnqj lh?71nr N0}E $ǫXr( Kd_:P/y,fN>sYHS͆9\+koŐ\VN*Aamlj` G` bd ^j4\ K+&#6am l @s  tϟ??vtђWIENDB`filius-2.9.4+ds/src/info/img/DNSconf.png000066400000000000000000000262331506574241500177400ustar00rootroot00000000000000PNG  IHDRsRGB pHYs.>,@IDATx^]ktU>ݣ 35#]g{l"h EcDΝ;HϾ9Ce#hnbA:n?0}GID \z5,,03f'Dd䐹|DS@TqrD .i"c_ @E[p<"t>\h"x2xD|B[-@|B+>B"`&  sy G @\_{_;xf#фElAY4hyYN%(f4c,OC/#cb `9 =x˼FtZցY@>w}]MbDM8*-]l&cF~6?&Q1W##/|z"ܵ:^Ẃs^=x^1 m:fVLz$UP5ICG@H8g 8v,vr91k7 3FpEG,_0?AŮMw7k:С)gϳQ"B-[lm46uÍ͟s1S>KM$MFMrZG-tg!RW9l?1)22 ϟ;~D$8Cd(wϝ[)r#! 1XR[/*XplFN+Cgopղg⊏Wd\{0x3r>C>`p.?H~o|hpXyom@ jήs瞯BsI%JH<ʱGF<\K".e@[4ӬӛB<*{JN"Ɠ!/m 1W=6B$ʳwϾW Rvk RW:9Zy˼NH]"qQ;ݍcOHZbBdqt! yMh;~4zzrs|Dh3p[b~ZßxsVWo͸(ld`yܡ40Bڷ?2!UdnK!Ju*ZejCE-Һ>V; !?4$Yf;O=CkPR$i st?;,,LΣZFK˫#F\^&-jsy:P" wֹx҈Mo4?Go^ZG.Ft^`D="̥UG#̥D="̥UG#rߥZ߸hn_ң㈀lQK :DK.#GKK :D+-|kF:`>4@fz\u:}~tў&њsiˍVk7kX} 7>N`-i ,+iXw|],Sup~K[׷keM]l7ϽgO'W喰svrl{EQ$m=D7/A|6q"tK6(h6lG2mn7-eA[[mZ[ gOZ,'^zaؗKk֎;w|Ɉ~jsFY6PդCW(7ESM7/]Fs 8C'riӦ-Y;mW@+̆K>/s:U5SH0C]vZ^=UHB'B?=  $6=Xr!.!Dm~Ͽ(L۵<Ы[m"E޾˽tq|$nN{Ϯ=1\4dcu{!t1c=B߯oTS 勖\:Si! I"e= rj_1%$R{7WKrwivZjٝ+ BֲKYܓzvf{n4B^z^"ݮm`d$0$zֵ2Jhp"$6֫ c:vE0wEDn^ug7DRPjRt0eu%]E_+EXZ)sZ"Oj қS9/"fK]*J`x8)ԤY 1yPRx Ͳ{?/{(:݈kq:?l11[^LYl ?u@a0Y7z:۽e'vOt==~qӠ+So^_J7P y*ս=S蘾i{lxͱ$OrY ,sR*Ci>Wj=N{* RPgb\k KgNZhN4adZ/)1S(@&B֮؍#Zjk o5 /),;]wXH3\%z歛4__\,d/ ξW!j=Vl"}hgm=R~OH'PS5:=ZAI}OjW.9h4X(0@]gN}i٫uPWxfeL >CEgFu%(ʧLyy?tv#Op>VSU\++;e%'ZAֿ ^9P$RNݣNQؽxxBJ(Z/.; g^Ⳡ3@0TWW O~~^^(u/6nH]j]t0t}q0;QH<^w]-hHM-++hmZ%JDhr)B5}9yT0psArer:K\JsOfqA.T77zH#`Wky.2\Nǻ‘1 Xi"j\24~逩(5<3Lkgۀv&2L3pnD+$oxF@+B35u@G+{P@c Dd.o±" s:" Xo[3ڼ$8"BD W`\8@dX4@B+p0"0,5?u 8"~Md.ZD `Hq.DsGi7 Fͺz*f \o,֯  fNP [4Yo2h5s}7@⪡͈@@-"̥UC#z5M,XҡሀlQ[@42 MFtֹ4S򯡺V:VWFq 3;'O~{ >KmUl->cpfs@u)^F̦֓a!sP^g޳ !O<C.**Ą0JS糧BV4Zh K.y{笻NUqC^iQUP\lgsUrtz]i:S(;/' bZ%k)0L*t (Ϛ5W "q@FZOqBzDD NJ!GyVhk`{}Ԍw"uD>b__8ٷpvԆ}-]}>PEA\j=!u,kՑ؀Tb= 9 Q_(TY%$r*[Ja$2K9HJk=1IWI.SJslE>H N,AHĩ݃a^4! zWSZqh} rјdyo&Ca#x93GF/*ư|nff.3ZOُpTRSU< ~wExHE^1v`/.ϏPzfu Uu.Z.Sr.Z^B0z3bgqD"{'Ԕ!w}FDYPnˎ23huaL0^42z-i@ewB5󽜡.:ăem^R*#7)Oo)EA4'*"v46MJ9ME0mVCSSSGȐA=%ojđek`677-t@42 ME׾D@^רh6" -R#@b@ 2K :B+(Jqt5 }h'z +"*ZZc>TD?xEs7 D@CZ[#sihiO saCD@C`KC"c.};qZ@qֹ4>̅3z()\z/O.su ई"=Wxӵm@P gbD|h( 2W'/N > u.mn{E`@\ گv/k@: ; * k񑁵gC`?ڡ N'7Ĺ w1D:|Sb( )Wm X{;uR'tvڧ6=dk:W]Ye;~f;.{k]0z8B"*oʪV]P!\z|=JID(8/ĄЯ0CAru xèQ(;/' eWkS*9!VES" h61nܤb4~hpdP#P5@]:}bz){+ym^wX1K7NL3:xp9퉞3u:rIIhc'+10b/[ɜƒNus y+^20 [2 S"&1}oQ~UCB18ac_/)+6=rKH$Uf0U>y3bk[ݖ= [\ZZpDRi`hO;M˹F~sDOI" 85?WU {\SH[u*Bvxzf5";TsKEU] ye&<4#jٚhE s\ŴrqQf\9Ε%†W~`۲zw0 '#9) JtzlZyc\NM ,TT4zbv:(=pr8{1'fˎ2~E֘g:,1K9HvS`ر1Ʉ@+ב>^ڥ=tlL H·UR}ږg?|ckgRngqIuV4-Bu>M bU>j *="R_hkyشًC5G5_#G[y=Y,?ػ:nTyc1P{hamil&Տ<ȼMp%G8ntJ%+Yk>Ya#5GQƎ!7RM|nbAd?I=+uvJAW*vI,w$;P81T5sn:Q7&m~hu( V cTV\=.?ƌfg+Ncf0DL{S &5)KYhoݺ4E?33;doIu)ɗ>Մe!Bz@M>a"T;iE%MKn[ :l<1]֑ EୢoڠY.X6>~ "hd.Z \D@{StY֤۪h1"puAD l1WCgpW :&Two*Z` "h5|h<"S ?N]׷k>ܽhnV&a@5|ƍl2ƖO޹s',,OP "85΄ E\!@ l1(P B$D=P_f:~ 0>~__z΅٢6FU8Ԡm[!"C0[ᢣˈ@@ᢣˈ &wow.:WBוG#٢=@2=F2=@2=Fsl;9Jl8HǾ^}@y%7Ny# =L/k׈#UՔsP.r\D!`g.sgyێ=v؛_l4/ɮYFk~VHh 'TL>dT$\\(^eW/ˉcXVZJ=L&!酶 &]ʳ&1=jjj͕Hܤ0Q6*|MHttFmB V]Dشfj&[Dv@Յ3߀#ē#Ǜijh*GI ⬓XZ`lES(WIG=̊/.!S!PW)0A1$Uf) ]zkz9`4 !cS[Nl&\.= L`iaua V3MHh!rXV:>--^Fd"_lj06۸\rYCE9L}"_\s&O0LBgbRK¥CG*c˾]D*WZcw+ۖCDt n۶ IL4S "6o'QNs* -9\"J:|\E=QX"ي|X@̑SwK *geZK7l;iR~,jGh䍅Jߕ^=TX{PxT|uZ#U ,0i@`%P"mbg3^Jsrv*I,{ G ;sA3cɇ3ikfQ:a+)ZKHe FzEItEK Y7|+d,FN"WTuE:y!Ÿ!E,t .#xќڥd4)4iNF  ׿:c^9.M{7*C4eUNyJ=dyL81w6.rIfnm)^ƴw *^ԳE!2 "YIIII5R._Vgm;mm]=R'ZtJ⒈ƏJ{KW]zֶSNRnUHgCNII+ӑ|(쌕LJ0xv W2 %jUu\N^vPN켜86kd2^h`Ty$GE/Ԛ+Y&e%G2Zo\׹"ֺ&'7^ȇp'gsv.zɓk>X x+za@Mm$;3O!"Jy$;y-F`I~ƅO:T`@i JBN<}zon"XpdQԩm"r{]Jzr(TY%$r*ׯRuPNRZ']~+!Z"cyܼb2`"G@.|Ŝ2`( $؀Y ƨOoռ??N.ŧţźF?=\ FK˾@- #DZ@ܡd4j4 eK5R̯vIf.w[Kn> ]((쬪!r+s͙<2 "S9FOv!v?{\v=M uM1Yxrs MZHVVMzo5|i4ּ?%ɋKA- { MA qk Ōأo0B5`(g)߰Tz!-A2H0N1$REx2j]ʛYbyn@Q:"G;2*t0D:MB͈8 Caa:ָVY}pf(Q @\ԵG=w -9\"J:2NRMs7!X"ɝH57Ohm烈t . P7†XιC.<Й7/YKCk1ɀx ֈA@>~My&c~ S_"G)UWk,^R*J 7)Oo)EA4'*"v4O5<f 080ܾ}ۏPVxuXh'G`"شw܁"=fh1" s@!̥5CD 9h3p#Ezf#lQ."!2W-):d.,2XtiÍ۶8h6#P"9ٸq#d?[FFY UD"};enNMYZX-J(z:XG|o~md֭ˌq|Ա?8Ǒ$vfq(JXuةz>frl<Ñ^Vrco2Ha&۷ogÆ l߾R4oxr%8_D"[)686k8ڱ1" +>qؔJ%8]]]aI$ rYXM}*2wLJp.ʏ Ɋ:֤p$00 G:DZT>!~ 2_̶q ޛڬh5YsJT0 V^ʕ+%˲H&Èi܉&]ɱ3.W>,f\~vyyq[;_|.=o%a:444P[[;ׇq\9Dھ}O~M&/Иpm˿H,ߺfr|6rP(?q/_.$8R)1i鶓V5կ~|p ˎ?;?G7q۵pIcc\qi6l^ѣBq5}A6=cezmxAZ]:O]h4:G-d2RSv"\.Ǐ!p9Dڿ}( \.wHsEm=.ք`Mo}bAUuEXޠQ*Pkr%bH"8l|ieQ,ihh8_Dr"ÑiM(G:;l_V3FUE 2%VrtjYoR.1M/o1J$3eYP,ܩf}655oG"9yHsʔ`g%ZEz%V֍֢DkR{5?GjkPuH-E6mQx;(lܸ'S lͷql:OOOb?q]Rk/7]Y|̷?85P"[:ԪZԘz7+H O_=Of`L&ó>ˏ~#^|E;3S믿γ>oJo(zAA`n MO=7YLY&]שZ㬚,5+Ov xicccΦ>gKizҒ&-2izN#kYj0vp]wyھӚrM e3̵+ mQ5m@5ɩN0 Hm{RI$4=Lݻw7&k(:jjU5y%,jt˜\WLL@ i 묮IΝ\y啬]v)df1M!n(jN3'K$IH DV"bҜIři%H"kT vWXy@gT[lܸ7f͚y'N 666F2$Qí-ʞ%M@ռV4E 6FM?Nea\ĚxLp*}2cVTkj?<O=7pk۶%Ld:p]Jigb޽vx1fS‘MHAȑ7g#mܪX&]asdXL#bm>yN vqPTO)*h:{bMqňzW/ nMqQؖw 4t_+k+^:EpGX[f W]uռK$Gta&iڔH$B U9R GR GR##1! DmeB8ҾRG <\A"-puUqirޅj8'P5MUS-/SB\k\ִ̩2OU| he*{C`E*kѧbÆ \yRINX###'̶mpDr Ñ)Qc׮]KJUq )B ͧpib-qKY+qJ9ϯ8(oxe:"(jH%\ ATDP޳cU8n(/GymlSO=ŷu>ͷqa]>ĝj=Do#}nԟ~S84Er P"U,jQ"<ڟţڟk1n0>hWME#u]4vJdw~+_%OrgnNDLYTm܂XtއP4EiLNg9PSr2` Z@ib#ÑI-OP`ߠ[H¸@SqꞀtqaZ+v6ž( ՎmU(>j}ߔy]MGSTj, h]ihh J͉E-pmg\#ÑI]cÆ 4 G1H8F!S28,n)[syR[ǭp+E﹜-fӈF+ca#э(Qe\?swQOcmqy\u Ç]x2[\1lj26GIH4N!SH2X-e2sd`?o8N_dF1"(W~#\U?B1PܟGQ1UGwyKL"0CeH)B,#R*(J)^qyYC"#ӟ<0Խ>(*!/ {Iv0fa>^#ⵍl1c0>#(j(A ˫݆XQseVY#{Ҟ-Vcy-&WE}4=c' 'uq_ᠸs \q\h.w(MeȤ޽{ۿۃxb-FEXR9\d$ $I\';%p$gl!#b[nk,٨o <`A8{TeqҒv΁ ZGq}QBq 怋&0?ub[ǼdgXSp=QB<,CZ$'_*/^|BP*l4MlQUUHN0d8qҊ;v^ wQlT oT& ,,/Ɵ]׳)V/6.+S ܙ/WUA̷R]L㐖40ַ[kihvr˲rض=Vd"Ñ8$QE 5lqh>\zY&\p\W־Ե-\M?۶DcW QO-I*8 !+@;Lͷqq~Bpʼn>lj26GGÑ^RRrJF)땴2W\3?:(U)TV_潦RJ[n U`UZp۫x峌(1I8 balf۶mln̰w'6 jEE}AR{}B 70`ȫ[ *nֹJ%DP㦛nBut]0 񺦦믿C}eYd-Q%8$yꩧx'>RX*YĊ+/g#  5S`ZT2˛CzD: v"2&(T*$IJe.˗/gʕ\uN^}^ĸiձpEtJ8=Q c&lWQ P &@QMYW\9gߧD2[ }Y}]>Oq.I$D#='HmL&CPLUUq]N;+Vnz<í:W jxfBmMyON>qUg\ȹ6kOhS1~a^z%8 "\D"93E ?30OL GBPlƙ O:+wsڸillaɒ%tuuvm/ᄏg^`Ӟ>ڢ'\6SpRP~g[|gVU\up Bm&sl m(`#K]uh,u)9hiiattCmsN+b of…3x#ÑJٶM.#ϋ—{塇"H $Ilwa˖-vip ,_|6>an/ͽC EMAAQ\Q'UTn:"&ZI(JiD2E շ*wuw}7rywE"2s”(P(D#7oZ.j;܂CԢDjP(V(؄Oj|p$^4qa‘y%8l&H`&UUU{?O>Ƕm$â_(֢:|QJPjXf 7(B:g˫c x#AFoQSTM+#렩U:^dE7&!81^oKU瓣 ??.?Bg׮]Ӗz겉Ι,Q* ͒)XiB!(PWWwȿ/KpHN5#IHpM&#MAUUO8Ϸ^ Ut ZF*z3w?;]/~}[wd2rHZ4կj"XȈQs۶)V,g/"u]jjj8{u ;/ͻcEԅq?[TTU6C |˚g bnPJMqYX,׾5q|ɹ h޳g۷T*eYA&9HE"!jjjXr%s---st"ÑfygIs]bH6E4QUM6{ Y"4q*Ql6K?BX,FCCNd2lpj J"$( JFLJwvu 3(j S4_@k{BM B ˬLH$LJ߿{wt:=4uٱc===ضMTBƎ9sAFYz5W\qIN~\ץT*Nq]p8L\旿%[la``X,aI&!L.eu˗sWsNn%Jsg7?`7bJjy5T?SGQqIϻW {jaB\nMϚPW"q~"W IDAT$r:b### gjkkbW.CUU2(ȈP0, UUCuL$Hd4x?A.\ҥKill$0<1QAaof|/kVÔWiSbVXڵkgsV*ʮ'?Ν;)t]RNEn*BUU2 }}}tttFRaxxD",Yzr$I4M*cii{n^}U$B K7H)R_ :1И7$M z!z{{;FDu:,>FIT**Ҷn?OP8&v4㚦Q,Iӳc.4A8R wy 69u]I$$I0/h4J2$JDhnn&8e( \l6͛y1MIpa#OokRW77=0CR$Ug48]vV=]s&,btt|>O$!RTx'MX߿|>O{{;-]9pbE$D(TWWT*ضȈPp\.l:,X@UUmS(T*ꫯ222‚ bMUUZ[cY1 ̑MVTʹ?ଳsϝS"E e-[vPf$xA]]]\|Ŭ\x<(Im߾;w ɡ;i\4,{.GMI$ )S[[Kmm-d{w^t]I5R ۶ Be7::J,U|f&qrL&!ϳo>6nH>h4:!upak`-iYEh#H/St3-NZ:f``۶Fh͛w˦MiBq%ktwwsNFGGY|9g}6sN>93YlVu]FFF0 z8HDZИJp]Eֆ뺌/|GlٲEĸDQ\t&?ӸknvȠ_E EQB1Pňx4gvz=8NWakiiz+WΏm`ŊcP*ؽ{e|(cmNd2tww*(›o~3 emFcc#wVbɒ%i07峂gݫ&Jwh~,//6O|k_s0+N4IRa~ O?t%ɐdp]WXr~,bדN!J*X<gpp|>O]]XN])yƄϼFgDQ.166[o[oE<l5r.ZbQezQ5AU+g?\B$YzX,ƭ:ӧC"U;'ƘJ2&LN4==āP(Ğ={ա"Ͷm<->%F#͟piRN[_h_~9|DR$[uu5pX֨%200@*"HdٲeB!u]]]]2Xrq$7 Nq"w}4iSt:([r}~DeT"*###UP`,ZB@oo/455E}}=uuu䂆8m  ,JB>) Xi&CUU躎NG{3ΊqZe}[ISЈQuľ TQ%^ફd f\aժU\466 Ŧq2P---444 $LR(4MpBQvtt>Xt666&̯ARa cSSXb0o6P/8[8}JzGWp2 Fwd68Z⭷b׮]8#bHr"-\.7i])$2<, =?$ٶeill4gPSSCXdppT*eѢE,]0eppb… innR$BbpXX,X <;v젭MӖ.brEӷuV M,:ݵqm l zI555z^fTY05qY@{{;_~9 .$HfI&Imm-pXE) .DUUCu:;;ED"!t] e˖Q.!HP*XhqV*wJ("Hm6^z%' 12#MZ-4(5"OfseJN?'q?8uޝ~ _UUUkii!000@}}=9ɈPRiN#Ͼ}ؾ};|ccҥ 8wh!Q[[K("EwT*5ilfϞ={|_W^`&/+v3|>5p?5om)p*yj}\rNՌI͋ԆƁn->,T ]ׅgeY"[D"A"mioo' 144eYB!Ţ;O$tttPWWG&a``@;B >a۶(7ǹ꫹˄P(-*.}C% ?~RT7|3mmm\cΝghh4ihhX,F&a߾}eN}}=EQhmmIhFMM hT ɳ,XH$ԧ>']v [x#.ɼi9=H$BRX, E;\Z0W\!|AM5EQ(JT*4M.줳|>/M0]]]466bh[]]M<-jƄ8öm2 R6,X8dg~E/e˖*p˲".===-vB44m&OD2LI]`k׮W`:sq Hd2EtwJNtd8Gq]ץP(کb,`1u|llImV:::+Al)AIT^]שR>c|%Kl2(B}P[[$ EJHbtvvr뭷vZ%dΤR)4Mömlf޽/̣>֭[q)$'$SEx^z)iBI&9 d:prgr 7 CCCc$zy1eAJ",ڶmSTD멥KrS(璘EQXz5 DQ,\0DeYy!p]Wt2hhhd2ɖ-[xf/t45sm:`cccejjjD"Bʤ {x饗d2,[n38m~ONfTF8fddl64443665555;{{{P(Fioo4MqQPOu.b?|aF4MnU۶Yxhu322+oͻ+*(q֭[Ǎ7xo\"ό 9>::ʃ>Ⱦ}fݙy衇سg]i&ݝ G:|8R׾w^TUK.s4mLӤO3 Zoo/>(CCCT*L4Mlۦ뮻/m7nO?-.MDm6D줪BRZN?t#Ja6XFR吝MUVq饗EbD0IYO&Nyo~þ}ׯ_/$%Kqݻw#dHd2+TjRF)$:622 ͑ 'ӍO]6QNEuE "]H&455tRBtݻwyj.ԁ0M˲hllD4$DJ"bms9p׋PHd I/(]3<{R.T*eڸˈ.b- .Tr,544D& pB,CCC.ʹ8=== :lK/u, R}-ǡ: ,87[7aÆ "[4֭[ǵ^˅^xODrOM7twsQDZ2G?J&S.dxx˲hhh8HM'‚}2X,1e駟Wxu1"m@kk+VEDnRp+Wb0D&hRahhH ܯ:;;+x\.9EdKpL/\ *l@&ϩ;HeY <$ b\f @uu5rY4ooiiʽ;w.mmmB!4\DAMB;묳8Ө /ncѢE·t/ whggAHU y..BlfÆ |_;瞣X,~qͷqxV|cK_7>OyNR\3lj86d9'|'G?4MBeET~cj@~7ML.&wy'K, vh"/_8ر;vx::0 Qض\.H"Y3Πtwwcl .3$(g?Y.]*BT*x].䒃ISp > PUUpX^{_t:-܄E*'a0QB!*n6nSO=%,kAԛI$q àI8pHꢡŋsy牀[]]M,:HdR% =< ###gk(["K ӟG MMMߡ,i<"hC-4ʦMx'ٴi(d{g"Q#>iS\TbiB)B*_7ΠiBA]]W_}5֭#N29kdR\$;4JQ(O4VWWJܲ,?Mkk+P(pP(D4%:e¶m6nȣ>{'YnիWߖH=܃87|n%,g 8WW,^Q"9^r9qSX(֭. /e8҇ GI\A;RiTWWT͛7o6_^?yQ/m7xC" m S.E6gMM _~9k׮u]"Lp8,T("̉߼y3< 6lwHVokHN;ꩧ_DZ>```@%ϦH vǹ;%"vmذ\.xg+wG 'UmK/ I p& xX~='hjj4BO6Z",jkk0 Qu]l6m,X4F^u"\.ڨsQfpP,I$y\q`]۵kdr̕W^)29Rtb-GDRaŊ|K_F\qRa"UUU-90i}Qx J1ׯ_?'9u(҂GWW_~Qe'qF?X>UJ%~a8Ì#]]]m/&cӦMa͚5|ss=bM4?w"- \,fx4Y'ӭsmx xIIKK z+sid(dYl n J 6O^o[neޘt%'/H A8"(pb-p \{bDfY> {l4˲kꫯS9eӦM<裢f<gx: G#=V4a-O FBI-gAV˦pӉ.r訸Xp!HdξW^qk |RIf{˲^'Z:::Xz5+W<@ a IDATe|In*`EZnwAKKQ'_֯_.*+2ifLQ8FKK X ˲HRNEGxĒS|bzm|>7x#200{..1̧c֭?!Oą^oƭOӣ.1~\UUjjjfӖ'{?ٰa{tM_|۷O~j&k8?gMh'8̧ظq#׿Noo/|_桇//غuG{P:::hiiYX) q饗WW|{OOg?'?Ioqm 8O|Y~3 ܩmW.R,Em & t}̻BT"ND/G?hDD@u2t/ˢh^Woooyf>6o xw^r zAɱ21&-5M'>?ڵ*Z$IoN*o&-i8sg>3'-%<#>^`z+gf|^H 'd#S QQъ0C^炙[(&VZE]]\qFxvx)׷~KN~Q :jTvP($fJ/}>8|3ɇgǎ{"leѢEvm_~Ve8pŠERRB L * mbbHXw4MqW^ҥK(gW^y?w1'$'H WaL$aY<Nx4۶E /~ۑHoo+ضMkk+~;֭Sχ Xւ@麎BMfƪ[Q* L4E,\7hnnxpRqDp\b4M"XۂwyA!9V^astdmۼ < prw5{'9 ܝi8V*r("Ho2Xx1*$sB.R>9>̻/t9DD,ˢP(tTSTWW{nK8O|93DĶm~az!rqM7_j:iccc+)A@x.Em{|T<5\I $bBTVZzA\eJ廬XZ|om jH*BF0 $dI&3ǹ$!$1||眙zs}qw^7h&"{aӦMx<N'G5 G9 ZPߏ32Ct [(2yedoΚ5q^[sNdY&..oPܥ}HLLDQ%$L ­(I(X,ƎˢEbobҸYfO#:pkCDKpT0<|pFŨQPTRRR iѣ?~ Z$^k[gcҥl޼^/6l`ɒ%FAo5RW=i JGttQ0!˲q7rp\744̆  :%KȚ1Fb[<+۷׳d fÆ \/OS߇4zÇnGEdY6.\@EEUUUTWWS__oY|>GwޡcǒpY6Wׅ%jeP(DSSHlc۷^xC=OF65L/ lDqi&)..fѢEȲ'izk;vG~_WGj >5k֐$I<̟?\L{|Gx}>S;Ebbb馛vLf/qA֬Y'''5k21@at+Y:cu%'-\ ^Ox衇fxbV^MFFFD!++,|A8y${ʪz+&NȦMXjNbŬZV6qy!PWWǐ!CU0a/ٰa{A$-[}2[8RWH&!_vcǎJ'NՎrtܧޖ9s&@UV[}X-3fn_O"m<8*++Yt){r1+H=Я*xj}ێ>ך]z.bĆ쨭̱tgklٲ^z YyXxP`G#PQQ+BJJJ BQQ+WLV^MZZZLxw‘z=pZ:3^:%t[?b}?*.\Ȓ%Kl\2a„+˗SYYĉYn݀&h,KWjk}W:ծٝGu?b.EWgzyRGe[̈2aDDb]@ YX|J8p~A[kڥۯj=18' /NQQK,e„ m}v֬YC `ŊխSW}_$-i;Mٙ^wfݶwRRRX~=˗/e˖vZ@ &"[la֭,^zGԻRB__kdDXLБȆ e$i&".{\|>˗/=,&L\ضm7oT6gΜ~Qa#{(>kj~-3Yyg3f(sGX-Q[^^^GGsW}^m=~X1cR^^:oGcvW:orWB$v???ʌ33f(o5cH`>>I3a+e{ɺua]aW5.;gI몆Og0ѷxWyςf"2&oHĪUtR6l`傊Gw?Yˎp%AΊeve%D æM?1i&"$ff͚,ˬXϒ jkkyxdW"%] ڽg{B' LX'j~~>ׯGe.]j40X|9SL1 JKK{u۶mo?)|{#++$kWAggDֺ٫o{18iEOՂV^M g޼y0b&:0l^ɓ'Xb駟rw}ۍ ƣ'|cDzm۶`BG#ԕ> )fY6+͛… {L Lfb@fj*222(--eٲe=V};Y𣢢ɸqLFXBg{nw1 Z7jEE+V󑗗ŋL\vV}irn:q,[@ pMnܸq/YV+ƍ_:2uT233Z;wz>.GP.լUWַi7EMߧWgs>{j>eYfժUTTTի9rH@n 0%8L XTTTxb^/fbʕ],^ݮmȑ3$P(ܹsAFʕ+Y`A鸜+w>K.ݧ.Q__Mtf|#-L7ڵkٳglܸGZL$ĀFqq1K,dxNmvt $$$T_̙3{<Ӄ 4NAoդ$\.]'M1i&4222Xj$qƫz?g:tAEQ"//[nӋ>bnnh|ͼz&L\w諹Z]]͹s/{L:LKA_7rظq#>}vV\.b0zhFѭ}_q]t~裏vk& PZZK/DZZZD'`U&T$Ġٹs'_8:ڵk9x`mFQ.r%455QTTDUUUM-0.Dl2 X~ʊpsՄ0IA@ EpL6E?E!>>.JtB!Ο?Ϲs:yرcijjٳ455Ί+ @$imU&:)ŶHy-6u֑ȟ'OΫ&Mt2aƏ߭E… =zpfGJff&wq񤤤p뭷Ayy9/ĉپ}{weG$3g^sZHϕ^x⋈)aݻя~Deee%Ibȑv_˙3ghhh0޻IKK#== HOO穧GL\%-2{nVZEIII͹jvj%11ɓ'LfbP?áCڵ >#Ft+3gt(q7vIpǤȼ LY0窉±cxZXV, dee1vXVk0I:֭[{='˘1c]7 Q^^Nyy9,ihv233dv>|87|^1}$-2`U}7xb~ÜO >6JTTcǎ5*RD*~bD`ӦMWAuQv8p vJΟ?Oss3ѷ֯- iii >[z={DQ4! 01PaULlr <0sí;mgϞLm8xĨxJwXB@?s{=8__ל?xOQl6Fruj F$I2tnMccccbbHOO'>>IEI([43qo`UsF~QUU/>TQdՒ(2B8>mr!lv2ijjrrdׂܶmz+ƍ%8q'|@Xկ磼צK&`С >[n@ @II m\3V4Mws4455QQQAӊ4B59JԐU&!_һx/G}{.󦌡cZ%;;ώe4ŋٷoǏt&.:{9?m䤤$ E… xm7\⦄ wfΝq{ 2Ge@Ipt5U.*!*_{ ;wÇsiC:R#"m<8;p04j566K˿rV,\p"jkkt:2dHnT+y׉C$%^^u=25jJ[u[YY"F#8qN!DǫiQ yg3Vkѩwj":8 lL*a|V\ɖ-[:| o6)B-~p8^T5nʴ3 IDATL… zܞ\LT-h;TmS J(/ + 17kUYYY{NpPK-PtyG1*q:)D@Q\.G6kkk#!!]^6,:.i$$5F5k(eQa,ˌwb ,29|wѓ< #R2d3g~Zϐ1;NiDoœ\d薴 [4(iMY#lAMvCF [4ӌ3-SģC P jBH%g@ZqS3 .Rup4/MIpP!:8t N,S[>j57RQQѯt&CQE!>>p8lbQCmm-.x466R]].w'#+ RYYmӧf3,S)77_;EQ8v34)`ɒ'HM'ٵdYf̝;ײLs՜"]jB`2%aUC)a)&kL,hrLly_{O P Pv!!v Y9r$3gdԩ:C8Pp8(!m\">6]M2**aÆu+7x!هb! uIMM&%_mCBB%ⓘFuU%yFn&{1ӻ&M"**#FPYYISS-f1tnQ]]NtRWW(j(sD!̅RܘܹOoMjZ rs)-S0|/^$&&ŋ[c5a/aUsF*t(:D "hA$-(E%kE X`QT5))[9В)PBBаh@QB qRu2.8 kSLf &q:Px+oӟ~]k=Hi AN<ɱcDGGDttt|{݉e B\x2N5rssYƎ~޽ ϬfðQ";~۷gq70&d$}wպMSv}1h~͹YGEx/EQEZ(( (~/aߋEinki{ 7@+3V߈Ue:1D Aphm6åE#]((N6XS74[oͱ  gy^-1iZ )W3̔Z*Fknn& 8xᇑe7|hY#F 2P@ @ hsW~866^0^jRQQaK}I3f ;I"IǭkO \p#GP[[y'{ⴙt +V0{΀G$UY\~/ƃ(hnN4j! n9Xv A"Xl $JB:oP-g;4lX۔jYcݢ AA1'8l#NeuĤ3#}6+CH3Q(4Rq ń'??)Sdǹ@XP(穩av, ( BkCCjpGMMM>Afc1dMM7eofĉܜ;3$JΕ{f!b#Gvc̝;ۚO&LD̹j"_E'Uݛ;- -LqwZAhLZxx(%dWݚSHb`3Xl*CV!hEXFT6r65 Hڙ3g(RFK@N8t]5ENZjB(,( R2tvt&#**M6إnF IJJ2EѸTw ԠbχtESS^rvI݂o٘>}:yyW((WȅfwN%, a $ӧd ֋gЄA$UFee%ǜ&8}4gkAv"J KA$KKY$1+dCi-dS];ŮZ!)A+Y%aFƬ*Z?o6B/{)$mәhIKKX~@c!v#˲qp8LCC mBx"pݝ|^^&L`x|j%!!aÆrC(<:p# c4443<Ä zԙ0ѧHqqqո8fϞMVV9r]DV_ES2|,jjj[EqOLPRbbb7]) F˥L,s9#u6ѣ7g2rD*>?Pk:IǔsEbcc~>΄>E@ {tرǝ"..IHNN&..hILLrHbb"6ιC5u 2FƦfI$ hEl`Ո1ժ0[+Bfن"djdA6,hDPP3IG+whE YHQ:a(Nw=H/++#..jnf>SdYnsgP'}~lmD6? ;֯Xɓ8| ?Z~? qP&NF9fIIɜ>U_._}(|e˖uXلn{?}6W~;fBE:ڬQ> tg9*@zzQKLDVH(^h!hBDJ$wƑh6ڍ@mVJʤ)=h h:l%hiqJdADj@G^Iځ>}z'mv0yjm{%55Kii)%%%J~L0'O(9< Y9p8Lmm-vеkk.^B֡Dz|M O!&&U!8N޵ Bl6233ebrrri41X~W֝qXMFFEEE}2W/Ï~#nN,F)ϱc1b:^^UUE}}=~OEEe%''#2ƒLrr2iii$''sȑAn8SS5Py<@lNE质4,v#ST7ծ& 6REK3'T=2-vCC$IsT6!$ \tͿֈx ;vpaTGͰvt8aSf%+(([ZM u)7]N%ljJ(Tq, QQQ dx^<nRjjj(--vz|W^$Bzz:rHHHg?O&PTTdħPC3IT:IzwYt  ׅ^/1)^# F(ݬ̜MιSRR‘#Ghll$::qQUUfqwrm1ydя0%8z ,˸n "VTTd+t"--ѣG;ϳvZ8@ff&/^ŸLRܕul6c\1VvrTג=6c??ŋH… \.,sq:)--eΝܹɓ{8qbW&"f޷lBii)?x'GZZU?)..6[IIq3x /ĉ'?} !! v`AII JdY &áF!4=_ l36Pf7p1]yˁiksUa7x T-iE*j!C:xVZZ f q:q:Lɂr8]MNǴ3MNЅ,8-V&!Uۗ ҌTuRJISI (0qW_nqiii\ZZF.CCi׿fĈ=riii,^ŋSXXoM~~>i&f͚ܹs}0zٳ~mw_s5;;5kpQ6l؀f,]=_ I`ʔ)mtW'pNԩSF|S[tinnnX:u ܬ&(NG% be)!X"5vA5 WOFAse ;SЈ}F@ElRPW1-iM;ʘD2I ;;,㹞>rx'yGٸq#oE;_F >|/QzU.77\dYf|=z]vk.ӹ3gi]3ew^ '##)Sp]wa|~̘1?W'O֭[9x 6mv|rfΜŋ0=@'q:|>QVVƉ'0?#Kvvv0UU*QACBi=_'ad8=~L z k;YBCJAR,kBZ!ѐ.EMYʆlPwں.Nw$U~/ߋXU~ס4nҞ7nGinAs=Js=JI}_inP>@JЯ2pQ`FtDlN[fBE= $Nh/N|=z-[b ̙#<š5kxW2==I&tR6lo͛Ybͣ7hs\Xw}G}B0,7ψ?1/1JĬY;^yqRRRplڴ|msGۛ㹖Hyoee%[lg 5k֭cӦM,Z hl=Wy5Wvsov$qwuVx l6gl߾þ̔)SxXb[n7dƍ,Yn-..f߾} ,Y{Ǹ.mTzj~-Zٳ70~Mi3Cp U-c hLMYFj5C(`Kn]zR/GdBV% %Ɯ?buyyo~܍;Z% tt$Ngi1fAkmUB3Vv{QTPj(Q ԀK˗r)>3N8Aaaa2E^^^;b!ׯgϞ=TáCR8&==C|ܹ<衣7|'rrwWuI (;;srwv{yx#rVTTqF<ql˖-3s.]ĉPXXnCnu7^[U555455 2/"O?P'g GWi$K(h$#\FՁ`CеԬUOMA3l]^EWs ُxX6tBqĉvwk))),##&eQZZjd|̛7ŋ{[F!.F[n_WTOTVVosNTTWI:@ }x׌X3t_U&乪ǫg: x^n7ǏO>ĉ nTB,hK-z د#G>.p(1u*Q31x^H RUT>a5`Q=nщYkcѹ!:QsV__]E,IO~Bii)} IDAT..#N'IډOMɚՎhqaə~[3hefl`A6{YO2d!b{) (,,lcldggKVV^g6n܈#11UV]O>O>{>c07|3-bҥ}x݇otpqkIҮkƮ]|f޼y}ۊĹ*2۷o祗^" =Dn裏(.. is:}vV^/,#N{ 2]1A4Xs(q~UjCda1՚&%MT3t֖uQ#iy %vɮ]5kO>Q~?'Oo䮻bĉm2$&Lg[g9X\(4Ħ&!URKFm)Hv)FZ NYFo[D=yád~g( jյYCD/ЮUFnӦMڶk.V\Iee%999lذC cֶkWyWxǐ$={0|֯_] t^P=6l`ܹϔ)Sի;$hxЖ??|rN'vbɒ%ǵF,s)|>$%%q ~pv;qqqPVVFee%@TFEJJ ` o˿ۿ""!VSYYY?9 !27of̞=e˖E\9HFEE\.2;zI KF̙$I̙3￟ހGEE=Az!CkD( uuu|>x^vٳgqp8\pJ, P$I2\zuF, H;dsT)c=~.x*S4\ChqkVͲfb[* u-`qgxzս5t~r&MĐ!CH:JΐK2I/eAvjG ijJh)L[ЃEޜnT mZ Qb|L8nO逃g޽%KOOX~b̘1zK>Svލb̘1bACQTTĴiӮR[,<={6O?4ַLbX,>s>S>&Md?EQԠ( 8;EEE*jjj5$N'555p8:t(p8L(B|>{ 9rdkba!tfM絔1Z5$fZ' D#e\-_-xpSNjȈ i@*^yii !cX-B@ukbmu к?X[H(u,j&&h:yr+o}[fvxxꩧ(((@$}YfϞ}ݐ@ll,ӧOg;wb>̻˨QHMM!:7ϪUxwhlldʔ)>bihh 55٦̐D߾s4wiJDPk#Tg6ͳ VȔW',!r%+Ie( m:/o"r&TCEU!Ydk1-E Ő0P45 A A%mSh!q2xGMOP\\SO=Eqq1ɬ[[n5hٳɡby+rrr]BҊygyz|raÆ= ٳ9{,gΜwߥI&7PWWGYYQQQ$&&裏ػw/ FXii)eeeTTT0|pn ŋx^bcc6lvf~?QQQANl}n>s>72Hu( qN5m$32 ;4aeCh%ũZlMT?CW FPe7~W_Ntt4 Ǔäa_MYe\V5{҆&<+qjK|ǒz2E-VJMOJO 6\:K'2dur-(**?!dff_ѣG%ַE||<_~%Nn=(/&|>6n/K<:̾hhB2 @ \lbj'$Nq- 5ZjNixCCBk9$&CkBC ؀0FX ڗA#h;!ߟGB$H9Hswy}~}IMM^//H(Yp!.O(--rAziii! ͚vŋz~?NDbb줮v;~jcZZ\._Fj Rh4x~:999YլV3ߗν^j4գ&}u@#R#碷稙 Lp}HNNx ƨA]-N}k׮0k,֭[7ٳoXwԩ[:| yٳg}sNm۶q$I"775kܴ$|i&~qY8pAn݊@Olٲj_;z ֯_/lܸq\$܌`0 Fz=~s94 .$8rssZh4ڨ!f#p5^/:N tww&W6 {w~dee C3\a{>.B&LDUσ=*5eN! &!IgΜET`\v^{Ǐ q:˅ԩSk\tIʉ,^W}W灌bt݉L`voCeYЛPL=aJ5@'od{ 8p@8hSLaÆ /ԩSl޼ڳy^_Co0ۯ?H}'^Zxֲb nʁn#e띌 $?֮#իY~=yk.VXqg(gN yvtO+--eL8ʕ+9p@;16}N'7_p9~ӟr jjjYww7PHDz*/^bO.WTTϔ)SDp8Lkk+:Պbh4R'11@ @gg'aaywذa[lO?% Dٜ)l&* .B։׳ $;̨F/o@T$80^I8sKNӉ'OfZt8]xt=%rt "7x<8BzVJ⭷GѠjEړZׯjIOO'##ǵkװX,fCRLvv(Bhnn9nL& \~Zdh4ru!rp݄B!B&MOO)**Q5hii؇2Y.KԣXrICF4E$ZZZhoo`0?*tSqFᠭXⶺ1̞=Q*y|"r臠?Vh4-( ձ}ATѫ@3-yMn!WvVia*|ٴi$e;C9Xp"i3gtn@~=Zs,}VeFƥKRTT9vlܸq\hօa\.NP($$ȓXR1{lJKKo~áCh4F1(@ @GG$&&jz(h4L0$:::ʕ+L&fϞZWvh4 ǓGkk+`0H0$))IT::;;??طo .{`0ٷ2xΑϦ9a$'BX"QZ gX4Y FCJJ j'Npn7JCC(JEWW twxB# =ر7xz `4F/,;jN:~=>T3/IE`g4_ ̛ܼo֭[ENBLLYY="Ily9l%N:CeB[Ah4=Ӟt~p P"i~}; 7zu{e۶mٳ'Nz128v7oի)--YMVG뜩lٲ7S^^k+wB3V&;X:yR̙#6:;;$ ՊJBF\L& l6 0F֪$IQWW'k׮H @R#rp:h40LT7ޠGyb0&L,tლ:2 H ԁaq:;;ikkjF ѣtwwc0D~Ny9p==6 F#hrl>._ieW80J|R#2DgdIm .zkjC /8H0cK2k,6l@EE\ŋV.Hٳg$`TLf|VGNٶmwfڵ^ GyPEJYDF0j{w9soiPy:1OF" ؈Nnvx(P5۩fCc4$IyȲ>[VVݴ@0fG0VN'gϞyqI(**bIUo؇N [sHɴ4f3l߾w}K.ֆ чZrrr:u*FQ+B!&OLaa!'NDՊDFBBf^/4Nٷo/2vUI8 |[BIKy>/5uh>o\G 0v=`XB}cV$I̟?"[9fy]xϽ\/߾w~R77cYYYlݺ~`0ȦMX~=~?lhL!77ʊ+ؽ{7իWvZN>}[I[26վ1H;Tc(˗/gٲe~֯_ϫ:Iiw3^cFxgX,Bc-##L&|gj222Cv x<x"Lł$I8$IBӉ~<"_Mv$\Zr-r:RDϳ}v]FCCCρ?wҼ^/fZMkk'&//Okkkill$%%V+III8l6n A~?np8LJJ twwt:ĉ\|8V+Zp8V%';SL_ģ%PkBVyuTj L~!lF: IDAT.\Lw~_rh"^z%A'dee`Ο?/zfggl+3gpYf̘3m HĿm6qCh0Jq̚5kLi3~<Hl\~#;.eggCaZj* ׋G x<477%77- @AAt:cX0L8Np$N^/vTBtvv"Ĝ9sx)**BVc4PMMP:d'"|97䭷 %IQx$''@ HHH !!A%HKK׋墶, tԄ!334QՅJl6c0Evȑ#TVVbEJ )̢XMԝoƥ-A{taz HmyM}BB⥗^YfvZ8#..ŋŹsxwikkc1Dzի9x ^x??W>/w!EEEdggsqΝ;GKK eeecQ0"#L8ۉrrrX`V$Iedb41L|>!aZ455ֆ&??"L&MMM >}: $&&Vikk09'tl&77z|+t:t:𕚚p\bVqҤI!=7u"uJt:]7ͤIIgg'pXvhkk`"~򓟈ƶ Z͜9s(**TTT駟RVV&pbXuҪYjբ⯬,f)D&N{GeesԴZ-P(匿 VN4jg׮]8p .`HIIfgg`233EPGn񔑑AZZnqqqqlS0DR6QhԄFa$''illTӱZ,ZKjTNd.$IBRQ\\̔)S N,N.>2YYY<"Ze6X[||L&tttֆ"77"z=MMM"wmʔ)#wY;l6'eQݤ$RSSrusr\HM%'>=Z0 |eOx^ddde˖q!Ԩ08̙3>j>CfϞ3QXtҎ;ի蠤W_}mb^ڕ+W?qt: ",ro,SSSÿۿ~zwɓt:d… Z477Ȝs&ziii5 *744p5 '>>^H,/Dvv66 HII ?LAM8~udd t, xD)&h4tV$|:v;Bp[.^.@aLIIl6iܓ'Or)z=Arr2;Y OKu7^VE8#p5_Wz{sNL&6Ial,XO>*y 0e45'm۶mlݺϢE<&5F,MǹtҘi4T*x MMMڵ^z{&"seee'?6|>Ѽ===l, BOnEрA$1X[[K(bԩL0JKK7)~ qCBtNA%8%N_{y|2>r)mjj*FQӥN8]VV tW멫$ӫF&d?q@ N#99q: QUUΣS7AjVXaŊ<6I!lݺ={D]R $8\.k׮̙3h4YdITlQ[m<O r$.. #Gt:|>|>~_[ȲZ6^g…<7 ǎ% ILLvP!ՅlF^^3gd2 |hZEh4[s9 _KN2}kbTNKKK#%%p8LSS)L&Mq2i$ihhJŴiӰlL&zJ%?nٌdp|tvvbٰl\~O?#GEvv(Zw$RSjD[p:Z ҥKmbRz=gϞj{NUd2nݺ1,0v2338}4yXAՒFAӉbyyyL6 G]]@O HՊ"y:f*e$77 b٨ra00Lh4h4bj2 id"!!G})SD`<+˂ 5El( ݌CGjZl6^vS+;;[[0L&±Nfޟr(._Ncc#,RRRHHH(~"rC89 E||)Χ{$(7:i8ȈӍM 0a%%%9rӧOG%:ZNZkk+/W\!55T:(6EEE N:ٳgIMMjrC.4 n7JE||{"#Ky=4⚆:i8ȈӍ>̿˿`4~U| ngʔ)?~Ϗ '_|F شi"pL:Fӧ9z(cLCRT{Pww7HVd2Mqq1'O> ,cѢEl6^*AM&)))c|{'hZ, "l6<l(Bvμ^/:>Sn7ʴiӰQ;%' z.LVBh^U'[PPJAt 69VI(ʕ+9rĉ9egf]nZ-iiiA._LGG]]]XVrrrHJJpP__O0ncZ}zL߲&=Hď~#(++RTM222(**wO>f9.yG>^W| ~_~ yWJgQb0sLyh4|K_Y#N#>>DᰙL&ɑ4vvEhɓ={P(Faԩ̛7/nnIEn7`߾}deeqƘP䐖|b„ rl\94 4|Sv;lذGb2شiӰٮp+P\\̡C)**׺r4ׯ+{{p:;;7-ˌFd***(//iӦRٽ{7۶mCѰeJJJnX$҂&:ZjjޒgQD<)!/j6P m>{xbf3.N<t:Lï~+oĉٲeaTQ$IbժU9s o.z E]]~H梨zJKKIKKcE, )))9裏Xz5$rJx[Jj*Ν;ǼyXn]ME"^?&FI[Jutt~9OMՊ`'}KRtvv!s^_ _pVZ$Il۶ +DŊ+eƌl޼y~KK.QQQҘ9s&III7/rYr'pp8Xx1WTPI\.?3n݊fcǎXB#X,mۦ8jc8ix<:::D3n&#Om:m-<SNDٴi(,,UjkkY|9GT*rbaL&z%CCpUUUb]_sf3'O&??}^ԩS4iҝ\¨a83شir=Λq72k׮'x%KD.^@V)Iq=Jkkطe"OCC^j&&&bTQA!̞=wy*"3ƈz$?|>^o/?㩪ʕ+  ;w.6m߿7reHq%o,VT駟 U|fvMogdff*ΚBLSQQʕ+$ 6PVVmȰ r*;;<^/dffF||3 Ѯ@ @uu5/^Vnu[ROZ, $3g(ic1X5X|9;v=b@ Kz*pt5>3Mu@ffΜIrrPTTd+gyyG\gwSwz>`5D =}lق$I|;Q4Qʕ+\xQ8`Ώᠢ!KRݠ8߶ݷ-v:u yinn\%_M!flavY(**sF:"gfQs[nǐQ=rϹs੧9 wmmm;wN$G:S .d V7w}lL"trejjj8q"v6((-ݮ <kX}}=oXr U>dzĚ=}W\w|I$Iq >SQȑYlP"rݜ={cǎoTM$o(PWu،3Yu}S}n*}+@ccS[,]h0Nq8:u|>W\qc܉P#l4iҠtdff2s̛ڨ0~VXAUU˖-gc6P`461|RݩpTWWsA48TPN~?~!UUU78*`:zEF`(SvGo͗BA)++c޼y85`W᎝hZ}ss1GžXĉ9s,ϟmsc/HLLj4f0())!''F3jQP(dbAځȪM+a„ g}ݻwͤI%KDSSj呚:=3hDPWWffqa|>?8@W^y_|4'C}>/YbR8';v젴7Fۜ[ϥC5{۵k K r}p'+f o}d ##SbQTUL>6hOjEӉkknnf̝;Ѐh~>}Zf3˗/GVVWuژ\Dp >u{{,c ņ 8p˗/駟9 wEҠ'Rg͚5Q]]Muuu/X$L{{;~H UZb0Na0Gբjq\ɟ ۷#P\\Lee%>F?9O='Ot, VTQ߿7]QFTUUqAL&K,9 w 9Rp\V{U= odE&ꧧSRRBnn.qqq\``2{(,,z, qqqD|>Ν`żpI|I;Ȓazz) /^̌3hjjbϞ=6Cnݺu6Ba:{1hp|$&&{ z;k---;A~qx0(rt:@@ 0h!77N'l6c6h4|~0k,hnn &P\\0o<2339s&:NvvvR@QQ… ,]^m'mt:ٸq#p5kֈ 4`0ө_GMV8<'O$77{ٌ K&/jLrr2VɄF! -33<7jL&t:jZweB\Bqq1SL!99BQTTb򫣠p{l6\¥KBJ*Qqoɓ'yXtiQ\.> ;wW-r#Gv8q")))F^=N`4IHH 99tӉ}hh4{9c9ijL8@~~>VFõk(**Ncqq20.(..淿-<7HS'm!I7nr /`ۣm]dSs!33ӧOTTTWWs$>>$I"k04u}F ]st:VÇ3sL(**BSXXHqq(LPP$$$Bee%,\0&(8k;pMMMroXGl Pŋk![M);j^ioo`0LFF&p8 Y`AQh4L$lFq~_4y~Ǣl$$$0w\&L $~SOa4{m߫KT*ZׯsQjkk{)7IF\ cG}FÁ9Eb͞#Gp Z[[)((%0֮vb:nNcƌ,X@tB̜9fҤI7Ddj5LΛj풒׾5^pb9WWcg,칓t-B$vU{䤍֮]Oyya@aL9{ؐ$jGWWpؚ&w*S/ 1Z@||<78w7ocQzfbz-[ p(ӝ1J8)ZGSSǎC0QPAw}8h 7!++kh8i1?6$r>|IX`KMa̒׿u~^f_:EEEbsw78kr p8-xy{Mm*(( 3Z'-%%E?XNڬYxGF w3g(//fk.EL}Ѭ[n][;$C&艬;w.ӦM PPAZ-|K_h4r5N͆^QQ4,W7,YYY)w)((={j())IEdžΟHMM'? SMHR;vwy9ruUPYY(Fx#;v{|M:D&9$!R~,˚ǟ꼉^w,ˢT*AAaxxᇹ[җ4q?8oNcN\u,pp--m\Ǣ?j~u4㴵x "4 JҤOuGwreY 0߿{wH$£>J}}˿ )686k8ޱ1"{7]ؔeYt)-NRaqgL本=.JJrN_A8wX|9֭c7Gt1rn"qՍjYlO)ؔl׹ڵܵ"2ڵkYr |rā 044Çd23=3Z/@K\uU\uU=6U?FM|e0Qn9]]]o^ Z,6%BX#? }/?ywPRDOOO<,P홀ţ-f׮]hصkW?M> pnrW2gy7Ny!QbQbǤyJ괎[űqmlӋ_Lv.ϙG6`۶ml۶+ 6gV1"4Mzzzr8 Ά3a` X bo'd9]jtqkNaD"lܸg<3㊴h3jߕ5űx4/Aq|Qf[( 73l&GsEU9u 6^&^x_| 6p7Vðm^:N5ba(~chkDwTp}cǟ:;Tc'D~8s߇crֱuV4MW^!Ϗ;x+Q5%Bk 75:x7hD7FO +5Oo<'?mfgضm>@MW)qa}+rי, 4G6G?z?HvΙX&b:{'slzƚ˙ܗ39L>o[ NsϹ5ܜ65+ ,j޳YWq- *ezY:'xׯg5[Cܝ@?}}}'s۶T*# drLj ;#zA8oˎ;BXKua5:iTn'<<-\ĵ(mWy'k`eIKrl߾۷b 6o̊+fV40i3œ3a*4cO/sO6n<oAww7ͣ(:U󅙍b;ݰqxʹ0Śu<gXXbU=q[\ʡrz56da^ĚTUEӴƄp]jJ\Y3q,jęe8qNw9Z{p*Y~=mM@wWVPPQ(aqgI/>-ֈKy[P#p4])8!RnuТGPbuam45QK+鉳}C(Լ-jJ,:l|x?㝳ٗΏ~#/ͨX ɄDājaRx$q@0zp xضw w=ή`ςkAQA@@UQTE@սMQT0un0Pq[ӵcl תIU\ yU9~Y%dͬ_oyZ:RGUURZ;Š8^vDjm>ښO06xo о{Lu2jm>ښtCӴNڎ;&qJr2!blr8[kj:,b S\uss-Ƣ(zX5~|Uw} IDAT; gz'|~䀹,bpppDLXLXAsݻw/|իWH$aoESQ B y4ŷS[\hfh=>W\-Y>†CeƔ3455144$  ?[9s`USTpUg 54W?|xMk$ 3 v𴪸 VtۧE@Zl_i\bVq5W3h/[hrkk3ic:dikJ hA|bݺu7fx4EUX4Tճh3TO@ ,2RMQ\+ꉰ:y #VEx-nԡGQRԤY:kS,g Lkk+===;6)UOP' ~\.Nݳri3p7+(zWzV1#k3+ _5m2X|lYq0]uD P(PWW7b_4%P*(JfR˲ pP,q]J -*TTjj{SØ4tϽj_|Ep[Ϭo UJG=ĜyfFhn A1W !"m׿~PSD"A<\.S.*^qu& \pݻ2H,jPsU/F Rj <'"YVE#~7(gaCס AE?^DBa1r3 Rc+_ BasE!3k,$h`ib9!jyLd#먭:jk>ښtbv+(%8YBJ攲^)gq*9JJ-q~RjU f 1AU@DPu:=U$IxlJ$'ϊ6]\Jppǖ-[u WW,^xB۶^,pH g{A۷?i֯_?-Lܝc׾5~[ok6fO˲cSA \4WU\\WAQ\c% 5"vgl#b[[ļf8c:6cymrzK^PT¦WT/\t6_6bθ 𢸂 p@T=e8T߂{B v$t-%Eqf:8Qccظ ojȀ4Se% 477| je!J5ހOS(`KDQ"1c^gdPgj6Ŧi%8x|7-vv5dʦO7ݮN,]~|&NtAᜤZN)X\|Ŭ\o+^]8u3= 7HP mau|цB0pGСC aYafO;b`khh`ʕ\ve AB@&mEa'?al޼fAQaɒ%,]k9Νo_:pS%O8wa,ų)*j@PSM[G ,m6<../ 8C*鼝uVJx0`:q]vqlۦ\.~yEQ0M]Dir9:;;ٱck׮妛nb֬YӾAò,)躎eY<Ӽ sa^u֮]7̊+B.K,aٲe\y~?u@NcշLwUUPEFGѽ.r} 6$'*uREqˇqu¾VsO޻w/wO~ž={(TP9r;wrdH-<|d5YGmGQ[9uضM&۶㴷sc"yٵkNj˲e˸м1)xI/ V6"q#bxQg+{r :u Ѧ sBJ,ZhFx -[p-~2?8w/عs'|~ı;umz^.҉"AArL&u](J{w}.a`<:XE"@u.b6mĺuBZ wa~a^9ԇ)UAAUTK(6g3B5wzڽ^۩{};mxWt Q⋔e>ϝT=[oz7I={}v5fu\.dz>+"MA0mߏiaw?9Bh4J*˲@`t]P(000@.c8pfnv֯_뺡`KR\s:V\#]Ϟ={x׉㴶qh\"[@otm$T=bDbgSy4}޸hEYb6l.NF;G/~;{&n~P*T?O~|C: 7R*rhF2DUU~_{n Bh]4MǡfE ׊"D&\%ɐfaDQ$7tb#ܠPV9x=*Ϣfu1w*LX`I3u58Kn6oG?<O<g˖-\|S}#HP,`kAqHT*I$_^:D*"HH.#ϣ*r9 dò,TU]1M~2 |'x__nT* 5MӸx<חs>vfX4+WCQJ4UTՍ0VLG w'';UUEӴ}:fΝi-Z4,3qwD"88'=n2,i\rɴOA!ZaΝz,69/~/H]] ,@UUzfϞ}VsYhS& \aQUwy{ 4e֬Y$I"n3glν{R*X`]tQy ZI%IlU*\ X?<׿fʕtMXH$8q_{ kVe eBO] T5 H9.f6 D{{[T@uYEb SITbpp0,:裏+iZ9:|pU`ɒ%ա*;v ]iiiA4<R)ϟOWWWâEXjt:;;QU 1MЭMAkkkXcΝZ{˗VT*-7_ek{v}΅WBkW[MQk(ش9s&6(??T( 3=A<$RJ% {i*wɂ xg$͢( ˁ:iH%Ki[E7oS__Ͼ}0Mfx2---T*l8h4ʚ5kVtttM,c޼yhaضE8??0g6o̵^ŝ3g[>/7O|1al'Y_`^*b ٝ0ŹtR.x l3s})f ^W_}dw g뺡+}/f…x ;F" L(dY\%LFCi*"yX|9H0vK.PJ<GQH$,^۶"͒illdΜ9aY|l6˞={عs'3o޼033LK/<Oӽ/CEO@Dk`$rQm.]:cElD qhsaÆ =zii'?!NOK1[i 5&f9z(P__O6رcJ%ZZZhjj"H嫊"UU?~Xv``,Xezl$ ZZZbaBٳI$J%z{{ٹs'v"҂eY8Css3]+qzCEzGWp sg{ 6xL4NiDnEQطoߴ^|PHA&}߼7axѿ-odlT*E4 b&̟?UUhllDuD])=zE|r* }}}aُ 2w\)Jd2TU%JQWWx TU%~;^|E>ܹsľYfqի| }&-Ʋһ4)l޼|$DIl7)2B'xgU )u+c6YDzТl~ov/؂}W_}57Ç>!xO.رcau۶ٻw/o,Y+E188HwwwhFb1lۦz4t.8k֬˲줷X`8C&?O<aAދ/OT=Rm?===3@odɒS~~ӟfOZKm֭,X`uҲ,o3T[ҤN …űc!)7/Eu\_g۶m=z4t%FQ|9:k,.\F0 4MMp Ç ;\tEa<ݑ#G( Ncc#uuuyzzz4 u L.\T*ar9,eŸ^AVÇ9|0sϟ%N_\555q 7N3NcӃeY#) L&RB@>PgSqZ[[馛ŽR u)^ Յi,Zsb&}}}d2xX#-aMMMal6ƭ-]x<ZJ .d444d2TU멯r9$K,AQ4M!2044D[[ۤ3EJp:tKN++L&޽{ٿ?jd2P TLT!RTh ,dA)េVuQUk뮻;vm6, 6cǰ,+,Q__Oww7.gfٲeD"2 a S*hmmRp1M˲² AjJCCT ۶˸;Bd2twwSThhh'4qwַ0 ;:g&900?Lww7m0>uANJBww7XESS .α\x9NBiktǎ<#ܠ388HT ͛tuu:TjeYa… 5kLÇ344D4eʕm;v H A5M[n6\ ]iJB4eժUg=l'0 nʜ9s&4˲xioo3z:D8hE/^|kA=J VX,ZƊE.䂖l˗eV^}9Xd2rTF4\.455ڊi 288ib1ΝKCCضMcc#lGEu8-4Mzzzq̙C*Bu:J\ Z4 UUlf3 ""-j6m⢋.O==wqv[,V(я~DWWpRV??A 4% QUus:z(>(===TUL4MlۦoosoO?ilqFT*g,:"]]]eRV"bY a6Dfhoo{i+š5kw#@(* fbzDI QT*|Iq2̸*^IDAT穧;qrgc8 ~5sf#먭:jk>ښώ;ZfMMMTU"KpC=ă>ZlƲ,LӤZo>l۶\.u}O|STH$466<4q˲b``#GtR ^cݼ;e]ƺuF۷t:͚5kXp!]tԧؼy3T* ' VXA[[V J,ik¬Y믿T*5%(zk%Ͳ,z!8aL"6[E2[naӦMA<'#688PX~ɋ/kZB0 Fn6>2{^{-r901X,ߏeYRPU*:;;q]0H&477m9s`&x+knV__O2 L$LL&gix"Ͳ,z{{5kVV֮]ʕ+"iunϞ=T*[?xV>2{" \X B!$FL͗_~W^y%,q"-FCDٺu I ۷o穧bhh( bRB~1 ٳgqǎ#ˑH$Xt)MMM,^/x>I3 …LT ; d2*JhYK$u-ܯT*|믝x%>kl6,QVYbghhhǡ\.z.<qGѰ9eY?LDZ`^d4۶47O}j pS,~A \c0Ywc;W_'we]idY* \4D"D"RTqmQzJ|>( rkX|97OhXGQ[u|d5K$h"ZZZhjj0Pւi  mfÆ |[ⓟ$dn/~]l6KOO=z|>a̛7Yfꫯwg3&4FƤ9 놦 T*%fҒT]޲e 7n0I+ p"A ?^h4J<GuTU lݙcU4-\Vf߹`Ν;gR9qT*E.#͆X 0(a}&<3k֬{辰qZWAx:2L|>OXP(X,62^i`H ^cAQTR=<2saҥ]%i>L&fa_ 8`FXa9x LNUKQ̙nݺs. "l6K6 OP(08oEQu=uAHgg'$k֬9o yߏ8TPi9puyt]'급ڟLw8444pWuQA8)J Q.CZRqL ,aE[XJv4M(uuu]e˖*_|1 f2U àT*#| L455''\rҮ+ SA<'tG @jxǂȱk.CQj*֮]{Ab%__ -`j5l {I\Ʋxx0SZ҂@M6q=LA,bX.#"L~s%L =F,^͛7)!a:tzrmS(faꤗPU:>Os5LAaq]jJ\P(PVGx .F iG?Q؜u MZgHOmذObi]  mcYr9l~""m 4Iww ֫jXc8J<*mmm|`3\AAji'_^T*MeY'8H={6_=wqG@ADRį~+Iknmmm|e…32AAjiT*K/sNX544niW# B#%8&8G>WnVnf͚55FL&Yv-w7@;Rkq#먭:jk>ښ#%8~{=x( =z4iF*3 _~LOWAsiSHZ%|BWAA!"MA prUW׮]rݳTXǍ'3'q@A ]v/&:.j^%MAq9ei"V֪Oǒu:u*ʂ8H :jk>ښ#먭j0e_>Ogl">6:N66qcQ CATӵD;ӱLbNS;ATW51q @D  >j+2?)jET{- ?Zll2{&S1HSM =>gZ|`ʉuj  GJp #P̣&b<gz3Y Zx:jk>ښ#먭H p3-7zlROǒ69Aa,[[&/=q`"A6ŚMbAANdw22d2KrF&zh p 4F3:gsɚH4AA8AADܝ  5I;ͱZ#먭:jk>ښ#uAA) PHAAAD  "AAji  58ͱZ#먭:jk>ښ#%8AA) PHAAAD  "AAji  58ͱZ#먭:jk>ښ3gIENDB`filius-2.9.4+ds/src/info/img/DNSserver.png000066400000000000000000000151771506574241500203260ustar00rootroot00000000000000PNG  IHDR@@iqFIDATx[TTWEBlh&E$] PP *JcA"(*@&0Co03 [﮵wf_9w%Q^36mtzO!]gP8o^K1{v gʐ].AVVu2BxR#y1w:%޸qځ ;۶2|RΑ&FRSeB(HN"׵"/ƫ.G?Gw蠧gf& _!1 /㈋@ll)GLL%>](B >}*A[.u =IP~jUCq1-+gV/g/gOe< >rDFU7o(|R!_hw?ﴺF~Ԩ'/_ܿ_Ȕ3"@|""JYv/e+GxWpxQϋYӧ"9qx(6Y"plΞ;c}D?³W9x6aQqx]Ne3(;<\_;lP7'Q D۷5"GyH1 {gx.|?\ev2¤"0bX>3g;Oˋ'Y%'8~\drAѫWI`c"&՚|ʍ7y>LLZ# <WQ2G"42柫@)܋g18v,Gfȑl:8p@ )̈́bqA!>xi~5 ݆wԯm|ΓGS0!(O܍|˘UKx *rNx pp#H`lF2 B "s&v΄SvJ m\4;x7S6læ0D=læfk6[lBGw5[L_Ya؜9$C^1-ōH#}Y3eD,F4bNoĎ]8o`0 QB:9U#Vc-\ᙶ{'aORbu;^FX0b{,K\x'mO\ceIǝw" cF6uxppȂvvlݚ: RZ>F#fY!HgB2A ;Zv\h_7/`՝9; %,/V8k&3g\XXd3Y03TY2HX~\\N"%/8NH!, BB8'NQ({cL䃉ӟc8Ȭ1n X*&&qX%}|]i2d{/bVV5Vr`J]d&aI&r aþsgyaof}l@s+hɇI.fȆQ @[;: <WH>}[co'yiW ű ÿ`Р5sKv}MSd\PEK):0))oJd"]a,,P LH&+9Am 7L?{'0C9zx{{—HqSat$ɓ/utr 2IPָH^@T/ДIK&Jr]\H9Zx;۠Ww Gv|oc$9\_U{DV.yqܓSN$k h86MҌᑦ@< =A~]]LѺalNN}1Xsm>/oq=Yc^L^Ty+p?JcUDjaX?O YEV WFUJswxVTS#-l.<ݱ9;:ӣ1{=}bn_4KY?PR -DAo0  16ӧqw{hT 02;"g`sCP2{q.X8tNMGp߆4at ¡R yAgx"rz7rM &8 'DĪbР hZMDKXHORBekfyAа օjSi΃Cku-Ub?y U4۴W˅0c ګ6uI~8o/);-C4{l2 4i҉ezNIq㮬7cSVڮ,ބԦ ?uk` \8)JuU}ԵYmb1\]FKР w!z3s KN@kle鎌p:'' FW5ܛWhbŒH$8Fam pr\ /K>8u!u_>b\4h=t_ ୨װ.^U~PW,i4W ^Y`Ս1Oi/A7OTm<*֩P_$1fY&B |' **l""<s+k2"jip.\BZR++|.yyχG`֬xs椣}2OɍMN ]6gz4mx 6> MPP^ZH߭WJ޽y6g :tD["U5 @s1 tof]svab>ƈ2HQkp}t2d н~K Lhdi1|NIg̈E>:dž&v[MBvb")~fh&hҼmR+)K?kBX1njo%PA̶TFPȦUE% yH('$-?1E2둓ݎNr>Gє8a2*IQ\@{d})n֯sOP`Uy"R/˜~>o|' gFYGFȁB3 zSK!\|tR@O8z4C6)rs1td@G=ONs}p[Z? 0*:$` P>b03qRKPe9e sۚyl ǸT)#LUB\;hG?" ؄3>f3@(*#I"P cZjepʀ#PA=URńVU@{~e15y jPL.~AF^B9$)$ !G̸8 sj1v,q(0pG C8#":p\12 ̠8) p'Zn\(hC;ӧC2}k *1{aO 2B,FH!ߑӜz$ 2j8Yةc`~t #Vaw:t4yI!d=O+."o5cX ,y8G S[ T<7yJ]P]Uր!.ŒaZ g?֊(O>Mr:c;Fzg @  t-Q}j%P{6X~Ȫ~Q~w~g(Ĺ_ Wd9S?0jA<(z>'jLE;XC⧵ceFN@ i`A )!T?*D]iCc,deimi=/jahI&ly[<O fl[|5j pwQڵLi~k&qFr0A4т2)h^Fp0'ڤFpĤ~Y1m븚g*BT>,e:com!$ @ r~`ԍSGض?R4P߼ݻ8֜ȊH* CTtP=Ek&Pgvg8e Κ;sz*J(k|ӰG6v;g?ΛE9$d 1pr=u5<#dF'xڠO_֫Q@ 4m#?I\IzQ@40 AEKEH.$ p1IȦ4b~bʒw6v;g?Λ {ϓf܎ѻM_2$uZm!Bs0¨g(#T=j:(F.0`Bw;,qB*J(Cs+)# iWo08@Ȭ 3(ϷCzS7#_W$72@3К3(` s(Z([CT]I7mm,69B1#'Z>%m4*5G\*X:aڝZBjJwPpP5_h3ڻlKx9U4_(3hv*?JhǜU_/T_Ouz'|BHT$25exW''>_%YbǮ*V>j4O7ZȭMր9WR[e$ow2Z%u?뤟jM3<@f \?o$1¤0!Z+<yxmsj~j(?/Ҧ_Z/]:QךxnK5Z.#^[?tz5S}j|TcMy6|?thh<# Ze־{l\[Ǹ?΀9OY<Ͷ.s)ӥVZIǡEakP1uᔌjĖp0PP'DĹk!R@ E-R@ E-R@ E-R@ E-R@ E-R@ E-R@ E-R@ E-R@ E-?i]I2\LI4hk ص̐hexW%'>_%u7I7@4_[z'ҭV&Y5ɾ/']$KRiJO]#I?>iS׺6@>ןq{jۃB*ePJ~hw\_ Q?u\_ ;m$CTa[~m.#m/މXUJHH٣NUOvu¼ų'8*-?WV-<Ⱥ & *#")mY Sr> &X"+}vG.bSҸ NKZI\75ߢ@=A俵'ݫTP2:*f)QVH` T0iuS@ J)hJ)hJ)hJ)hJ)hJ)hZ)[TmoCPye17޺ݭ*6D!=*zv?.7OG42tVVf'SvOր)SֹY)J[ֻo%E;oҀ8ODz<4kY]֯- 1EqM4s]EBI[1VU濛Rvdb(G^`zƨO ˉ< $-λ{Qz GldKb4 O+^}ɧk16v3cutb8A8&xz3`-<$@滍R)XYv((((( )0§EIE@`naU5jE\*P 0ħV0y ؊O֓eQ@ J)hJ)hJ)>m>E;i4moC@Iz)@ҀzѸz1!П΀"=isN\STqf•hs ZKpppQz\)i )Sii3"ʜUi$y;|@{Td{T34On|0Z9 7֗o74fghtTsTVk]IX`tTķI W*jLr~4f1@ ϱϱEF\9f\\CF}UѸ(i&h;mhN#i8R(PQKǭ! \b )pAn-&NqpNqq@ Ҙ<y" 0Re /\~&\ޖ2GNi OGwt )+ʾ:{L`z E3i }i 2PjR64󞂖sס@ ih@ Jm#/y@RžR:P JL:ʓCP xyckcocRH&?i5pQI֓gN&B!M Bz~*Ua7vG~PnbNផTڙ/yk5B*C%VHBT?Udp+!Qv"AFѼ4,:M<qJI#J^=E7b27rZ~W;Hs*{ uw?*Vqئ}h??*PTd}bQ}hܾ5GDѳ;?Qߕ&QR܍mr~cO>Tg?9ǵI-bw-Q[cJ0ic~tû1LEK#U(<0*HKNNT)I shѪgvz RO@אoHsҙY V݌aP1FJ0*h=A2Y(hqأoF5mi:6@)|Kݿ:FQ=MDߥ.ihw7q3!yg q.'H.Q.I50QO1Dmԃktr~Q5,>>M0\" 1{[G*2:?a X /֤5b0\L}ӻ9U$vJS1 }(ؾ)͋(ؿ.K}M 6AQzʀriJ0=)PqKKE%PQKE%PQKE%PQKE%PQKE%PQKE ԃ㞽W ^VxMQӦ 9ć;@ )'Uޝh❐$iy>$'iEޙ`xZu%PQKE%RQKE%#}R7?JwR/ݧP3 TMeV0FE&(:gS-7DPlxe8=*FG/TQI\8tDO~>Tb}{sگ LU}HCm\h)GmF[@I]ĝNGU#6Չ/C}))HqөJC Ul~VzacgHshJ)&pکɏB(Izӫ>K;Nxs (7c?t@$H}ꦀ$%iR$@d 6aczk:yc죁PW?ӡ,QCU7,ԧ4He :ZJ)hJ($p*/&,zYr\<ǥC%ve"y.4Erΰ%FBZLj^*.~*pHoAk V|7K%%U:~n}_1笤8bWOHFAQ0[#lcH#n=q,;t Xx) jYe jVŃ;om#:69ZtPQKE%PYU">ˏI05\l@ 0UGܣ۫*}j)#ㆠ m09<&2uq"\TN*+^sWlg pM@r".{TP[$lz-RM?4eYXE,퀠u$ExjZm `s!ރ#4ytv5ʹ$GxLKƙKvrbA!tkbvrڂ'X* 3[H]A8s:[Ѳbӣ ASA-f3Oh. %4dKd4D͌27 8UG@U'-RQK@r͖j~+Me}n>5dXGՕYAT|I/O*F# zoX[=:o+ qx"Zޕ+l퐃ĐrH^! ZG4o*Aƀ.ψioAqg?bn.D.޼sS.u/.svQj));?3^%zy7S<WPQKE%PQKE%PR~COҀ+/ݧR/ݧP;j]UShl[+1x=K#;r)v֚W9Y$ W94( .AU/1ZYG,"(#q_[S 0RIAA?VDpi(2!A$c9Ƞoj_",_ʗʏ*bNT=*//B23⬄Q@ ]Pf!PY(߀MGO?'hL;PoA+?R]bDTg׽>J("ھ_#֣?k$cj[eɏinq֦8\HA9)PE-6Zחm@v(ǢO❬j.wE8esQGv>hS_ w] ړ tvH}\~-kw#Fi .:=ȮaRD`J+s .2{4wL5j,S( eén016O~<QRV~`fnFf79894P;qs6u}kZ_ƺݰvVM[VcA3\(!@?t5U;iba~E-%AaE-%QEQE-V_өҝ@G%SԴE[OM%Ͽ$̯2g 8 ʖ7˷qӧҤ!` ʀ$m}(RAO֗YҒ-QHaEPE9$مQۢӉ;I'\O-OaREQEQEQEQEQEɥ&8RpI'ҟ\|wX.m\N GB)kV&MkMqHQ =-V̙6)UsR4TszP8 T\ Tbjf ( ( ( ( CzvHw &>QZgU$Tujr$4qnTp޳2IXƀ4#?J\? hTJHz ƣ奈 1KE!'&_ҝ)63Uhv7PԴ )hs@R֕z(R@ E-R@ E-PFERKy)']E èֹG?5>-Vtt-]dt|uQPxvlyk놖W۷"UMqktT:kB[eTW=e|syos o}hZ)i2q@dzѸz΀ w<1DVFܑ8>\k4XcԚ[sj;)4%݁ 2`>ԪN/L7J2\?:Ym65@ E-1('SyiK?*uR@ E-R@ )hLRHzvH)Ѵ#ckqnؚ=µ*`'|~)v/\}>"ra4c ?yq@YH LZ(?ĩsRQY\eH#w-FS@' ~ YMF"p2yb2Ice7&QZVv..ETQEQE4P8>ϴ+^W֝jh"<Ґ6,x瓌ՑEV svbN灎b Lg4*by1lVW6I=E 9{UR1)c(J)hF_Z?hвbU@>Sw޽K|,~$.Dgs]jQωÙxJ,Q3GQ]$6.2|̋a5=C*X}Elwd^B?0CkjSXrCޣ"]כr{B`g$RǠT5m~f?6D,1۰9g{ָ\]ڤCZ&p-fY.-.ql@H֪s\eܱ?R`][~PZ}z s~rli)X޻igdK <@iW4{7J|ȣ:(GmۢIi$_&m D mem (@[㞕j^ LGn'xP \w^L%&q@)hRA@VWc߃kwh. +G,g#_Cs4o3|r|}:&<-2-㇍taPMftog5k}Ena%Ì$֗sր kFl4tuX OZϷ9<͵Ys+a:?*  >fet2 8(k^ SLυB˟LWM[ǥ+E%Mo2e=:v hKF[F#SŸBrp{AKE%PQKE%PQKE%PQKEC"BwS\bbTw`jsFX.#c X?JA_'*YH>DV{Wo4Ih\AMiS8(Cww-1{d*CE4'sz/@QR|>)=٩i~N^: (֍k^$(h%-袀aOx/Ҁ$]H ,˞(rkԾ-پwcw|ޯS-I<ʋsm7E& s-#^1nxSԧƗ@h%<56+U:\QY+Uw*2'FK-Zhrtk uJa@zT.jQ(:J)hVPEq,qi'$#=ԎWw4P̺u_ w,~b8|a2jz2tJx<'Z,ק\G,Y*C#^|L缇ovcӰRdzy?SI¹&׵GAռKajT+2[.gE_9iW-pAL<ײ|͹ᙼ؞SL:a֗yx"kr|V{4WQgd B^\gËAN1>\~`]b'+J}xh{:1d7(E'WaV66/m({#S<Ƥ~f/7>I“~Bϩ:;M3AG\f?]㲏ʛϽooi67K巵sUy.q^Y@|.Ř$)zLsT%FNRUчQ~,/@:E976y>S9溁al?~R/la^A>v>¯ͩ|m[CS9W WKe15)"(N{ Bv\Lhlm fi/]O*2?Yn%>8P2Y74$_tgIvdu#$RqwBi5fucIMF͹&8oßjw8 F qڢφ|Aq|.t$P~koCTQB &}O^=RkRqS˭ҜЬڕUh]#-Z90c—yGA0}*hWvj(<"oj\QF_ҟ{yҀ){ 2SGz\IH-Q@Q@Q@T-ggom$s1}[X0t v{|AU$~4p#a)¨xkOmWa=_Oq⶚)dTs8-Ҡ,y4a쟝GNI0S8UՊ*_>((((((A@Q@xWyq֝%N(KPn:)V\ݭoaԸ?*_/TؾQnAsgx"PO f U 1T>4ឳc}m*dce#M{*ńr>hHeF@ {yW70>?!WWWju?gY=5>e:($oG63Ĕ q{ET\ ȏؑtB}KS}N3#+V3+&cc(UTTpzU(F zRAڀ{Ron¤_Ҍ?I)7/3cwjuMCRvFpl'=T{՞߶[yXUg[1V  ȯiOxo7,^[yL&F9.0'uQn(;b1NSF(Qn(;b❊B8|~N3(0SMyq#Y ؁րEN >U'O+WQr:*˒fw^,QM<8QKE%PSXpN2=i?0)8NA\ RnLܜU|Hnp1׭(ۂ=zy#n OFbX7Ҁ.ЃBCi }c__($};zyc_JO0v)qLj6)Y=ZrMؾbt~T0FI(UǪ⺝zݔKW4``,|}3W[ihἵeOP{qqM:VMC2yێQP]vŤF=L`1h2 ¬qNy^edlTÃ)>akW]뗷6e MSO^E?WHki7V*Bay27 G#uR@ E-R@ E-R@ E-5J 8cgv=PaLm<,9 ׊3@rIϵ4,`( 2h]韭._ҏ(S6zn=*O0v?jt~T|*JLQ@ G{v= '=eIޗy%#(E%PQKE%pjRlK*/V߀z=oP\ew$vll/9$pW1:'9+>QMt^%ŻB#bwg5ʊCjkkyX-icK1@ xgR KHJ$qOǹO |0U sǪ!פAo 1 *FT}{þ 69l~4hBi@gv@PR۳zPPΔ ܜA)ɒ>ԃ |ަ$z6vx?7ofsz Z(2޿&riPv0)٤(h (ǽtJ)hJ)h[~9Tx.OY8-ׯ5rm.n_(`ֽnKz[>>A~Sм3z 6HFwGN: آZ((Z((Z((Z((Դ4 Ȩ19'4 $ӄ{6sҏÌlI&ͻOpjjAZu%PQMTAsN!%8/ҀED.Q$¢7o݀h[ΝȝwI`sGCڀ'GE"FIHzPTgo884@$ɧs@ Eƀ )qF%P~sKE%R@ E-R@ E-R@ E-R@ E-R@ E-R@ E-Rhm@ K@R4(M.uJ\{R@ E-filius-2.9.4+ds/src/info/img/cable.png000066400000000000000000000122641506574241500175130ustar00rootroot00000000000000PNG  IHDRP8j{IDATx\xUUj$j tyPTy  eʂw HcEuepY\.a[D"!~Kd3^Bx,/`ݛq@*+`s;Xpwj xG D:)WamE&#Q;E(SyH*Z 9(r+TӘvIpoF66ěnDYx7 -f~/PA>q01c8H=X$zhj6"SpO0+" xE3`9.)hfB:"YaLrM~ AT V0}ѯDz\Q`*1:cthl %;`<-nH:-rQ5V6ɓ|I 5%kMIgQDʝMئ1![Z3`4=~ cȺhv&3|Y^ H& #0̜=fӖTǬL )o>Ot]?u.b7a\Rq(ڻ|+їa[i:4G Yd5/Fqg/y찁gK/2O Պ7_S^2ErtdrP '9{2"A_oopg8G 0bExYXx<e0- ^1z#&i3 lC~Wkf0SxUލ_kʤR{RH1]I6 j8Ň~>tKWFD0]_f KC qy}2VjW F{}"g0D ?:Sy @˃Rhw>M~}.X?̺H@`{>aYBvv.͕! 4YF/"JQV`{2-N8,Γ"_0miܷkd md \VE)p~dZL, nF?Lv}lS_ڡ='&0uJsk BԖS#e(O ܣ:Ԕ`˽cJ Bx\e,kfe պ\6 ssfB)? G/^?FkD-. X$rn8Cp V2-Y19F:/0QQ_L 9JQ+Bxz!htȞV-f)؞Rif7LJߕY$IMQL Y{|oC 6v%˓"|mjEG<pf>4_^O$XXE5!`Czᐯ`ͅb-x\伟LYr+H6$AE~<ɒ+1 <}RpԬJZxFhjʁ0&:cۊliF[0}:eY6B a-*YMtSC\d՝pG:oc I~7}?* @s][w#ŻMk-4)rjkk!Ok3&GUu}TO]+@EVi96F, 9je,<)t5"* >Ÿe).{yؚIzC3hx}B@(w jjnW{E㫌l?[h((9Z=fKY՝U Adٜs dYծDLC,l{D$XZT br[2ՌW=5{v+^lhk߈, x[ bu]:-Lavl@+k:E&xnZ&Ta@QU."=VMj6.ew]ڷ+2<Y7[VmaVզ#8I ˖&xgF`g; Le~tk:ְDZtk!xkHN - v3&6ZϮse{oWGZW}Q{I\VI ;P$u @7v(&FBvz^A1/vErx foOޅN]vn] ֡2 }v F=M26.E#~| o!F#텏79}c$YcmuwqǑ-ؑ*lNn`(0ſ Q9 1p=η߀*عMO\J n^n&4ۚu-*?+OT`Lj߉%jIO2$4>7lD%|{t,Z_W#īM)pJu:]a;3Imۄu-F&@zP#@[(PCqB1GZ4p ӔC`=ͳ9#L`:Z6 ͙Ҥo# gXqՎr=y 1u NFJMp뼡{]Fsݐ$*|;ՕZ•דrǀk3Q?!&%'Y={x.%=^B>H+KgЈy+U r7h2b\ B'=g!p ;9 ۣ>49"jJg=]LLTt]f9ܕuq%~Gy3a@AN[ Wv2Jx"2Oܺ{.КB?c*.i"߾77Cm*Zv)2k\uhy*iXV%M2o iT|V7p5ka9s@-4_ v7xIЫ=`x0iG؅sl 麨wS.d1/Xv'PCi2| No'`sL] HXso_/Fޥҽ')҇bW s t7  sN%D G\ 9 촩^3[ l%|,CPPs:kVp–/O(Wk;1Z4!l9)]^?CgÆ ξ}۴i l[h* \&nݺ5jʕμy={8K:aک) <$ W^+νDvL4ma0=KLLt;<'""9q3}ȌڬFToL0ᣋ/:Nr>S',,9pwj'|4̒U7ar Rˎ4ISGV!L&!\7xa4cR\\p!#G8֭Tzl5 e˖ܹsi =z9qc[)p_9Q̶%w6aΕhD;AG lvFeS3RDڵ+;6m/]Ν;bowܙ|Y{-FjC>>6SyUyy}~¿u.fk{,9s6*oFY4lp%Kz{Ĩ(8kڴu Bfu1zgtBx閕Kk f2~C@G 2ĩV yyFJظqcxDXlP`WFO>]uK],&mSdScpĉ8XV|>Q]vx=N9rwﮏ!/ןw3dK6- aCu-Z ͠g5:*ThO< ر#Fٳ>!>bח5i+#ן#F_Ҷ d),g5j\T)xlMG{!#}ܸqaM6˽9mOEG1'n7;7JF7RkRO۱kҝYUяK`o_L]kZ5k:_=@*ʰ+d!'C C%aW?<;Lޡ;YӉ1uEIENDB`filius-2.9.4+ds/src/info/img/chat1.png000066400000000000000000001434321506574241500174470ustar00rootroot00000000000000PNG  IHDR=CsRGBIDATx^]]E7)!A45A i" RB("VDb )TADB: !+̞r93{f̙5{Ϟ9 ?xVT@ :;g7hC׿ayǐA+XWvRj2r:Pn*,]" I qhvPL. TՒګp 9ET 6P],LeːXOk(Y|R ~ߩet"8W6Qyצ %֜C m眛ue3_?)eb^>g3gY2ܯ~~w۹%}\yW}Jo~d hpCyyO}nj 3kM6h62%-xIJ{[RFl,A( 얌in&-^Ąg5} EE2wD/8DQR#Ζ9)U:j)T0_k,Yeo*&_p]91|!u 9ԫ_jمħU6i/XYV5{T` &? '鷍.HfyS:9|xTꉞ7Ƥͨ5ʋ cB/9҉~MQ.M?͆ʖ#+4FQ2UW^q3|kkR|J?-_EmTW* `I` ţ(wXT+CSzo署yo/8}}Ow;^_ 8˖Qv Xi')A4[_c(^"fj'D WiU4u%be4QTe*Nň~n1&Zqf (ґehGL5غ8\=UZyfr69MYXb43'uvGkT5\(*HU:qU׭kWӺjSsKS+ lTU-&/TO_!SJV)GW.(l+>u k5̹2egM}5H #lndή(Fj nR'7伩S].UUnxU2mƬd>ʌekg5WzҸ`hgR3(O/͘rlNnŊ/<ʹ-R'N4ݓnQ]_dؗ$iJf9f%+ D̒K]VGMv`q3+Nku{35SsB|fmyr*d/ AUg" zbiϺټZ`g(ñdq>0E Q\7k=oahSŐn( F.y0Y%[ WZ9RpsOEz#fZZYDp/&R8sN |;'| &o۾9mؐ7|O훯A,D!<- -1A`'~9$Sc ^3u;BXlJ$z/}ԫgY` 6{rrNaY4ղZNVkrI.pZKo`&9/vX%FA o4,SIJB̛LpV|3*0uLݲUEV8MZ}0U=%,ˉUZ $d (14׿e [;PeO~kKE\!W} 4kdaDĬP4'Wm1]]<"^xf5ԬtGd2dӡ̿S(Iw6^̊Oqu[V|e-.=d 'ϺRlZ|y}zރˎߞa[=~Ũ]*wu,s?9X?9ggo>uo3Y]OV~t•$K†yǯ#/tƔœs~^(Tdbie]3;/L!E ,<-×=cq!qyJܲv&;t.7ޥ^n_FժyA=[:g߬顔u=(\\?"3@BhTyШxJHT,7XYZ+}<0CE*=BiS#gT&19V &6(.aFR?cBB%643HM@l>6;:SC/b4HbF{uԷ c7cg̲Vi1n׾Hf7gW/>hY+cWDD8EtF!|k IX%VIRRGea&C!_Y[^l[Z]Njm4gy]VWΛ}z!aL9M=\Yf[!wo}tK|SQm2~g'n]x5rz?v֮W+` y~[~즏2]uYD`7,]<ǖA؏U9iY6P3M&kk`8i;ol5mB P_SG^>H!/tg/Dpi]/*lڭʎ wo _rX.BDx'CȾM֩qst筿Zukvͯ?p)fډV.i?}aw6]ɧQ9>MPqi vW~^lו9F{YiK&D'_x[u85wYӯ|mX MFt'7T ?˛>vݧ0fϩ\piETփ.gUקtt Dyh9mGt]fM=Hu9w]8#B,mM2W]^95lxؕd+a`4ayŊf@chX36 YtӈOڹEۇnAiu4949׬]lԛN[kݱn}>S7fǝ?GI>HKH/WL+t;U _8Od#uVP{C.W^Nj0' 斩$Z>;+L 6 Q8ɴ:ף?Ai,\zVL!cxj4ilƸb嵗Th̸XzYd9 mr29NtkʾJE-Ciަ+-aB_sߑSYQ'6n?ׅ#ND1ɱ;Sjto jϨrEwj"i_˫v[JÊGכ~/Nr#9"odʘ!:=-C?+UQqB Sr6~ϡ?7 ]k&A뿳SK/>XT9t3ߺ}OFM;{=tQd\EZ6gsUˡba,78o%\da'o/ӳ_Dc3;ԝ[it+E%(OќHOPT'SĬR޴DJVQjm؆*M4zќύcY%KcZЎ!$ :I *\yFn_m_<念nzЃ>m7V{fit݇S{GO&|mW_GgVNuHkRuuMI>rnQO|Qi겺gN A/y$gfn_pV%=eʩ?^\D~#5zOTο*?Ol'.zK~uş~2O_ȻW2CUg~O;/rmm=tOaS5 }c-rLԧњqjZlJO&d &*e`.V_& ѝonjgOa~o(U혞k) >o* k2Ҵm^\B4 k&6i1u*WUUjtժx/6m`Gn0bؐ;|~_e\A03kn(ߧ~o`2,'пBo8|#8|1p1PKhE#4|KHwO 92 E|)7b9x tv;<'JP0o~ƲtIbo#65Ӯs9R]ǎ<{,>݁{qm]yOw3[T+7]+wuU}O=buVXB[?~OYq]+whW4|VU{>~3NSY>Y_>*F 'J+q͞JCVԿOGO􅫎iV{ʞ~[r펷{n8* p>nDQ]IEҼ/o=}]ɕ[^|1N^ʼ8V~iX>k<xGr,Amv5"d$̽CTjg*M M™El~1*EXHs  ~ȶnd.`]'%\̇kLvsWfO +b,Y/Hᕣі< `+ʄ(%oJ7Z9 ,FSrob7*1te ZoHo5lM6WH›̳ tΜSVAhyEtW3OXh?N oŦ2 U^<5-dnV!}H/YԳK9K~ Zk[g^ 6hР6xz3FL)HSg9V(Œ5Nb=f $'Ғ찬Gz?} $q:OIY='ҪM(; а`;RuiAT2FDQ??~cPFkr aݎfjݬ|yܬ]E IZi rNĶes~u=]ÄH4l綟kI 0K&CA/"`hw)Xe~Ս66dtlCOݏΈ-x%T=PCi0(?ۗZ$ߩ>7X)E5^|Vz f %Zbdl$ecbr@;ODUKbi/Uy g'r{7 eD̤Жݹ`ʄْ7GXvgXmt3ήf|aĭB!`9]j#X6ӥɎkҳ%lQl vj[rшnJ$ j,%UMkM$g3f6|q4,#&"2"4): /&[I<`EB[-VcQY=VYj_HZ?S-dM߲fu#S0h%.D*zhr, TI#3vσk7rh6H1E&gYV Hv-gC"y#ebuqkw6݀w@uVc(yXlX1A3a־6>6&=֮A6mGK*:g, /a&1 q&(U@sXR^6C#D&H Ղ:E`{m:վ,CkӡU5g;.NedX$@`V>Aْ#m}sٌVُV5=kTlzTL]6 ҄{LPd`f&:OYHul;y3XrO2Qdw*/i8ߕʚUgfR3ǫi\ͺkZNfINf٢ F&mIS}X:khzv,,fo]\j@̠ZJ?bP@t MA$E?.7N{V3qoɎU5jM "qr[rgW指#k,Eυ{rkf5(ۂ)G6^ϰOjۺQI"iJLâzz1?VSKayYXjǾkLNȶ  YD#hV+Ӑe\#f*hEck*ƞmڪm<ϴ &P3fzsĪb#Le^o?TКi~zU"F)j*/s+=b`e:bM<C^5t< mRRSiX?zT3&c5 pξFgF9U`bw($ZdbSNne%L'ԣJx05g|ncgB5stjGrFVNb$t;"b9[ڷ  h&c9' h:;%E'2WҲ5=[fS6k2,ҘmAf~-ҁ\ת׮AcIg -IPWh0O_V'vfr:[˶1wO_:ALbhϵp3=)]ӳ> -.@yT*%WMEToMz3g Q]Z)@C4񆽢<=t[3j>715 99qTiy6J, ub\Vhv0 ޒ2n74`ɩ4~9{b)sY#;AO,Hs]>CIXm8ZH')c3Ok Ugf6a{Tɣz ]2wHF9ܬeȾ@)n;+ԙ4?I3:sʜ /7<_*fKF\ mNqTaJbew LWUP5woR{{F6p3طVzfyQGg>l1 {@!7q1a3Te)°F8`zQNqOĞd5E~mA9;5SݥrdUNE*EZ3܁jMK&D?-3%$٦4i l1PyV<ҝdz?vp+b2(Gj1E?DCkfsPGsOK>V3ʳWur!9YwWzDnG+P< FdȊ߀*:4'֝qZHkSkGF+_bóKO[Tm+[k!y.SVkzdhދy=WkٍwVMu %PhFAӝrj̣Xھͣ V/aQs4ohT1Q lSeJcpH5LI ur}K:JXj2j5fpOaF(_ܨI,ȇo)&=63Zhϊu UWE3i5RϥզK=ɂ+^rLILȳ|YAEdDUGwoSz=4|Q-zPi| U=&D;a2&&^47 b/z4@ @i9͛?' 8J GD  p;=zRf/'IKrRD\Nu&n1Q jC@ҍnK33nmdAxuqK!c/\2& @A`S{Kɘ:ZӨӫVb _JBԛ=wQ }ZYps)pkygq1PX j3s3ElUqs @  I՗Ysg8pmқu$@ 8 ςŬɋP"v]*ف[/f`ͳ>/ٌ@v@`Oʥ\߭ ~3׋<ì}~שBHCyJKr_ܥ ?zoP N¢49Pzm%Nq>a:и0ɩXo޲q,6$7+;u ꪵNq.xlc# '6C%Nc w׽<_7?p[YP_Ez*eGIL¤_ӴN"OQ+M t,桉X%WƊjMgZ$,U߁@[@;F W֖?_vķ?dAw76 L)v [pMvn9Mk&gbږE+,Y'E/G8N41Kzkժx䧩]kk^|e[\7%ȣ> 3^5Pu810_Z\v>.I91Oݍ.;9O?![.Ю):Xw)Td/X8p\j*TDfgox7=ro{j=k}-ry}摒<0i}׿u\}}-}V3LBini1EϟOaL 1O~9ռ]55X,,y"NM;޻nMM-k6557EjkKccU_poo3 /-Q}Gn}r>?8#{Ϟxe8A{SFٖI^wJAʂl˟N6gZ;A@}} k}׼ ? 跶I[rv0e_v4$ǜ}ON߃+gp枾gXz6.y~.)42/&s/4|U^Y)c1m$la#h%ي:ޜlT_YНvMn^ n>XYתzk%H*L[]ݬڴ,U2Mw/:k} +N*̨+|R~VڦgvS(Żfַ툟=ӲNu2!9Yu95͢R*|mYÚ"q%+n*3T3BL1EMN,Z]'ޏ+\Ƹhޕ:otK_bΔdm7e -4BnNBNrfqV6zWvE] |!C b-ėuֽnO<;>3&LXf]“W^sUf0qnqiiʻ|76C|F֡us>k_}ܧ)@rHHbuF[{32rS3-{/[{^Ty7JT}CԆZ9-"8TӬluWy4pV9S+>P_cꞻ b⬽-^(BDGܝ;_шAOA$GK8c.[Eyմ0QN|)yGG)3':pzf:+;@ XP%oC,Fz뉐#G QUʻ?U9?y.^"׭ftxsT?sսX3'4m#.[4"sYiʱ![<ࣧV>5xsR49Oėt:+:Wvu nUeW~ʊHHt%E|C;ڭKX\JlN]De38t>aқib0A{^:G޷ųNؕ" Qd yhӁߟsGm+sO߅Ls_]SG>YDNԵI2Sf,uћ)&.o|zꩻn뮻N>}=r- 9H.<Dk~6U1+XTXEe-jY(Ki>o{`gw%DՏE㠏N"v yͷTY\KKfͩK,uUR8Ds-"1\w6ongP%f!WRkk,yڐJq5Q)Z8"+/pKbE]ZHIetqE <`D~96I,9_}%I4|\Иdߪ'Sʄ ]Ox\~ڎU妛"9/jJH"\ >9Vˏ*66>l/V1--}IͩK *׏iT |mث;Do1>oR4L 'DV"J!*T䍢!q,'^+"DŽ{K"''v)#E-ʱRi,.˗-[&\VX!YzXN <$}Ibڻ+ /g_&(HCȳ˖U[&{|мiz.)j{ŶN:皓v@EqzȋV؂d4VUQqВ%kWK"I;~sOM$s߼F~/蜸$ J%U#߿ 73nV1#7xx.oZ"葦k֬]iMqbsƆÆ5|'5^6(8qO!nN~7?%G^ 8a\TiJNV.D03>賎Ezwg&Wrur(f:i@6|%7#JSG 79InaME%Gؓ[YrYvӺ=dK#@[oϼBP;p@@hKHB``kǟ2ڰUCЕZPf1E6snʡ:fWQgzXy6 9JG9)"(SfD DѴn]Y VD)~4ȅ ,~F ވ 6hƣ6qr /K8$!0i|~WkI/?5-|΅B2!O+N[њIE1B-„gyuÿr=d*uY1tc1ޮv( @~7WLjePD+P\@t6mU"7 oJ8OE0zWdR\3i,G93U<vo7BNHr)_  @ pOy_*iin %D(җ31oE7`%+h@ J"`$>#j?+<$\Bm[ke~<IHi/Ĵn]j%ѯMf;CI  @>\7Pa< m $82(+=8]M~$2Z(1PgObMpqt2n@p/}bM3V2OeK(-SY'`hr%JBLϮ0w!(QtV/<# @D Eat6Qr_轩;{UFUP9+1EI2>(@n@ίQ׳g "ǃM7DA@#KFmO a/ZSI$A80b344-^yi Zeǯb{;/ Л @"OۃUq:iVp+"k6 eDO*RP:"E?k{$r@y<7g77KeDGyLKg!',=Lrכ噝Vxgv~CS{@";Z{~G(sM{Ixq%&N:nav,H~F+~*U =T^n@!`|L5ˊ_~lf g%Zxz'l\@WO))w1rr7T}94V[U(ǏLň ݞYJ"ٴ]*,! tL@*@F^?(Fx0gey_ʃ,sg.`";"Y6Wt @Z+ l6eՂMJfWVR~mWT#?S@Cz[: DAl,|s LeE I0`WQ*#_mVxy/j>_ϭ={du 3'O#,8w˹rpF~yz;HG=J[P((Xn6k䥕rNU1"9OJ9e<[4ٞ/F祖W߬ZomE@6l~蜅 ̞LZ"=i̙BQ{ϞvTdLRSNFi%5&,{߅a){%79bJe.2}ʌVtEsf-?`w &T, S-TW5Wb71iϛH Lrr2.M(={H1+0ݵ Ҕ߼|\OL}N'Iȑ4Uh2G=KETd`aQ[OOURYh-UU(P*a< g A`= PH b=5r/9Tg8dW1iX&+ҭb]'2ҬM\Bf&?)rc`,* ^lTWn_ ot^&IaonōgeS+G({}2/"~Z8t Im}b wniVZr`Kg]flER.2֐3FQ0x@ 2 ݰrܲ oL q_ٞe[(XLK:j KM +`c\#Cu򒅷5dvjTGhw -!" C ^oߞ>)ǑvIh*%ZDˆͤCIBˀ@ O݊O.i6L6%sNæpϜmR8L]@ JeNZz{8PWDڂ)Ez޶_ZʕcG*6z @NE 7{MdsOyael"^SL7Džuj 3 "v2t/lZ0|IEQmfLz:ޚ1v-E\ mA ֒S디itm*R#]ZҦ3,ZV;cK7tT"kK@@!^4{N?/9.#|W/<|S9 |⇗\&K{aO{n?pt(8d_*93AvVjdUzd~)Yi3kq hNL74H$kt^o+ًeE mCNcKR\&ҥ\ʚ-m@B`TN\W^#kJ#[xR%~]TМK%D@A!eF.xTjWt4e7mįCE1ӜfFy*n%`T['OY/3oXV:Ju 3/|STb5yh(RӁB/gey$]В!YKtLL+2`Ϝ?ثͻϼNN=_}fOn]4]EG]i-[hy…vwD`7+@F@q;aԎlbC}_|Q_O̫*hWF&"1wXe~dqLŻ/m+&[lY,nOs+3*ڹS=)Ӌ̂w/LPRV:iT 9tix"gT}SM\yVsK} ]y42kHG 4QI :φ\U1G~>Jj.T9 0;餶kӦNd,*`KنEOzVAv\Tׇ qM*{08uYS~h g -{WJW&UNox:a>˟ 3䢕i4~`qaخֵҟ_leXy5~+صD@NL"Ve;Yi)oNZ?˖Z/͆ bYpS(Zjif- IΒ5A EQ4.MଅSϽbTiX}bK IGDpPf Yjn3)t쀥]f1wqdkgj8R&y' r%lmay$>X0$%˄]UPJ.Le}*vu_J+ z In'鸒RYKU痔!?NL ؛Ysuqu*MphR W^-ʻKBF\&ٟq3ə{4s;yV$`O69 0_yc|@V:e#[DZ3jiI"ϯ|/^,J+vUN$eyi,i>f!XzSyfJX˦*OR3ܕGZd'MH?mr1z;&iʔ*g*V;" 9$bb_9f9e-SB8kU:.0 mj7ijyյ2T}1|_=mGo9ʋOܡs"9@{~ҪB6f}0F%4|Yb/1Ѡo' f;]  @@Yg]I"jyʥ֥7WR5K Oܬu;\HVBrwo5S9Io\onX *psxBPE @W!rkdhXDGY̝LcviϯmOpko57`v fסK3/@=.zr[]zs_ 5QNOܬUi%ߘ: j @DMZy wgVtutYӜ?SG#TwSoȆl "(^UV:Y]N[%j]̖ 9{~4E @!:-+ϖiTjozqVuII/^W@: NŹ`)vN߹ƾת7X4i@r&2k_-kK'Θ2*^2-ʀ-1>\t]š7wt-Ŏ2ozN߲쳩}?#'+I@l5O{;[G#НIGK7F]@ v9̫hrnvL\!A؅尵p4g`9OK;@3п\ ,=OUd>+Ҫh.9>,v1K2v+NT1KҦMع ?ӕ{QA[Ջ U@)]DŽ„eyNgF5ɂ!9 3!_ڦmjl45?#&别in)/ c B=Tv[~([]xWl?Mߑ<\r F;SG@MLܺ!We=/`Q)'yVkU;Wdzr ǔJL/N?ZK#^۪rcP6؍4+JڣhjssZ[tuM]Q3uu'Oڝ~,(d9)|5KmxaZKޣ[ @oX!Gyk'_0ǮKi+g4=TfG@}lLߕGߵ:{xU~za ?;O8?޳ʣ0zVEEǝƷ.Y\S6y-yQx b|OU`ڎ,y2yXxhWg<՛u7;NENJkխUKޛNʸޅT޵>_i1c7'bW[Ɓ5%,2=}PuVy|qg6>.kE<XK []@BLN$|9O V2Ԓ2DPȳKmZz`J"uC.[A۬lժ@zs25 @7+;,j=f$,d}fef9GS 0z8OL! Ժ.,zϛ5zU~B_8t]*֦U ) {"0F@#ZͧV ,JzuhwWR# vgvbks7]xusUjMιuCg>u鵟=ɧ^̇O|M%+Y&QBVߣTڈh3˝I MV E9y&6Kx &Nh$fK HW7YXlS_@o<>T߾ӲiMrcbnn<'Ӈkj~?/V@%GC/,i6#W* 7 rdSU|[& vwD5i7ԹI 3 Ԗx6kUz:{Y{aElrjQ1sC5j'4>@t[S;Vz%֛˜4b2*>^ĮּKhjvL{R;VW^n h? =Yvbo.pJ V[Dj=T~hAI@Ξ 2eh`1eXfGr)vu9>e\KmRMtf,V彻E@t[S;MdzRDpň9,}Kdf9 VL,?@0 ;tOEM#Ay@ > j&rMaIu6SBG*s5K;}7kyXz-Jlh>sC)PG}c=TfR*OJ4C*.D"#@ wgߑzOxѪbUAF6պf)fw¬Dfv].ut3w ;(J$}^Ž{՘,{@=+ *p*C3Ni<'_NTMz3= *@ P9l߹WI*{FoFMYn1w^r Ѧ_KiGSNens7غxqM ,3JnVfN7X{6S7Js= Ew|HJJi矶{ kH=wQ Eb"K8qBwIBAFfpo6@Y8|sMƥx^t Z.E`„ VǪŤ2@Ju|T'ϼwNo.#Z]\ظpPɗ]2 f̣,5GKN'(QT|Nz^ڼa-Q:iw9f PnݭE-JMekn`3ERpѧ' N)ܾ} D`yu"3+ Y)$k6ɁH!pO]a|ScCdF|L^f+xn(f˹?Sg}w.LqFr^\sY.,B26;9}TEgNW^ƬĽεX&9CTy1v DHv4u6K-3_)n{XS%! VGB@#q);B=ѷl*LG)>@ ™{YӦMU:xaS1abz1W.zn~Z%Vdq,{T&wW$Nl.~nI9ؑA4W=7EoP?l22v[N>9w=wc@BKsˠJ+6w'8#̄证Xs ,$%>|aUw>0#Zr@d# Ltܴ5@^3WիBsSEfߒeP 8ܼ# @W m͡ospp)N_Dj \0<@ =`.cZ305{s+ vvAmJ]7J7A뫛__\qchF z'w0:ݐ8[Yi-Nd L2nN82kW/E@MڸS>J ]T0k 9<F5=|02:: ϲ+܌3;" @@*͉~ڱ9r8ne..q,b{ŠbZ'ms.D{MLr9v43* VErdjEu\|OS >\sp"T.;,@u26K#MVK0U#؎b)ْnyGjgW}@]@|ͭ-d l6CLzs @$w[ZI/+z08}rjkx7׆b @;# ځ9Xflg hk'<ȨUYrR+nHf7s8  U9=Q[g4n\zK9佽*)'NfZ n !2@7l[!3ZrY II6Tt+?;ۻA@ԆK+ܪ=#2K'ԪZT{f#߯eRScA^ں b @g#`]LLwg1 Pm˕; zsgw=>cl+5,"34g5<'2C"38ӖjoM:8#X?mn' )Z2ߗjq .Dec+l22`5ilnVB Joƙ#ݴâX@G =CVA<;%s|Y^[Gc=: !/Pm.eUgϠj)+EXFǵj!msKQt7 " @F r 7 5 ʶ;{ET#p7lFiІܫ<*XX w~p[[o@ Й)9&eiʽ3LY5mM fݙ=y F%sO.O{3X'NiE{2Ӧ²f}lEP}$K^,7Ҍ_a@Ex_"ڞd4tƈք I˳rM%7)8Loz3ڝ'@x`jQl>bYX)g,Dh~?/#/fX=bf7 @.@pϱss:ufLxKUjjs;8o&[<+ʞa!{y7wA K nYvܾ פ=OlP;i}/Q[ra;/r}|0j'U@tǖGܣ*SuR{bk̒[}n_qJt%k舏38N@fFA@ ЗTBDĔCo,s+&v!I:jlf6J%|l)R"[k(6 fu@G:ҩmVYYw0Bo֛-6 ЗTWW~ 7&{5fMnU8,C<g{VnUg4~"d܌@DRFdR@4z6lF c떙忑/XְM;Ϥ-lښ  ,F/$MgbK~voƂ2m"i,%'VŌOK:L}նzpsewnBn+z*]@GjB`IbRF-ٛľVHҞ Ekj9t۷HZUfU|sK*Xi+6u)8[pZ]_7wAK"^@1SnFTԆ@7{ܬEE̳2{iOӌlS՞OMLE%gKbYQs43a܆3lڵa@g~{p9?܃E"nŲx6UiFQLM܎m:dnǵݳSpV7{.ٴ=n{YA4D$5 @Nzۃ b~d`q,8huB'9LE;swP*4^#3՜s3z71u<8=T*Dw[{u1g]&̩Y 28$>VLO1`e8n#ش{^%6q1Wæ]. 6w,ۗVFjSce\Hՠ9Uőwr#cr|3%\HoAtY]T+o2#T6-U4ߵ^}G|NrU_1c'# /S>ӛzl;1k-?  =?Z%+or8ܒ-7G']@Lj\S*0KJ^M[Թ2vB5@"` |eٰ"sR3y{KDfM7X֡$-.%۾Cʴ{ ;Iwfk6r3t` @4п%y2Ł(@ *l0K|9+%xUYL0NyM*Ujn f]*VG@SskK|Yk:Odz 8rvz/fnƵƯ9fEbomu눤%]U۩fE\:v3O]mvƽM-/ɋ*mםuwKy A̻fMejA-CL{Ws6@p&mee U[dȁ|ABSk\}W5-?hvwMe0kgk+\-In\Oywn53>Iᣮ],L:d9)|5K(;SۢK&7e([쮅W=ȂFrm^l<ŘXd.]U[R۷phFz MmQ,_&_:Ͽs,VдhS\E?Z<[:O>Bo.\rszvok3//XHˣ7v6zxq2|d΁I5&\G`+\on_! DV[w3ǵ>{|ʠޅT޵>_c4~i8BnS-h@4\oC<,KŜ#mB}ʜbFSIM*&rS—[o" WMWfsicT@sP[^"wܲ߾}fsKZ 76.nƞ?'Ϟ/b>k"J*97LnPކ@MljǦb#btkm wrC<4E Egg(WAuYSrt ͖i9OD@ؓ_]1{SYLPmuC.[A۬lժLYDҭ2瞶%e(,|G(z2NTKv rNh/W3?~@?M7yq)E8t]*"() қ[]rGX@@ebr.Er0qLUk~v`VM,^ @NʓEK^3}Η'w#|ϼݴҺ顳ORO\JoT_Z1?"US=[*drps-d@ 9kR;N':Y6ҾѲtn{Xj$rZxë.Njڠ^ u1C9w]|owDZoĻ.:! "w뽗ꢗWrigW]}e,Ue V\T:SJK+>Hĕͺۢ-C&?|>RojhFAm{zЛkJ7.w;;gY+òjgQa=h#6Zs;x$ HKM;){*-Szi_+PMY+/$dZm@;gY1ylc*K|Ēx>7c3Uև@K*{,̒7-Y,HR[.BzjǺhqݾY{(\G;K߾*;[=j[R[h[spT3J1 9gYR4 ˾W1[Qj|6q;x\W3; Sߙq݇l7lݸs=1rhFFn˓ 4dwԶ!cCiI/GqYfPYCHBK58S ps[Ҷ=nk*s;`@#qڭfgܾ s{.Zdvv̧R 0r\l3oyz<׉]y>ul)Ce% ^t=Tݨ4V0h!G"v4__M*j%Mlp*ܝ"b;e7bW+zCeo}Z {gCn=@\Bn&| Fi-'f^psNe|agl n>VZkQnsmc z8EYnK;kSC~avګuk "Rs+s+1l#l[{"Wm[7. z:hgm}Se*}—/pV^`܇+ѵwmnA(ɬe$n7N z K%WvFYEЌ| {Oh#f=^@'VS3?`u[_n7>z z0lHsfi[=hmi8G-./aS _>R[r@ZB_![lgYM.IM, E#lRgލu_O`KߔUh=>Z7jm @ p? k ("n''AkVc;bf'lBeGV%Yފa#@.}GIʬjiG-l|!n7X68mlԡߙAF+6/'s|Ь7Eb.߮= @َYPtQXʫה2^̑4E+pSx'=ʷ>~^ߵ(XV A@ :6U#Lʉkϋmj+lF&,9_}Ngò^ra]DY9O(QX(KMYW*R~%K)mWo* 1G&nO&qfvfv.dgQwzʂ-a*ˑlX<@7bm6=mh%wͫLs\}aѿxIE* 5b-:3!BԲcJG/=ǻsi[s7W;l=2YmhYh%q{N̻ː3f4qĝ*,U:l',A|î,ݕ52j15$Hh rr̬-]cYNҒSIXUEz/~yQ @]EҚG1Gib tNUskzu}OEٹkgzơ3u.`z,rrf|Y'-M?M_Ztz?ukG1Z,i~8oLwrm$29rr>wQjde.*ߕL:/u+4p26Tx]؞5XGpo[QN5l :4'HN r53&$Q +7k[A[HͦəS[!fIz)fG1Lo7+[Ke= n59] ;Nrs̯W$#S&Sir7?z@\LٴӢs&D¹l4`K?EFܯZKҶ\یߢ2.YE7^lL#ט[Lq*N7IgBj-" z8٫FaḰ[bgVjfr̲۫YrT00-/X zxipG~ahY77yt\. % k豹ȔJ%G$ߟoƮpPhD@pˆkDe|Ĝ[!f 4ac^]̯LEj} =[O5m'϶{r(g6cХSszsg!G=yhOY&NN[S;d\Uz$lK1yKܨFLa-2SAPyfß2 PubYs&]֏\el-}sklZ׃;'fEo]7OM:Gӥ W}+6W*UM) $W͓R߬L/]O7'")3J*';@m[O0#_S'Gd @OE|i]ڨI+'|1];nC6qeLOS}q ϐ{x|޹g2v-'tZy;=]bǷ^ꎐS& P@λ.nܑܛ'/Y?kw?߽uJ_IK!r9S\Srb<ٳgPۍﺐ۟ҚzQEy>@#K*>b5`cʚMz4:ݏnTX[ڂFYQu-: !tXhD V}[o7[E6(ź7! ]ysFzobNl_$ B4Y-؊$K8xwrnj4e7s.QDcof\_ZҒ#.ŵ my̮Xs7B Y9Ӟy'Ѓ4B mY%t)W8W2G}xK)E^1z@,zvY!5ww 23z3 G֝7^ @!ub*EaB:0ooGֱZMT3CvfHdeJa6kd @!=P\ @oD^#OIUq:/ikr6n`WUJdTtcңJ6;DG`,K̽N@@UyQЂ͘an 8D# ˖J:维q{қA=@މ@I!wLYtxtoySWob`e2&Dk*F06;  aFh-yΛʣhSn-%lf0Ykʙeg9wX_` !`f:#OŽ^֘ 7B@S}zI}d=9}W>`ǜ8KEd3%Zev<қt,9Y¾r"' o.5 @E5PrwC}@$nW&Mո4v5M}]Vn9)6zd[}d- o @ 3gg*9.ۻs1,ye1u|tSlp  2'-.'Q ;u +hs<(&@̧ɘ/gF oj3O1Èz).?\3dtΩnMJs\, Qy%Ir %@>PGϾz鹊w9AMZĪ͝L\4%ȝ <Eug>  ֽ+ @Dr)Ck Ӡ޾-:k5j39TpI{iGd5k;nnu+Pj *{4>= e۫MV'Jrq"s4G=i\0s!^폀A 2gG:lz˖-y@̣&V팁iOuqX¦} @F8։I=fuhfz6dX:]Ia:'=DK#F (ެ5 @uZG \W|= DML#90NVTmbvqr2z@@ Еd[=/kU拪ʱP%yY/^9eɉPL䕉fh]7@#%sOKʲYHz˺LiY9 oumk繧x-#T "Mhq#cxdC6h#ě=fvEԵš+u%ML@pHi( 'HY=53zDi$&q^Oo7 q@](ZqUH8Gش۵A@ԃJ;XQ# ՚YLxt"@vC t/Pl;"vtkzƂ2CnY[@@zs  I#]\qB2@J:ps7@t!m%ops6@BOQja 6@l | л.dg 7* z `^h7=Pߍ_ ș7 kKCG@# tY@o@Hnn(Rb@ P/ᵁ[I֛qv͌t@ ##bZ.lڥBD ԦfkyrT2"hrQI&Bppswh(@KwDPkDYvv::nVfjR6/@ X=Na}ˠ@n@=L+I4Ӭ5zuei 9lݬ[8@ FI^^<Έ}sf<@ 3(2MRʝi8of| #Qj @Bfka,dBQ&UU7WnT |S}ͥ`B$ "{X4EbqW\I+b1hpsu6d yZ-rgܵf:4i+{>nN͜2Uk_aYŸ.x3+n :M{(Js'rdB}%vi1ϯ!$ ps=! @RNȐ\ X;Y:mZSe ֻ  :"t>FXɎgd & kʙW)pst$H@~UmZa[jŚp򖺬Ք+EfSB.^l%7;3GKfļ|I!IMڥ7hk.%67:YyENj d@#P̹Vo.B@+3Gvﲔ}dG A%rLwI1K')l]7@bˢ"6alYЦL6bкGY^ =.3Q57@n%<&h,$OZ5lN&Z`;j۶,&L&r@\47Vm @A@XzQeҰ幒lEWgoX?Q mK~1M1'Z1ЛKVtT 'NƎ WFz¦ݑ-@ P@dgvIr5F6ۚLMRYmUv/cc+Eg\k : + ʅVpKfQKly,lféW}JY  @&{TRV3wIn' ɟHֶ9l}7Y@.E mٍ5WΠj.uZ6S%' QkάP. $^rU =-@fvLd󘅠OiuWr >&vXڶ֒ d҂p @"`V=}9fmĩMhkON&x9r ډₛG"w 9,ɲgmf}zSO}|-is/NR7Bfx3O@t59SÔ7VA]5P``g4f#37pLH8߈CvuD@ qL+NMvd  bVrllȯiޑjKjwBmKA>VOK\ڠ2!e*ÌwoRzsow : Nٖ{vO&'1Ӊi$ MعYRd_͠s pԺic+!fqKX4ۦyy<>TFh;N;6evLiAG.^oe5@ B=Trq8S=(396]x z΅*Ә똊\VI6*cAg{Dsk윲<-9 }5@n@FH[[-ʚlӆ]APj}LhRYj`V Ϝ3fA|nM:*[,US\'di%egV8^ q4i' t|:7E-@t\-t(P:J)ZQj=ewNY$'h\5;=s3. bebRe,lzslz Ы bw^1 Ug&J Gv)  o}W:~=TEaraF6lxyPso$gnچMwvj  s514hU[$;.uِW6fګ-lOeuL? 4I] -N{QR @_G iKH٪΅lKNH @|Mάi0Ixx-٩$\en\ 9[iISxMcj:  @"fX)fIEIY?q2N^&[Nq  "%[ƅlYycY֓b:ӄHnT[:;L!z!K?ϝynSe%r nc]@VKT٭16h3N̄2nIzn!M5 L!.(@zw6(jp|@jdg}LKS)`20 pr*P-0b_0۩A @Ԅ@|%Qڝv홥bboj6n)"p-[v zѠ@ Э֛yYo#eIx2.9m1m͠/)vQOwQ6i亢T&7wމ &޾&cʶF_b/EK_`Ca7 Q>9yrDࠜ&+׿sZs(=,}@=@,BƤ~h\*.0NRN1Л1B Hh;ytCU!#3mw[ڨL뤤mn2ՌݒU圩 n@ P 2)ǵ!>8Q1qs{#ZkȜ{ fh;-0SC }fR@}7M[:'5ړћgڂ-Uq)I)7Oׂ h8Fo򓼈ŝ]ۆ_Ø_*Z;]TҺ; } I@ZzJxLbȺC (#o¯*d֤Q͒w[N`JN=ktMh}2~[8;Lpa7> z>ɹS9T9yum'gsk]rV`9ae%l'X!7 z(ʥ1&;5װrȒFjl5Nhya^Vstbg wxIu־4EHnƮڝQl @oAGf]C\)}$u0 z<R MWަ)k6*/sRJyZ}h.Qs%s dfV2 FOymXf@@lU|$MLUrJڮ^Śhd΂Q/ũ:n<3ߌw\t. Lm4(ܬ=MA|!fs6iRELuF6'YU^Iĸvno㧗7^J 15;Ԥ1w3s,*NU,mYuVh8t͹&obgqHK7cɹZ  hwUSc 3ZɳP[O &F;Ym[!./{< ;a6狥dd`њLcV3X+&@Ib 6Qa2Gw3D lBY[۫ˡuN ,1CffH7R~M1}@]NLIjUx#fsz%WpNؒtYfz֛OuٮUg3f*,sOs:1ѥ}!f % $Ѐh$@*V!1;dL5z3VÀ  'mJp:a^]O{"YT)ŒNùVٲ(@B{֘kycn33=bẅu23_F̝ha)ʱ<3U) r @GYu])[S\h+Zr (R6BvIv rRU_V]hӖ {8jSFP2}el: )uĬu\Z-1[M팲\}$9Gj Ѓ` Q5 ubk86w51RܴQ=SۄNo |Ӱ)R;k˲a ܃7۲A2.㟕gYV/Y$a>qh͉ @OF@S^0H-v 4nvJ3ɴS&׼Zf;Uz3)R fMyC/=z'?箇V^|w{r@ف@k8Ͻk"S.}Somf늎Q 훝윱}6°85:bNX#c'>K0^w7?XnaY,2p I+@|VS9 {ʴm?+c˲/}aisOPCXue.[Z7rYOMqsVo5@GfK1?iJ6jQ8 Z+l5lOwfsJm_61hdi_rߔ/RfLHog[Ks7#Jxc1WQ_Vw0JkS'!WWм@tW:?/LS@gCN{[Hј:rkb yےXޝ×b_Jٶ̸kEMjg!n9 fMlq, mBnd喙l3b6smլ7?muH`lnu@tOeأ^vf#ט}b66;Va^2 K00:,*m85 ܗUuD@W càVl,gk g6`XO!F/vge}VCXXڎLvWUFМ{EGG%=<7MȿEeG,f^b9j5U9 z"ZsΫ[f/OƸM෬f^a28K6/fwxl վ :Cf9yĚi`=W@-j5ga)O 4[<{!V59/_-ҚqSkf&-A7cOmE| s+֠ޱ:,: -4Yh~D{U@41SBB|oǐ!rUk̊1\)eGv- @ @@۰=JG˖=m0DgUǵ|扦[ JiXkJ)b)2zf7pz3cGԤ7a Dz!A)ZXh~l ӚDEB(fv 6Ta6?eZ!fL-TI񜆯-s[+y?{Ú Hofܬ },3K6 @AϷ ّ2FlMDуdsyQvr`9ۄhlW+΅K+ ڷ<6KpcϽAlùe[CK/<ÞEwj xXy)&T B燄Pm2)_֤\=_̔-{tu?rf=1|^;o㝊霅nmv+I5vԙ1ĄݶoȺ`v흯 eSϔwt,7Ncf$&73_G5^,}ZwU9@8f%3qe~W Ӧ{~v&fj@/Jup߼R!EtYbsa"oR^($7(0 mzزH\c[ǔPW-.l [Gu-LOH HY' :%wy^|"q3<Ϧ+&O#\yD̐Rbb#Fƣ _%G%as 8YfvTdqs#ܣLw_n': nܸEfmwרAX?mL6|clM.`hDzGC+&fFʒf"#ߎT# d[K8 9nnR`!9m˹(>5i.iCl`,pҀVc?4=XE#oGCŦrp쀫scjW_3Swm){\iU> Hl!UqM12s:"pu˲%CX73tM5ODtؾD S|NJ:)2p{x\FqcOMWJgB dG]"dž`=V _Ftc+Jse#ɐ13lgor/) j}_>A1}{U#X%:gFLEǷS[Ќ$RIOHy?Ɔ/㝍c;٪ XhOǍӪf\1ydl HJ#Bj U`.$N8$aBDU,d%iNQg-C(-f$A\8Ymͷ|I>&V$` /iMo8AW$~'efN"Ư-b;TmպdITFż‰{g|U942dCp[5G%Pcq%Lw3{sR CznH,Sn`Z@QJkK[vӇ(,kFI4a&d)FtmZc&UD!noqQuQMu9R/`D˝b+bsS.\!$rg ;uf"Mk0m98||Y-k7vM,&hj:|d!*T uN P;XY?d%p=SWbV=vl^MNZkMñQ]k'4--V}XL/8ʚ^w<5\TZzԨY!6gf3Âlzy4.k}8N8jÖE2%{F7-֛mg|#f)_@ O;ۏ oQ})@ ! d:@ ]Xd @$> @ Е3;l{ 6 `]& +~aÆMMM;Ӵiʗ fh"&@MZ[[?яN>}{Ϟ={͚5 Bĵh@ +[f--wi(@ 'XnZ#Bڱv@O xYg4 cnƺsX$@ԇ`oG%SAonG0!  z?/}:To@ F@-C9̜9_~ERu Л  @B@(Ƿvw#_"?:#F P o]('|` /pT}対귾$gzQA 툀,I;&O,N&q<)wLaӮ1@ "@b/$7x㣏>Z"4=  @J2d%\o$?[ZZ ,@2 ҽ[ck $@I`$3w-T D`ʔ)˄ f@H"֤ G"@ | :9>PoN }  @; 67H${;\hps! @A~O<8~O~W^yyR%SK8{AD@ .Gf͚s7;,.@C@6"۴F8 @T@ m '@lU[z  P&Ni>޴Lykx;O[}FOb.* 73C?QeHxRjVJ_Kog3# X)}LG/N !h4NQo~fwd4MA UFX>{ԍw(}w*GT6xj5=SqpC/jDe4T3R Gqǎ7!ԈpD@"RWV z̺WV[[i;l:qĆ^uVPfwOϫ{j5EYӃN&S͹]0" 6s3o=MPl^/dS#Xm 5tQjC< g)bUG^P M&~q![b )n0|жc?G?l _W^|ſ?|k>ж 嗭m";f^ .SҲi0 RoYy`)LS~^b95SYdƩ=%Ow2PF]Tr̐O0.d'f\ek))Dk!w"_~{y1o񫯾3fZ U-yCP @ -ǎ>|1c "<atЊIENDB`filius-2.9.4+ds/src/info/img/chat2.png000066400000000000000000001311311506574241500174410ustar00rootroot00000000000000PNG  IHDRzFtsRGBIDATx^} SۗKIDGHDÐHd-K|$%Qcc!kY;?\\:˽<wgw\眶/w{7~뭿< Q&Lɦ-Sgd{旾o&10#kl?+&B r,Nؾ )s~gúMD3ZC(.qĥ~D\r؂уqO|*(۠ꦅVT$mSbEJQK+W .X/Qާ\溶W/_d#&< o.܃q^|!~Ù^ۺkN޿|v^o`_a_Qu#r&%NR\0մ4@m=>] =_9Н yzRMBmIo[{]?ʭuϦ3Ohz.6C&)M"Wx^GYjF!ƗSH^g>{έ~;O|$ƇzCY'ѿ9{};o<|?5HRQkmv&ĵW]H~ow>orv!$q DԳyS j0v1ԎFc@?h-`Cr_*cݲͳPtG*k6XZ\oc[=<׭JM*u1R},薵Y{c}Ú8GW*YT6e墳jTf"ɪQ)S'Z3kNL쯃?bbX]wy ™Pc?dPޕ[ G8)-h3GS͑7@Z2% N6Phڜ"VAmQ^ uS[%ϝ'$CN%FonPB46)*w4e:P\0!kxo<+{Kϡ}}ߟ? *ʯ9O5ymo~Kgҷ|re#UoV;oϷT_71-rb+a[,I!``cd J9˩bèTkVQҙ|LDebcNciV.goXk*5Kņ> j5|mG;}-gSKD b%gZZ:)+0Smd&4GbMƘH“f4OҶNfu,H4H)CNazuaLR5:~`,؀cZG^Hj"S̀;39uj ReyPƥSv5*~;u(7ujUU $Eǯ~qLYzvJ قI]QK ::WI|,0{Ogꃩ7kW6m[0ٱݪz0;潯*)Py;2U|qqv_eQL o6nH)_[FԐWZljKj-Z',®ц!Ro휜fKZ:_eIrC!F^ ׿65wNW={˻nfo?_fiW bF'/\xM[F=?} Egr<5͹M=W(-Ļr3&h\Nv?_s3>ϼvW'y5Mm/Xvu{ԐZoW93uQi4]ӏ)n6l9`oEVd6VZNI0I8vuS1fiE(Y%uyc=n >LImsTZ|\O8s2[,r[0%fcM̆VR8+0vE:HGC,x3+7,\{ԗr"S?as֠%܊+NτV^nKqxARɽ1yZUkK. ܣ6ҲKqOuʏgzIZի Si2R_'~s 9}6xuyh֔m-ߪ zQa7wh~mvwfps|VGp{Ͽ6't俪 dcnnܙ7>'o鸍.~&;K`R29˞??_~ᩅF:…/11E_w}/~Snhs\gy{gܴQw"Po+~h8':\_uӫ3qWpʖ|{߿vkUo˟tS^}mr˟:sxgxn1wT&0>dZFv?Afbگ}K"eE}̌B-'Rr=yٲM 0ڡMךE4xz S9ʸU+a&:>pMydgHs!7 2-Oaiv[Zٖh܄nYuigh'Y"趓 7a14+jK+k`J aѩVw|-_-ٿ3?9 gۂ@ց"YI ʗI -qkfQYԳrQE)%\rZ;WUjoApkO>ƙcr;\pȺNM4b꣰߯WQsߩz;\uHoO_k%m:OlMBvʴ'7g 3Ku}kkYw+w㕏Xn]6qY|6~7Snsz'snr'v_[vvgo|bVXuw~Γܰåg8z՟e;WT'7?͗1]v-ٸ~+#-:Fu&[PBn.)#<Ȝ'Sq-SZ.E wi Θ;!">tSw6IfRf8`'=K)u ȩwإ^Kӧ8.Q>:6"'i|OA[K\-ǙO/bܹϿ@cqԘU7x#ntZgN9ceI_Z_vo^z6e}# or2IwN?ݻx.vR?ޕwq?N]e2_ؽ5:环q';WIƨMczdCkrgZ]r]O>ߵ~k}C- f_أ,;Kw׿󌭖X?p ZB$Ј U@<ȍkb=nu_n q3д e -XbT!?EumOYjUf@-{E)-ƚt#ɰ4o4.q:+hˠIQ_0F쑺*ؓTnO3N9ze`#vޓ{ҡAww9bvń;U|4[rQw%]f5w%<ȯ9}葋.2r"#_T}G,E/޶bOb;|w^jI˼J_L.'.#[xn/־m䢋Wo9y[G1z@ rnnd_w3{\>]0O?걗zG;[iX/vвY2y+o\AS׸-׾pfZi~P?_~럽+>;_:ۿW6oAR&،g,EȝxtOh]ew㑗WK; eE6͹簏*}+ut c)!9&v˂ʌ|7{mC.@HDŽ*%Qr eG'lKv˶|-:mt'VHO0MyjPmWIT}~P+W1o~qܖ9]妓2:ltͰT:[mٽ{)\+8E[q.,ګи,3qOcFV_º>ըѣFjRyV?m(0FuLw1_jR0r1-_b.E5)%w_lmKߑ& M[F 믿^_駟|w2-Z@oW=oG7v]˭׮1V%V8T<~<|嵪v=7o8˯}^rskǚ/]4\/Lm{~A?IG{6xm]%JgVϦ6/ ݬ80*Ώlwοuz~Ӿk/j;5~paeOΪOo.ܼ֞uYι5W|kzYDyhltڏW:n6;QDU9p g,1 qmwGv(2mu(:;=S!3 vePuq;Oy~2) h-,W*zvAclheM3,)6y1,qv`r ɻnG3"Qh7/ٰu~fMGgouz X|3f,q(φ I7`EzډJ*Hv!0dg#Gы,6jF/EȢ[tb.(CE"*VBG]|"J\^TřwM{2Y$t\ȓB 6Nݘ~|bR[t6'g].WOӯj!)]F'?ЩyK;]=m_VS=c4ޖW;yRI4.Uv#-iCxd3#pzؚw{@WZ/٪wo{Go՛n {[;ߦ=m׸˛}wg~[/|koq<j/r{0oxuqͬnιGf_5^rWr6kL9z/±xN"Ǎuv[/ǵێ}TdH?K^-ta=ɫy600$Z!gdf39o_CL4fx7̩lUP SL!{w63ȝBۙe~1 d{alZ"[ֶgXj"o,cj4o܌RXoڌ̼Clܩ*# ̌ؗn Z5N<#k;ײ˯gpƵᢋ.<曇vp [3Q\_o>3\?`s*v=K|4hfBZzU/ Gc.8̕?=UU>~_d/]mox4D3ax6kQ$=.aBNGjt̅Br>}) I]FԤ5/@Qm)'-Ѯךm "v^ѹԸnoM?fW̶Nו6 u55Ss ?Fsz~}K0E,JnUMb_{y`ɎΓJjOeuE7Oʱ&XG^)qD?DZ#D*MtZ#$"ٻlϋT+yfPGœ E'%kWDK- r?>ԠIƙEA"Km !ARZl{\BVԇ6 5KR\)#vѭ^LqA^Q7Chwˢd(!T2ŝ²/?KK7N E6}^f:48sPo;b\VWmq׌N\9TCw᱇P\[o~^nEEX`gjޝI ]J`ih``]t῎bF~ʷ$MZɛdQ,I.\ګt7\+T噀m:]<=gɵđWǏ#6f\aS+}D} wS-L]B9(LN][ #}XuL%)_6m9z.DoۥM5iCp_T(v92rh*JCʤGQ ok0#~,XOғRθv%ʕh._~%Zo}OhIĬ^m r?)-fuZN1t +7`󗫛kH_SIFN0AeM-תLh]?4V@1rY B͕}Afu@Hkc0~(V4jʳ5AkXBwR[0Fz1Y¸Ѹ#ۨOJĎO~:Z]07`*g\屐ne PaRal} dMB&BI$E'tȚrvXKL{uL≏AMouaG幚nef<۱k׶̬$Dn3Rm$蹮sΥ'IOe͐!O.NA/mq70q@&(**ΌKv&i~#|i nЭϾW[M&_#f#G,18j'MVUDKbtq\Nl+t+9OΌ&vԎrxQY9],Zu=Ǥ+%W<&Pr3۶}qn>d'Nbd$<ޖDܯ}J7N`T_JvBef83%q f m״)oqCv@P[e;]X6Dk{9r`TrhQ'HZ2A%FbO\'pr+#w9_WzW"Wwx :&ivBLHQ4cfdcjqC-Ksz_áFbf+0UA%rdMpUj)t&0Ll끄IP@nU¼nkt$ ݶYC/y B?}%".sQ|Y"2yλ]JPtefdʌ[Qz`6 q|`+:㋉֯򚗋M,}J3Q{'nVp[:h*ECηWGsoahVs-d46FEh&-NK8ߖ8r Y?2+nnt\a_nb܆%ͺ`YpS;mL#%;Iinȓbn1֩Ve\±.|4qm35NnK^j!2$]ֺģ!>fjM.'1%Ef`irޖPEE uҭL&T#~KV^h(ڗ{n^2cdԼ5* Q 639-j [zQd]sEucp~*r,{7fvV7ŋ;~fYƴ{בJCVy7럊C,k,[ bZ' DeYK7( b.K' ip{T^#!R!yn"+t(c vg5֢A&ha3i]O%J}{¶~x)B LMa.W˼aE ^r ,įkVx|p)%gVqnÔ;c r@` ҭ<#ιg|s0DF@`h"pa뭼I]*[}>,4˪$GQ.= @kh1[=5&\o hDm4VX\KV޹&`>  ÚjR¬ȚU@V1v,&H\Y[[nF| 0@ުS+vt[-[:Ȭ #񖋡2'ޤM$Z@ wGX%Oq[N%Ru m؅r#@i=rB"mk.Th-]1v{!,%@ GN>Wsc"`hO1K~}Q h4-MsԨ)62gG rIf,>E8On& tvv,:j f"c^}*<@:VAӮ͂fcⷛKf}7%)t[CkI$DA\lޱ*%kD(JT{ޅO]O^}m7zm{Ξwj.\01jDFaϷWJg66_mvENF? vz;x]\׳>3vѺpToZx0tlxhqlr&df\ޤ]a;zInv$tlDfyB{zƫo77k9{ ^}9{1#;_xH̓vYkUVI:P  :<5Ry~GeM6wylYlv;:29@ؽLC[!mک]8f! !8@]qFK//-giv[7mvnfKNu(0IM g66_m!Wq-]R'V/P**!Nn8@i7,U3#RHtXK{5j«C;,ZU/P>Cws!~M|Ou_С\3qo[4:qDv8M&i<73@߷vu>45|5V9W2n'cS9?n+O?~֟pv=&W˸sSe005&/1- ["ך\嬮 K6vЗ_ˢ.[o;vVZ\sMv?/ /̞=?ςf|^|{JJQ\Ėy3?$nI:oN՘(*<jM jުPo7`/nO䒓>}ȩw[ת+0l|Bኌʿr_3mV[+k ㈒68`RS4Q%nytYTC)~1cq5.XsUWh~{jfmnx"7g_65ͅOgwIW>[r火L&ṇ~RQD~*xwϾ;^b~sZZd GamTub~[:9nB -*-;&,rJ?~]EJUnJq(?_ nq/_ }^4y|Dr]a>Hճg/לtJqOQxz}onbvk6|q=6[lZOb3UMR3p@@6հC_ŵj$:r-l&ԫ%)mj6fܯ^5(kB8NEǬ$vuO,9Z= 'qU-ܪ}:묣ȑ#_}%\R=(U~L6/kӟ~[t7>Eզ8|~)9ǭx_yc|UڤhrDT*۫Pp>_Og; Gofy'Tl|H T*XWr8  dE>9I4LŠks^%n{yVQY1z8k|$}q[{?tKj% |*:+<n) :6UV[EJ]} 6`V[eUג75s~x^,|g0!J*{SAGWVR$:;V?Mɗhoz}哶3[*U=ۦ+ oSw[b\u03缭e mm@:0*L\gfN}3ת؞y-{OWJ~P_J" FR,16l\')x\I/ Il.T]7=nWXaɈn}<)`pw&_3>6?mSl~ܭn,[O7Gt8UuO\I5W\$w<jg'tK:d؝]*G}w:?'V%G򃋏4i^ՎYO"]f6 Hl d2^ H Nʝ<[-*V_{^Ugή8lfSWy}1;M<&m=Wi/{Ƈ>ʝίU}/,l<}>ާ =ٹƊ+؀;[9*{4ڃgR۱gݦ*C%el>T"w<jEU-hV䢂(U}+Ά^J"BaIRB, b5J@]!kG=eSxqėחڳJVՙ2E;w v7퉽t |yv^m[U{疧\vĦҥcՇO;jF|* vZ9%KV[ʾ4|{/wOJܛW7&G{`:dYug\  qJU?6_W6tmձ[Jfiohվmdu(U:_g|q[=U g^}(؆뒨E^2~[ .Ίp5͉u/~LB[ynj u̅"ZEy7z~{7|f[m[YmCù?Ơ*8o=%~>5w=]j{HŬFiVs@)nȳnUKvZpc!`rΔY?:jN.?(JEse+s[hᅗXrq /2vBG㘱 ըQ^f %oXc釞yJg66_m WUNJ[ ȟXcN}A%*^l.PS:by$WMU_K^SȨ8)J_qrHl==s>_>er:W~;+{w|~llŧ?Yz _f-&?Uդxhh!W}ӯ7n7l3l*"Cc'6iSd}ϝ%",(p<?*>vK7θwSh @ \wI1W=|O`*/Jeqtnkw @ #>#. DKk,9Ynm- =7=GRF'yt7;JVEU& #zySJF)_rKJM'snTETְJfa=+"@ P~>E&Ê|X~~2q1n׾ cI X8 Wg !r%6ҷڷRz;^z7doP*LQPA4@ :.:vYx*=9'ֹ:7,-@ ЃHJhKH.g]i:n" tkn^jО*An=h@@x,8Ne#tΙMTY[|@+t䣶jiSxWÿ)%|[y}ΖbԈaM@ JvJ(j얞t[mlbT2UN/*ؿ#˅r; Vy`4G 0`ڕvFֱaEȑtOa0Jkx%2*3kr3hߺC缫"]qqv_䣿]yug̟^ er} EP<Y`Ml 0K + }䡧/qP]wǣO6VjN=@DA@^}8ҪA(㫷|*OG4ڭ*T>Aצa B YqlS|ceGGc@|GiTkۊ5WZ+sT@ :5[2bN%mN[pQlmVF 0R(=ĭru3PUܗ>[ؠk0@`0!=IW)S\6.W Lc#S3T0X0m倚ty3/FnKXP}g+w |jԱP'YDzBAm6=cm||7qoֿzñn* O @"cLju*pg&g8[{Tk9` ELnp. U*vfqSk~COrǬRTivQ̀q>f CZ^6'b>'o9Z*yE?Y39f[TFo8nA@ФخZU%';\5+Wɴ73ɶ80y~GWZGsYSoDŽ_Cһ)MSN9YfM']6˸kozr͙G.q~}QSo_l|%h7JO2V[f6w҄ƌyAO_(jmQ&`9`O;'=CT2,%l>? W  @CRcD~oUջv7c} h">wO7V]e&ٸֻIܩ3f?jʕ9Cr'6kv7.jw?rwI>\eϜm.ܯ+{ tۢV6ėFr{J6tR'{wj_2~qϿɷ^wH*%ۓ>po8, zr(%WUs Ӡ2݉Q*FW6 t}v;[iF~j7^UV-׹J5O-0:vϏn.v[|V>9߬hvV К~j%-vP2/t{߯rx֛U1aҾ$VlkyuknK Oۦ(bV*cgD+o4l}@w(8)R*ZQeN!QzSr3.JmX쑐|zd!D :k_V\aʍwcb0zΆz6jcAYc-v;qVk51tf=NE9?lBUR(Ep(0'ھ.{MW^u5%jm/2mUWӡ{ñwb@t bYKlLfKt c0DO urǂ_jLً֟鸻߱荛M)tu|۞m| ~'j+}U̓[Og?+~}Ocln[+HT W-6خ5E@ aVo[0Z.7n ג.~[M5LY|go)Vho򗿨HLáHyg^x^=MyxϦ NF[o<}߽WO{I钫[>]{n' sIpeDJ)*m_EeRfSmICZc'@H\r:嫼4߅Pe.$&VZk͚iw?6۲M\{%l3r\v>7ӷoϦQEkqɆwgO{hϟ|v/ʗvۗ?q츢f4ޣ[In9^7&]ha-khd6[-^lM~[-cw$h>4)%E:©8Km14g]C2$-衧^z.۶o:v;+[Gz7?f3fk}V;H@rEVE$*KUVX$-HS !':\yRlVۖޓnj'Al@ɫlciN*>,dUOb[%ȿ\#j-Eϭfb@#5䘕3jI ].R&?;$K+we-cƳ{cV[~wP&ׇ >Y>m2#""PO0Z\dnnGk;kuцp=v* @_#PH Jmv%9ڀJl=V AZhMf|]g}I"= pG >0++92J̅M,&\u٭UZ$? v4@jʔ|UMVO%fE,5H.?F/ÔcJ6=>P&t? @TG{M}∅S(UHSOV+^}۬jVR`mqF@ }_( ̺OUwS,Au b#@Ad_/O;JkݔWS)T#Y7ƹSH ě;uM{- UW Ħ T)K& @!PagT)`UvE Unœ2.sOq;@ |VyIPz-OFQl*JY 3T9 U/>T?fx0__HXA7p 땸g2g"XJ dy\ 9g^ޛ[8zVurzVlNi4;zo`?qfu͍'ZNŴSZ~ J;}?wk$ E KJ@GJe( ~-}cJ VndUʼQg֥Xm=<>V:Z1#i∎bTl$"<H#\CۆPHs l1fCA9,LŹ%3hR*N"up*i TMGl/Q yّ̋m[n2Wel&N.@STDxC2)u2vU'NhശZY`EBV"`ǯݒ;Y -ls.asn^;xS3gvM>=l<8# ޘ~mg76٩'1pu"tDɞ8lS% ':iӜ sm\@i.EɍJLdmTq:VQ6xS~疧\w;=튯h̻u)Ad:-Oȳ&J1TYLg0>7*JKcD)6\- WFn$ނ6gmwq NIkM9۶qS9 '  EZީSn\,"vʍ. ,fӍVmA;m+6$[&4EUY*pn | dޥ^T5%h h5#ouD]kj oV,*x&A%mi ~+@QvEsCB݌-o=eE ew f)*)"o~.+`s@L@#+TȈ.X]ՉO(:S)F\TFtm 7(!@Hl-:1MN6.3z MD Xl4ժ ON&LVBؔ谠A=DXDqFŅ风79¢КLUmf%*\; 9 g.}/fݞ N2U$)+*ފc.Mei\=H 0wYӃGT@ N X|FOEȞWq)VR2᫪|w[Pݶ< 0PgEY'ԪaTJqwW,%Uo2rm .w^or}nA_I ޽{|oxamN_]AA۫|(v(B {N2(Fj+OR/ǗyO:U n5pmz.Uw!LntW k?Eګ|ᚸv@FSfU>.ԙZQ걳 ;WZW'8qm]wJ>ݶtonz/;}"pQ@QdVtEQqO+n٧ҟW]n+oK}U-Wj]b&+'XɾVT5BoRմP.dD.W'\q߭--)ҽ|*ɡ4,@`"PzS.*kФJ҉Q%u*%@vRtfr{ w[&Ww[׾7|Z wֿ7wK*tɫyS, Ac4a=JȻRuJkֹlA)jhu_.W\m$.*71ELF/㢈,+Sr-=?Vl6(m[lx(݆\Xv!m`!п7T= 8d1OyS'=eyU>5 >-O U)&\Q[ @ȅ9x@ĎїdRALT i*\m=>$CƸ0*jOOͣ* h+PVY=+@>KN, W5, t;!28ƍYmp!_W䩺 4Lmt `Y^tp.U) [`P3HHhRpKUQ(bTZC|S7(~VM_2Y+  uզ+B9:'FUsaݫv&_WЕ\*Fܹ%~΄=>Ku4HgNL;{9}xDrsD݃ l-Oy3guFcwn@ڐxL{Y _fk̞91 M*]F.(iA<O:hȥ{1P1d{UɷBM5U8n KЮ=l5-g69ڴ+_]*UVd174~;uq4! :Tl^8L<ņ#}K8Kzg~xV%^يzN8m Eq3nU=Cպ{(# ֆk<o1&ɵ|+VQ Vpa.K28u"~m2Y2Y䙻k"̣T[rh.ȃYF0SIli2[uZKM6 }@>{tA2_0誃HUc.JPF붕W[`Eؠ -8B3ⱍZu ,),Pj` k g?Ue.~߅ %y0M9ӑc-mE=y'nesmb@G4Vbo=HL+P'ILĤڇIi9;:t`kKE6*ݺz'>޼,"jnQ¨[mk"uV7.i]RY8͎u<ԹӋSR7a=VcwW68*d|㢒*  ĵѥl3~D̚nt_МߡJU|;c%3֒F^W|i;&[Wku _NJN}6W Qmn֯:4 UhgPsi$rtQME8-wk?*[Q݉[VW| SĔVe9UH=\-a}*p.Y5cEnu4go]P gx9jާxɲoz9ӭeto,vlc*od;Ub dž`H׮%9h-pMQ(7PY56vl^9TZ̓Ҡo)2Rnݬc- cOgĭ\K@@K\OȈV 墭pNJer%5°[u^s}dE?!u-ORЁPz9 AKsDjΉL;d ULnPm}rK})%p?!hn2mruiֱr- 1sVַtʍun>.< @"1VBIrJ-qϫJos;\ӦB3R;7i׀uy"}oc%7rPA&n @.#PhN(j*?I(`. Am .@ DOR,Ym( [Àxm.i7ݢ_ @-Bo {YͦjSmI7*dx7DA*%Fom-  =-Z&G>ܕ$goՍeW|^KV&ݚ;ki[}þ E LNa.#ݑSU6)Ml 5C HPv*hovnTԖp-z@# dԼ'*#p?2N)[yd`%xZ?7[t' D *h5չ[x0rpc3y,Fdugk\:0:ﶲ@`#FYqDtEa`1qB:{B5uNU[vy6XL}þ E@QWo kacǜUtTdcѿ\K(-C.%wn] ]C\8Fm ݢ GJ~Ei,`EQqL 7*7 y366[t3 djèJaΊ cs"ݓn E_@:v&j~Z9298Êwf $bcn1er @)r m=eQOrɶ.9[vP'8^]mKҽ)ݢy ܶ$όa[=R`*=">ѭڒ(kV歗} : ~@W`yhk:bn@b4 .s&mp`$O uz)m$Z*Uʈ\+.WQD@ !.Q9R 7E̠ k@aTx6<Ωz5׈[H8 y8XD2J^A@ 2"/ً^\xY^Mrf57 r߭$Pnum* 0|J4p#ox5ؼ,[ m! E֘ !|Q4  AjbC m<_Z}Mp%6V19Goh0%eY J% go/"9vEd 0Hb)p䌛'qjOQ$6-m z̺S[P7 Dr7Q/K몂4d$udGȺn{!r @@-R>XEnmB E̪I=.nkx@^E@2SXH(ɦX~"u^Ɓ t,j B@ P2D$B XLl985E$!e4ϖYbt[@"H7VQv%3lGRī tۋQ @-\$*ˑ~c)3 G F&bqzp]yQB Z-@75Eg?E_Y9 /k 7[uܡi§/7@:ւA~{X=2(o'\hc.J ,2Ö9Wl  zpld%s[EbhdNl"a"o@Jڛ_lͮF ܋Q @dlh V}=b R2I[9 @#ԴFtVOr4[Wgj9iTkU-?dm(%n{ B h5mdLY8K s.X%Zdֻ^LV0T@7kVI.g~m+51]vI,3)W|S B퍆8@7 LtNt&M˭bFؚYjAbf&2ݶ  z idD#e9'$3M'- ^iHSj zʽrOt٪^lCmdVQT°vQ^tq @w "OA(hE[bq 2s*mw @ 9gI~ Ir E)fϫbe lHCA$@ 21%[fSz*rT%[D*lQ%my]˻.6tۍ@ 34MmkV<s).TVyLR=  @Dt#zح8h!VDlZlQe`Qz9!FA@ $jUwfN\ܕ±QʕPzliqdR3ݶ  zCfNR%]8|QȲr֘.Jyt+  @ h*%zejrWU }/[UѪnjl(Yr 3 D6Rm=+@Td3UER̖W]T tk- @ Hk))ʥ\k:L&SᲫgʰv,Iˀb+QdKnݶ zd+kiz=JotT-SgZ!{ci`!JA@ b Lr:x֘Yg`]@̩lH!ƼN\qCn[h 0Hfݱ7TS)ݲaFgftRy9Ufb䬢l^^i9 @zT^Jrv1UQMdX0 趫@,نfyrow(8$-cJ+oUeO`[ 3 "@."wJu<]Y]albrq|l̩V]&)K*Isņ`@ m)rϙKe|Se*ͩ }򚮧UɶVٞ-UpH݀?@T!p'yf7[ǶKCS) ͦRfі3SdNEj%W%m#=GmU#{ uJΐqE W$ɱ IUz#B@ P@j*%) #넂k F Nt\[fK . ݲAUm@ȬFW '<[TZEEwS?2 H7G6kݽ~M|(q"6v#B@ PeӃE1PlbEjjNe%K [0ǧŹQ,TF@ x"Vbѳ4"6ufȜ*Y{<(<˴,e A܆=@B *\ǖ5*gp sV+' MEN %Q2P/ݥCf]Ƽ1l̂.n{ n @j*ȌiS,/ ډ AUi(a̡#,n)um@ k$l)wSsʛ"%>@MSzٝl趎0F@ 0"cM hP9eebB]P$;:qй3eiӭ!U[0po(?@`@!PdTDW]3Z(Jwc%}P& -$j %%* d^ɕ.3ȏ7"ɘe[D7c#аm(8@``"3/2[ :vRJ<ʩk[/'1bYHT)k|@5K%1e|,gePMRvv?##8-/ֺx! 6YTR @c=U:+$q.e,DIt co W&XVvy0}<;5kh@ 0pK4`(&Ic 6jTrCq҅$Գ,pʣEuOGXN!sYei%EHB9U !ܖQ4 @E `mۅ˃xqBD}SlcūȎ Ւ4!_@aˠ̩Exܝi%ǑrlfU_YvVsaԜQT @E@l7KpO&Q"fI%AGgQS^̡m0-v ֈ| )4z,: mTZ %K!Ƌb QQBuL y3+X&N691`5v.nP;CVAv#]RZcagsIѤH V,8NNl=,9Buݐn{G˲EKL,zVTʲ)Ӡ _xv ~J?n!@ `,͑aS+`yɕd[QVD) +--9@ X4r\KcFO @qG %͗)RZ0 $#Z#Hu j./FONbNQs66趗@>|u ,bĆt I3ZɛWN%}T)T[ $'@#Y+l&vLvk̼nbմtC[T9[iqc[b@C VOƧ=9ՐLLA+3X?%Wa*Y8ı b@"ВTS64S*6r_Ⱥ 3 pFTä-؇#VXn+TJmNQ.SN"~֭%;xDR~pq 6٘f%zSކ=+N6lWDƫQ,KM ?Z@"޵ddH6Ws#&ߑr(P+ AT,V3Ρ96 @#m Ide#&rf)Ul $W[.DX W|YV&8:JdbIfFߘ#} 0 XX+iZBEEm/B Xzz'˭n=Ws(LjK¨|-bx'^e//HLn-@ @ +%$t=Ec/SݱBuluP4-OhNn!v, 6MZMՐ-=0(KY̒å\O ˦2qFF 0%특,$G SVrmģ*n2r`lJ V LoB-yȮB1|@T# &Gj.oIT N-蜥ر\ʵq-F( g,pM0n3)[ZK(fJLeFO(IX jpl=KdnGq ̓n{ !z B(d9kH *k%ZIm, "plA 鶪 =@>ĉF%t[/,H&('7sȣ2J!~3B @ PgT7U@U=@v̢t+ ݬsS^ '%Z;*rr$KVH,T@cP&Q#]e-pQS)w:8R5 @/#ZċN v I j\,Py5 쐃2@:']'KJ)IRy,zbiTxӷmXRA ;/N+ GR~gYҠZ1@`8!+9jI8 $>1z[2#5ir3gdrNkmnS{FYX5Yeo Ƨg֌s˖:rJ̵ݴcrcK۠ہ+ Bw8XET.F)Dd <1hW%m;:<.Cl:K˥vj(,@ @@Z)-~?VK),3z[eUARtc m[y -<@7[ [ˣ0!"#,Pu,O d:gkwlm./qZّO,'ͩ2S,G+n`?̿ƕ̚a<6ҶOy[v w 8I7D 3GODZmE ĐBօ  A@K6FA@`! ɲ0rb:R|5H5?C" m<^!fbxUCĩIHTdž%Z2Oޛ/=Rqv[5\b6kC|@A@9M.ZFO,+1`V⛷vfNYi1"1;? nE3D&h۫&7o9U% 9/Yeu0H+:іQJ 5$@@O%j󡺾5("@'Leݜ{/V.nʁ@ GQDoѱSl)%<"HɑNc̚x'CsDl3v߭  `XћezУfu$zFM7'*TjSc*u/ש+ELlŒ@ Їh^7ڇ#8Y0c>O唗-#?Ɵ0H܂"IZ\er6%$(mգ@8CWyUV\+'.[7\%לݰ :dtJMv~61pj9@@HfqS%mHTJy,$Jora7ҀJqNq-ϐ\dH<4k@@( >+*c?(dY!42"` eܦ -vQNXd1qe&ja42g~kPL^Ҳ[?S#5o-dӲQR S`*էp#1 (@|~YVH4 s/B%pz3ValP8)HYԠ[| oRTZ> WsѲqO2~@>amw$,n8ٯ̜*N>@ yzf $')Yg/sDݢ/JYq;#> o( @`P JeIky3T& fV Ll$dY/~Δt;(Z!2 ? 0$pd@HHu*B,=4bPCyIΉE:nDCE!CȄX0b* Lc =Y=`B۞i) @P&} @@q+Z2 %EiGdT^έ~(egn>Nb BX(N_LYZ''<*ϱt9r&GVERk>H NH. CHUőE>{K24(@@ @@@`\W#ETtD(r]'Wj [ @#bdv&&#l-hu]ec.Z@弜a^sht;2Ŭ0C^F%)*&jmD[4q !F@ P@4J$~f^ &kɭ(u  WCm<0jzPm$rT>k2H  Z\kEziii{(tU zS=w%kմ#k)I4cά | m @n[ @`#'2[/QJEP(3ݶ@A@HT[cl?%c5~C9]%A @  ʩ֊/ ŒCWun/ oJ­f.N4Xmr @t[t?Q"@!$w]1QG&b8@ пT 5I] Lԁ@p#u 2F)&2'tr h(@ @zHl-W8 -L{U@ @ L|6N܁͂avT2 ϻCF@` 1v-Jb)B\ 8x@@Du2SXq61Fc=ኂZk hm¦6VsLn $;eP%DsA@ s0=Sv-k ?1&ۖ\seFL@ e}Hh @ 0HȤ(+q(nT#Cf@y"Y dp@c·U-t!z2趥g Haj?6ƢȒY>fh@j7 @`"PɊb1 Bpvߦ -T HƆR 1UKT _K^ H[ 0v7@d 0T/gKT*mJ'M ͪ9A!VQ8 E@dB)6([,O,>v+ZKNg tb;@*0beZ]eCF."a}BOZjA@ 4[ o4QȬ4,0fD_m72T)5̪pJ VLgH _Q;>@H͎e|,;V)ǚ ].?A.0_tV+B@ *<)+$ $WP6ԋzaɶ%+ed0n@B Z[ 5x@^AL*sj4i? ТLI212A`*+  @Xվos9 r7 2*lD9[-x@G@e8wJɖ.Fi )( J o7 PQ֑?OHCA*{­FP&V *.\[\988.#.`I=6wP\ [.Pm(@` 'T K$.7KZN/,(;&6:UAb;xZ&r raTOCa@@t^q)TAgfLd_|yV92ɻ(yi72ۍ6@ U k~70Y"EqMYIҁ^UH`ܮ6@@8_,c9Q2b 3ƬsB5fmXWh J)#&*`.koE =zZSy}&5n@ 8)9l J@@`!^E>։N>|LNڪTzMʚd#%v]x 9@ 7Zq ]0'5 0p*`r%鍉_ium v5j 0Jv[aDOE`kYRœzKM<ϴdJ"ct(c#0i(&@` P)%5a{O!.'b !V봯NYlJ"1o<I#0yҰ2? )1.r&Zul 6d 7*&ű<1R S>$o B/RĝuΐiU[䐧o(A@`"zT[ v)ɯxȜKzY6dϒ]#l䴬TC&i!@ 0HP.N֜ʄbmgJbE!qp'X9?ҥV.59O*P+%אtKB9@ Z9N,eMKaf%o?|?2!'I4dpr(,ľ[-ⰋANq @ v@ KVqTF~l< +ʓ2Gš@ ВZx<2o{cxoNnMv-قp!9 HSXׅ[w-@Cg I\H뵻ƁvՐOK,ƛE$aPz%lIRrY6PL2 |wM ) 0q$wM2[Eg7„EK#BtGa. IIm S\'gr%[EZgd-2nI8fy:s+QrSy߭a*Շ- I aӠtk jayPFllZdZHӻ7$#9S)7g|h@ ?14KLzS)kw_T@`"eh.:}v,LBlߋ#/Z<#R KϬYumLZ?~ۈD +ôQl c亩_{_eȒ3E. v6Rq-0[90< A"[;!9 𲣐q#+$%"2K 6'F6S$z+@u;tswYMϢ2;3ٯ΁@19JlR r`D,%WDɲVehۏےDdl YfQFr@ J(':V-L3P3y(<8L3@#3+IemzYPc,]tmYv]J/V;Lv:\ @eRձj3wٴ.[aNjn~J.U8Eqh<%֎y8w܅2Z@X'ljt&ekYN5.Re=">: J$9qkB:Y7Je @D+d'bk]KHp-JB%%M3hEYvj%l♁" uW}[G!@AFOYV"6-@ԥhI?@ @(vRl %XeE/]f*ͧlB ^@YwC @ "U6!Z2dٺwKĎ&[..M'RJ>ı."@7`!XhL,8K&LO4Yʌ좽P.&tc%7;ft[ zE'Z0>.SɝVlj\ASr DBv|˵&`*{r:[ygrѢw$Cn7\Xp W1eaV4EV"b(65xWK kCAl@ky 6tvvrjr-dOq7\%r^ՕM@ iv98 bv~rp#()[X&  Ƙu~UFɷ^N*^_X+e_݃7\ͻ8Whhqb<+V=C$M@ #+4J?P>:+YT-nŸ*ED+h21 ]!Cd4l\o){SJ zo  XHvUDc'w2'\bDJuFZ&And/5*N(\嚘@ ԑu {Ø=ۦXEs]8(/v12߭+[vR\jlBJ;<J萭:@Pg-\[}'Zs#)]bV]k^g-UHF"vKց[ &:O6@#(K"YgyqIKDew; );IBTs.,[ysd9)CZ242] E1<\asA861.A+ =nAڑm[{2C0¶9Ulx\,¾:2 :Hw ixH,U@Z6,*3-)4KZkk-J ˢ!ZJE>cනqQX:$0P_j=jra:OvtÆqau$a@p͈=xԽ)aN96* eč#{ ~*9@Oy!*}Cp,Q>K6t%# (y#`*IwHۢ(æI.ntvŤzA8Eh@kS[5 /(&e۵)W.1_̨ՉN[kv,\_R*[f|㪌\\g'*WfSwf/rΙ60=J7Vzn?QMrT- -mz CVv!O87h#U^r @"p駫}s_{5mV@Lg@ Чnn$w mĀ@`x"1ZcqrD@ h}{?R ZrᑣFdb|QT$iV>]g[@5b[W+@)zDc]b^;Y’ׅdhDcz#}t?wօCO27 eS:W -|~֐+iu X^5º!ɗH +C4Ln]p< s)TU9G8w2!㮓xk{Y4h~i>./C y0GԪ^\Q /]5|b c~8鱤O?g6ً%Aـl$O#݇ޚWӴ@ g gy1epCIr@} E?m-$= !.6DB&B9HL?L#+nV;6tuV/@A@{gǼ-7^GkNcjKH-TlF$>)vf&x*6hZIuFd:5O [_(H.=B˜W%kfB]E6=I;'Ȋ2',VA1*9x1_*[e1sDVñx]\CďC& T@ (U1#&#:m+-\j+n-h>^&|hmǯ8 e'Pi'Ti.dB]Q1 z_ݼ}yuMOrl*[-S݂^M$U`TAٟ݉YA9_hF.IOWtd#4a3#|ecvI儳]w(6\X4cj$.1E{= tQ{R̔ r\ʂ]S&7o1cƨ7ͽDL+L)4z.#]ke1~ =F7c%0"ܞ*&,C"xnW,xM*Uډ6̘mS&]S_|X.ft_^!Yf! I!FMSYaU '.S*UjVM  BϞ$IENDB`filius-2.9.4+ds/src/info/img/chat3.png000066400000000000000000001250511506574241500174460ustar00rootroot00000000000000PNG  IHDRgosRGBIDATx^ `]U7ҖG mA*T&J0܆t?6;8S< ´ЦNja&mrh"֐h HpF9@BN 05}6ݓ֑,Kk~pF͔5L:C ޾oy. dh`,Yi3QZ 脴 >1ǐ`*Fd&5Yt_!}ֵliҗ-\]5xQl$׌jHf(hبDc:%bnL:3q5K9br>Fq,}x6vy@MYNNϥi YXO*<$ȿ:o {g)I JiPims vwy|܊Q yp9op6zyDL &Y G]Xa,$(PM?{_W?52 jxXx6XxlśטX$(4~I/5@ap [鹺LXˤ-V𗵏Г3ja7t*:p`@ɃC-MM`[]h=g]0s qC{!= 1zNcc0HhjjƗWW]iFM+CCUxthaX8[#v4X)UN.\$ ~6L2&FdFNqbbgquqº?_cC ֙V894FdJ7i?<} ?*ǿlc%*凸:mZ>`OҞ?!0C{_}㵫Nxm<6H)fzm1: ƘM? , Mux ~tXl^LYhFtLa>*,MT;6J -:<|xJԳGB&;egӌmT3tx #[)q̝*܅~I1tQ̧qvg!/ûBݚbxl&4֌LAt#V 6.'ӦGQ.`+QUqE{x=eX8Ƅ􃶀1xEӡZZP}exlL ^|ݳŰXt th$:x&̄c!ɲD#>kN2o}}gߋ;CCdݏ֧W_k8ߢg8e0D EkT@#siӦy?틯YaЧ1+QVT5Ƕcd홮b;dbDy;iS4n,gyImJg\Y9[JZ;i|X* e}jS, uy?YWh4n,?%afG fC .Ρy'J| 7 M$ vGgi|GUf)7xM\ x^x^(٠$%62\M5zUV~1ƒG͒O\̌se=qb58-dZMkOB&!PU/ đ 0MM{8!K0FаS_<=m![$qpfGv؊7߲gW|} :8@6|ޭuZvI]6Kh> TP{ǧ/G{I'vݡڸ&1aVlR3~ύ㜌 HI&#"yD#GIFQG=D!Ţ9=-&/`_q#جmlgaWk \_5Z91e1 Z`4m4Y5ϞjuΧZ(LEL8A}O9u.:{620|~YI?aopϟ4J5AXiCyc?|Ƃl4²k0ϼ [w~m|ww顗qΟ=p>o=olc3]d0;O?~?/=6[C[IZ$(kl[yۼ ^%Aid=n԰[$L@&SfBɾ*sm'ͤTybɣqznI!K_KT\zw20 XPq{7y6zAC*b~6,bx4RcU!  ,ō, NhT^3gcpw-Yf =siGQ."{DX8[1!bIc97o=fHj {[ں`RD"C4 B?E{S>-o|h}U||lP"\~nU~~~w }፝7Δ_a7^wp>g'8@d7n>qGbo2{dLHsrΖ,a0@NEsC`IY[JٗZ59$CXn8Zǖ6f̸^λE]nq 4ULzLkNP͘ee~r14#qn둏 kT תN01lH.$o^RH.Ž-oD}5נ MN@q/>O^iӋ)r. N$MDQjeo& Deՠ׀=FfN7MO^Cҋ{QiX~[gs7'&a~ ߤ3D|gtN|'_8UD*NQ :}_q]q;Z\+_>vC<5'so9xo:3q݄W//N=/(kIqxGk[6+:f9[}P7:uɜk2rـ27"|? +ɘXLB(wr}l^cvp%uW7 Lc?"kC3c]쎍f WeBԸj Yd6 huO"fYڹ'Q'"ZmZB7k۪U``S:+wOy ^~eym䩵=i{߀;qnF ]:;?pk޹jD?iR^P8˪Y/o5=FI3P03p9caLWKUx,!.,3Ԩv?}*6 +2]0;̦7j,H Dg6FͅfAUt$b'i169R<N vJV ~XZod oEdrH+o|#e ^P Y؍`X4Ɩan@3.;dkjisϰ4SbRznM@)6kE]?VK员Ti".c+ڨC?A>81_^)sx­|8W$VGL|q/SzpYʏFƏ?aQ&Lu^mw~=GuW_{}4#rV_ۥc_yˌIϏ*]u4-y J=?jr'+Zyg ?_<8|:7M>)zy3q$1ljoYq?K,fRKZ(bƈWxkr2r30a&vn^4y YÂc,lH(>-|Xj7P9f_"|\3orz>̄1X;S.;^?z D"#zz*'BN=) a{Q,=S4YnkI=L]XoDNN`bM$- fp5q'I)GGLT3y'դÓ&ɓCGWB U/pƺԀ&P3 [vuXa#`3\ƫS&O:E^ۻ{dzOo_z];wk?HҼ_Vwtpu`%oz+pS}s}\}uYǟW/5O/K>x*p<H  P;~kKOWoz=kϼ:D/sk|^O[|_DӿkkW'_kۿq Tk*kOlZM&hQwb)VV 0AS᧗zNeVj0/ExBaZi$oܕDO^$ ݁47Nf:Âü:ሙc[ʘnY`u56 ƳYIitLdMzB<ٍD- jd|tv) pkJaceu8͘cW{_@0q H]p~r87IԎR M7iISMR3uRMq;u2}kn|_8*U,42҇dlUG = rKA{3%04GOԟ}:N7ڣ׼wOAX{:0vJ.xw͞e!X>pnN~b*>"ο;vb}W@\'#"d_VϜaq0mQf F]7E קw?~Q7و'kަb-2%O3ĉ͝,nVMM +#ᬑDTa]p#Ho,tv웇2#77CTNqG:ejkuLcg1&L;v|9v±bcjks}׽E( <=Ԁ&R&-hڅ֡ГpvHل]b.* A4v9o9'N|3;~̙@7в.~O>ﺛ` lS|U> A>f0PV5ޔTO_ )h\wi՝ /HD[?rnY؝uۈᨂy$${>Gƴ 3|sTqZa[ay,JoN t%Y'MueMTֵA 方~/;xj k7g)lrIfk5I# /Qpup 66!sTbD -B@a焒ctg?cGe_[AĔB`}euȵ,0Pk-7QB^e=S:2 ΔM| pN|gOPkQu V9Zvu$^U{+/_D3ܺKTy{~jG1 &'|)! WqQ3???M;{H}gCa7yzktάKw[>nT7\˶eFs:ͻx{f\5Q4UWƅX,\3"kM[9 F'7b^6A6)G3ّPT7heG\\1=/R~F6nW`j? Y3Y/E464MaUW8F/Z$Wc< 2kPR'}]6dV4Ξ>0?yx (o.(!nu[~rA]oT(KvXWa:Er9;;Μ:͓06vL8혉ϣj1*D>PC-{,ҩ4'ЇVӡ]h=sˇ~ng9y\߳R 6eOS= Q`ظ7`'䙴ً3P,Ga$~OӍ_rːO<7.yG6<~O}p~;7bcp־~3>$>F0λ^kLW/i/;H#U_37-?uw~ԇ?EoMR_;{w `GN\>HA[{ j[\BGk73hٰmQOXf0ʵBP(և2Ŀu4C097:[; q8b(e,n$_bO *Ehi|-03XǯJ4٣a1 [ʵ8y_,p/3&w] _g350l5v\O{N'quyN:o6߀&M2s8Zfp1j&L[ڈB#z?vՌ+kod[!][E iq0-c|Bn}4J܄D } kd X6Qtyp1e :f{glEܣiNKqM3qdx1/N֢Us:;FeGM?'L2&Xl\6h磳k*Wz"N]Q16Yc0;1.V}sURڋ9a^~A\H 0vg}髻}C~yЇe/zBkb i~!T< .p%!I &T|1M3R_76L/{3W/l[1d[lv{}ɑ(b&釫hTVɌ'#RdQu: `xDǬ;j\%:y\i|2I84A;P4~ u|B.PlRf6et#AiaM̃#M4mӡ0GJI`ێcDXQmLug5BΥbҡMĭq} &9[WL Tb$w{7ڹ`FHkŀͭo"2i=Wc`.fRN"蛶|n,|Vv!(j*F7AL!ˁzVXʜ^i-:[BLOp(q'S?a[L6Z)x~Fi'ɘecxzv^HV,We[8,ݙFXiVI#E ` ~98믑 ٞdU}7tG*Rg\_թ\:@cD"f0g 2Zce`޲)oϼB[gCU7 w<1D"$%&bcO_73SO @ɢ9VuXڴc $%9:LW !sLX@ZBDS$6`dj"l "U|SzӉ?:rಸkc]J̬؈qEKx ) ?TDdxvC˶i_m0mVHs!sIʩ{[KX#j̉ĤRj#<,QmIK'$$ljl?$+{S"jk5l&pHA胿#rarќ R"MKog+rg3S=d~$κc=oM3K'@U=+hrLThj"aTھ@6$3D$`5Q{@勱s(MW ly`eN1F Л8F/\5uV0'*M'.#? {5jJyS cN?0]GXhg <l-_M5Z]D&8S퉁WB#oF՜$#:ɀ (;zf)‘l[$d^űch2N y*F[$1U܀nX /Q)K# N_%ԲE5hmXٰ/$ܪ-3O7~9?6:X=mQ0f UZLḾiDx~M 0̳C"1[ܜj3b Jrѣ02F;0QyfĶE7 "BL)h77>k&TxPu!ƩZξ҈t"'w)jはRTDvԴ \ҙ^x> c(p53 Ӯ8:izDzuRDh#G:pr?m}d{Ov@wY7k AOF~P8J80FU)`7hkJ7}& +_O y];a%Z/[Nr v"A<) s 'ϯD5dT9I=!C-rp m@j&NMVKF۫?C#b1SA\S"wq07:Pk#G i$ZD{Ëj?&mKqޔ&k ߸Vp|u4;q1ƾCzrZ٥0%pw&`> !Y=Ig-n8j/s*)s2̭gCV7Y~fkXO*ez,tF)t؟긘e] qk%vgrHWqt`3}ѝӶ^Ruږ3ghgvh{=K?Z?C)kR&gƵkl *(kehm㹹6;X NmȔE[Ʋ-i=`&t͗+ ސekY:H^ڙSQҜ/fS&|X0lG orI.}yʁqAdғ1! |[ rmV\ds <9pFS<$ӝa o؏ho82s).gPi[zt>"ҌM?xcHV4t *) h`v0[_j3ń.B06BG Fi:? 1dj9LJ"Z1D}[}Ş6fO4MGv$39GgKAj7, U@C8F ڲ`;;BOT>[x> -!FG+G 0B7(Ti{5x{sAF3A>82"4P-[:Xl _rezFgsQxFu.''bvV+v8BM)Qb~h, ڮW9\=7sO:YMHqA@A@# 6/mpV'͜ⳟ[̻LA@A@(zs/ pA6!A@A /5UH^REuw ipqW52M4BO `Ūઙj 8`k}^qwV8Cb Hむ 0(kX1 2A@QG ܈ȣ(c}.?|Gjeo O}<|d{S{I]NS~s;ר:DHIA@<mH-֡nvyzSrn/ :nwFmZ>rotqWʪ-Kf;֥l:}.H<@gx?s4 ,v=Cߨ7 5o6N:qر}vܸ7tt60(™~[jsO8N"NSɝ޸ԨZGA`H淽YgXb3lG7mo¸ߜriv׷.Nc Av`-mBɭ(('N3'™t~0$}Z=?d ϧ~(zz@SfcG)" P2^z6ŜDkj޽[246.ڲLqܸ{w":8{:yj^!ꣷ^1+, u_bęyy+nh=1AA:0 HAsJl]_ Vk_p1:9RLAdWEpVU~E/i{__ttmyIq^ZK6/5~K8`s5h#clE`S"Wϭ缵 J65> JYZ~~:6t!/޺˽ QR\A `KIM[>< c yP ZQz~N. P4! DYCT+gN^HAWBG@ކtϑP E g>\c5}SY>\>BA6gϚrYg 0?$?'^f;Wxe &?q؄o¢U޸b_D#77!w20tI#1d#x>iC,8gA@#n!oC3, bZO9F`e$  pUz C|YgWYУ2iDA@82XDG];(~\i4s A*tDCGD' 2T##ϕ/#G$hE h;c `$Kv0e)y3ed`##2!%D$%hoE>wz)hI9ab @$- 4  {9rb  @e XȍE @Eͯy܆[b_Yd:'F$r;Of1?!WN prv;V,8}4XŇ6lm |o$sa "cKꬕdd??t0ݐ9GJ? MpU_!ks)C\9`/}W;rx8܅V3Ez)x8h4_z*>7f'tn7eҊwco|p`R@Q fsҼxjK=`-@@11z߽qZz8 M l[tK]z(IȠ'x>ty55M]}P{G仉uK:b0QIocȣulDQ_Ш~⇖\mO^s.K8]cEzS'yzwt$l&j5{}g;l>2Evt8ԼbX $wy8G`Qݶ{Ւ~03l~?ߞ/osjj?ͥ g}=dOmHXմ ,VaƩY݈mvucf?gTbWǩYТF}DW:2o58|g))N%&\hNJRX}.~ۜb Jٞ/A ){ q|DݻMOYJ2E#@yamVhubQEu||drO1%h;#{|xA? Js89hMgy}_8bx ; _*j8P%O} /ef-_rF Nl҅w+(ˁ>ddԊ7w۾ 7s+Jg:ɼF ic.m>0? Rvۿ| wĬq"O89thlE;E`AK𥲯7vo^f5фyi[>/])DRjb]i^6uғ7 m1 (^*]X6ܟU?t s߄Zt\ʼ6@E|; Nb1ʜmL}$(R%|9aJ]t"0,NaKyJ@Kc@|X6[y/0QX6oƽ`/4S !\ًB w5a؋z sF|Е=s{1d:a>$${T=vC_%ytއ\nqqֆ易*-[Ŗ/R3҄7-iD۬(]25 b1&X ,oR FG6;IH6%:\ VO^Q^Pƚ^9t|)C6e/y7.6d C)gB=8RFS%&o.V"p<{ߋ|8^Λ ? frěրw- Jct,Q؏0A;j-8cP߆=^B>Yyuuexʲ xv$~$9tH1?ѧ;G +_o#ܓ4L6h^z !CA@H <L0E`JA`A~E2{$A@|?gz(6k{w!x:"  Հ t)׀{y7/V'35hv9GEӁ[{x\{l/L 5<_( "_]9*b`&G(|w/\*#IϜEN]4gFN!2hisM];֝ZUz]m@bzdչhmB\Ne.I'm]dF#Al}e_i^Բ#[SU8d0Bgiz[:R]ㄕ ;6:Y\b~6pp-, ~d-9 v|.tTÝX`c=۷n'K;G8gY.UΊ)5ι3L;ԌN!M>iv11JΎ۳ >49y-^]n) ̼E s`mG_ˏRQUe BːQ?e^Q gu6c*D^>j ,?@vͨd: AC2ۧ,B/E/ gl#He"CTsQC(pYQ7m Zf(r=TOG+?gڼB-_hj^noWgaڍ;Ռݫw [ۖQy}W13oȾY\ Y$;Q#QeɱpjdET nBս57L݁nٓwOkpEvOn]T&]:!ή.xۛW noSWЙ.ie,) z!#ʯibcYh6aqPb5ʸ3-gR)ڦ-לű=2W^2[{v&-PN)8i}VOX9gG=FH'(rʏ!^Hy}gjpf-h.(Gq v9 E\ ʽwK6.8u!OTsf!ۓ2,p g9 #3:tvSD >N٠Ӛ@Yo݆Ocv Ms8(\Adz&v6p #@4z:'v4ԝ:_haOQ  9>jL\ڌɆ!cYrat|*uQi689(P2ʹր1 {1KhnO}dA,JlNFUuH/C~F";}N v{ooԖj~r=zy{6,gCjEƢ:䜨:RR>+ė|,e3}*~4/? dyb΍\g,Gܚ:HtJ.JҴŢTT*ov:F鶟68IZ!pfӂ=X8ޕ [h%ɚiUBn)/H"O?[1cͩrǁzWN*@*cyȼH\fy!rdWلlP 6oWfg{8`UuO23uIۈx~y_SAœdZdE.ýX X:qycABC=t50bgoM?Eejd[i(ի" WG[h;2h,wQ\72I][N:7E\3\T*S B EaQw$o;I`/IƨcN2tiyh2埑ng~TZXّIq#+Bu4*P񘚶*ùPnR͞XͶ)`H~q.y{zmZ[bH6$0szt=Sw¢'JIbczUD20kk8f#=퓇an*!bqrT[Y-J/ Q-՜2Mٞ7n޳mCV8B~e@-e]6fu2[ث/Ɏ}Ae{}"r؍ ̤%3nbA&j١J++ΧmDU YG&% 2p$ rjOYUrwu1>m^yl7oG}M3R.f4VQӝb`v`ْ/1t1DOY5m+m\Ѳ @EoTP(VMOT oSm[1U@\خ ls (0S〡9I=XCiZۄL@b>B}+đ"Sڭ-'#JӴPrTNB1|/pk/^x [u#:B\A( $![~v~0M8MO(kBAA@( EMOr(i}9G6$PA@G DF?kR"8zv"M U^>leB  0x0-a֯{9wm{~&;FgB_A@45Za <Ź;|}C<:0N94oyҨ߆4! "|OfY  0B/\a @5#`L م/cGQV|A@)X6'a_~ >בMXWmd @/MX@89tz<L  .i6aK, $ 7EM9#+ TxfewZ88|FU UA@@$# ʢph˯x <77Ir @Ex߿^j =`~M4=_y]A1zf4/ULJɽcEz>_1OmK2aΏIv|㎱|` 9@.^~u>S UW/lE v7m0([M#Rfu0Rh-˞v`ڇ+9;W) u.WG]^/y3ݷqxx t̅x*]=˺OU5dk5KvX5B8Xeײn=98͋(J.dI|0cʒ*H3I(xui5XIs!)F4 oPߺvn7>;up϶Rywj993vBdJ6:RLglڢ:-Q]`-u*%pI:9m'3 ҄ 'LOC_i^ԲïQ8p`vh87u4~J`,qj?sOee8@d-9 v|.=9fKp'Xyk-;8Js.>!V(t@`fTpDE; \S&\ߥ5<ymƔUgrBQ_08p~nzA db,<%H6x5!9cGvͨWJavҜR#`} %;l/^dW)4$㏽}j"T^+pvGFJz]7!K[pGBpȊiO2CۗJ`j}hSs;uy=Hn^noWgaڍ;ՌݫwŶ-/b}W/c1Ԟ "fqdHa][GweLGpDi]i쫥u)I3lzPYo=Uc-GG|`0 SL R=u瓲åoO7l}9lڲ{YU`5ʸ3-gRh*3UJ֞{I˽kԪS%r2ز +'7B?FMS~l=5wBʻ;T+6lAs$S:rH6(ջ%9[+UYd ; 6}`:ȌmZ B{|(gX:ZB#`ti<6IR]8ӑjDZ8,}z1 I_.lm厊Wx.X(#;:Q^N]^72g$ӛ田hoimS=U7ku%_w Ϫ;\"EdC 펶-نΟzgc\{D{nHc$ }ӫU?`2=R\6}"]=RYV ]UV :` DFƚS1QveBi˩5̛4kSBSS]f *xlY+auUn]IFwⲮ9{ïU~ɣkZٶGԘ#%1I.:KIO>X) fsN!)fWmdqֆ-ǀ:sQe]?l$˞7\? V ܴ&Є-T#a}s.^VQǜd,dNXސ)dw;ЈSiafK ^{粁j^ju4vV~p8֭]3+6l/:|XeVD{T,ǜYh=߱W+w)Yv%##o+)l=MQk,+mv) #a /V`l6Zr%ؖdWW_;q(J߹aB9 QIKf̦:IgYMECNX]Z)f{j9 WUCQIn BIX=e{A{r޼t|u\#i)3~`EN_0;l#W:.{)j3W_h'!?fA-(oۥdI ِ$q6 y{tAu˲viIBfQWF*G `ђ#C>䠉UW~/[zwutUA@@u|/N0[waLPkxA@C@VmHYCiiWA@+@Z/6ր䎓CA@( ;&A@r ۯ{}k`C=m/sj?MdVw'nŒCA@hjMWBć^槶=śok5<흼 K7 ܂  P]V~ ZAˁX  A k&,*.!  F _ 9A@A J`7auw ^ nd9A@A omKnºv[N9mrmH\ @ǠI5`4BOA@0z;Y+aA@A`D0&,sJ!. P'NR IAFv1  6m\2͈ew`mH.Fuw;VJcb ?i2淮xbmSrU A@$lZc˽ޣ(5mJYӫYKX|֯prR^DJ]]Sgר&ԇ*EnZ?oUsJ\)4&k)$ WՐT71O'_I߻+`#5#کCK,W,˻5C5D]4F<6&bZv#X'tՖ/r[~1 BSg XpOv~dHqHrV䓰wb-waQj(P`^չURA5J]uPGmc9X0V65Rq]R\O6%& ֗w-34[U"ï!'1 U`@h&/GmP 7#b ʍI.е?Gђhyni"1ڨV.$V5jB݉t[J(wc+84 5+-+CP'A fjok NE;?͠ ,f);8ȳwР/ZudiԶ} ^4↕u(RmH0JQ݅ȒZD*ZЧmɅ 5 wѼ( pt ĵWMcΑ>0^ۀՔ1XJp~vU:jrlZTwG7rEp,"44d mK"vQy˝E!@311փQ-г vSJ+l}# fR< a޾v7js:p2$iUPP. vOHm}d[#2d[60"jMjVR;=" z@{zj%([_]VOːf8!!lH4@7V .Fċ쳢6Xly'> M6CH ZaZd[km7 3` ai }&#:=x;9o~nk^; 80wك' v!Ay`*#@u A6ͺ IëJd*2q4j] [ -R%}  nD,\%(ٸĞ3Xp9) szd~VcȐRm7 h|7/ˮg2$!E kTuVҤmNRno9pďW r[NܣIw=;X_eC߀I4nʯÖEldEn~8s$PR$t('f"ɆyZ-rLCPDLr(gg!3F܌bb;R&ڋ%*yڣAO3 򧐪 />x˴/|A^GX!ԅ p-9LV΃NKkԪ>ձ8uHHh Q#q2BLsxςf,P A  Bm{;)_X ܣX P6ې"; h{N]b !FIDs_<A@78gs=k,"cA@Fh-&?Ξ{lj~'^Kw'CA@hjMW]<}j7޸ϽJkkuǞc7aY}3. % `vXV\REA@({/pK["RMA@(==  Ԅބ/*ubnd9A@A V^{r{QA@A@1(y֕l1  `Zl6`ɣ(]A@#G x AA@A O$g$NA@*W?m"`}%%5x'z~7~JFg*ղ)O1,e%fu/oSw3D 6/d(Ak9֯p6W5jYmE*kTs һ 7 [՜7P@ MkA;oݪrvuUF0k 76FXSJ+Y./ڦnpbM?_W,,$M\EJD=[L'dl\M"K -<"Iw;zZ0Aj-$J%}%KiJyʠ = prst}XZT jUkN,jQmx)"p=j[*EurlO-";aKxA-YH&3 Vu Ҏet*rخй\\_Ah&/GmllGyZ9R.(7ꃓF]FQj~#hId=ciZ ZZW=mqx}[/@6 W5Nۺ 8HS6aiQ6xPxtm"8P{"܀k+( ,1R- 84,ڥ<*F, r}"zxgj*`ɠRMJχe978Nᆕ崭֑B4Qb[eXbFݥ`v: )ѧ"jhC_%R3 MPqeAÁܑ>,F!'qUҘs̾6`5(s@ Z8$aWjJ&oVE%xw"8҄=+E).k| C1e=b8#ࢸ=0ζտ306gTTNўZjюԱ֮ >I #яs0ms-K$Dz8!Fɠiy.Ȝai3ӴM3X2hq1,iSok0;< 6(m;C;g{盷`uNI\{Olg}p^^Gd*)z\H Wp#SGGBFܚFS o:r_Gx 7= =d  pqLބí/4|$7auwV,9A@A t2?oWϘMX5 su=ZA@20־8e"UA@A-#A@D߂ewAf-A@AMXzg3IX2ZA@!|_3' ,BaYQZ,ܴFuf=SJY=c b i2fx#"SnyUn A@ zT'$?J:f W-K1k E*kTs H 7 [՜l M(Z 5 EU5:ů;jW\1aN}*Xfb_ޭy*/Wmpy$*1'k)pPom hԒ~WS\LHq\6 LRaD5"ǀmT 8Wuq;sEuX ?TPGm+T75Rq]R KxA-1!T MLv[|Ҏlt*rخ[fR+{{Mv$?߼ZqA42MGKp30".UO['FiGQ6x@7 mF¯ fyKD$$0"0@]AؘhKe3D3Ky)|:MYBH#KfA.FUXcIڰґKg]P"!M!8, & Ke3X2KŲ>rP ӯV̸%{ߐ^v$la*jMjV";fWg o^_hevt ͞D#J!!lH4ۀM7V!Tvd6 &g}y.d+AI  M67AWaz Zh]4$[Xlm\nG c Z H9ǓüWKaHC8JV'KeJu@#@[r`tlm:N5Pַ;ëT}Դh3WDYUUjE=^o ;)kSm :di6kZ ;j|4PmUJM .%hcB&n\P> UǓ`f(dve]tT{b⺋,tp3iCU(+$Sm2^H6i%YkFit% ,G5֊Rdz9 rn6Fkoh)iӪ;*(-<%k87rv#OvsU!o$mgi7a1B[Re5c-c`*c>|-h.Fg'cz@xn78bh[_ʊ8Hhw %0rYuDhQJ&eVd#;Edk\fAגbgޠU߃NtJi} XfA7*lqIBHk7rl(ɜTɐDT8rNbN ӊw`q,DZ/Fj5ኟuk؝L= sIH;Á 5W`ߎpMm7:C? km hsfwçՉX "tz-YSL)R\!իd XrPmH1d#W~JvAǪb7? ULw7#*lJHAf& ҄tEUZjN G+p,~H㿂+)-sN;  /Q fY̮[Մ(\mx Zj+/d; owƝ4U:4KT* l>5~UL\ 0V(/ݤoXVu2tM)mS@F5ݮ6z6uMMԙ&UwNWt>닟Mdcy.) ҄%M~Q \Z*504lT֩M'Ϙ&H[i:zT=wfXܨ6\:mS-gѣnTG,S'XDժnw]DR\IK)h2HyMF bMTGQT*rQhav.o]B:0oC_xxބmBttzکnoB&5Щ.Mwo_lԶ{\|R!Bn1;nTuj^KIYXU]'UFeAmZ\@E zUԕݩ /7IU׫JWt4 X=; v2pȕTR![)6җ6Q|C-AO0v_D 168y`dj&B8؊])-m([*1BCUr` Cg}/-' I 9n.8uG5ʤPrj~)*X:n-z@97K{4{(B Jj'x >88d [@ʺx|3J !4N Lҁ\3Lz I=[*RIiE ?WvD p1 pZz@^:h:uye(~{Y|^yy DDlY.W=dr%ǥYfA9BߪMH&$yUp%cUrE]Մh#5|"Y=VmXVpm%V0mm(zl-Mp%ۨ]>z>tZrZY`v>~4Q{> G/ZS #E)bdv:y=1@6/,v ܮs0p!/.jjU+]B<nY`_ YS1倗]Mj=֩k`b?ˤ卝 Wj?rڕ=`qMP*.QD c@]Þ)sln .av33怽hqv֔3J6dc+R &//el@cs4c,dNHp9)XR.8;mE\^8lGlVmzԦUw)ݞ`6^SЩ ˻at5jaCs[USڌϚ^,Ű]zl.U߮JܤbGu+p[kmU͉>[M 4YKд&_\=`V^`.znlVk>V,\c +a8ص@R{v(jqKPR$NM n<"qI%z^ʌQ :.ܫ6*NꝤq[΢ց'c!2loPJWj]Lͯ2zռǕQ)$^ N%dZbB`AV&;ܭGliGE6:G9 lm2L_x%poX؎3C3'ר!rc8ie,G?g5-ME]H׫O rjmnoQ+ڰπ^k#z/Tീ,7cA dٌLjo 2xAK$o,igRj(~:0Wݩ4Qb[e5 +]k שu6Rmdn]mGէ' v£d|ߧ"RzoaK.ngAFǵbT*r׾^ں׭6`5(s@ 8=`^z]UN*e`/['A=6Ֆ&YV.;Y3oxbվQ [K ǑװF+.cCDCly$x~Q Q0Ztdhqu?@)YM2pu[ kdyxKp _IajuUZ N'E=O}mA,fjcVUyz,SmC*uqYk^^t8Cl$8;|-B JBǖxRu ~2;yWmز.щv1n MuY)GPWs.'+x2օ mX) /qn+L[KEފTd |E# E%H;r`mP+ѲSQJeu Gxy p OINܣ])(+Ѽl0IMXG1ar̂4/n%oU.+<4<*•c GỈaZ,Nh^ňs@4\ün RL= sIH;Á 5W`ߎpMm7:C?pvE{¡'zk+xFa#V'A ЄAp>z\!6R2#WY\)kuU" t_3āb3wpiB܎]ZjN +!XC` '*̖3SZw`%'@V_ʇY̮[|(\m] 0Jv`RҌ H>jR p 0V=Y3Vq ~˸ uZA@1(A@\;q+  @So 2?q؄9܄~wl>A@Fgل5Ks " X@A@=Ғ @u"߈6az   _nKnZ6aGQ'aeXA@r&WlpJ}A@A '͉A@@ź   P 68j!-eA@A p: rJ0{_eKl6juƝ4j\VeV 0"Pdl(T$k_5TW* @.꿺F)MjS>IJvVVu`JTk)<W:vIյQqzlEY HM!?T7`v_D B N|,٪P9+6rx+J9`ZbKkT=YKUS}U~PHQ}áw=zuGUeRXAz95W,UTSqGaR @6ʨPC.Z ^qჃc~J@k+ZHYwrpF)!ܤ Lu&ST[o!gYE*)m(3TFAD4ȽLڭ}XqS G\`J>/IĈ\2oHY :˥W]luFfҚ6w kZZaUpB22Z Fk E%z:*&J~' \&Eϝ^'Mڻqv >tRχQKnW= ·X@-oCn - V!)eQr`4yEوy`}kJܮs0pK|M.U8\T0&u& k7FR IU:u Pvxgtp\RJGBN,}j@EYh/ ;~̭CUVaZ/ ̰6ۭZS(AؐQQ 8x֗26 ی9RL3 2O*rNd*֨Tp5x@+4v7Ui@]JwWa: Pq1Zjw`*/yibhȍ}WZY-?RA-y%ua/M6fẔr >t\lt%ܫES6uJZB]W_ pVav Ļ: _ Ĝ Y%V 0 ..wTTp17+xl_!F7F"wvꔵ "ܞԆ*Oӡ4/mفeK&8@ p[a܉@7,i$3/-Oh)K[o`'C w?p˖Ako~O>W}]wyM4$ pD#| 73k;[.xs9'w C:/ jh76$y#ᡞi_A:"`0RA@8gqH /M  T6֗z؄5# b! 45 a/Smfkj.?s2\A@*+PA@RK,<" *aG}iLA@ЋIX3߄u3'! \}k[pcO,|Rv\h@+ #`,TA@5bAA@B2-ХA@E$,C pղ \A`H<ogY]FRe9(eZvNnm2+rG+743/Mfy:y4u ̐"Z!Lτ T[M 4YK,\UCS'aGђ:̆iH z渝 ]F!0LߢViZKFP܌1/Ww$F#QK6ۢ`ɰl榙wRWudiҬ#hؒo/iWV,\ֱ/Gj#slS==$S"@xdr}J-"vӶBv jo\+ܑ>,F!'qUqJ+HnVS 2`16GyU;R[jQz2dAN)4d m"vQy˝7njߨC XdBXLV88#kW;æHLUyσ%fi 0f 32^oLL>H>la]p՚UAqz_.7J =@RP-Vlٓh![26d4 )azi*5$l.F쳢7łl!_pd8a>!Idv۸܎7,&L d1[s|-h.Fg'cz@xn78ώxs[_ʊ8Hhw %0rYHuDhQJ&eVO3?'Hj@mmMAfwt*nҭR[T.k)d*lq!~}ȋ/,HV\<.80> XYɜ8TɐDT8rNbN ӊw`q,DZ/Fj5ኟuk؝'Ac. ig8F\ q:NCg(g`ڽmmn Nu4:kA$[%PWBJɀlY&OHN>[(S6x!nM\S H/8zjOɽ.5YV\6}jK|H Rۣ4!`ǮH-q5'a-d9ܞiWl3E"/܁eTo@Y}j0wbvzx&dؔwEn1gi9w/--C?dN] Z$l\%*oWI6čCm~h!"+ c4]5#yJɀlYx`R);ku>.#jΐ`b=c.}}d@!#Fnh:ћiIuB;FҠ DCεK#ړUբ#`EA0F@?RMކ$&0? >#wsA@AA0 :xf\ Olg}p! 45]W`F7+0m:\A@CC4) PmWfX pWA XYhb Y>$FA@*B DU42TA@{ 9\: s-A@8 a8)%A@1&xͱ HADBhB>Y'_G-}t^A@#{fyvܹkbuʴ3fNA@A`l  {06Rb}(A@FhXg D3f-A@A`l ?8r]w<nX0i3!-D(A@ ._<ɂ < < < mΔkYyoօ  PD6aш/~obL&ӹgl `d H"w}  ]1<cS'Lzq5*lV.%뢎RUs 7rwA H*Z0krҮy>`#\̤d >0 U1@L^#N:=y.k+N|5@Cml2k^;O?ɒ혧ubJ(}b⋍+:(wr{py7bϛ1! pe[t7g@?DtTHDeSH ׀k4ܻ=m:>쳗,8ݯD$ͥ-Ya sKf,8];C _mim bM9i F_V={57Jh.)oGԼrf3ٴ2_R<2±X~<3GzL0 C9g4jXO%~YW:j?yy&(磜<{u:5kGkڠ{K~NOa˓t ؠ#rQ$ ^ ˕Al[ 3loF0o`sឫHw;PppnS ɟ9VK>F㙑H;c0h'GoiܼcutyV1ŜE'z@ڽ1ֱXMMV=<: QDDE"WM;@ߧV5Gsg fwܴYO~`Hݻ;ZbzADpQܑo ,XFɪF6Lp:qU)199r 2kĮj7v%io5&&w&Tgt=aϴE(0ڷ^KyA7LHAaCl*ײ'H$`"XЮ>1Y+$2 ڙtqu ܱAFh"QZՂ,3foI?`vk&3ck響Zuq`ʸFlO`ÏHЅPܖk5)B#0]xL(We<?Ysq9l&\ #fƂD]+&j9ut*y,:ODbԱHxLLѵbf^36jc[TA82 6OSbxx rt`VB2#h<8nxHq<͚5.?5ׯ~ƩCp>A|K sbhGeˎH 7Da-L TJRSvĺₔ e!\ܳ`]08 G(LLŜz'%R=ܛo|.B/Ze0wX }֚I|;|[ B@A@l7{c8ښCڝhIENDB`filius-2.9.4+ds/src/info/img/chat4.png000066400000000000000000000267101506574241500174510ustar00rootroot00000000000000PNG  IHDR+$6sRGB pHYsj-mIDATx^]kIN># r3GB L/F負Euw/#4B > aGBt H:ަզa^̗̈[zxYއ:B&jj%D"!P'uOmZ_y$WOٱ6H9k"%mYڮΘxv3_CZ|@@QIi( x?~'\3<~Cp@Z;2FC>,]61y8)qpc;O{Sqzn6,`ht^oi?9捖DŽ,l#+gLíL>?w]j0;GϞ/J>1Hx5.,G ;ں[7ߖ65X`7_.Ϟ,uk0:DϏz%xrZM˽7'ʑ'1;pꑽJVH[Gup%\8\D/'/px4mݹ$KwB><!,V7v}su4ҙ>S[m^He-{-|;FNfb|0Y]ȫd섀/hj ӮevbTa=%PS>t;zy*#XTI-5:q\{u[&G{\q)1!Ic }=ԹBf<:]:cO> l^>tp@\Fp ykpv%IAf>>.2,qSwmy8*Yx{r̞`se:cU⍳RԽ|Ƚ#@E@ۈoşN'_~ r=Bd@~+Cӵ{Y!7oZ)6:W0ˍ!=O)Dv/X f.1D%TM޵S O1To66bś+-%R$M'?zV}w?#gObsT *K!2eOjZk!E.]C4dJ Y yTac JaF366-n$ܮ XW7]BP6t @ؒn4([2ԍ"GJBN/5Cq]d#kuj=_xʍ!Gb7Z2aD7vEƶR mI(VPERdEX0u9:~ n,kun] YڦF%+6*1{z~ ,z%C(qxkÇo,L\Rde+ $y>?c`+Uh4q8QC7͖f|q.޺tQjHd4Fv_ga6z7l?޽6/w+uh&s3wnCb ֲ:#=(4IEIFQlK䌽zG`,P~ UQ(wX[7^]CBc0%1f5wQxKMx5pPz;cBZP:@S.]x/qhݫ>q$(%9u&&nZ"\ja=FJ%T@JC7 w:`LS.}ṌMi< #ͳtyMI< g=K*LO'iCSv^$ k~G&B{>\uN+3N;q^&M(7.˦ Y|30;*/ *p#i= #I.! c E 4!:}j9@ԉE`SۄE BN">0֑>kSZ+Fْ ^`yУG^<[L $tPɸF< ] PM%Pkx%-AF5Y!-sO 5KD2|={OyJ ]y|x͘{$[ryjW 5|3G`e޿y ZjfR+sA7gH|jzhۺӈ_Aw*Cm>\^(m%[^G: s{BhMr4^d;}h3qlf4$םk4}t-n2G|8Zx$7~ƞvQ^ۢ90€U-y' ՊpyG/г=R@'5g|p n5ZjM( czZEsqiuM!V4 |vS~'A =Rcђ!F< &˺ x XEǻkyS0s!tj!@k``# @P!*!@X 6*DB"0T !P JQ!B jJPV  $UCTB 4kVΔB\3m| 'Z<ꌃ9:g#Fη|T,kB bP4t2d9[v^ y%t͢2 y 3q#bk/F*5ߺ8b"/X˝TO[z\Gk`ի[FFG)"=,r-4]>#J/}0gT.'M`L'0-vV9jTD8G'H%$ id;9WAjTYXKk{g-ZV26O Xqŷ/ 6BQ"Kf-Ueb_O6}13)*=T}ƴf/MSƌ e[͓?[<>(XZ|BޭMR ~BxrY&M,rb 2&eD49/;6Y5 L}xdž<]ecP;6Y(SE ,|񧿃?ɗ=qh5Pt t#| S3ݱrVS2GEбa2#@&ۃuAӝ}j/w&ԧGqkp*L ~55!0_t[;=zkZ\*!سƓ_Cq;"T{zw_=?}މ5q=f-o o5aI@/>%"Y < '1K-Ϗ;[.b]KZ߅21wW((5x/lxLxm>/^/wl=5,RlFF>dH8!`F| (z(|IC W?M0 ۥd8?EY^"UuǹZә?vnG?+XNU|$"##[wX:jQ[)[9ԩ IL7(!#0 @lhC"}cJ=jM-}PoLGMB Z\ϟkdv8W9i`}ܜ%3T'؈jSB `1|B0nwT:F #҃GmQeL2DHPJ 疉#U R%6|M1XICW(Ka7i[\KyUop1Cy {!i='6LF\:a{ S)ﱣ.uD~{zrhK5dw cxn ws^Zr/A<$CD3>W{LN  b Ҙ!:1ν\ak)S.+i۩.2hۺ.LKhs ]nqc ]OBLE2:YB42tF׬ni<[:_+1tej;>,6eKbgZV}WDoEX [ |Vbr&Ɵ *&PlgcΈGG`$KonxضAC,ir@ vFDY<IEy؟QMZM 7N5;H љbk\ʻu` e&ce%4!" ]@O B F(c;Pvg1"@㨐OA";cM=G|kiP8*Sw#5ґ00MG~˛f gwBe\!Zi/[Z[sV<$vkL ]I,yu\a2r=[=Ы5&q~] vŌEȊս4X#ȴZA 5;+wg{{G\e/"2Q "<ڒ%!ed¿`.vliji_'G`e& pzdcxgb |[,WvwvTuu->* D@D9 4Xޞ읜N볲YGԲ"e|pO3aie.ʟ-Q}i>X$lztL@@I˶~ OĞ-=_* ,,܅nl*[K]-%7\"GNu "аЖuPvw1 @(E"cO=F|.{y P0 CwSc@ #rfVAχ[ InNeKn!֏5Fo+B&Ucsb@`=ܢ>rp@K[ws*]W]xO慝t&G6HB>]_ AWgLshm_1ƓYzq?AoVW~2Vrvm0J!Ea]*0tՊzY%P%+6>W@[67Yߖox'+1y8tRQ5CωL]RJ\b0Y$X!Hԃ 3z֬?>`*j,st.FÜ{黮#%X, YI88x _("pgi,~"|IU:@khH]or/4zq+n$@P/һXs ]ãAhyu4Vw/҃|q#7ɳ%֪:Ǩ'_(@GJ9ޙ"ךyZZw->ib/0 $&?1BRp{hzF`ĸ[50G̖>i8uZ1Ne[5#Kj'Pud_@"D^,䟅~/w}?yI4L B^A#4mzM21Wt?$HdIt)"#3M6% _8Ut3Tעu_ȚᄏM!50# TQRU[#U9_YVuWw!@f G/ z5dU.N&"B~79Owg [IS&B`1BϰB34٫d9d$*HHie9jut2({x@kUx2BX@L$bRQ!ta‚dOWvlO{gHB0,=yv챏IͤkVcjoɞ87Nys2j!MjƩ$wS NJFmS$2wB<G"_355q(;>5#@XPG 4".')KC#N{GS'l.Dq14Ѱo &1Xdcm13Rq94k`% M0MA*u+nr9^M-̘eWy\XP~awos "#D`P<;,;XKr}}sUqrDk#pk-+,4=y\`:T"F]X83Ԡ.H"PJ>njk6t/8Fs#1 lq8Gm*ɣV)O0w7dR޺'[,΍݇ Lw(,[8Y[[pWPGӁ:p"$@ }``\8 !!@HӁ:ѧ @@P։>MP D Ls ]^qm0y"rQM d-sH"e IJ4#OI,)S;G,r; OuS_޿N$$kբ0UНoG`%`w}Ĺ646o1JXjjN hS[I:N67{Sk% Kr"{B7əT\qDdJ HB[)upG"+$#@SpG"+$#@SpG"+$#@SpG"+$#Gj#d<;mR^WuVP |ff8@2uD w D 66cO\̀K ` :owV|wxϛuxQ ;KF8Cݒo˔ +̖sqq/Z` 笥t 5&er.6K+ȐnqaaL,!>gEn ͏)\7+&_`AGj 3hpa\De0VQ)9Ӄ16vynQŎ@džfr |Sjp1#NjFxPrepgɞuu!T`ȕG Έ@D24D״M_vMc=;9S$uΈ9s"Qn_bCiB_lRbG"0"ڍE`Ǘz;nb@A>!gmqaV2@Ždҷ TX2w27ya];UIT5aɕދ(JWngizt {g\;&}HkJZg-\1 Xl\'1Co~ >`C(~'B.%kp]J ǎnN,(~c M;7ӋK&a]M&M[> F^Y֗kfulf8KQK#;+śɋj2L6NGg7G×:EK'#ye$8$r11{XOK^ܠ;W]gsxlgкېxZ! xw%NeԮJm71ʘ_ FPNiRܛuN8CVE]np_r_!( >еACJSZ %剹," Ycܒ8.k4*d9&@.xEd{3'UX ]4Һk+47LMY?܈4K?z!mq^@ rr 0tuDa2# -o nK>uӎºZw0Z|P[, Yp)pԄ.0?k<`II\4q\8ɿ?:]ZZJj;>yFpWD$eBK7ݓۥ;+g_7 /1*c [Q)뗼֟-.SJ^2?\O1/ZC%Ͳ]Iiv׿^/$Z~4e` ~e1nL;r*cF=$!89F5@"LhfdPF;4X'!ICTSh!u`B?Xf^}jם[a<IYu5"(;;9mM6WGMBU ;zuy;JxbXqʭ̷{yΞvyzڼYTuښ'r2}?>y?rg/*:묒Y =oki`gm*J‚/_p:yPg G{F#3մ)%YS(}˖-ύ9G{D]=}ҥK۵p?z}fq*n-[n޼k߭p-,---**oi׮f6mu১ϸե9*59zhNV^f͚ǎ%w;GӞEeGILLuW^nwrrUlVnnsx˝L5b(eM3gΜ5kOLN,1b"4}?ڴiS2|j߾xҘWKԤ {ZRMu2PsJI 9ү;0S_uǡrVߗe6E%V>U%U֭[?*KIIIo90s9 ?s'(#bD[ls=aیVmNlz=<}׷8ګw2*MU^G'M:iI͜f!#.C^`ޗv.ќzd<b\ =)>3rƎ 0`T-6IifķϖƈoG75^xW;uGhmǵ4!5p{LS=*Lr7Wbğy!qt@|Ka0 ϶]ugt|0$ֈ<Y0DŽm,Ýgsӓ7pDԷ&iyoz ͜t(Aϣ}63[&2mMh{‘[#~ uGPc3 7] Ͱ'뎻hRiطo㯼^\fh)0!RQN "F3SA!t=OOX1yݩ;#5^1("1iuh-ItgΜIO<6~:g7O4pF){tWFA~$\@LII;v$if(ET$4Q^z)77rٓnwܑ/Pol.#1{':um:uDbbbo[ZZ~g6Zr1F)I0_l٘]A0 _tI^||<}aUHG=z44iI-М^zASO;Mʋ|Ԛ&kG-I0пo)"h^֭v֭[x 55u[o}G5rj1! B222*zꩵkN8SO=F&?]<#sOtTt޶m$|AڦR,YҮ]͛C={4k֌!WԴn iOI}[?c[n^T.SN9eƌ=آa,x؛6m%w}wbb">|IR0d:GSIعsg0tTϚ5 +'7RK}״iSWMZbN`ݺu4Yo~뭷@X8m<NF]׵_ٳwo|dz 4yx _Q`y ^E˓qfq>\X࢞{7bGE!Za!^Ay o?|"]vJASğ`ĵejw"%萗OQ1ܹI<),ޢEg7St]ş]%&EHV/;]"mΜL2坵DНz1k5[(;t皂myFQ&HoW!iV wRfɏQr~!1 DqzD!nbm1̊@ʞ^uc]ai.>*&b+? VE!N>kiK[Pe.Wv{キ7m=/ŎH%\Ť(Yk8jFzDzR273g'ߦZ() [x=:> Zo(>EYQ W[aI  V|ڷo_vmcfV~ ,s4V"cy=ȐHO{'a`OOq&|ٌ8,(3 fj6c54S ʘ>~ Vy4TO~?===Z^^^߾};oC~eph06p KߒL,+#;,3BNNNaaajjĉbHFFFӦMn:wK@S|B5$x/1Mj =y"T5.3ɓ';vf*^EpOt{u.f-]DW4g.-SVzOw١C5k,]m [?̚BҷnKuenC +#5>,̋9$.Gu~3f̀v=33v}N;mڴ .>hMxg/mkQ![E g7m*Lir-['(O? %4iR%"d͚5n@gϞeѣSNj!ZPP@۴ip88@ ))).kذa+'DVl~Duk`إrCP>At$h>8axΝA ҈b0~Wx ̠~m |gjA9.gґIקk&%%A8Y e޽p i`BBR)@=Aݻw+!Јuǎ?  _7iM4B3r?)y<ò"$}m۶!PZ‘1jʕM4AhZn%}/vR-G_>߼peq-Z4c`H>zhEUX ߩ̐]xᅫW۷7Pe˖9..s=(Q1\GpZR64wR<^)!6x`&''_ը&eF ۀ$(R(-X?_zꩧ(h h^)=E_P#** =j7 }H)J7oIȓW^y ?0r %#Jc@>wQ5t\h\G`IU@!+hܹ(waGqWsŊӦM# a$JKKxKRD, V:TAq9VVb - C :]v4AW]૲%$▦NJ1c|P"x\0lsUJv=zhmVPeJbSO)<1}J7=Xx49j "6] C̪v?O,/}*;0|3jԨw}KB5`FMGQj_]m?IKGUXB@ :TƢj\Tv56lfVΌ6!ln򵊷@V*spΥ8MCÿ-@YHyP}Tħ:C+mI+6]'S0(OngVk?+($4/o8@ ֲXT _TԲOXTj4w I|M7TW5PHMM. '9rd͛765.z[ի_x=[u}/M߫w w5+z[Ԏ;Rs 2[{:cǎ!:>iÆ n ؚ&oH˼[|ሌٳ' * ؚv>}-7/̱quä@y 'wO]XлwmIE0nk , xNiҜU#)%sWנaͦm&0I'ss}˞+z⺴x9t2w ef3b߷HRY>ۏ~q&x<_:uR|9ws'l3!C42-!%UPPSc$a7xBpl݊IBjSS.D uZ<8޶|A_Aۜ3,]%)ܦhuW$PeY}rCW+tG+ctZxk& rj_X(nwu7LZp|Hl+I($hYɤ86r4[e RJHؿ͇rR+uxcRpU{h5&p X6'2?)eRaa@0lrT 'hHäx],3"c&ȚvviwD%"'U[,#:) +kb=Hۙx k%h0f*`~Є9VVcFjX#JD f߫Gz x`0예knlٲ}4! <\wb=Ig~ VE%nچ6lX6,;LX Ri"=-\miX%G% {ALEbG+~7-n-+`ir8AB3"S\Ti۝G3 4qZ @b&])D-y/^3T8"A؁HqrpÍRg#*Q́kС_WV'47u!BẺ d9gMm)+"=4b\!4WH_bv9@bwvq]-ncm6U>|8??ܸqGzyn~[IB Q5GșXo'(Sԥp]f-5a[ywݥ'Ͽ% !*FY@ '2H-tҠ ++kǎފvg¼9eN)ZلPm/tnla koJ Q^JN%קj3ȑ#~֬YqTT}׵k믿ZՁ.*(6ZۃFNB1;e,>އ- (x}SO=uر۷mO샢r F^)vBtau3Kl+$[vv67O?tNNN۶m뮻s_tyVN#%wYܣY]k;a{N={cǎeff[oLNN8͚5{ksss]R3sAK!N u8JcQ_5R`-[_|Ř[n34ʩ[S6c)pÎQj# *im= %4i䭷ޢ@]vu >p7ߜ%TPtCBdՖ$OVmAWa. 6[nvڡO fa K.3fL]qܮK҅ &DwOQT!$Gu0eׯ_v-ڹs'J ߿-[0@oܹnjLܹsZZZtt4޽}B0:ڀ!VdgϞ}y5Y R򵦷kv)ޗQ(~QQFƤ4Yux@U'U\`Ĉ >(~vX{h;l=PEpd#p{/lݺ׷vFtQ.T`{}1ٴi7u eGWDEMvwF2 T\.G($ZIqFްa~GxvgP8q=z^)L=<% زޛco֚Lj)+_g㧄!PO*U n Wc ?FB@{ABݻo޼ 2dN !{OBn?YJʷtewW0\O8Ծ;W^D٩lYc/9RPg =@x4hР۷SOd k<:7Lƌp!P_R6~a7tBO?a q  V|s=ܱcCiPvԠ:Rf܀o=sL G(2X\̣ Euw*c|ս_{M IRxGv ڂ@Q:@۶mh0 ͛72sE o&",' $H훨vC-77W^y5\rʏ>iDN9 VKl4PmYx饗ނGJhF)V)RzI KH' )x RD(_@1*k֬Q*F}o_=mZ*p&Q+bR%AbXjj*O7yd8~XgϞjEbMaСCyΡIygR<',ڋYj#3n+l) mm8iBvƻjGADl ?»LzpH+o5ʂJ"CI&>N"lX{O,P=/rE@䎥ѫED ]3& =p\{@y$ #0m4L--Ů`$=0ʚIW0 kDAN-eUGM#CCP@7EyӊB[GM<*>`al4 @C _z8"-"{~ ">'N|Նs"XBvĝ< yŝ+rUѐ;,Dtِ#*GF- mއ*B6QN3PsPA]8T>VUV1ǖ :oIФ7LrW4{j(^'ˑf,Ąh5!G* .R V?q e"WWkVmR!DdEETU5<ܨO o0*bUK nÇEՑ ŦVe˖6~`)k(ƿ( ) uWBx)` YUŘ3 BJJF6,]kΝP͐6rFKfs"g}6Lq (ZȔV<ƥSQ~%1fJF ^XkYW #V' T59x0@]J ')P2`(s=I-ꦠ7 EL F/B &Qx6.:^B틪Jb=/ OjܶUGe!LjP6N̎e>WPz23ԬmJv pÈ$~aVSlҤ# . `aKOw:c jРA wF=Yȑ#|͢Ev;"]}!СCrrrYRt);;{ǎG#Ss |unٵ۵{CB۶m媼Fha۽xa%2׿;DC붿uWFZl:B/]Zx1׹[ QbSHӶ̢>^ "6K>Zپ}* V\SZ%uB Cn8l4OW0cf+p}xteZJ "SsrD) 5oɍ'ѥM%]!cczݽ{:ĪO: {+5}^@~ᔰBBYGbu]k1اL eb+RȓCwUQԪXOD߿-Yxڕ.Ю873uc؝JIDg˘J |,(p p*v;%dơLR' a GͿAU=ҍ0?7|lu" HD(rJ |j9h7Q?)YX7gI&U""3ǚF8ary4Sxt2bh)|^]0t#!:<OȒ|*2B|]9 !BB9#bPڢj,]wS&O%2ձD(]L_ !4Gs 1g"~۹H!.|z6a+?tnNM7ȑ#W\q֭[cb~~yfW 5Sr\'ՒF.'=,+ d$R9彘q5?1@vUߪ138رcn~hԸqڮ],Y )͸,`k$^^)>:sA5Kt5M%u{UXJ 6E2m٥p9.4!>\@nn.A>bN?"{;'޽{ٲ5 ~qw+AOa [S :\~xR4E7dAtĈR5-yv/((INNm߾?O֭B v[DG3557}*X$܋%&IߦC;)I;=:&;l&)dNɀX2BhGMv4CGOJ],nxI1ҕ!avwhPPχ8={̟?"zeffKdPG֭[|) 9\z0H߬YsI:Hwp=ߧd4MPBlv9x ?)C}krhv ؂LRSxé}IhJA1EU8W_w常8Ç(dhٲ% ANP9u1#)311qРE@GkocVUV( kwT<564iF}b R08,kZ,˭]4,//OؤIUV_^W4څIJJB[,IT 攠NKz=w>[ o#`7I{3ɋj7仸vg ⷄX-ZD|A0^=zpݘ1DmCM02Pv׷Q9ᣝsLMHm4hPwp(M߸j& %ZZ(c-Yz:݀jB2 @h,)SN"f)'|2}teΓ@9xZᖊu(MӠזqc*Sh꾚 mSſ^ktJzHG%^ZCg&+MdFG0ԣgϞDTȏl ~ R,Z؋Ыe!١]W~~ښ6ˣ[lͥHY 935 ):u & UjK9 42ٴiR!eAHL[n͛7߿P&Wu8pl<IpI8p t6mD?c?6oI}gtMf%CKRFTᔰ.hĈM[lOa:+BF8a+Ƀ`Gh|$' ="4"XCQz(9s E޽;Wz'O y}՚u$m#AWn)N]U.f W"K,AUTP f;C_h/P!ʘ++#nUPrb %)=h /ʕ+.\hJ믿K.d8aCE'ȑWnizӵv0(^azp d~O5 ` aɆ;@pyABwRuRS"9ٸqڛN2rD%P*43-- QIA BQďGuQ238|A/D"=}u1Dk.a0my9}kO Jt ̐uh$.F"=^~Ps̙X\"]L5I˱.5I4M T Ҡ0hK^(7 AWnS|I2Ұ+x-nFj~HTgs;s_ifLy׌>(YG2X(yq 0@aꫯ 6p;uċٳ< ƘT 8+!ܻw/F]Gy 1ej.岰oĉ0e]6h 7 Դw=o҂9N153Zv~%D@ KiiED3fW\R+jA=l-GHGQC,ʇM%c`ҤIH]]v%zߟswq7nձs zU#\kjO?͘1S lpѣgAjF76S++UZB*VEPEDR&;%#k@G`zM:1N H?y0NjPIIG}Pբ:2նOڵkyT*8{"ڱc D݈)Gp#Jܹs1dq⎩_Ll(^F1>&% xrld J:n !(9(rrhU"PONNF"RT]o#̈́P}(H_0WZ`hԨMxWE}4:zGԫF,Pt@ ̻={$8,|9X!: Q_`/+v06$@vDK/%L@B"|rD5l|+D 2dm۶>Q$3@*7sT]&1> 4i%:"ߨ5&(%!D_GX TXK""*BD~5CG=%ZJPo`!6tOE}c X)sU#T0JA02DS),tٲe!- I͛o>xyOo)њ'INxRÿT Ԙ8jb1EkȠ|HPQL݆6)zJjzu]#DrFh 74WM=>|1cJ3gU[5t+'`E[5m^Jmx)) KT@u3Qp&J"9HzxG+@fTs KPR"AˊCXjiRPw<㷣GcI*>"jڎ`jp!ܳv?j[uh!1ҥK`#t5@8R0%{Y!'/*"Q*C'Ӊx;`СC$&pP:UH_O9Ы&c',,W k#Fꪫ4Hܹsgt#UXP|Jj ֭ߎB:(R4Nj;,[Hs"@I&xz2B%|BZg-9$RADgϞ ( Sݵ^K!թ1wϾ.dI"% Jy4i{T/T.S$ڄ}`% j2 0'򤳺'j$+D+R!Ch<ўatTN_}V<*S9PKSjzV^ jfʪ%@j`La"FpYnMaSъ aN|{˺ZvP#JDT D cWJ+&U풒`I>+fաD`'*()y`͚5ȃw*|"ժ6z٢Z(T+O2BC)ݻ1+*RB觚 &AJwk׮={ 'Qg> {*B)8aSW5y a(҅^ Φo3NB(Ee#X^n԰0vQߡ[YҒ{m^L~(  ?xx5]$WWY[[A"4U,N,p8YR8.8IE,iE>vQߡ[񮷦Kp+L,H| 9ޓogJ.H2E, iqQo]|煩CߡG_m~fb=@-YqgcqsogU%@L:ciTF؊teR\ҋ|УJ/BiUd7ZZ$~^d$nӯvUxfk\YޛM;&! y\~R[qִ|C=;Hҵ+VWSeP[F6<3Y}rnK/5ҋ|УJ/B4yWX35݂463n8$ѵf;0bEL7 qx'Wu\ӡ_m~QϝoimKZ{2V#I$P2:1 ~^ti&Ƨa&RgLh!$v7&.o~Nk QU0?;o(ҋ|Ь!]yVڗʎ~SON/B8 )ߡG_m~fbQʃӋ|УN/B1F(Aii>vQߡYr4;o(Ӌ|ЬQ9Prqϝi>vVf(9M?8 ?;o+3bT_m~qϝ1G*SON/B8 )ߡG_m~fbQʃӋ|УN/B1F(Aii>vQ[ߡYrZߡG_k~fQʇiiE>QZߡYr4K_(ҋ}-ЬQ9Pr_QϥiE>Vn(9M/( ?K_+7bT_k~Qϥ1G*SKJ/B( )ZߡG_k~fQʃҋ}-УJ/BqF(AiiE>WMDtUJ̇쌌g^^ow0ڹ12eíCJMm5(k5To-$.{"o|z)rpq'8,sgnp3wȷFi}oWKMKnSvTzy=7yFm4Rҙ+ͱyaq*-r׏:1Pr5{_^KOm{oͅҺh1<:uݖH㄂˷gʣd^}&42X*20iW\o}iڻW^pmٝU{pfuU'~],xnn݆7$$agqּO>'C,Deb^(II@HPmmP:+;)C-Gw>)VTK;澻 'UH~Q: *7 *7t_N\Ӗ.qU3ѵy6kqbmVi2~nƖW 6m:{t}۴eqzWH!4H!5mCߕW3Q ݔZ:(> o}FbhӪ#2FpަHy?Ɨ%op?2UUBd|ps⿷oGm*05M4(FEV^,cX֯ꚄW'>ЖFc$VP碜ƹ?o7׭'oRw_uy ߍZ47mrouDz7.ȱi.u>!?kbk90Jmm7(S??ߗ^4cŦ//Žߟ#QҠ\ңElVB_ A9;=b{;4ቐ|m3[oA3ۿ7ףssMZom 9yyUou<)iF^}BKS PG.-NT6ں\Ii?`@TZ<Gʮw+y< G')VPC0I%4d©99rtQ}{=RsGsT?NO>d©99rtQ'E^2aT9?(ӓa_hjiGװ̃U/?4y?5CNO?A*<'ErtQ? KOOPӓ9?({ '4koc[[qu8Tx?xq^e'Ei_Q$62]A+:eI?LUz5RծlXe:wG]mKw+oi5]wn>fY+y7},9V'%}촟[^ėH qXHP1sֲRnNik[[>e`UCt ;Gcyine#z>!'- 1k'H4ϽEYw-|iYi}Gn%OD1h$Ҭe(j3JG$P|uۤ73 W13gVE|shգV*߻zi{NiO FtAzz%wZuMb] wY=qp0?AG{*+W5%k6(PpLXLSVee{H8qKK#Km; 6; 6Ѷ.,+Rmm; 6m; {ivԛhEqhRmNrVxG ;k~'xa5#fB/|:3^-%%sʫNVm?m.7m?m.vKm.,+OKqhRbm;6Ѷr=jMhG6ԛivӰGmI#K.,+mI&)va\mjLQ1Fړmh3m6Ѷ{hRmX.GmI1ErTѥ?OtSx~5ZG۽3K[WPM@*k[;b6'o^e,ޱ=[:^'/sCf;_κ [RVȲ\^muH3ZWĿ" B 0>A?k`%,u'sMY(uDQf8z^LugS:[%bc:ƺ ^]'9cS淆o}?\&~] jGR.i Km]3m.~)qN¹]R❅qiva\;bT+Km⟶m;K~\Qa\jLQr=jLQ{ivԘm⟶X.GTivӰ\bF(G1RmE{hRmm ⍵&6Ӱ\m.~]XW#F*MbF*LQ,#1F,#&ړmh\bF(G1Rmm &(+F*LQ,#&*LQ,;&(+F*LRbF*LRbr+ѝa./ZP[9좒Hfݖ\c` Y`/NJ[$5f%vv`8iKTߗ7fޏ֢ -..³60Ww%9T޵r֫xEU#'9=z ׋t9,nK켖j<妜4C>MҼO-9Q$4@g$|xҫV7J/᯼14r4wb2n; YtMR #;cc^xCӯGQA5bHYCrB#t+CNSsm_D$Lȭ*9 5J_̗J_yͥŕ[A,7G*ayj1B ?_Tj1 )FmOK3m.~]Mbm?m.vmOKqiva\f]a\f]iW#K.)W#.); 6ѶF,\Sa\N¸ʹm6ѶKr=m6r=m6a\mjMb`6ԘEr=m1F,#Fړmh\mjMi.G&6a\mjMmr=m6ѶII,;mI1Jr=br=b1Ex&)1ErYŒWwU 4~ʫu34Mu34\vL3IaLIa34q~Mԛ\v4ԛ\v$Iu&.;nQu+uGLҸI{7Qqؓ4nRnp&hQEbLњ4fę5h7RfbLњ>gދ3Fj<њ.%FhX4f&isQf4\,I3Lp&h34fęG3N&isQ4$K.+f5isN&h34qX~i٨sN晚\Ӹ?4RafNN1;4Ӹfij&iiӳLCfJ 1N%ՃAnFaQtEEYU,-V׼3u&kRnIa3u&i\vu3u&i\vu34\vu3u&W{3J&Mh+RnnJ&i3L&hIL3IW fu34&i7S3Fi\,?u<Ѻ7QF.;n3uX~isQ4\,Iy fqX4f.i,I3Q4\V\y;5isEa34fęG\ӸI\y;ę4a34qX4Kw3KfisN~isLaq-0p&óKm-12LC˜)EP pHNp!AnV m5q5>m-EM[@*]]ZML&a7S7Rnr?44i8S*c-6SQMSE-Pu0S2G u2*c8S)>SE-R%N$pS8S%NBNN$p hS?gt֑hp5BSs_QfSOJ+A婻\*SwS7Rfʰԛqؓu74n?u&i3JX~i7S3IbMԛ=ԛI5hJaQu+ě7T{EbLnfu fh+3Fj<њ.$.j<њw f5h3Fi4$.j<拊KfisN晚\Ӹ?4f4+;58w4Pi>ZXx4ꎜ)<Zm: u0Sp pJ)(HL!œ)M>SE8URJQTHNuR%hU! )š)™,ZpӅQ#8SE8U"G pp?X,!i4}L+/7TG/ʫf[OV AL&róIfi3J7Rfʰԅn;nf4;Fu&W{3J&i7S3I.;nL&W7QaQҸXufӸXuK.\y4\V$.j<ę4a34qX~isL.iV$.iӹ6Km-1-0S1e8U pS>SE-PNNNQTҊm8U8R ZK)u2E m8U ZAJ*c8RRHNuR%hU"EH)И pTH)8U( AND+ş?#[b|[ C]5qhw rOUsV//ʩ^61^M{~iKW%,SE-2XQMT)Nbx x$pJ)JQTS*#- DML-8R pDE8RRKNp$pTbu HH)j,p hU"X)8U"XKH)© +ſ?#[[ C]5q4>=IqOTzİUDxX?Sݤ;4iRnirtkQCh Al &MMKe$IMKe$IMK) &M)ԲiR()E )h4 p pI pN")P8 l8 T-H< XJ(lZP(RD@*%SH pN"[)P8 l@*l@*%S (R$@*`8 T bRDS pIN)U$K`8 T-(RDSRDS%@*$@*%SH pNN)U$C`< 5ďڏk|a ?h?>όQӮl"0շ *x zWOfw9y](P)K?B/hi#Fo(/CF29r#/P>*tf.T| _ѿn!}E9(?tŠ?i_7K /?}E?k!{?Œ7iO7G(u{i?K?O/i?O7^E?o1{ CĿ?8|'i?O7^E?L_WI?O”'_O7^EX}Z•_O"JSA}ZE?W1/E{}jLS>"/SMx4'^E?S<@|iO"LSQ}N_A?g 3WG*TAd)P?)v}JE/*M{~'^E]/^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>^m ¿R) ¿R)O=>I"X[C%xHB1CwLMiZn59R2{50Ũ:F[Ƽ{Ogk[ . @ ns:~UՂb)׌$gV%?filius-2.9.4+ds/src/info/img/echoserver.png000066400000000000000000000056241506574241500206140ustar00rootroot00000000000000PNG  IHDR@@iq [IDATx xWc) ADRtH`R)ѱjK*5*A5=ˆZJ[HPbɌѩx{Ndtyw{9nn*DEEDEEOhㅴ~ufJʈʊKJQŲA? oѢŮ~o"U=-zJT^+7h*ToM’152C .] ϩUZƍ/ ny=-~#Es'/Igc#uivSnBؤh #kX E!FWRܲUT9 e }ym߷ bSa9t Zt_@gxT{NbTկ_% !Q~~~crU ޠq VX  RM_} ]FF_ EaR2_xtBVoçqx6y?XlٲM4!U #]'m,```0~@.ޙފ^t 5+?*MHADZz`:ûAH*WܝSJ*VߟnPV5;&l|d= ^slް4h_Ɵ ?χF5Gʾ7㳨;(^A3r8%mEVf͖qoݭ(r@O@(? s pIbOwe0icVXf0 &4PCTxf1    Tj?bG9-7ΥNxv(bCH;l[o@;@qv~pf!1NH,V^-[dĉ|2\NE6X+Q.C v 0;jdPfS[V1c/_(,\3g"""0k,̜9SC-]4CVpV~;.IJ<#9(gK#bl}v@Y$DD ػw]۷c͈Ś5krJ ;w.fϞLw!-? 5z5؇N7DMrn?>/^؞={k.lݺ7nĺuT:^:Diذ!xVEC)pA y9X2C|`epɒ%* x v}dlK23JLLo)14a8Eh9hѢکSpܹLvJ8dsaÆ ͛~:\=zTU4))I%1VrXv-VZߴ[Ž͜@uJ^VBHMMoRyɓ vniy+$mKak!iq$Fc1M#=#( n [[1GǝIj<ƭ5L+WRssSDɗAs#-C\`@dW/>Cؚ8IVX|P|OOww` F1  PJ ]V~4O{k~ރXp0_r4j`+I(&I93]anZ\kZDE0`Zך<@c>ݱcGO*nCpn7oS'y {̵o..............nuqÇհ/+oƍ7pE5?C?@+Vax&ye'0Yj@3̈oNص;@hۺv|}c}ضK{eܵwCر ,Z0\_7g1rGI(l ‡{6u= H#EGO1K'\f \!vrC0#>}?y0GA?}F0A͋alG8mb< Vy(2↩t1Ad`hz1|su{3sa nl.7;B Q !H} _p mÑCXS Y(m[ˑo>LMǰď%G 9{'k{x~^[/.27`ϝN'ǀ|BbPpy0ig;c Kln$)[j$}W[ abdEc^Æ a_rX>?ZEE^@ ϾϮn!F d"p#8o`/+^@/NV@Ƨ_yع'=<H۽6.蕸}12 .Zvѣ-5b a#C9\rՠ,xW3nn "l6XVa<3{ĞO ?YGF|bRٶj|uV] I߫vsjDW[w-\ :!q0a#X;w,>N{&A<uk- >8fcp: >Y#rR dne᧛Moxk>ˑ-As/g.!111t\\zUUUFMM T*ARA*BRBUBc q9(\nXV:HJvhhVhl&(hh}>C<ĝKq)+g/BFy'\DhbX+CrF `,ޟ+d8@Em#9́}ab:L\pA{#77鬬,>OoApyP)P(q\,r ں$HϿ*bC%ù5TNr/,=O[7`ΛױzB|j1Hn{VR|+rz G8|#6mE$|Br̙3ŦM^dBvvkE((.B>+*P\XH%J4#.-bEW/> nBy.RHÎ/8aRwbӑwlR3ؙR PJu 䞂At:Arbjz@qvb8<)<"̽~_ҰW3)ژ*"KF$Ytч tpeTbtxK c 9etFZŀɉ?#Dgvkm$؝ӑY3$Þ 󅳀^m X,6h zE}ݍgN ǞA88{*bm/N`BK)%z<=18a VBmB;Qˠ5ZQ\HrYFah]]0]I0 9pjFLu]ݔ7 t-1]$Ĭ)y؍kвu=2|/f/GsEMX)3V]#1mg6~QAq1gURtJQP"3zGGEp Hll,=*I$%Ǐ#3; Wa"IȂhFWyEpcTJ9ya|4m=v+Q<} .݌@Ej?ZI|>- TPz2`"%'3W mG[kR:|s ES(HJJ|p9E0>cAA(-/CrF:f"t(++qy"4qExgb|k@2d'unבfPK[/W._O'Ä"db8|Oxg8QbM:"@4^-SC+l'š&(%fɎݝ6h~~SA؉V?kP{b^zbɢaᤙxgὉ/EPv>j6` ƒ)[MDjr$l]*}ȨAp 5dޚ2ޘ"ޘ=k]U ;LUJ5&Y1+9f?WA2Rٝ6zLT0}{j*x;8[bTvb㧻^gRj~};ѓo> ?,\@xZz)ܽi[48-Qq0bhԂӱ`d,;EkHZk1\["V6B&BU wh4Dp9 :?eoF]}&c:fG+7\RVS(kqv̜2N?×wJet#(+[ٽL6': z Y(Lh'WNyӱ杅p\mnDT cUe"FY >|XWQQR L|kא(ɠohh@iIHv͂,:z](.$-ǪՏcʤ0m=xk8ߵ qTrA0YreDV!xч1yӘD,[2V@`P*QE_q9hood ((1LnOUH_VL, J~~>*q dQ&p )JjyBG!X }+F;4"߹j}nEB}$yxb17^6@}A@ڦV*PZA̴Qp{8<&_c5DeUuY>{P & ]r;T?4(8ˢƄq8Mp >`u^0 2K^u+p%S"37OıS@fzBLr8SRRDcXE">sƁp);P7w"%HKA|~W( #5 (>է"Z"sx,3:=ч~/ gOipKivFhMY+ҵIFa$zYYP},{,~ 𙹡P% GE$چiq(nk+p9y#N{ȸ@J\Y! EH]RU;i|~'}^8)v{PN\2FT!#Z~@O>b#s$Bw +A+V2l@H\9a wx;0|ÀP e<(K`m y"=ҟ OYL x}} ȅE&`"Q$޸F9GWx|rK )RC}30}( @ɱȌ~fB&hϦ }Xm-a6pmpZMX[u~F #q>gi /Le8Z b|?yY?n O:r6;&Ҡ:0j:7Æ21۳G0k !P%%BS['a1BL~d-3 ;Pt~'#"c(p*-:%R”R U PHdiթQ+L fllX/H {@%V%c4iJ{g7?KOygHRR2<C-BJ0+W A I+#XjlmENOG;j++p).U-C)@Jb :Z!j%@&E^NyjsPQ 9Ԃ:*gзWR'K?m$q Sh8Oe=Mhiu@ obv:0MBi(kpSPLJ)/<#׿ntvR2ZPgNę!WY+IHtAS. {@v=RHHM8sXIG#D|zS'`׊XΡR^Ҝ,D*^%6\j'!p!RXj\ J&<@##$mfZ*>rq1HHNBsS©s*}Ԕl&z b /B{O)d' -DZ! Ka͍B(' Fޙ5(HL4)ehKP~ ͤ8u6V|p?AVZ/00Y4g(fHePPLPU(:LM<]Z㓫= JcnjX4d-B`o~ڟGgLwg;tMTmo3,Qg`ȃRaldxyyQG<`IHAc;yW@gg'Dj\74VBC!'К٠YCsf(ijRwȁ}iY ] EK,SڟE7pvr!y&h Q!\G=חޟk7 o,3Up{H>XTZI(fڛ iǕ$TUBB<ᶹ{*DV5$dAaTZ*!rwS ӀûƠIۨ (X܎} Jmf[SJHD/|3 /_}&@,#N H!(p1!i涗^J30hU8uF?9ciu*q|4=h݋S~s+A? ތ˻ GݏQHIC8fH>B r8=78 ,ymqC`8A |RXKC=N$Ƨ">. 1O4HNL!=7mV\s1ihiT}<[? 4m/PCS? DͱQI,'*?kW`"媓P0LX6QYWt1bDo뗘1yA,@A OՋ<4AZN"ImEk],v$jKLv& .qk?LUGb'H8Soƒطɢ. ةZtP.|@d.ة&ݑY)z :4ݏi]HKL.mpCZ$Аe/SFlaրZ  j.o2C}^+X|(|h?sH+'|\C,C|Rzѣp5bsOޜ@hhkCb__-hmhm:\]SEiB*kikj i M%r84gOGp4DAO*݈j}J:4SQ(,o&C\%uG $Z}qc"" gNJx JS'JR磱^z3a5#5- u(ȸkӋv?prњ~5(z _} FjF6TQRwX b7U _ c;$Ե43gPPR.flml@C]3 )1uu%ͿTV!)i"Y|5E's06̈́N< *{PzQ4~L,$C >XPAu~_?UVAz? &⓼(xk(%8;y~Vy{H~wzBq z? #OTvJd]TtVUT+p 5e}9r!?0 ,.b:}P-X4]]0jpx~^z g=  J֢F\;CoNTiDbBRp95E9KGlYu?%- o*nv=8|W2=jYjҖ3{nё2ϸa :<~XrwA2 -Ey>4KjpЫ7uCݎ^Y irE }'e Dik2X^W0P_(b~~Sc*}}tJs=ټ7?z Qw ;zCVPɻP&õZ͒F9$~ZG_TFlFStXjG\ t=6{D $:3}nz:J RF⨚JH:;H-ĺ#Y @Ԉ 5( ²*_Ʌb|l<.ĞAB;ĝ9υ8CO:d{_-3`C./Cqq@'^^ef b^KĚkDPTzJɵ{YF`9bw/ 0tQwPhN,+"Z  #ӏ.9Kjz" vF'm%#7:5m9Pksa>zOI0YȀ>)>7:?##/~鞐's _0"}BaNw͆B!`Ac[e0kp9ilGMkzNiv#hYhBsf< =+eL4 pBolcId0q"ci^,AL%6JVY"EFeHؿ㐡v+_J=tĿ $B|kړxwX9kֿp7#_kC D/ƥltH֠[NMՅزa51ŜEؾ k`HWI٣"iksB ߂!!E@xxYxwcS|]$\#[do2஑QQoPMg'W҉9Yh@s]$dbӸx)QBb~f޴/>G,1wlǞ7PuceqP$:'4CVf>B˲~x~!h) Q0GϯucW]\>DΧ8;|[\i^g}Y Ur/DK*k_7zd#C6 Q7:tvbx#Gi_<`~8ٕ3;勅kFuSX j<騾C $HvbXd.bbM騣 6r4آ1KLǃU^d6:\GȰbUJK[k=f&ب 4R dNo'0y0ZC1{k/0;nܸ-ܒ;vlI(ʠʡ$Js030<@nԩA||LA58W%jMFq6P*+ٳ1QE)+& >GJϕ̓A A+%7a„ܘ1cr7xcUbox\űrd\Ey(p,u^/=zt]wU?qĂt/h(h%Qt_%2D "7jƺoQskT[o'ܒUaـTH鼐ʋ*yK^W`ˋF:.ȧ B`TP*yUD"\((+@3$R8Ql i+%o(J%yؼΫ<֖u t^{Q+x›oYxwE%qO>$O|g\ ^&[o%?Ypa^{-y{.Q?,|W/~\AJOˠlXy`POQk h $ 25kVX(f$Oa9眓W+H^GiD.|/ w}'/&_uk@ `p o ` a0 ꫁!ӦM q -pRX_EC^_|E@GqY80Dl/"KXhEd&YIk"RYnΝ۬7˵!sY喽KA4egώ9&Dm"M b Xp [lEFi(~GbD:^rkdP.-ER *~:zD$TIAADTc3 OJAuYŌRCB..ӄV(JT-J00x .*N*+/r*j*q*Kx7ߤ}?駟* m$=a 1/c"b\*{bV$\S`NŴTA;U@!f* ?Cdɒtʕ/ҥK+VI$knnNװihhB*.{A۹Kp/@10Ap`#)Ō,ƠԲeR6˗ip€ŭ & Bhc b%mq$f@% `\D1%U6侰שLoř4 `eFA@  _ eM,0(cP P((5( l0 rb, Zla-tĠt_cqe }łbFQAi!PZ06ʀn`yYoSt/W> Vgy$$]0:SseCWebFJ_[>=J#XlrL(gD-Ä5Q8LpLЧX,=Hf@ P&J a(f9~_ 1b.{P@{`0/6xjebFOz3(NNPU^U \ߝ0(B{+ +TiP@;p=h9cn266*@MM hR I0E1N(M]O6,p6X63z#aySi〸`\s&`(OVc6:`L:Z3p3 06yj}  2L׮]B$ Ї+ C}ܙkylTӧ(LGQ D]ө_.@kL.L}@S4{HJ0((dž\1*/+ Bm ƀ=Q<0&dL[E{b0?0W]1a6Yp՟P6P0F"* eO^/X`q 77,)gNk`i hN$:P`dnc)pP̑b(\@BaQkVp,pYxyͩ0PtDzRnjGQ,X`wɟA|^ L0HD`H{9s&Qp0x 2%%\7=zt@ߋ :1,L{}3Vd?УaHCAJ @(J`G_/`Zr5/8{~c}sd `q `̙H49 y!Jmc0Jay3P`me+\0!F.l s:U㊰*wix4iҤ"(O8'N Q,f""(X+dm[##J{ҀpuM جz6P"  RD}&$$1f^Pgw&K`c\0[+@c.bl8o//{+E!2+P1= pqU uKG\{¦.9`=JSq _/~`+$_i@ĀL 1@ 7PQ΍Msqr 3Qԯa,L@fs @~STUU.%M]`,m}?_+\ E(~A+qY˒Q+^ 6#gJHjԨQ_e?v/~?Zx ,_r.} N|ӫW[BݻpgHd&\;d 8 . D/X3.{SN96{aȐ!U~\=`/Ԣ R()}e_@\zA]GqDEcq@%gڵk`tR3t'CH 0_v|(rV  eGKk_{o` 6;&0`0iӦmPm۶-> nѷo#F4_Q@ZEP)0vָAC`e8 C1߱u[fEfL qv/Qƥ?|0(fe?U)F\?b]y&G9{(3Rj)EhL5RJd~'DVg1-fKzf~ \2ufC\g}:ٳfذabTrX@-E5VD1XݫV p=1hk`_`zΝk[@Y\P+fy`8ϞCE_?E>8'`YY۪۱cQ[[;[,-)(S. W8k ׼˳ m ܢaJ:˳Y޷۷oߡ{5 -,Rh*5Ñr` x[S,bJ~@>&xbvٳ.d:uԣf\g, [(uL*C@@-JVDXa䜕.gC9#܆1p51(oǓN: R ]̧bUv =kIENDB`filius-2.9.4+ds/src/info/img/email1.png000066400000000000000000001465761506574241500176330ustar00rootroot00000000000000PNG  IHDR sRGB8IDATx^]UѾѫ,4M1ƨX5%QMLQcQ ƈ"% Wgw̖S}\sٝvvfg}ǟ~٧~Ѭ/`F`V-[ok۷W5Ϗ-/-k 'thTR~EHKhnyy`ȏ\*U%Q)U_"TAZaH^o,]&ݠS#0xhCUYZ97X<ɚ󮭶ɾ> 4#ts@hctT3㮝|uf[`57~|YYO~[e%+Vwzߴn+.o(nTu[}5j`6kx/ϾjX^(g=yn"ZL]t:J4eY95Kz礂.U1gz8P5j7DfYSJ9ڈakW fտmq4LNÄ"bP qhņ5Ԋ00KDPR/Z-^A=\&<oQ5l6zi4Hd4s2,R+>(Ul\@ӥ' #U6Z:bS-CpJ'n:r6c|=gzO5zv=3]^>?x#]C[(kChm}@>oA|kŷV.uILi H^K\[kQ['y,0*TP[oj:bEF[T+2:٩Z-@hDo@syQQT |"XAq PQGTE슂dvÛ<Afձ'~¿ GG>l(\$X\AOA09Q TPB m~ثDC=Ih:Goa[QĺatjCuOv`%kONrNO+ J5• i#e=FS#B(G=4W6G0 ŒnVZn&6{ejG+1z6 pTP_W+4J pPlh(ɿI_YASR2W:Q" E+}|?-~.A}e;^eh} HVvMp""KIFFBB7@aiE5R*Uv$鶶+'|U 8AV#ѵ5sd08P?[7nS_5Dbw҄WUV7Ch=-qO+c-皵Be󗋴SV-i{ <&Э4?HS|MW dK'i2x^TgI)J,/[+·TXUNA%I6D%`} L!Y/,^*6ZD t ޻ j:U:z/ 8( tc=?L;0ye^fʶ4/y5 ne{L`n1_c^t*R͉H/qyʻՎhJ qlp(DOu3O|`xjKM蓀)-%pۭڐGHt3ʥU^EڊB϶|L[o;zFW#8\3.,%]S aVR?'SP2=Cʒ)G⡤WH@ט]`M@'Jcy4}BjC)ZGMWu*E矦t\zx ztt/g|3ʃ- YX825I3<OPkh=ҕ-8g,~'#7oϡ_3Nr# ?׽R3ww fO~0` X4:0smQfL1 7l&\)crER}u#_pS_54IERX%ꖤ}5"/]F2[*q䬜YH8(R4Q\ ϗB$u}r֣=7Dth9 a|FF+Qny<!W|36~C)gLE+U=S7a}jtH!OIZA䰎QC4і5쬞V4"idLY7%գ臿>2Y$Z=nVO;A?8hNxd5c7A`%1ށ?ɢbk{M~qƩ?g`kvz@{N]1djWЫ/9ՈC1Euʳ 'Uzl"NZ99~Ϩ:z6zW@SڸՔxVSʻO񺒃yh2mj]O=&V}6jKRsSM}ntWXLh s82TwII{N::H){Y\ҪNB#gZ0nUV9+^>@9"T8'20(ג hZ!,r,i<"Rqe F.0יE=Ւb*=V x갾a+Vw._+_RR9x L}Wu pK%uKu_o^~gn/`e'pҘ-]\%,_]"Z4֭O*{Ah#ez=ݽsi蚵`#7KSSj̧Bi730GyHldLoyL$ث 1A[Hr`BEd2A؂}hrZ@:@DY䴊C(x P{o D`nGuN']իѿ93%E2}^JSyQ?Ǝ‘rk"?kC-Q:COf94Dp{CՊI:]@N䬡""ڤT3$ bMf(ǃP ]%C"!Δ!HR2D~ؾ⤼lm . ޽QA2#ٴښo$2qCg4Wt1m*6v&אLhb.ilXD҉e\r vB>5"oFqbBy4{[ 5PCL̫{ <5$v9 dԷe(I볂t$?x۠(d7校w఍M e/դs?p,ά3#;Ơ/[ll킮$X0 ϽhGQBԩSΝ/_,[ 쮸D@1wѧJL}ϣL͛fvM='?1o=o}6^W4tpӘuő|z[_dTtu$t<{ܫvYJMnu?+^ )eFa‡3zVO͵J^J>fJ7J=ֱj'^׾sx]_5 ˟WncuNCÚ dwB?yJzNAҠ_MyͭXΦhc;'M\r!AHPBMȞH1ђсE5cUHe[EͦSE ur`lc&,:1/ s&W:qAi (sH}G^%~m`aUХ€9s]{ϵwZ϶5|B ļtR*7Vi?}ꄙ~]/'O`u]j'9a-]{Oyë"h l}?-?zwOy~Ƥ9y&t$zӹh}k}4G{N?@5:yOM}u͟y}¥R ]gQw},>Z67?|х<9/cϳTÇ;q=,}Ż8gM>vw<.#P?}PeEQoN T.=t3--z* J0֋x@N !~a6;2g[8p{cxNf ur&(I:c_̥&/ש?p. & R?UYP.,ѹ Rܮ)ɸWuNGmjy_QfU sV>(W %Z«4) 1`ѫߵm~~ ?Un4,f3k&z+w58nOݴ!kM!߻5덾֝ ~l5->^ ?pՄnx߰j>sWaB|.z펻˚nbga; O*V'l٧OFaK1. ֢֕tz)לU5ε_ ?/G4Zfhlvѫ4-il[ԯ}EhA;䵲2ĎJA-j(Z2)`!mcgUN0,~X'^XNL.{90bQVA&LnQ|iţõڰL(qleHDCO<ᢘЃ>$gtiH̿xg==:dO :?Om5۷/h>xput=|r622:I_o=o%xbBV\})\ ೎~Fϡ0k67K.ߓ$gau:aM?5u9kiT Y[)APC]|f*׃PE4FdVs%U휖fbyVQE1W't@񟋢s%*K]y[9wGBlQRB&I+ !LIMc04V 1 =e.a |MȾ"jhXm( +$I?{:$nvZӳkmn5ݻN=v^ӽGM5]ŷԭg[Bw}K/8 N>m҈ggS]~E{Bמ ;|d; Pz΅Zۏ*?֬Y{9s:ؓ+uA>s9kZ|ڹsMΝzܳwo5{vթWBݽ8Qߞig]_8޳N)rB/սgOa˭3>Y5٥^:ynݺ{cĉgϙ3떽񓅝zТ¢zOރ\9&?Z\qoS&WAh#{7/z ?BֻU( 68+8ߜv' Z[4{_X#SzM== nBG=rDQ޳%?u]PIͅ}$# O諷=C>@^, U(lٿ #Wۉ&5UF>(l kY3إEͯFtH,V7ꙥ[j:BOMK܀c`l˟*8zvN6 =:-a%D9$򊵟eԵZ+\V*;67!iVVccC];vQN]:u22z}9=t6^@c`\۩^ ʝ:]_ yu6_<`JG,ܵk}zw]Zvӧs:wj4}ŷ~ospgqtۻF&.@:Y_(,ٹKҳ/^|q|Ç]v}D]ŸnzK=?0jMG]p__^GO!!g~`:Y"un~-<0%ʼnknJx,`-n2Z/.V cYOCX%nB/ϞP8soK<9 ݧ5]WkIW)hc U:U˴chV)y/~|/*\%3M!:)rG|2h4zl^ @(#;UӪ/>]ִ>P~eygڥbmP%nZ{E~*J[ό2X mh٣;E.O.]KWA.ݺo7vu[~2"Ӿ* 2օ.:wޭw}W_vZ]\]}u8,sAvvŷ/EtD=^أ8NtwCn魷_}Uس\SD+W%lc¾{׏O#쉖,?|c 򡻟uq]{I39OHPT2~<80ao=uyUqQ#'#fWbNRqc6p!DH8뀩yǩPPGhѪCo|f?MP\g }Ygم=Q{k8k΢X8}MKM?~\^J҇1j`Ғ[f׼61}O1-b۞!kK86vA;JpFn*Jhˍ}WTDZ(QeWMɟБ(%TBÍL _t{Adt]>+G:_vh76\wzѭe8oViL!s0Uv[͹eBjХsn]zڻo>}ݽO﮽tۧ[>]+tӷ[] ^(9"j]z9G9}t?z믽fo: ɺgC#Q#!`>&Ź> +߇Յby^p>7kM&߫9&X v.r4ޫOHze< e;;hVg#2f$sf2c0_֘mvS"=/P%V3ސ'p3naE^dt=:XY/9$yu9!)#OȨKH6$}tOű[DuaܾB]1n`,8\bڳغa^{otPxk_82n;\r(S%ε">Dyv ʄ#v)I箟{};wLMsVIͶ$M,=C7pß4r3|a~O>ěӺi&@'>jGH!?-^4G~z^sIW"0c%e D5rٍD =Kԫ/4PO1; ES{EϢ-Iӡ,,g3vaTG}=| q$葓 4+`J6R.fgՙO!Aɜ"#i'Wݠϳ}Pu! *-7>kTE?ǹ%EE6|/4$_OM7|\^z~z#w %gO?LЀ -r҅WJ-g%6 |͋UqJMG^K|ƻ*/桇#tdogE+m,B'g|KٚaO`L|F6urJ|NRlª]mۘL~WGֳ~ߚ戤͍R+H(z3 m2fБe6% V>WzJU5xK=\]OK\T:D KpuIMp3!rtF%Bչle>nS/chLqeN/݌r3}鹧,Y|~@Z"G"ضH4jWAa$ $CW@,z3n}tiFq]9ô*%&e7t% 52E( 1'ڳSmTRfe1L ]Y}жj%z{ Ml86D{A}ƗEZo2 g5395f RE. $~xnD*%'O;>nɌV~pw@M?'OGyΘɎiDц0o_ !~ y;bY=I1zN2mL_!(EPLnz,;Oi/5 HEl4/:O.57d):k)ƚ|XZ)B^uaR'FɌqm^zp5rqJ&ڈ7LA5ʢi =SM+$u4zƾyfQvЊ5OK>{l|iMfgs<#Uۯ,q7 MQ.IGT HZ?+Ɗ.XG5y#o+OCfVHO3kr;WuV[:CD*;PE p,JGtU.eƆ=#Uślx بbpXa; Q#ZuRR1)dqda" FV2gnAK4^'ېPnpn4GK`~sME  j%4IID}CDqY=ޒiclMhb>j@ӻGV6ʂǭ>뉘ǀΡmtv:5; '~nn{xS<4(ahvw"u2p d]NP"'FHH42tQO i*6)Cb%ۗЌ<|z2NŐ2ns*G "D#BZ~K4-P 5.aQ yZ͎yBD+A̱[Q29Bɳ06^h Q:Mrٮ\AФLl'-iwgFKΡ&j+=|H!fRy M=e!27[&j1v;h.%h9 ]*$%nlH=v&h'@=X f2/»&hщ4fr`C[8M] `A(<1; )Aj/Js>Hҧ#FfiSY%-k39̣[ Mj#;K#Ws8ng6ڽM8V8 s+j"=)lg͜JELN ehGr}'苪kh9ŠzVeh9Osok.π1hIϲﳘ:+}v*6o2FϨΥVv񫔞#GD~> [}Jy&ûTH5y+*4ӣMM3}SRi@6eh[W* My^%[Ѝs?rwL3?}z~}tJ d%\0B5 5qfc̠k$*6 &o䉬z;P~X)pIUR\c+#tS;)}%ox>ʂ|-\M#=8W6аo,Y׫>:!pHK%.A㐒h^:PcT.ț  3V K;*׷GfW!8 #0#G`םFQNvɧ_9鬓|1`FKOlT1,'#HJ˺94sBI9М`FU ̂LL=ˮU/~%:%7{ ^N_礜)9#0@A;^=zOΔ*Y4$TјNy3&dIWd 0Eoz7J{os3}CS3#tf>$O-o{Ls+يnAO}^̦%djbHI"Rk 3դ|v"T,Ūr9X $X|qO8Ŀ.BzβVg8̒:Ol&L7C36Pѳz9eDѵsmSаѭؽS)3KJtE=tG23>aV'W}.b6%wt}}~~6w٫5^%ɋ*d[Y5햛WTdOx#ol ϡ;AGN!iU_W|d/u NV]5>AvTnjT֥3d\Yߐ}ȭ6N1%O/zs3vnhP?1o{?e&H3l|&*;SJ?\5Xa&O6^ XKY,*AM-[W_{5;IX}>fph>P_'pb7@Ju,x{N)5ѫ>U`,#Ut,TcA<┻kL{n{]uzdpT*6~'z4ḡazۥ[N;7y҆uOd%eO#~,r?iң;/}9x0q\D4m:uzLj8pX5}{66m3<n]:wn6[Oӄ{uWğOՈwє3wihų۟wMOx.kya}- KUIiyI<z67﹝@1Zwe]}>}uo󬯯/W.^Z]OVl-c;f>$|gd2 sΩo\Y?oPhiyGNQ,Pl^>(tƣw^gGu4ZN.]1} 0a􅫐~<8aj0%d]dwffTޅ2*0MZOvηVD/nN-әEqޜJJ @f>='fXϰ6nz鉣B-+. '0|O?˖~S]!\M7ӹ_|iۯ9lpr:O\3oGYx쒝<~k nz𠚅qԔ0q9\ sdF(TzN~p 덷J2_t;J8)Cqj9(vkR5Dcq`4_>`-pugB}t8c*|؝GA8e'noR٧3nF9;]~Y;Q'_!Yl(*Ib*d'_zQIr Ԑzv9ӎ]K.ojH¢OktxiqWϿ,~ix3ֳpØ*ko/s͝{הzl˜knvtxa0wʡy)DUqcߘaz3/n\xay~ܷ cvo<`H}:%njXC㣿,\߬]_?ybq8< <m.X̭kj޽(# {NN$r=,bBY>:3Wk\z#8}`qۣAnc|(K't[L Ч Lgbwלû.72Ͻi86sSV5ea$#1GQ߀sLx}c1袋,Xň].hӧgcSmnu] F bV8Wq_ ߿Œ b3w=d0>_~gNw)'1grݰG vGu>Ldjk2nM\~7}LQ6zrh)tG#3>=3#72pLGɆ~nioSpbKgADSC{Ԇ_֖Z$nac@3Aa MEĖ&y^EٻSN6mjpOӫV4ݼ|yG-nK-y 2WKO]t؅b8O*x⁷'xj-:}/۟9sCֹ֧,  jR}ΘEJ͸qZPABgSj+}xD3Z6%sޜɴ.Go VCœ/dʄ|럃ck=?38&.RO`F*!xh 0Mf4ZؾV6zn,믿1z .`%z8"ZQWank(͕Y?=^qH7xOh@qyo#2oB*&.COO]zy0oу o=1{w D"g\Fn=wNq>}@ LL 2iv Ő nIbR';c5'lf Z_mļγ-.> ʎƹ_Pk/ ȿ]r;υ^9y ɿv3{t>0';T v,F>K2bTT=eRiQc=iE+A <njx#g8ibL* Q`xI` j`kv6ݮ`)gʻ~w Bt2h]BT1/2ȗClTr].략]6ICc1P/>}o蓕<w[އ+?~R؆ HsLL{\ N~Wf8y-Mn 3*iRG~@ȟ*¼Ϡ;>fpX}{R2V?s;i\~{n_žÞ@l˿bKZ |S6!SBqBdu<[+Xz@@6C4˖<|ϭ~~^}wW1K5,|Eޥûg:N-۟b}q>}@MyTK8,63H(bzlYqԛD*~ӽJA:8BN%#:YՋ) ˄lv )e~;w=z#iVn6|ƻO7zaO>')|8p>} a |zD?g}O>Gg<撚qvs!W0v1MhC=QQ̠ HP(Tnp}4kyZ ɢQ?ܤ|49#0#AZϏWSex^H5KŊsS(z.6BO87*Ž31#0>75_x"mG$r;BYtمRsԹ]( OuaװܿF` p?^w'7Qh6C*!j7H$VIfqIQ1׊o@0/[ɡ`:Q< Β4!VR޾@Qg!JϗYN9#0#WZ={l 2D@Bf/r#ҊHsDWq,D$KaۚMU̘KܳF`U}=ِޠ:{k0%ˊ= ~&H뗮xjAg AFrF`:<1 8U7;NG*5oPJ(0_̒azw0@#n=Өԡ$#$ag[1er&ݏ`FH@ ^ TVFuTn zcFo   E./J@0rڰJgO/䰦Y{͍vLz;&cFx󫔞Q0:JudV6Vˑ OHz^=WݥSmT/rFhDfu&Z臖dW0QuY)a+=dF="MðfX$ vm,J2]X(Y ʑ%FLҹ~ iF`BWKr#`}Y#_P,*–$؄thS[VČ#0`Y5Xeqdbl.@s+Xn÷͏И>u^fu=rۭ'>"\Ҕcƌ;v̤٘x$sƁ.͞ &.Z d*@%-(jB߃-?[/rHIM"xJ#1h~[ty0>K9F%X %nҙ&)jVRŋm%Bܙ6zSٽXb+DBXkx|Tɠ!]rC-XX gfi>i 5Y%._hy|gkE럩,`: N(BvEC|\$q";zYΓ\ pE4`QiK)zsRߺ1|cvk>[GBQɞy%KkV! аȕoqڔ<2 #y´鏡(s>~<8xҘjNͿk ?aֿ|1:eKŁ_3ɇ?UqAʳǾyϳ2tgb<|ނw w>x> tx7Ls\”[^klУox )sƞy+L98-3oY=tCk^?aTxeG }OR7Q0_Wv~݆;x/u=-|Buhr5#ЪH+*͌/k]C }\"'&)dxJ J}:?:#.vfVh ء;m׿8`j%e'?iylQ@m7wlj,+ZPY{!|yȠ:[~A /w3n@MͨǪqӃ l7zxaΌUfH:f!pi"QxevPXT;%{o!?˫gdc6w#H݌3ح$,qh~JpShTnh,ox牙o ORAy*3M;} +K.}i^ PP^f`鼨MJp};6+mY"*y]A X<愧a %8Inh,]0 }`3jhl~RIHCG5Xѓ g?>iV $X͵~/O( UfF(䰯kK+ЅƝɒQL )ZD,)h[m?2F`F ?`ڽttb12i7:\AHRWUjءg_{dFȋ@#ĘXˍ2tkY;(c+oѳH@rv?#0UG 5ytZ"΢,+5ݓ2#AMbe38%HpQ>,:ovn1>P ^~yU`^XAP1Vu3@Sȹo@HT*)ÚS15KTwUtT$bDdеªn-7⇬cͶP<zuXM>3)yF`:8޺-x/HDIH@RT|v.9Q,ny݁馞rѕws|`Fȁ@<*xcesFx4{f,v֋:)vVJ2`مUfy7sIF` tF"Ae`G[ŸU(jz&vyz-’F`(0X"#HfI$[2Jjt ܉$DQ#ٵ #0@ #\I_݈{l}eu%Bț,w3dh4{аw>0EtNK%F \++c0#\bcHi޺ȘՔ7[SʉoH9w[ S_%B=/tIsnB=_7a檷3#бo#. )bޝ^zX32 M.pDܘ;Ӆk0M@ Fϟ{!Sy82u%$?kT\lR.z'AKϟT ӹVcڨd=L-#?%ł &^e Jj'yۛU雙`J&iųV@ܻyi=7}gКݔs[  qr=G7K`F #A=YPXIMMu H&o뻛y W!<,p؟P陋N̓//TzxjNjW7eY],`ĹE_#`8L.8F Y`2%9@f&ufU3GvwaT$sߖ랁EzYw0dTGIbq6R &Yw雁=H zo{+)95=BCCו.Hx e+L;# jn'zc.2@Xc k$ǚC'P4=6f!z^F$NcL lO =l79eɨzE1pw1J7P)d?vSE/ glb0wrM =J4 =i={ EO[3{m?H}_qVX8;!([ U=f[q00ߜTT4e͈30CE4GR0a}`^Nre!aF! cbimOW'S2=,HFڶNuqT.'ͬy,P~r3」oٮZo " FSBY+д(f( >t+˕ `Fȅ@쵋ޛ%IhJ&1񥐮kʌRҗS)ׄ%߾Y0#S(Hz߻`Y4-, p>RQIqƕM#55v; _?v3 W6߱PY6)wuF`P4/ Xhف`_j?Ǧ F8փ՗{:aTk{R/#V7%/.,g^z-,z̵nnQdw |iaK2][ԄHN(ktŤϗ5"gջnbV)׮,t)-}1cgpeUNHL%&|sU/\8#4 rMSvαIRdreۚufCĹ{yWه}ÃsCLMߴT}ɑStpX#x0RҾƨ^嬝㜚 %#;LsfG3a|acX R[;wa=f X!"M&vvi$s<{Ҙ1c&=^\pqNY3i'pǏmOŁ F JO@.R+Te>wGbF")[Fe.`|D[7 dnحHOJ:%iӳ *0[YXL7,8R0qGN) &cL9zyzhCsjNR}jj41٘s_sQig}Xœ!GafA bbqQ] j:&b6nDblO7*4lb {v>=='kf>6ahUYM 44 k.<TΓXfn9wک@Ά`Nxp:K*' O\>4|@Q! A$T*v\=hxBQ㨵SH΄|~k*c_s4WKd9CRB!;uH}yӐfKˌVK,ˏ8۫w#0C+쒓UЌǜ? ;Dʂk*\lJ[NNDW.=J O>h`p,z3 zxNz* Q$+FmEo]1MHAJIa*"P=OR w Ot#I|ĭϭ>z;fԡ2>>cN(T4 !dשr0^YZQs^RVX"gc .j\&wK *'Z![3:gnnIƆWL`5psU ybt 8#̪0_gCC*ë@ h"j cR4D6[ʜCwƴ7 MbpSBnt}ʟSn8X(FѹrMAw k zh>ʘ{^b rҔPp3/!ޟÎL`gf 林OK/qv[UvװJf/0"p3۾ݨkB`@ V~ )=ͼ 5y3|3 XUlz,ⲝۙM#Xvƒ5ԑj@7LًC!m7B2AL7`4Oy4#0#` )ut=IYʼTNj XwG`ay›Q/eNc o<8;mBT0#4=7ei1hZ$U8R8X:#qajh4lzӼ t }"akU(p_) 4$R"K#+MWŤʣ:5b-72+<9 #0@ETeMۤN,l<we-}ܯ,bFrm=TvsELs,1y2D7! n?aɸU>21F;CSzY#txY,{֮iLNg IAFe;(m2 gKզGzwt/"L#1XZ}(gIt|,i%ezntOJlWXgK86a3uw#3!j-O:@۬pF{kyWGxb 4J nf^gjF =YK,]aM[7Dno.5@ݪAϸ=dAʝ IiBϔs齮d.-+FvkBB&&dzভn5XY#T jѽyg g,Z1:cy!(E pѺB&sl}.đ^nb 1ًޢEԄ J{.ҳqk٫7 =Ыk&[ς)рa좁[/fn\- aBbև7:1gآTGlj=V|_ ̚B3(s&D`@ /=P撠[7^2D0,;0Km[6(_EM-=Oz^Yoj]=U/NKύUH1=wئ3@Y$19Q2l[ uɡ#=̛=K%Y[qΚ?#0*E .2[w"s󐤸3 nFN{DF}{|"dY*\#0M1Kqm#>f*wSa*>ۋFY =tv4vnlsvϥ3) z5ѹ#hW4 r=&K^5t:',RJ+\Ȑjr\|/8+ӫӹg`LMAyMǐ%0́lê\K blĂQ2eR-1_tJwRR*+U" yYS+=`=bzv=y= ~cUs9e2=1oG}̞k3 Rz~fX:8Xr"ܘ=ҳV[YsLU| \4#@9vFCcΦ(eGWHAIdm{~XHzA S>x`F =uH D}_dٙ%9 ֦]m6'w:F`FL\yIɜmQl 7HDqB=K}ԫ&upwe9N0#P&敎d13STV;.e%m4SY.V|6Ymϣsp.!64f x]륚(ö㛬X` #0RL[eEƽӎl4?MXá!HO1-aLlYrwXjh,÷X*F^a1LycU3u5+AXxaR8;#0@u0rd2e9a?M=<9SYLB"x hȷXlh(,蹮+/૕V_Xcƀ5Qv?B,CMUU2@EN΋pK9&mWܢQ2ɻSGrv) )+zn,>g_կ,ױRa(Yjl8dtBGAp hXHY3_s7=X> |vxw10|vjjF86^ Zڻ&=$X0g=r5 q U:mzȵ) cwn:#QԦO2^it6('@.`گʌ~!Gb#FlُXOVI 0EGFⰢ&VeW{ɟN)n@1 /~o8){9<_$9EU"0)o5&uЧ~jQMyB-Y{;xB̬ <_oXV!ꝩoxwVO:@GEԐV&bĭ&f5-F vt5ukرO(cħwMz}{c|+)’F\"qNt>0,nQۖY~c"R=72V6rvփiCR KEY=/*[`ct0'hN(;mXjl3ֿT(] oxXD?QqEHCF4Wm&#>p'>R&# (Ƅ[N$բ[z:7rB|3u'O% N5n8xwuV!avE$-HB~.r^i) 78-"LzBU`BÌqSOsCIZI Fsfz9cw Dž!o?1Di#7<+*AO;6RKVh]T,:exvLh235|mR"VLNU! At4P 3)\wjHp"d by 8ImEIbvOE@ΑHtϭ`+4l{gyv5%3@*;+8ѕV~)hIE*psY}Ь<qg[ekt,jM,5AqC?fֆ^[zadg1 $MZ=ڬ #"3)([紧l L>t7q^9uo'|Ux>ۋeh|mݞgyV߃9^3%ݶ}+_JS_(قsь@ _(ywzsuiQSI{_-~ee2.vn p>vX-BܣF`@΍>"IyYlpMLJpRYI Jb4n=sw Xbc[c܂sь@uN(9Mt־I*.!{%H韲"U5}D<OyU]RaE)}a4Z3w(nَܲs@e8 =,sv$mZfxK,vsz=ٹw#0@K#N֦I@;=J+j_8Mv=7]]M gznN3#S˦jRVMBG$~Tӝv .Ègvn vnW&I 2@ p~Fw6ف8.ۑht mAEc=>3{йsV5K}L-ކkF 0@\uC[|&l4P!vEx RE(=‘@FCԔ4@ OզfA,3e1z0={1=g@B.o_-ϞΊJ6Ø2OY5F!ahTcv!5#0D u#0orLK;Sr~"/ͼܥω˗1cYe' SpCØWe?F`( ;)K_kQ1[)enWtP #0#w KՌTtdm^k;̑h[+{)3uhk94̮se&sdi6_5q)-@?SlM],t7  n\/1 冉Ml#g  M@&:#saLZrk遞䰶yoeznCU)%˟u+¬DI!`C7Iɔ2]HdےT8KEKZ6+cqiC-JRfsOH+ [9dI2̆njJ@x= F /L,"F5Y9a nX"[N ( vҘa*Rto"J kU^yMWv)[Yǯ'+2}h8VY6͍@r#CGg pn\2A"e:;l2\ u1V"0CvO7J}tX~D\4)GY֋pΑԧT*OZO^ Eg3֘9w_.k۬t.֑0S~9`c/c+wHI`u3Yjbk(JI,C-x 5ow|?pCXַbCn>k#F9E !g̙dE͘j5TVJ\nOxaetI.N{~։{S 1ܾ]SuB;2=ui^e~ŚRQ{SS[_H]#7JYM]DQ,,B-A8ŭyۏf|rͽuvF4uMcTv h@b 1A3sk[B\/ B+?t(R7oOFnvn?53?9w"Ms_O}폘vCcy#A&Mp7 QD_6\:,u%HrBDx1:\:`;WIBTWlW*;i=ϻG5@B]z[]ҵq\~o=dX7=0\ ح^2mx,KT7ct ޸29͜??ҜkqکG:-bޟ#B~FARYRVK6@zTv XP\Xf4$6򚅭tnϛz܍/Pa# GVxsyu7 0Db w8x.gCs o?jb=>vEE̹vc [}o<`8OH"YHÃxYqܻ:zݳ_A>sN&+P\J K[o|~Sp#P6=7 uZx{ ]/iu ByK@ҳLKiU,ι7v}&NLw7?vvn"cC}h!r"`ʊ_hۂS3薞/lN& IT02I&[TDIYwr-+C~rw^|<4KQ{ Ш<"^Qsr.,2y_`͎7nu&^aϣ"bB20d e!ʛ l5eP.?Ep].YU4{R8[M9s( r5X־}on{w/q>jj૮ &Vֳ5rB̳ER?3w[p L)%fo>}WLs؅ э""BAB$ѿЩF@"Aܿh/}*%vՕe}Y}ʃjcg}ܔ{J^x!hF펿hT;}Z;Z_ JbRfyrRUi,9`R e컟vw%9gd@9r#\(UE N)jR)NLS4Cr9pPH@-/xݠCU7ʴfEm)IC5šND+t ærlUDA[JxoZ "?l=~*ʵ9}j6Aܗ‹ H I|dB`2=7S?c'͊@;4\#B*cD"Ͱ"86)(ଡ଼N4_u:&IS ;,G(2giBOci1:Pм /^,j:yaU{h:$.Z{~VX+V:KG+(cGSRiª>X&Y]|F _m-G0=W^`z,`:i %j<,JtYTt,71!Wbc\:+Z4Wy0ԩ.ΥpӦ,01֥o陝*2#H_IS3֦e}%.$, z.ɔ홞[{eFhķ%1xs UFghȆ$̶]?Snb&(H 3=n5dF#@7J./;9jLa4hݐ %]92kN(mc@3=N 2#p^BX# &'h\Rs.GhEm&ut?2cG#_{m#C3=^dF#P6>mF*Zl$U4- 1M[}L鹵Z֏`@$JR=*a_bhpj"\Jcz@ݞ0@+EI:p8)toJg'x{\[fƲZyyfv+#0 ?E^%Gw4qXa0;Zt:y U4Oij׹#d5}R]V)R(T1;ڛGc'ѡ@bst_֔`}Ovb-A7YVDzϊ[ 75גLO(9 CGiA>oTٹ^#-X/f m_Knn赔 @&'rryT׶Vib!aDB+1=2#NHzi2#r5iV|]ΔJ*6-76[?~-Cigj1#NNlt {aш-fe]$0gy)=W6%`kpN nC]>ئzNڛ0#6p;9lM2Ihֳ/s>!/TrRmfсYKF`% %[+:ɱq-SntWg!O 33+lw= 4Ρ=mɭssFh[[7rŶf)VnXJ`ZJQ%OhML[m`֖`v&iqt9Y, gZ˱L}]~\7`f1ÃmsFh,XɊ𴵪(U;$qCah~J舻~`>8o;`@eFhhv2<-NhZ5pKoIyމ{WlOcCi+ݎ!蔹gۖ鹽y#0AHdž +6p l]< "A݌SGkټ&_z:8ᑦQ ٪dotzNʷfe F`>yg<̋&Uа*4׀`f4F&NLLMIg:3ȁ[ٿRKҬH\[b8_pLSsFh)g:ߌTmy&Ӟdt,;6^iUn"1FD$XnvfFh@m愆0Ehw[ۋ^LO>Ŀ:6W`6 6l,\AE7q\'W=c/U0>FmdwFTpI7O&r4C3=>3#Hچ#Z{+pFh-8\J,2frVC;aۄv}:'a m ExiYjkZm9eƽ0#^Hϔh9(j61uzLfHs{\/F`ahI-?;͠۱aP@ }W3kW붋&Z u_V`v@d{EoФ_:iV 1 *0 7ۂ -2!$M7TѳqӖj1#fnO-`j/?ic[ԞNvixFv20~EҳLkn:֍`F"`}- C:c: bdI4]Lȕpg1j"%䴳Zo&SLtjhzn-cFhbvVѲbݴ TdƔQ|0 2qm h-1#7hh6T:}/|Lm-fE}vlɉrrjZƄf;ޖ8@O?F`@ <-0lo"9Ic~XRɥ'3-^cH w3Τ3SD2FPY-3mLy:黭3m$j }rDww<;,OѳpgnwW`D ']DӋJ S[ ֧e5N&*'KTsg/b.'ΐ[17dF`<,Jþҗ^FB}(%m*mKF`V9h&of陦Ѵb;)_|'#2*fW_{VY*o.`Fc"P8 r63IԽxmI+cpF`ZWhVviY/-J{8;Z=Zkc#I+c:imo²s av vP.`vܺZ&KpZ U$ˢ<5eH8F82\mQEx/pI2V0h^XAo6#0-@4*B\2(|1E6Ǯa@|co#ր`F @v&q\]l\>렌YYlL4ܚɌ2ϭd1uq.o7nWɦ3#/*H#0#*PtNnsKL զKh;gtw,N8mVѭVRMsA8 ЫWrY#0ȴ"1Mٌ< i7+3L\G7fJѪiW8㜾BX{ #0#  W^ﶶo1i#Fl! T$U4$ju$͢cX!^0Fx[a6ےXcVaF`2 m&0BOp~9trUjgg&0@7SsqRF` %]lVEgvt]dlhw2a sƵ:Ylk%FMv˵{IY(6A0#0Ң$tY9:cTZE / +ji5i6ڤ1uh:\Y/t t >eN0#@53S 1Le!dC) {ahHО卼UJX_}3( ^iF`᥸Aa ejmhdďW 65YLy2;~2 Й=1™0#0 X.QnL)ZQ9j)*0OBQeXd[wdN0#T'`*R#Wf^oՓE;Sdksc)tW;5/Kͫ+pB=gi*@"F`u.T/ےΥɆHi5;ahMu}]6vƅ{Vla儌#0@LbnL{C$d,Yޖ㖱-c2ޥAV4a0#T %6>mft ,E;>%Z8&AgeI("h̐Dt#I l]I<#0MELsJrRhP9⬎hNBS+/lVTJe#0@S˙S蹲=W[ʴE y%Nm%WYPkmp{ n,o̫tIsLn7d0#x1ZBf~tk kGHf|}> k#+{6|)P.gـ.[qrF`&"fzqzXG>#Q5Čl_'* ::iЋg YOPKLAJпؓ"5KqvF`�vBrVjjݺ$ RDO_5Ѭ1nbIS kLXlVY@?X,g=dY#0#PԫL.Q&xr/fe ۭH2F`,3.@!]V&gly-fWI&j+:!XL£~}}&2̳mw#Q4I6K٨1~6H /E3A=-ISp'mJue;r| :3|` Cɯnmj3#z2#Оisw[,ˣm3wMaP/<,g'[Xi<45\7F`VvJx7r9ī6P{) V*m .`F@YZku(V8S*]u8#Ɯ%65nhLEf1&vIJtx 8LhhF`F`U"@C$g *Ft71Gap, af`1W91e]fMf C9\#0@IDqv%3l_eIkF[ܦB[4fΣS7h]`FhAkr^Bozɮܕl},!%R&vukK\)Is#0#!&CҊʀD陭@BF`r#W5+suLBy)__\/oDV7Z]jty])IlgvԀF-dAu Xgf]2#TוmCݸÙL3#nH(1m&5CtY c#x"zϼkXcF(pVo?ċ!b*hNE+e4`ihP̷. \[9>?Ms5<\n'#0̀cjU]ͽ Y$.],fNT7 K:dF&KLMjѢɭ|d^ܢÅ3#qͪd:c[PF?9c k;d3Y|e<-GK' +3^M=A3tǽ9#Y չ‚1ZEj%.S/Td t]D3#X%܊ &p8 ]x\0#0MZvS.lbv6ǩUm)Kr[ 6 ){wל`F ۭGKք`F!q+S 'rKѕm.4M.^,|m6mqzI:eF {zI2uZ%TD+;}3zeM"+Q63t+説#0 e([f1=˘B ZДmR=&:6)XkEJvpuFhh"X`1wo)m;CҔC[YkKJ.w0]ZcdF`:$^XҌ#:j|hw*d}&^^nhR\~_3LmTE\b69& -O3= 1#C(vԋ|WмahùMH(Jh3+m># Ga>N \aF`Z#t/C4Z=$`;&_ y#H1.e`Rk+džR_c1@5[c/eFА.ZզK\x)͛3V)TIԋr",hAo6#0@CF3=aFp8ARdX i:t|V!9Zy1Ԏڷԉ+{bk\s6B*6:s Œ#6a}q%xPU 8)kQV7?v,sJގTW3t֊鹵Rև`vu JC0MdhCz]R4/7=&M(=mqUgɳ\eF`Z| XƊ Ҳ%?Aӄh1_/04!}_2vK7P60ogVIYF`qn;xw3#2X58lh6cid~֤+] ^)kG b W=Xc^)oxuU}1#4(Zt(& |f 6ڶŏ]ffvS:eF`9rYf#0o+Zj=.n SҿgvK/f[DZXE1AA`FJ!\Q ٗAt;GF>7V/s0#0͆@MdW,83Si8c9(1NZh+=7[b#0HysKav{mToaPG*JscShdƪ rJF`Fh7V5G ,`Fy}df*bz:G];Գ c#?!dhvnWu8#0@Hs{XI,+U0Oi> }Y8 f6 vn=0#e94ieLmt϶ uSa"O[z歯Y#F`F!zg1`¬/#0 4&8&~]2gkȴ,J, mp\F`FaO8vYgNSa=5wX֍`@%gznF`V8 tzǙ]&od6WL畕ks ȼ24l+ɨab5`Fhqh *0ɺ].3'M܏67!fm[Gd-F` Ckulja#0m)Ue(k;ۙEsf@17)eH0̉/g4',*3h3ʊ2#1 f/'}IFǵ&EÎ}7{xw E8u.7{c*U#dUnUƚeFhGPv6!]fmjlΥ,egATd(`UJ1# 13%$5ČuҊֹhG0@Cڦ&j ّ2Yuq; Y,ֆ4-s\eΨљNu[s0#0i֜)'0Ye^aT#0DFT%n%We"9YI2jy;$.]"44'gWeOF`4#5kFB)lkÎtFSkbdsxs 0D L h9+PqG:BNGw!F`FY6(+T2<+'M8oxԃ*4',R8Kf~,`F DRe֔H:(bᶚnH46-MĔp79zfv`F I)OY:;Ei$V`F"@'eM`15R866L5XqJX_Vp'"HcGAoLsFh[H^e3K}žX19C6# qZ:k;Q-<rСCVemF8$PkKP~6+4ZsZ)0r*30ei}H  6d֘^/s\SF`Z-:V 6,+Y. aBU-3{[xzA?Ak+IfvVV`F D gWPZeֳj% F`F 78j`1߬+gEִtb(Ө.NfaaAoF`ֆ7kK_NE\ǘy :%0@D %If*1#톡B9j-n*]`F-#`g뗖G%s\&:II.8&8hs]Czf:0#E0 ĦhXW4^$Zq ",ˮuFcg;De9ot#0͉?y۞3%Yru*YR^m ׬O: ܜe3#T@2TحLJɛnWh`F! _Lb"'6"el&1KgǃI0`Qq%JҫDIۊ3s;\F`0.9G9i+JՄꄏ)jM5nsͻ uE't3I)i66ܗYuF`·)3ad&:FksKիYtd`t@Ŕ^R!fwpӊ6GZ0#Ц@ _V2kiw4gYӧH,VHg[_R.4!xbzA+MwhV`v@>2SP(փk0#Nv&6ǖ5mFLJe?mC厔 a͍MOz :etpIJsu#0@s#%xJ{eDt!iPZS9+:)q]ivU"VXܝ3#A!]k"B!T֗ R֘q-]F;(,_bjX*A9O4#0͊@ļw9p-֫xiY$[Zd&T̙ʸjkĂ?+*6'|b~Yz>9#0#0TIYÐ.jR6lVAy&dkj1XǚF| 1#ZLhsh 2db D+wq+L2kIBw&cFqg%8l/i4F`v)[ga}͆422P;qmz5=Yԃroˮ,`o=wl2̈24*عn'eS􌩪{341&-|2"Dl%=Ĩ5݆A C=,MS7t֍iM6n6.7kzJ=v #e;ĩLdhlOpYp( ^2/&ޫ;я{LOd؟L??n7)O<:9)Ty޹\S쎿Hcm7"K|m7tQLmJ G۰W / Я P@uS4t.cд9d-+@̡ʎ _Vp@yF%4Q-+W_*% B"^(0,"LT)댩EM:ô) 8LTI JU{-LL`zQ8za!T8SW&-P240a7zJ"^w(xIU 5ioZ*L&y7E{3eZ<wGddO1,49Q㨥%}k#rh ' H|C9L tcN`R);ׄ*ϘO*Ь;ܐczWwub94Ն3޽DmTRD!3VcBI5U6V[!)U)*Q*Yb'SѺIiQcd2MͤF94@6ђj&%j} B.X/tCl1bl j3(Kֶ,JA3 =#ƠQ"FꃽXBU&bM#bYa4^)&jGiv!u;cIU􇊢u=Vܗ޽M`T5݌rh]=൸emMҔ~E]w3mkY=d𫩑6Mu<-zy݊gi/ݙm47[:4&Ϟ&D&U9uTFn?̣o}aUF:/2#0@8 rnKRY"#0#R'OY91#0#4v9sP3"F`F g- 1#0@#'x۰#0/}^< ҽ~-m as ,loֳ:PN0#t4{q~ves=wŊ/g{ 0#{޿nco"= 4q7P0#[~L+ls**F`P3gٲeM1=%ֳ7YۂeF`V@@˃Op= &˙F`:@@_XlU sZ\EF`(JtQg:uK%9=[rتBF`Vy{'/+g}v>}1;zcլ7>7|d~cUS弌#0N: yҤI]t3?쳋.( nK-ɷm%ds+ד`Fh&Q]pcƌMKv.9Zkr miF`:</Vrw LH19w.0#FXG_~_~ 4mW?sDtҳo֊2#0{gv=zt݁asW Bz c9 #0@D`]wfmWU{t>lTUl8CS1#0ה={"={4zfF`Fg؜VrGouͮ,#0@GF[ngy櫯 m_}՟~ind[lKgkmIڠ tVnY`F`*A@oKɼ˷ےJ <#0#P-Wb]-tY#0#P<\l`Fh.쿱-B2#0Pa3_Aeu{Oާ~:N0#0 0bĖXz}b^(N߮kڥEhM~ e5+ WzJ|[p~'8~jZ%;!GWaS bEOj={^!sW廏Իbeo2Le4Lp@xOGXQ1!TQUiZi$?r`2+g?@9$扭7ÄUCvSx5>Y%u&ݮ;bĈo|sO:u{.^v–R [JGu+&3tii8|O\\L.D\FGLysZѵ5)mzGsܳͪTIFF; 7Ĵ-tHQL~c_S-ox53#B#mˠ{$ p*l'LU4kS^UP^.TڪG|7L%6>\EU15V~y!YkOZCs?y ʌGAI,r|} Ve3Ӯk^3ȃsrd *)eeU@,zIxE1_1^h*-3a5ّ'aȄPY $͐TKtWӋ4[LR-2D3R%Tje=Ѭ*zC0[h]'G 490T `v4RI=?NO ӷXxwmvJ~h0e#cDɌ>c rrk2=E^YfMĎ,%!G"DynY #ZHr Y~ܺhYOD~?|36To(.y/QSp]D,r2#f%>QKu୚cFi+ 09x^3s7r߾};[nq7 vwV6V7y*oqxg\X^,,YS!טA{mGFyoe|)/]3s8KP!OUMj~rA[Re2Z.l OmYIz~'g]҉{ie7=3[ze]NTs薿z2%K_]Z?Jt:0S~xr`*XP 6#US;rEſ|K,.]:gԒe1#0@~Vz{+uiQIENDB`filius-2.9.4+ds/src/info/img/email2.png000066400000000000000000001507631506574241500176250ustar00rootroot00000000000000PNG  IHDR sRGBѭIDATx^] ^=HH"҈=IljHOv"bMVUI VJeBb(%$Tl !|f,g{s]/;sf7soΙ3s+rއ?>>xΓ't!@@"0lﭻnQ^wQq=d+V>;T&g` ZB\Bs~*RD*׽~穧Sb"2^U#L^R$[* c,RnP}du|›=@ʄ,g,gh6t˔&}QniԁW=XFiѩSOu-mYRSz)%%I<U}}w___[Hvm>]w*pNyۯC~ϿNdq/z2݄iuS2r5sRACE<{?bALϫ?^!(;Hޚ B_uQ:J^VEFӍ-(2d!y*..x6 Q]0bvU%TuaNaطhX/cm/J-^_r@. J4h#6"ǡFfɔ"P~eŮRņ/; ~@/ +zl!B•8vSq%K[jxFa%p6x.czmt eQNSO=˯! ӎ8}eUҋD*ř$7_pXP^veoo v A̱n~Lux}6WYfʫ~r@h#-Km}@߄(S\[U)Hɡȕ2\beעHEQf\"/*+\*j$bU%OO!][2J~&O47@$sp*-ZUPA~!NV^GXY䒂DvEfա󩃋?ٿ W!V6_F BLlAѷ} )xZOz˷@CIh:Gňa[9upV=پC ĝ\WDf lN+BR?5a-Fj&GI G546xF0 ĢjV˛vz6{0ÌejG+2b,vkvX6.T'Յb--?kjYB&D)Jk ,j#˗^7ՅWeD˘MfcelV9qBby@E׬td&'T`,p(7'9gpK1D'cg`ȅ(s#䪘bPQ])IYueU Wʊ8+;*PSS*.J3|,Gj8W3ÓW@=:/lu]o}1Dcl A7K=2l毇j'(Z+C-a^yKEڪT G҆ц`<*7Tk$ i2? ahWv$ L+*9ʥ\Ƌƿ,VWYS]e]ƝkYTੈ\kAdrB> V0"fteK*mspMwh"כ(s ^tc=?Ό:Wze^^fR//mi vp ms`țiѣ_NTJ 3R +6yՌ8He1Y&2{\6vSlz]й_vV)`]Va㖖n"큾iT hJ)RZXkܲ]+gY~[5cb dŠED*f=(BF0OW/%4p hY D qDv-P78@W U`Fܸ=uͫ0,)ڹmSoi~ux?XⳔ黉kD|[tYq0j fBok?@! z|M_FN]rZ{NSoro~/oݻv.Z w;\%Ugqʌ#NszO/]C{n-9o1W3cw|Хc!Hx4;w]L5PLRlaDm9*H1;_3h7`+bF# >4 Y/eu#ÁȚ";WʓU-u-Jle(0BZ(Z3&Q ߴR#g+0GzEE3RV7"Iڣh*&Ty+6[햁N$J4&B2W (5(T$EQKJ@(2>Sb+4i7b ȟj(I&RԲ^1$6i@!pgZ6Ixz"$mNr1ų@>jV3G-3a;dY,֖/6[W\4Ww-?$/\ϺqL3Z2'E~u̝K(+n8-1Z梵k^fpS'6fzѻ+fh,,JX YD^<_;s c`g<)2LzԭX )^7dy=conmОJp˱|=@ȉtzӀ[93wZƾ'dRw~0҄-1XS^ ix%E y^DSC"YB)q]eȪHPTx3}yx|l . γPsYFF'@2Ul7VQD2Xn LBSAg} H;KFR5y O$ȟ_FLw%iGɩ$)Mz8#UJxPؚy 9ĄW QCNkaL #IZrg#xpuVF QQ?ojOoKdZ,S&>1Ʈدvf|:5-#9-ղy]I^{m5*&Uuk}QUUաCe_v^]/_ErVcr#bF H7Mwy{JƉW5';bSdp`jIPo6|QAX6lXCXG"YƘ m'̰5It Z IVu: : e OƑ:rT $Ky.cǞrK,*X)O?A~;>,s~R{fsl<@ M6Zotz`*_*ftN1~e (;h;}jx9K})L3sˮ/X6 N+'I}: .Y{m>ݾڳiε+}(1WUtCo~E'yV׋:ވ* OHa]2No?j}.wo8OaGEOj?{ߏ:)֑ U;nNAԣ7n?>e|GlG c_0Ջ?YI?.䓙?jXpz!;Oٖ9'%y~~{]'=xG^hvmciIgn|WVkן81CJ£6j~[|y çqY^=:b $[F+WP2D3#8"\윩RSe#Bz8؍4An^BSap+e\tY%_ 滛ΠLR38RMҼتR BVo.iX[e!|mчZTeɇD2O_g`l9ICb]'Y wbz?e^,_Ԋ6SBGq"YoYgp-_y7;}ߓInÛ7YħdALWq08I b[?ߚW$W<)d'h@"=򶻘6Y9.yhF>&*D[͒y0!j=t?'}G~ψ>6#}OV?v'nwo lCɎ6bb{Ax.DMO\j{W5>{Lk&z9_؊-"Oqy{ lyjZPZ VV15EMse6Q+9dsO144;U 5H.־,4' ZIb KI MdCWiΆYbHJ2`!9mCgF0ȏ0FlL4T@b`jĂU@2zD,)Xzakmg=*tev5P &c}b qW.Hjo x/u} +ǻm>&VY?O߫W/VH瞷6;O{FoܶapN~魛lx퓻o!?6s9 6 tt#vt߽7qmQXe>YHCK^mQm*Hl@4F7RpYo:JڧN#%x|BC!\  $S /Ƥeq;oFO꠿(X?^d7:AM?559j_O|R 6ΐOU Y4i s9e͜bb}ZQI`N+HS6Qx\J*xNӵޜܧ#iA(!%ZV qŴ㘩M14 !p@d{.%LrCd2k!n9\ 6Dح\#_&muvҩ[.+tfvҥK׊*:ع[ҹkŜ/u}͕瀷ٹLZ,%]'ɻtK:udvڥJ嘰;vH*eɋ3U+bΜ9&L?~=ѻ3ӒCwܨm6T|4ĝ^~`V/tۛ K?{|.9C]>}>zɧ_?q0F$O|ͣ C)d-}a}יyȐ:vx~m[y"s݈>Q߇6/$Cɑ V+:qriU#|CF :XUEV vIáԫ/g8~)i+‡cAT`7di]?cѧS~Ͻ? R\3*%UV\աz{osnubZb*;uгGثGuzg=֩b:TTgq~̈́>]CevHU"r’ӫ֡:|vD]{_zGy!C }NT۞ԤG|awͱW>78j;_Tgy޾!R!>q;oO\jl;roe1)?a^Oۿ@EbT-ۿySv>c? c/,fUְ:2 d1o\rz>(y\nuktҕ\>!?޼Nc ;.iRv_G^drJ(f+CvѮZ1,rW| ЈѡiҒ6n3pJvja\jD8xOkxsYZ P@drxBiktWz WE :j4 ZKѳWmmu]Yttة3;vb'+.:vI:Yjf+-qa 2X`ltܡSνegu^tI_NGv2^Wպn벋g%gg'!LEGvϷZwгۉ~a;| _,[M ژmrG?t܅O| =0ᇭ`D>xK& Im<#ϙzC!$֑?]o%vvq?XVf=s87dB}^rشCEP,ߏgxICo89w>@:[m8gΙo9[kO6>* r؝l1'7m:"yamW<0w'(|{]_ ˟bڰXĢ4Oӿ}߼kD.cW8zwZ <jVacmmYbNL~;H-q?Y [fx؝;Nf|߻0O?sCH?Oe>t[J/[nӿ;޽{ݻy? -:j3ؗGmgXe=!xf O4- *Ȑܒ@_E1ʧ/{v?!'ԛ$W\_`7עT/H;:g ,RRqU:Yvᔑ&e$$'[`:Ll_!m3 .آ۳'ukds_p[L9X$9#x=N/#?QBiP!$ͳs*s%L!-H:ttM;`-mVUu^k6'қ{ےx:fGfq hfv cc/(dj}0q>V&>jH!Hhn+r# ^s#P$.$FĎr:n) \A\S!*HI@ӳ`OB1UdP`}/陷%jc}zeF"} 4J:T9 v/\P1!1TVZl|`ɊIsC t]@}ݶvCyW5y5ח=3JϪٗ)kkYsMB͙C Na2q kwkfz9@R/N.f/q6PfL CQ)|jD$ }|2ŋUhP僝<Ktfmh[pۆglDvkϢ^գ9Xt~B+77J- "'`c*ʤܚ9CFZzpG_;xGţ7+E*rYC-ͷrf 9X"j3 aTg$6IW$=T߽Cg> TWrĔr1_b"痟z_ȸyժU@'=n}aCx 0Hdr SH.O+._fP3uIx 4 BŴ2&D%U%s2M(17ܳS~mTPf1Lš ]I}д*{!zy ML86z3ZVmۺӕ/hASr0g;$jy<`iwJ#6N6MЁfU=Iu;0p|QWҞ@S꣆T!B5k[nj-D1C PF{]Vs0j9Nİg7^'rA*Dy[LЄX+\dȣ=W[L)C,e>VUQD0f,ŬG8w+K"K-Tj l6[m+%շqJZmQh5f-`Aρ.bHW{M6֞~F %RLgI@迼yԇT)v/Qnj^,ȅa#гD7m@0pyxs:DeHZ̠%9 Kq[svaZMT0ٰ(H'ք t+aZiPeG97hkDON!hS>t/PZҲX) "ds(99չ}tAИU &rQCKQ3@J +3Jõ腼F˕)]?:%QJfeL eO}[!{tVCKV<-XUlq5|d[ne?(8F$(Gՠ$UQZ4tar<x[zֳB~ \z: (n $" &M@e1]dAzzV7|uV-2:v9B 1XQ vN^jJ +L>EsjQ#D9$,eaTqtԿTiPj@q¸"? nʪ*T^D?CC9Kɂ]5xB}bE_ ?$C!mwJ,_o1@Zdx gPShfSUވy nUsI#c dV-٭U7;%)K "fRnZFFn^XIjLH)6bc =/Wn_K3Y }z~hl$cpl'B("̊~-!߻E_E̪!gЅ>+!eO "\ b=艒qp Uє-K2"zNۥ#m4U g04.En#t8xs)7rVmaWbڼnl=S"ebh*'KYXrHƚpV 3J B-L,b!wuy")CG8MY:O*A~ $QNcvO) fV{5uWu+IW:{qUfeG\:̚G7 GwTWvEa=3 еQm٦[ڳe 'g3 gh-嬣AuZPSʊ|c Z0|,5OQzBreɥ 8<=U-Bj=?ZLq)]6{@})@[Pb]k0;6UcO=qޯ<.|zV!k8H:OlF|I!d tY~PZ2ԡ2*$TX{t,t*f&iWZSú#T nг>J u=n'm诶;=w嶇L=%~X7b(;QD,{CU& j7WGH4d+=NA jY͒p^١#|Ƈ@S:&Ph>P޻s,ښ_Sb+;TLhDmk,eWj&cSe޾Rd+8d`r5Lka\'YOfXU_~Q(Hʁ2b^z_;LnSBp>@}@/[PJ3]@uձsJo;O8)źPg}f&ZQE({񬟹l`Sq>׾pir\Wā$fVj5rk@p 4MpMJ^a/XzWN;2p*KreOr~>~Cw^zںP"ql~O=<;Ñh"- ԭY4$u5g.ܥX-z@-ӎLg?!o`5 VԲIp>@})boH6a;]75y^q\=K* &Kre{vͬb5. =aӠgd|+>tsT:7yJ-75s9:=ak/y2>cT~GNu].dEamzvf_\rש?99) VNE٠)?}.ɳ|r) NI=M+sS߃N'F]5r`7 XOo‹g&y-g{y8pؠkg]G:X\rj^N+C$p@ v̈7v^.#ړ&b!,\ɇ3bWvٻ̈d rXrmŞkbM103+&00 y!KimnO;8k;W[X_&?ر_ǟ2kFP{Fv6յkXpQz…[ Wa̹`;Rq 9 Õf};p$Y8aΗ:7D?{cf{ǎ2yId{ɨ=vL?_vWarkw&;|k7ʣ7CB%eY ™\d6h sBg ;݋\df:ҾOcRŋdSwd ҞΑslhsUS؞OK[./?; 굌F׬)d}7&/sσ@{zß9+9#?=90@tE0U˞f=ʷP` **=p ݰOyx'wYrBNNLzݖ~*;B@8Ph>༎wwОᥴF7k'>o^Y9}Nb6]=w8=ˎkEl3b$^BTv.uG#]4ep^Ya5~573i >Z/ ,IB-/qrgBMqQ~6ZiR8"Rf <5(4JiB?[y^vEߣ ̚>}:qO߳{ԭ^SoUg/?aqٽzkr][|cA'_Nxk}&CӒ&ߺ/jĵO}.kX/uZb]_pT?Xd-2%)λu3i/MyOQT~ŷp&Ph>|}О_J:\dۇܓ?z3N 4uܞ~_aF{~)W~H Pg+TH~.\D8L(a_[/Pwȑ#-R^5Յuܥ"Pl/óyϯfoѯ/+b~y --}[D-]j݅h{A{9`P؜=l-a,‘}x TG{kbaݜ )! H/"GG`I^{JXdYH؟J,#ҙSRVӴmGxd;0кoCO#:b&((s:_{_fmVd m~7zua׵._;b| Kцb^8;"UDHva?&c']dЩ!MR!|͙ŒM& 2z^#s?{װQ,Λz%ώ򮳤\,.+Д}y p^L{fLGZ¼BO8l4f*돜4J 6 ٩o6d] {Я^; x{/[ˠUYSŸ|$ǚqFbspon~Y~ Yf?YuzvXt|Gk?_Y+ Sp 13o{p>O T^;kبP[-r5B8ݡX{g&S&"@B)My;k Ss! 8/CLg|oGX)Uw?zoԉ=]}Z]5]z9j?// KׄboWoʋW򼜿zLH&LoD25IVQbo =?&vLHC$is?^)Qc/BEšyk/)Dv~w H]òuygu?OWϲ/?ObܜtULn_xS)tM8P>} e%_xE.s篐\ CvGm>{ry*yhG0,XRԣj LgΡXi\-=mJGR!@[<,e􎹑eC'Ix='}};HzoObh?غ,(!@!"p߶vgqgE΢ .<C$ٮa=sVԿB Acϧo#lBoVBWq[g Hמ]N)!@!7nk=;l2D!CC$1r斊Sx;_-] uYYWYqE!@4PhCjJjt/Y\S%GK*T:G?TB!@{BG[ݩ!Pm5q 㛯hSZ <&?+/g[`s^PVȱq/RD#uPlԊ>hO,O1iVviw&b@s!k0=Cy} $2n "-2z=ݱ)_B 2Ca;b'IgAvwHu+_~yU ]kX;fVxPBh.4igp3?3f,l<Q |S*\]%BM#V,H˚]&pMq+d_e,$A:lXC基U]黬hI=G*G@s I Qf}*"u_MR]Yͼ<8;gm VY>_0o0 c=\*f+qV/v찡;xڂںi2~]v6i.tfDgť2jԘ1cFMM?Gr0yY’b@(jIR3BgYot/rHI³x}e}PzBhmxse]f̕w)@5oP&`<֎`]T!V+_)T-|jƻ[xjq* _HpY1) յO_qM4K?xdђEv,](?GŮ̘1],Aj2stw麻m~E^nHCd@{ArłS(߮떋 N`G?z{L \`qG0&6IϮCwQS[,YYovμY8G2XoMcfhP"GF1dxJ}bޤZ˰BGN1g4D3n} 7ȩNPX~+ǕX.zP#&e ¦w"-wyv0 fT8#w >3yЦTGN5別;WAPw,;{~2dn+*v>F^3!cم!sebu@EEM[cyrYeFe3]%HGr棕]os.0C;Y۳oB:w֠!H݌3M-ǧ,IHR𷿩gq7nK:kjwf $O==WHvB®&Ek}{zirޣɸM䜞Ea= Fĕ;}e\|ђHF"gQ(Ϛ0&L>iy A|*8]۪rVW?(|euiT'^[D{RtBh%T7#Wmih@|e(_v"8u`|-1n][W(\2grԳy?vK5ںZa.40T39tޓ-|w @1?3S(l~ͳp^JEѧPPDvܒëne3 `e䘿%cpqtYVnh, O}!ҕPJm~xZ^Y)^Z 0ͤtO͠'_c,IoHt.7{Ienó9ǛWV|y&D@{˖2,¢Ke̢H.ȝ_6r`s{c^R,%tn>ϙc)Ϣ*27Hõq[ۮU4<ɓ#S+!stZf]?ޱ)*w׍fkDU 3LOC-X,.]D/֏1NmdrFҵ|Y0gKϽ$n{Ͼt{؈3sHVqЋ80:8b 4οj](2tDRcGLx̒r!ο{O޿g`F%U41#a3&Q#.aX1 M_2vO=zNYicO6٭3'`~="WE[23w/>w?J6sIAH.HB!@URKpK)C:[r l_8C_5XL\ص,ksUSzqյo{u=o$^|sNnwo67U ^,$saUmn8!@ sJX:8?[x8&xc . L-p/P/_|U?hînşQ*zIi-JK!p=i!@' ##DϞ$o+,}:'~73y]=M=Ţ+.*,,>!@!#挵`L1ZuSxZW2 n"]!@4ҝ90{Y\`nfWkD!@zĢ,\"u$(mˠ"gp˓E ҮMmzNB JFc+P]V _zgYfdq4;Iװ?ca{x/.H$F3\ | Bȃ@.._%HU/[ېSѯncÿ~+p _m]$=/lsn0zm܈ʯ!оsO& )l.^gg`MzUްzf_l_*T[ψ!=L]E4uSa\\O=3I6=VlԳCN6ihN)6ܾT5BhX|z󇟡,b=e!Hr'XOraۛعU雙@L'Y@~B{c;KD3(⌞O1 ܼbŊm( Ekl;*3!@4% =sbW˂aUS^H(Ejo`ۛ9L1) ^FZ혌oe[%z#nnMy!V-W(Mg8 ʳK9^%B>0-Swŭ(j* ͑2fW&nn U &F ڨ++HEE%B[eUbHW|#s2ښ,]Q 7ʨQN0_G2dʲ\M5f3g\y9#S4B F SCi4mT;F$mIbomJ{a 8.k'8oYQ9 _ 18)ΛtPLB F Hbm\ٗP0wD% 9Sm@cgf hAcxR2+k=W}+b:~Qװtzf:'LM ?Y4RkG[ ++ 2dĽ|J;nƘ+f++:̨ /!S&􈥓F1_ &@y̘d V#N== Obi/cVQ3;a5eFBm#~7 ɸ#$3,G0=|%w|\/PؗK'\Ͼ_'s ki:I3װmIu6`|vxװ\,ʱ-IlThE3*NHx=/sAp=0w̞+Dʅ7sb~.ZcV 8rc*+gߛK5j~L2=ݤdb\J0JC0E!BHq,5},v~gSbj9ۈcS'1}:R`̊]/w4܂{ e8gq˖,J!K0$1!@#8 G0DA*sdoJ{\EVPyB笵A׳gQ3C)#>ØrBŔ!6 \;93`LfkB?v9dƝl5zesgOܭfK)"!@C E˒QeR0ZO9F? i-.]i}:囒^+rCTXu,ssw='#[-s O {nfB8) ^GL6vȌq,v]I휙͟˝*_?X!@yiP9ӡXWBkD,Vd&s]zyh[da5oWV@wYYyZMiazn>@N@ \!]_|gR21ys̬2*hNҠU4,cO.[+o;wM:[r~G@! @ۡw5MP.mA>x/[_ꀽWD;=op‡E߹h1?OBFzbaBN8%Eؽ|p\Vڨ$?W4Ww9FɃ}J5x1Rl_OoO'1Lcw x%@5LgS`ڲiu GN|qҢq|;_œv>ܤqNcBf/9K(:´,?%b:fhV[P'aNVfz,7g,pO'vVYpBŜ fd;Y[4\# 8p1'/SԭL8= ^ʐm-.ݍ qk2`91j!s@3oɵ6gu~g.']Ä9^*ϮaKAjgKmhck.G_xҋyA0,|']Ο6r \ȝ( W *"0K8yFt"dT&:?yn6\h'3g|S7F1,^nU2*+D˫goY,MB_V+:];: \\Ϛ;iۦżk>63wTb(bC<#f]M]aԃ2S,Į=<>T E>''0oj[ۢ/Vi ڑ<\n.~p?,߸4RaG+Nឧ/]J2h3ӲKK B R@-тh߅^51<5g繭KAm5ZXev k̩73FW^t;#;-bEwCkLE1B [@R"gҤh )ď`*'FI=t𹌆;' < kžÓ@8Ɂ}Δbq.gee峗v=1m=Yvp2}:Oy8!@#` )oulp(~:| 74 bOWgN=t8=^&Ku|$0@mǜGA~yu2eN|:&qGmJn"B P^p,&fy9D7]<=BbpILNϜ L6+$s~ 9 y?s^Mϐ ڧ/f[=fTz0?zDB $A5tG0<lG, X=2^vcAzat*QQG0z7ܻ\Ĩn0ݶp^/}>xtA4/Ix5#2|d i1#!@WK궎KdBmFf1m2%,Da@@RP doꊯk8@\9rv\,#X>E*V*eVoSoZy.,u︵v nB BI[}`"cuG7-27>*)㒎%K0Ɖ3Rf!@4OscF mg m7mkwO= JbkaUxnSw#B JG@MRtRͶG$s|>rN54>} [qMK.^JA!@TI>\)r%,yfw+"Tڷ_]~/VLuĒp;7szM-A4<+ 6+ S"`,^8d Bzo%ptLQy"l,}eRMLo>$&OJ܋yG5FČAyY!S]Q8r*|؁NLne,^j˘̈^)¸-~7xGPAqMS\ʥ}""3D+LNLCKߡJo]HkF]Lj늵uv;ا˻;/0}8.5=Z"6ntfQ%$ >̊;Љd;<ꒅgil*8WcA1r²$"+nI< \hԬؤSf@gˑGEkLEgS: X ²~+X2ݳ7jȷP-0:su]k?\QjvںF?\s5ãFrR 09cw|p@)'p&bd8N7P/gnyeA? `eV~:1{*ozWaŴ0V3K)Xb./f,M & 1{:>͆<*f x+6Ĉk@ٵ"^ꠜhM?rL]gsyr1,ʫ d0,HYs]5~]~u͚݀}Bjc`zN +|3g?(Vzd4&10IE/9S\,%Ff;e|(B5j¬)G_@LT23gbW_n5dE;u3jfR~̭=ѭ2s!cl~rg-l?7s+e-N= a)waf&ل3˖j>9M"ᐄHyn1D \u6sվzKL*H|MoF[dQ 8tq62[tcX;VЊ]/;Nl?"lߐs<~xӐG !Be""[#Ͼ}k?o܎.s *=7 pc6,k+73#,*8i3'1,71G~Qj Ҥ67 !@4>o'KO[ grKib-9葮u(ss3+kiFr \ SO1il]psQbD5~BhzM&mcь=#H"ZY@ QQS [D?/=K[n/@#uՔ^\u۱ էV&1>[qM}ՄJ@!P'1fT,M7Խ/URcTZA82[ƾ^(F("kTãѾ u !@@ C  q 䤻u[c|o/Ý9dW]#_[X!Ћ@duAlU )1M)!@M(QƷt.]]Aj93mʹi@3bhEϤ77M\B |2IfXq ߪ[ ]٭bp(:?(I Bhz]ò?*'lC;Ƕ 3Hׇ&J6}{P!@z;Kdn[=u1 ʶspsWX9˫Dm:X+0)nIz&6= !@͏@ |?W؀ wJIܜ"^1{nH% B#L_Q7RBdRce<ZFoh*E蹽?TB ZW͑b)jkwӖqp[et@*!@@=I»xRZvÁ}lBTamJvF&zg Be"6Bd떿eWǫ+pHl9nh!ʱGo2n̮J"B}#P#Suahybَc1$]tgL1-s~!@WcK/^e"߱r,b,kҹ=2< EtL*!@i]a.ψG +wlm !s6<,Ϋ>A'BE vBfx4okNHJ=f{/kWOidO"  BG6U : oɆޭ,v&LeC+7_ +7T_:i@!@\b6T4S*ƔĉqJGWXiؓq4(6!@@#O4[ہb)a#^GR\}brBۓE,Q,DB @w3ӚV4Æ^`{.KB B@ ~6\Ǽ=2X{^drl-ݬŬrp~:ݝ DύH$!@@i]HYX&,J7Ŭ.(‹]mv-Mܻ뙭+dvMiaUib!@4F?^*(w̪-U^Kt2屳7w#y: J!`HsbFXmCx`6\)CVmnzY Iog9V/=B!8hcv6 +[)9邡qZfGLBk(D))B bQ7yeZlm)^lJՖt[N3D@sx&> BhneYEPBQ xuYֶֆ #bİ-nɍ#މQ!@q;~LM]T{]?!@͏vvq9Mg#ei:k{% &A6Ds@h[=b~,VET~dZ-zkt蹽?TB {[再ԒV0-H=eam"aaa->swK*!@B A%d͂YX \>\5NK"Jߊ=SA!(#P;u|{E4iȺ"˔c`SR;Z8Fu`%t|?!@@`W~L9gp9īmg4MeE5˭ڥyE:i}gz,B y\kT*砶%R\ $HHUmO܂{x'znNI!@b' \-^ h*۵eHTFvǵbt/}nC!CǞ¶3=!@-,/ˉ* ̑JsF @Grkv#װ !@c%F^dYYu+9pg+8ZO.ܝ9PB Fuvܰ*:KnONj:՚AcMsx@7vC|B B!`o0 ;^So_񴞙NWuZY%r,Ǩ~ 瞉9!Bh6,[K‹.4^`XW'Oj'2MɠBцLipq XHckXFʘ B@!PL sO! 3v,W.]8I8'5 $0!@ fEO+7Y.:}3k5l4wk[R YLVЁ!@I@u dxht`2TR˘mJJZvUSvn!\k|6nc`2nɞN"Bu!I4P*5gk Cɞ{dڌ8S 6 sTZB "z5%#>ܭ l-A'2VXu ܘFue{-#<4o+ζ$PZoD!p(ȱz?~det9dYkXv:HsKTB '; x*fFkoGgԲ1, cZEͨ{Z,D1Z!В0Sx&TmxGSzEqFdBHѶnK\6nCkMa znIB@D;j/05 yYт5]|5O˖_ljGbŲ$ VDQJ!Т@ږm+eZ~X'nw ǮX`9^`5sؖdo{"C0!@m8R'<݁+J!L!:GQYI BhvSζo(&tt^=P#!XhA ־cfns[SRBh=;w6pf9{shS1uq¾cJ^~=ĸM-KeJx≠.C'BhR÷7vqZO/mL cH{nnGY!@#ໆaMݜDr <--qX:V!SK S5RLw2"=3C!>T|2f+/0E꣓ nwjvv>E^ҳ67wQ!@+yS1ۛZ0Ni{JhxnܰmeѕѾergPB:uYгL;TeB Gضk[ *tTfg#ik1EFt (YyMAn)Q =7Q B ')n_G B |@oSQ~Hg9.Jg$ja?+=+X0sϳ BhBf3N>4r{w{SsgSY:܄}"BPD|>o"'^&"iǻ[;+O1GA7i*6k+vF>C!@4pkj=XKԙΪae*ftW3I Trl#&dl|皮Q('B B($j-^ܾ4g1Lq.m>9n:B &ELTЎ\l!=b6# ̺l:Q5CCZ˸-gRsR!@WEҌ%eIh9ls{:7mX\SEX⊤̷ZX_31r{}$ބ!@r#Z-(ȯפeLVb)ĵZz2ݱO]%e\ZDϤB!@(!aKCZ1ҸgҦ[qS Bh}?Zh YV,j6\FV:,^J1L)IkkH^ެ+2ۊ[ewB!6V3Irrv#j̻br* *0o<-BjVbrou^V0imp!@ӞLe]qrB%!+ qoC5ZЎ϶53!o Nsϭ#S BhsH㶭ʅ"PJ%kv"뒝t.OS6 n=.N"B!J_nSU0@{nWК6TB Z&tSYjQG5jʫKJ5Wpְ?Vj.VqvJeOQMHF ¾ 7d?[Og!f x/Er*Zo])َ T,IXS.ߒ-@5DowTB Z xG1mK”uPˉZFԫ5sI*k|"W7cX|Z`9{skTNB ^IܖF@A :kbYh/7CO!;ڪƑ,Rي6#GnOWB 6Kdn=]JJ@[AZyWӘvɂROf7UH`38`O1z ?àB|nٷ31t[TB Zis,AQu-q7b:׳ޅ_F h\5=K%%B= E`CCхUf^ X6&qI\*ŚÁ9U3x;E%we`~M`ӮaS B Z.BkXh9*$!@mm:ֶgϘl+s}V* &h#hf۴l\ĵlZ*k DmS BhcÚ 10mmTszĿԖZkFcؔq Zӎ sT@B :9<0 ðSe: QmؔЏ d:l<9m=-=^O#B ]-R/)ZjGJzHMXv Lvw4we!*z<[A"!І2tt1fh]Ymps}m\m6ԋxCZ#SSkmSB Zȸ]Qa6le!@jBj+ZKkꖙlgUg)ګ#*sxz|]n2WILw&Uwm*!@@c -Ŋ7&ZgNXo*Tm1."l▅܉h4ÕcKA&znNF2 B ؙ=E%Bh$[%TTI)=[KCbR&qksml–Fj$ B,ܵȆ-?03L3 P1fшB0o= B敱oŵU{{v`O]JǿF,*zfZ&i+VYC1lܜi7c$ BtNX0E=SIYc&k<8H3fVpګad.Q B B0_<jO!,fGnT5*JYT%X,[QMKk` _ a7 B1cbۼm%f)[pefOL OG2,cǧLdnNF2 B JBьcuuӞzj4[Z)q ʴgr++QdB C R|$;nڊcłߩĺEXx.4=3vpӮ۹H!@@!@Ö$!@GSK&v(!@@"@cglAa^zW  ;eE!Clii4c'ZXX*P˴P]W˖XE B U[ʝ BHQ/GJPll1sԳAL/V5=”#!@!`!f(F`8MtS"m+iME‘2xb˸vܦ5!@@6:sύ"$ BG+㚯/,=7.ٛ /^!\ǘR!@4:):jN)_8'BzpE"=7zע B  -{.mtdx,|DM]!>)-OgONfaJ B z#`Acr^p(z;fgҞݥH!@!=74$ B/Jbw':_J=lc칝pS!@m؂g)lHJhyyLͮa![d!@'5=7z̄!@@)ԗLI{.mK!X; ۥSqEϥgXDr B =gz{FN!~Obk9"(J #vhTsB Z$-rPh&zn(I!@@Ez=-R!Jgۙ!@#PK.h,=UA!@% эr(PB CWibRҲF̿\Rvr!@c_)-Asn("!@@ A4]2JI][‰[H_b!@xkD1lyw;o=z&v^(!@!rF =A!@4吙(&2Hǒӱ)s&p!@@=jNaY\lmnF,=W!T`|oIfX,LVbo3!@@`bMw,#K[9B ZQML!@@D3(YkUSNP~<,6*u?Z[!@4*fъ+ic"]]Fm-Kt^CFe$ B(rP4!@ $A"˛V¤.VO16bglƎ+hh$ Ba2RCGYmpPi^Qk`@hUδJqzδ7,$ Bݬ:n;|R75H5Z*UtÃQFqB /Ñl/LF4c\>mR[)**\lZJD!@6rklh0 6@]6g6_#;vX/QYd^F%tp 6XVM !@@s#0&,2Ygm)&m2cv6[)pEq瞉R!@w4;)_(ZԫaSVfid9U.$=DP Bh.'SU5$E4.T22* /R1,j&eK*!@@c!S}- [\uH.!@@A 1% er{b33)4(ZF0"2Z5 R%Uf[Es&U!@ ˉ?pHQ _rf%N!kcJe'SvxgECѐe;νJ[YRq B /Kd۝FsIMZ f6i%b}L]tb*<.G !e1nӷ0P禪!vL"lImL':V"sfj f3N5[sT`B *Ӭ99+SiR2'2V{ Ջ B)0Uʹ*Ud5'~}1[_E,-N9LsDw/4ݔ="BPTpưi:f5;YdM"Ω =31P+ܬFC 9 ԅB FE!Z/Sa͘7a` (ZS2/4Yj%7ZZQۍH8!@ì) u0Bv@7Md5r-9C{&v B I)Y:;Fi$*!@@[EO:brjWưtnf”R忎*,MJx]۫}^7!Κ1ywg{n] M@;@0~h~۞mR BhƤI3,@0 M܈ABT;hBE#zj>CS,j ^=+ypt^Tvwm5f/ֺ:zܱ7o=7b-L F$h#mn90GAcJ: he]K4 \t`ySZuTtbm0FǜŤNfm}m ^Vo?b;r;kQ/kA0L߀FD$JBt}!P*ZdH Ә\griZS|m˨5^vG~R&dӹ$\*@L@dʬOb±L(UId"н(*!мHMו"oL+yiGLhU_q ``kg15/ .Z;xɯB6E`ںڅ IR(cz!XvKg͙w.<婮}#oz/; P=gOT4y8-@hHeY'!91_lH @4][] Ѷ*Qg^k-΢eTZЕ֫Z=-C˨JZXְ[t[KOt ꄨw7jpk1 ywƲwҢzrP=gfi/V/d?#anGsgVVVN?OZX4i7S NQB v6?wO"U,J9~O>~˟ UЁst :%>Gtk*"!М6 0>+SLivkiyK`.2pƮ+uqwیg.Ugk̨0I|g>X,wNO6aJ3Q`~vű1 y.w$<佗LU5:(̋Nw1Res>wf亮qkյL:c~RM~H%"_8ܙ7hn]CoA{6J|l?O2B B @˙4;DKTB^7Sfk3\凘2C5;FiпCuPm{1&znľW꫙gMxIf77+;q,p 1 gn`A%xg$9 &ba3$[&,p=a:y \0n3q NW[ù< 乲jA"HueJIZ}HB WnЈݑD-5LYX%bk$UZn ,Ŭ*w{3L@r@s LGmf܂luOi=`-Yh%ek}{zirޣM1sMuF\_tʅڂ(EfU#w\6\< F`0~]c&g>CReX83эS4*eеq,^ Y~,Phlb(JV6R <|uI}up*-!Р3TdU)T}\חIE% -eL<'YآK|Ѿ:#yn³,,vFL{njy؇3>h >K{AlVx9 ,$O{` _6od1Àp)vFT2Hu2dXR%|=bmzN2F=2}-TivIwzUf&Uwage,(qɡp`a5;t\qN (c7KOK &p%wCF/?R ]H*wAW?~a ~(r53s%ZU`syu}D4vTޕ&5V JT'EKb<Ј@j Gɺ>PG ֋mʖFi9Uapljb,.6쬼fjЭm5Rw Wj]1d^zgBD1mܸq]Ȯ_{䜳ԋ=}]sozq#VX3t፝5oc9MkUA؏kpe_Cɒ_t,mt)68!Uz&b?2ޞc.XBb.Gtބ k*]ʑhw )4 @OS'B5"i )Z5ݖHr骗Ҷ/uTjARo4KW2lnNiqع5 !P* H!Юa q6i \ R!2k6֒mKVvl1[f61fּ-lM2@I>L!F@%tqJwI0=Q`}L/8w5^';hB%# YyiYlׁKٓ5Zw ..Uq{>z-RkĴv.8Ue"ЈݑD-ƶkThOPe#+rvoq1z] F S!e XܬL;lfO4bw$ф@KFVSY/hO &NuF/#+64zKcBar ,te^GInv&.%*[]r%#U:%? T-jMC?*M1wo<Ф1kR#.7mnVDn8c܌[yc8Hij\pʅ|aՒ/u{U hsJ s1I B!3TЦgGUd)ޚCך%*Vl1B|/D a\Y4=1ͪUsR6um'PuB ZymE:YheFn63j 2-f}*q ׊3`tnFs˰ a`m4 :. 6Db|n6spmH]i֋-.W\)Kqo'slMa[Fܳig6z!@ži[`Ҧf6*5hӞ/y`kE%EH:7ߴj Je$BhhӵYm=Udf`P˳l3Q񴹛jVS8em;flqi7RV܆5셅ZA'BiП=q3iʼnx!/V[eBmVT*ג_/%6pϯ[{il U0 _Gz幝${m$M9)B _5KefM&-/YS0*iX܌' |3Юd_OC3J5ўy6κgȁ'};3B %HF7i4kv Cg@@MPXХTpol}&RR4,Lp4n.~m\P{KA!@@Iyag@-~Я;HK?Zd{Q#0_懶LRj2z5W<ԠG>>ܳԞox [/VC!@s=,',9- [vrLYȱ MxZ:vZj+`_/[VnTGC%8eZ݅@A{y% 6)6{o rx&{pƅ[|5 'N UAŶz7$RzzU|0c `$n67+zsꦞfOĈ;[y8>e"ą|R;14,ʈ֭w:4~Eb#8>N9唪*v|(gC #AFBB S{챧~W^yWy]曗^ziϞ=SbFUsBmMU!@qFs9'NرcG=; 4?ꫯےk ږ۽mI ڜJ$B [&As+G6-۹s` J̈́ebo"'Bhؽދw+y_{2&]È}#B r#Xk׮__|hٮ풯MʾT Bh0}Gsϑ#Gv҅3ۜUi2z, # ) !@@D`;Ӑ!Cj+XUUa#աV=E!@(Ƽ8uUR홐&B "=In%'tҰaʐk=]DIB @Ν/zR}-|gm-?_o.%}{ƍi%eQDB B(-:^nK|ɀ!@@C!~%B!@e!@seFB BkHsn,I.!@!ܩ \g_-"!@@9 ;/V O%7i:V[VTPF Pr9EOIoqcpm@/E(]H?/?Ov".]X{zCfoŊޤ'Tߓqtoka, |c;(\*#o7'GJ+Nݖ ᝄ]"{~C`ˈj3Ll(,i+w3⧫~ZCɀˮja}F:ZZJj4h)g (nӰqMXӼqE$t3 ~)^^Jѕ)mzGs͋*Iђ/&3-`w@=T0DPW({4 "zH*%E,&4P-2dN'$3uVE][ xjcc<(PJM CtfZQ&(Tcԃ6Hg.]'{h x ּS2r9F4;6 'wjixL6| 09p0#.=vn?믗\|=Z@A1,[g_P}#+[Ve{l5UR{ BY\n%Rкh-vvOi*FhnR CYu/9ʖz^aK`$zW=NDɁuSD1+lvGjb׃<__T߿KϊÏ:n]\rɒ%_.+owCd!@@%ɆWѩ{EP8IENDB`filius-2.9.4+ds/src/info/img/email3.png000066400000000000000000001644611506574241500176260ustar00rootroot00000000000000PNG  IHDR sRGBIDATx^]Uվow,T)XPc4bhb_QDQ4E#Ei"R-3s;oa˹^N|sΜ9_=UV}7+W|?SB B`"pCrm٦CNό8뼃gğZ[;U萀#4F Ӝ f׌(Fy/衄s4Y|Sd g#+~UZwYiI)5Iq2W|EP,"zSY~$#Pen%e@*!RVҀjZexHR)bey~TW9C9!/q~#{^s^9ɴr%KB;}YF$y'qK0KQ Тᎎ&k;'! ~WUpNŵ+W!i]Rj1$E٭ C64KNLU;3NJ?бKv@)f%Pj\Y`:2o8~עObUVf]Uf[ƝAsUzoy@"~He3e%Aǝp2Eem[>9l)rhBJ-0 I0ӄ@XUXrTl0(Uk 0vX-5$ˁP E(=s].hTA)='#wҰ#؋>0hx6Fi8/έ~CNm-.3LAV.S b4ROiܗ^&.:l v 94KH"Hf<&f#FDg77yViI2 h[,NYDișVKí = z)@+.ԉlb/aAӐÆguםXܶ1ͫQ1]|Anŗ_vU5Z;nѹk7yyn쑋vſ_9L=)V}fL":w?\2cאa=[ {4\GElxh{|/jd楯N~%g=|E+[$CT^n#ó"l)uSи?ѳIN8&0R,S -FCmMA:`Yp3.}4 b>&bNP r _J&5QIC qKIO;mS!GRۓEMbPHE(&iݒCs#) `*%MH' =xS mfgVb^X{ެ%c[TmZ6p~oTO>w}.yX ۶jEgB/d,?_ ,k©B[/?/5XF]t_B=hAmO71=0"[#q]{jIt?h- 9$]%M^M8wZ~k&R'RJx+3{%HjfGVrSgF2Dd+XtؓYq̣ɮ ްrÆp ns Q7ò*vUW8U$A(-9a9(2#x|~` ߯ݸfƵk+5׮\}:~WkT=T.m+C+;o,Xp}$X,]qWmXSɦ2PQ`mi6_fuٚ-eiwLfr|g5Y$٦X~6(VqZx{ G+߈$%\fhC WWqXE{z||[sd[Rn*6- %Whaѭ:%eu-Υ iА!O ԈT(7G1|B'ICXHI>#J>`ճmĊ蕅c7WI|Lo ;ŊHw( I;>IRɉSQtKخ5*Vڿ!VrŊ'Ac" j6ʒ6[Sf*W= Y |Wp30T[oPFMuE/\s5lŷ7皍ٍ5|=xcuF*,+k[H7_F$ q83,)عp 'm-3>N]왹0wl~g6bͳ3_{~?YJsZ|_ZZZVV"ˮ׳畕$+AYK+^>O<E׿WSO3sD`szC{,绞qմy?x4cCڥ3F99Yv)G3͉whgHu=Y|rpUn*=]1F#wW/=7=좝ؿ+'_I/@l׾>|]öV)|錃G<+w~x\/Qۏ9g%}jE;2oy߽Ͽz.ay7"ް,Y8 Y31f]լÖ0U@ .mL1p&jMw00W O*jWAxaѦDBڂ(Gh՛W UDNGK&>JfiM2V6W oTU t򡃹x)4fW;i[.>K'4H쟔4&7­|ʫ|WW,[6ݺ :(ADѽs{7G/u~֞۔뷫{.l{ڶ˖ϟ4'.LT9 l)31v &Zl;lZpůWQ>X:raem\l0 J:4;xZLG7^U|YVPfmRz5ܪ+Fk׮UkJU5 rHp.M诺y <੷WX > # ثUodJf*ihQ_d>~~-\v9ch{igEلynz\)ETŔ9K?۹N`:>wyw}S'=mkɣ>pGYݾ6ma2n&/~vװάȟ>qO_gOq Yeck?󻎽|8KQfmEAel?^M9wCu"=RDQlnQe#|v#+$᎒z­^ơE)Tԝq̆N6A㨅"@/#%; z jM eaqjXG|#XlB-s-\s=UѴdey+4f^s/_5"N₰V.[o.,&Ip9ǥg搔l H~6oh(..\GqB}25+=+-zOO;uj#[߰73DRFeHX#)u;4O/~%G[\ęq%01,_Vpi{v#>xxD+|;8'w8#;[ŃBAq$LXdE /]Dek3|͂=q׮x3x~v?W"l=xҧ_~jqg]\s,h灿)O`lWU=O:.xw9'W9yzDEgēWLP#`7vؒ{Xor>;w^\O91xKV)ek?}#M?xcl_ ۊe`~̿eF4 q Z"3X>S4.tq3ܣFt- *Bo0@eg`ԲPi9/x W,\jY2!]>bpTŹ#PKR]Sc94Yq 3Va%ҲYYIfAf%-gZ5/mݢeeLA֙sE+j :t%qfW𓹳' *y2-aJTn뮻n̙cǎ?7jy }<=o㠻r[l՝qm(].a E .'O^s^囃f3U=տ=G;^us ξb2oѺ_}i=QrWhaW&;U*.XYevpS0/=~z^]xL$qB ^+b 2_{ h ~>HBbZ,eAY@I7fGw̌GVѰĢ3U"3LJzH).ۈҲɱWK5PYAh&66ĦD2NVYJv)Nkfq/$ 0BjDr|+DiQе ho.ӎ|()vv׿6Zޝ_7*o&y>8eosm:P|C+@lƞ=gi[ֺmikeL6%-۔imLۖUЦUкu۴޷_-w\ҦU ޺%ˢʴiYҺuHI7h֒^5=HízwhB3fR\y7+?{68 ~unR/C;s-lC/]'UCoUAp\t޲OȬ̂`ד;YL_cO|Ю'\U['y3?z,[.>7"gr6Sdvm߷[u^§;m>`Ll=m,Ncx& |b"xr`3zY]`œwl~|ߘ/%ZP/<ڠ,K K47"6Z(Cô'KAR 3-4Qm^$W|uUt!R$щn%]HՄ@FӰ,9ZN FOxLi,9K-WElmff͚6+-wY)AG?(~݇}3Awxff=mT.-kߩsmwݞء"igi^ҼyYemݮmmڷ+Ю]JݡC]{=mT9fpgpv)m6ckڗ籕P"g7KTۼcǒ6D޶jgڄ5\pꩧ7o<nm>~wosMOsOsuBB;enG&oe Fmv׾W{KV{Jwq*Jn0,nƣrL3ݺWo+ ~+p}z6n"S]Hz3qO{H펛Ĭ;m-`+cE+>~ɣNS ss.ogkqs׽<+z'ڛŚJy^we%tQ5h`c`I/*%ZiGG-u_wV#u]F'q`R&U- /n<򉹚+}adb:GsEPײ9iy#ca͜qhRLI1D5k׾u-7y3?ԿYsF-n"{8Ș7k3N}@X<ڬL3تEnޡc;4oI};4c7K;%;oLb\D,ND3ЁjmwbDEmv]z)7ORuہ5AM?Zo:BW>!,gy>eɅy,s^d^`tХ*lݠaQ\u@SI'w:`wĽP!;!ʉG=w֚7Kۉ,BdJn7W{w9%A28")7~9ti?_sR7oA8'G>?W/rc0]:w{+匪}jŋz̉~~?dk&um *۹n n,s',h=dNOBmVC8y5F&c)jG86#(8v@j۪g2- e#-'s~rr/l=xiTq$ cu)p!k*3DQ@ `*}"J ಡ:ӮM6Vڰ39G[ tl.I6|%_Kό[jnT8Z&3H_x&|^oWrW>ko~l]ܽcmyŋ2;(Upa]"''wQȭgz+7'a,+l~n'; !^_4z?olئզg'^ 1#f ś]'uppFp4,")S`t)}]~p:Ĥvߓ#94$xˋvOڧlWWlN'db ,0V̧[d;InV7AĘn 3ڍ34\f&x`@L4Z>N)>j.w$8 UDŽF'e#:gCjuE`:ģ5Xcx6kMϡflŠgCO,\q3sE֜FQ_ĩFo (JK0&I$ݶU閭˚: "Eݸ$&H\N7J2)ȦJܬ#gQ {`0HcffB7y~E# jy]fiI|mOT EGs8M:ҡۛ!P=Fb$zR$lބF<5:L#5p3ǜ\W_ub˳/Ms^|J%~({+9Q3+g?mþDE#aU"QqU9svSN}PV|t##>)*SVp5ݍ7, Fzca Y8 @]+$ڝibE1 49֋pw1(>kXCׇɫo0b*zrAD Ꜩ RHƄl#d\C zK+ACRiY5$-6"IQ7oA@ƒ+AQd>4b߶И }ȴF5M0P1}/T )Ev9x*a T 7Íp>Mޜi2<]0[#=,"Tk~,ўEJDU!VNm63,jǺɘj4CA[sT6RN(μ{].?̿Ѩx*|GȬEӳrg݊ϙU[>]zv]j"+&%ΔEZi6S6,9 ZCoE j:tXɯ"Zl@Cn!&=[{1sMx^j`eP؉ wV'tDT(6I;$tn65mS1ZwZDž6vwW7Jxdڱޥqe|iE2,=:qJr}q|ԴxE9Y4 ܋o\Y~EdjÁ1՗z>UttIStM0iT1aA SC5`WUk(4׹w9@Qe]I82AKT4 O!@K駀vW]8TxT-:3Mu6BEϟS/繗^y-U B  E`y1_ޠ#[﮸}U~Q8B B ^gIVd'6HZDq<з]_=_B7V(!@@~phż)3!i2f\=W'1rVݏM {O_'y#Bh:uRWNSz j8F/Uhi2PԚd1|[PyN%G* ʆo_yɪz[~<$!@@Ь҄JS8_#.ړ=,޻= v~|/(v!@!W g?Ba;r -&Q±:=C0 r g;{`kϽJ4=CZwjQ_, TWS55ʪl.[}V]_=צ4-ڶfF(,= ԇ6PafAI l6OY*2FYg3?['_=!.$Bz1f$vanfjq>ߵ^{SyYIJw7g5;fGUvQ͎>ǵ<{w'.Elk7TEgllRiٚ>7}Ÿ<ߺ[v>nc?s-mZCeNjз7C]& kN7rG5ʪ'{q 2ںofy6OX3hwi+h"VP׳MH jCwlѬXU>|`ϟrIY+6M0kML+C OMa >&S1[뵜/l]}wv|kέ`WVnٰҪ5/Q/0,Y3@m@}hyiBq%vMd.a?xwR$k xg(# K D;lM>yKjZ XseU5jA=ݶYݧ.]v䂚:]]qew+ĕu َ|3{f2q{Ě+crĭo9#b6R6 TsCzV2a‘@asX6]sÆJ7VIE`̀Ns* r5/q~N3h~~+bUu8ڇߘlbW~\F B+\7d;%WK3YK|J0dP6Ggmzɫ/OwwZQoͪXfne/v7mm_ղjEz~Um1_yd1ӫj=l<9P׈7~> }4~B9b۵Yp6@m>U`u,ԚҌA| \s7 @*)# Q$Xɬp+5|8?yݵS,ǰaK3޵gx_fiáfńgj=lsYd6U%[T/bw*=lQ_B%;b+g68xn`G5vؕ/ae}wiWRYgF>0'(?ྠ$+c˽rNcyܿעv-zS'ϱ;BTNN:mJpgaja2nñ;#sNc.]b=l3o>hׂ3ɸa~ǻ:G{gWb~cjRZy-[c~سo5M!?wݒ\9*8OۙF-y_닧~Tn]meCvٯ3[i[ZέP$\ϱ9]{f,XĤg~_|i&&? υs: u?r#W lAL{ĴYCw % ꅗ=5\X5;z& P?m35ݰ:q3[o~}4r3ɲ>as3I-m'M$*؅jwIɩF@>)r%#>GuGLL1;s[]| ٙc.ouY]r֫oKk][2W{cN=b_leՂݧ{moϹ~{)=#7#.1~/5/&gNxRPP̄+(,}IOfk^ِӿ;H' ï^R<`H˷j]!j xG<gzl݆q3{vܓY[@̢6c6p/yV,}6CG57_&=<S_yYPXݐYu~Yt"J(޽RHg_U5Y|w蘭feώ;V˪?̚qғ/M9=V6{gBzVq.^8zo;0(7 !=N xunyaھd G袚P?lݣd#=ȁY/\r92}ŋ~l?h߮TlLXd jCEeq Y~knf}| [o0w&]eL]6$ I4;,x p%!Qi)K+@ kT(% ]*: uJϿ;wZvs;|ўw\C|i _la=^wjqѰ~ c!==FOpۇ:}}λn-޸q SrڅMŐuޟni7$Ok?+$7\n^1\S_.٥?S dv~ jCpFӎ; gi!KkDqısH.=wr3 ?{*>f{=#S5bTΦ:nA:e qՉ19,.] vD=w{;V~a]_xa{tn-|f]T,wm{YX[*( = ԇ6]Yb\=ݾSpILWs5^eD#VaG{ǿ>#jK΂4xkGxWƨ?79vʷfZ/ E$$%[3ӿnQ}*E 0Kf~_mҪ_|qܻ/r[~} ~~괭oغCUy_fg-U̝ƼoޚE!He︊U7C!@ ::J-jrvdLĒY 1 B#- ɩ諌{ZIƎ6 ScXqy $d;etiIb߳0 Ӎ{ Ӱ?XmLwr-;  =(Zm)ܼr#e>Feö8XWj*)de~ ̺m⿏kfVaL2蠎%yO=3)yFV$}aU !.=d yDCآUP -W* ͏%ٻ6 cK4u٬ i!?!@!Q$#T ]ݶg!k׭tyȊMiլ&oc4Opn̊_#߄!@iФ䜴Q*q18Ӑ1H6ac̿HD-Sk4;H<=g< vX=VfL>o$Rnz\@BUn?%B !)Y6F+^E7wd#o5l-2`E(w@Uh @v/sx-Vx%闆혊9“3?=d> y%DHzP6O =̯Ǜ˵zy3`OQT&P"sv6 @kϪuy)YydXc*T.B 6'3Ƞ1Xq[F79 @HLaqG<+,1^1)tOjS`_UM6"\0?p}1ia1_=&g9ς'6-ooy;Rf2姎ƪ;MCXT+IP{/s*;](6`RDܶ$kyH$Fvgrgҵa`soZeM%%,ϕ yt,`:s#xyX6F7%zݝ?;f6r!b`\+ ILn g#ir~N6m)#~tD<묞Z+خ0ݱSF_r\޳w6pRrzKe`& {^5u=.Y0q lg_?!`,vtNt.k"[ժ=Tks'MlzͶȕ1ovrOzV/PE1z`IYR>E?_,V hCR#f1<"F*G8n٬= {1i=O+}ds^yÀe>䈒3ggCCmO\MYg-H@_:8g\cJlJb41i1tPw&AYGGsrVe|*6 շ|ax=\TYyޣ|QG|"4K LFxӚ}Xf` S*nfiO K/=Wl`טlG!ɡM j@'Ŭ&gi7N+zʓQ8K{k5LNM{jκ`#.lL0i9Ube&Hśm/byy>񕒲HFg9 %ڔڳf=+ʌ3~g=swujKhXӗ\|o=14ʟ|r2-}o2f+'1G3<}b% llrJJ%d8xl!3uɦ>>cGz|ET;?bzŮ]w|C? BXXoC+x7cm}&GW7|Ț1XaJq8W~[\TnV.ݫA3h];68ҷ }#}>opOB 8Ŷ7,(䣻Ot*ۡ!&fꘖstuKWY( >vxlv?KMکȄ!Pd\z^4 Y ydЂ׏ DN7$s3&=/s=UzsvZ͊o E!mǮ7__B/Rwo-؜s h PG lqцKyJp!@@%L5'^t \I%oE~V6`נ~gWߵC*Ӱ+~#|Ht+""co60r[(XA!Р/ߊRWy[O<6FNP+Y,9ם9.rnPJ%Bh8EÉTxogBdsU`=4슦!@4(b Qb,.͜=« AG%|6$jaH.g#:'TSf B IӍ4`+ B9/e$ØG~FҞTWc$plj2L!HXx8viwR)i7tR9񣓾҉2ARStڳ%=M6R!@4(|+ꕕC%o+nůܼr</~,￳pzܥ]_]=Zg-nPe9 ՘O1Q'e:X E62Fbd(Caƒ!5SWEyqJ.bI30iDxVP>TmްI |o{쵯gZwi,==3n_m}^JKKu!>d\VAAEEՖ[l]0hPЫ)ڹ0kw>L2}v~}꫹??E,|Ν޽KeWw LN[bpKEϜG1:ћ9 yHNQ\{gaduIP\0QD!cSV\KTs~5#|eee{ekLϞ${U>O/crMcXFCa_}o?KK6nȼ9W1PslOM W ;SoF\qy78c? {P; 󘔺zwrg.г^6Vg~=6?O96mʮXQ|y yˮmbwTվw anfYgGyyA<ঢ়7|҆ WC,]ԠOݪ!&9?YUuE%g2% lW\=:,қ6Ҷc1:OȐ-j["/YX PƑ;p'̚?ܺoqзk3˾vfv/[V _0-w׿nSƵk#'')2՘x]9 w<OW_/&F nU_*QDGJc#NuXW]<4K--FzvNxV|ˮ~5}_#Noj>c嗒.Z?lo}N3O"(n[uo]۴?q? ]De)u^G:FK[ -%YJ1K6tguC+ddwE.QxKЊ3AoǏ#٠V4?*jJ32ZohsS_tՕ+;~jy6{O Fd6q;hHѯذތs,6?#.}9Y|sx3`V~(G i[ֹ`4Ycy➣N(C EƉHG JnIpg'Y]6+k69cGn;[6n'v_qi|9=݂/_e5m:Zn'-4~h>ʯj|HAݪV, #,]!bsEXd$J( (g8\/Rn4Kѽx*:ԛ%BzPuA-b,owsRj٨Xfݬ="OjI[+V=.iaM+>X}nl}u*UW\S͹VW-檪74Քw t}?}Ҵ\>rߔ`2}OL"d՛Eanbr>dyEF_~WgK//r;ؔms]+G ~ي`Le/┠U:Tmb2`3y=N}3o&303ǀ,$`ʤ+J];Bjч3wvӴ~[^0<7WSYn:/{{29[l^)Ͷj۱ov(/d6VfK6V *}꛵eKg%J;r ΃g_/ٔ}:S9yQ\Y[› \Z:˨q Z),3v P<W] t+Sŗ w)%ȈpA&kkn]VIIɫW]xlPpTi-cI}-..!m0=uWFc/L,8RA{ k?Qb' ;4o2ug=iAh>s l.Qɔ:~ڴi>[ľYzo1켌0ylm"Ml1gPR|Fef˹\Fo܅5+fj%vd ;S*kYf6f˪6vzj}Y~QݖeeA})3Xw~|6:u`0^gM:mSG.8{B0rҔ)S:|2DZ٬`qq /yIl% Gvvs΂9zpIu_ ༈=Z|Y,[vRŨn+)m 7ƍ|-72I[GtY4)zbx)~Yl~㶓)+r0MI"`A2ێ8gD_u}^>##`V,c UJ/)#-T&i,8zYDSw>nKb\7~짖1郾b{b54g]Yjoj 3F#" v)T3PdM 1N>s'y^Xy˷^RVټӊ?y䲲vo۬b2JZYqܯr]fͲ.AdѴCV}Wf`g.,ggAak]Jx~[JJAXv3r6k;%\)۹*-Vޕ,XT g].2XM/ /WJMLUVR?As_|wk헙 \(M6JrcЀ]-Ldw+9Ÿꬖ1r!^gS*nfi~$R 59-

Itsk3}zůC>(bƐp٤YЎJf1jđ 1TlhY|u]VQbAyaP|\p#/1a7 (_~DIɔN9\U? EUU=uۿ*'7V:ƅY:C2:;y2g>eGKէ8?Uqޛ "}O}0Zm(6zעi#2lLÈi Q>´׾,j*3X ApvH)_T]d37kquZ7~ݍ존qu\OrͲۊݷrYCl)W\UoyOw#ﴲ ۂd&%ZqXĕrIʼ% ~B Jybik9p.+8=yś+ _~e{oٽRzOX{OgC,km;H1u౒b5 b3 ^{H;a{<L]S靖C l Z]P]{[7]yV$lٚ$/ryvL ¨%:xIHx&S;zm'Sw|4F͛_OYl~PȻo70ѡw>fJ]wBg`OuTLfWÄ!Cfڳ *ks&.zݝlIl Lg57:'#Mcrbkj咘k`a}.ïf$-_tU@/z1=̊' b`42وI`-|GS+r~+xbWHfܭ'+1؎47naDmǰSοf~B@r{ ?CP* <:Z>%YkG¨`L1+&43=+) }F^6(rSycTϰT%JbFgF.2)TiK6d2yϏβ\ЩMsyDz=}7miΣ.gfJyպ1fD[]+?v̽7+Xx슱CE3;ှ-:>iCncet) _A9+{,+ϷAoau[n<,)#M=5v1p-boogT `[ҢnW-$ROۦI_hpF!~ 2i;B|*o-Z>Hg^UcY,YX-1%;7s`,{7,˾mA ٗ3;i?۶Zۿ<]N6Wp١ƶF=tkzI8`Ff V0mVԱ_R"M}?;`]Br(S W8[ %7sen54F^QB{_GgC^5r@,E pj2w36M14ϊ۷>Ͷ+fR䨭nk۠[*W3Jjy!?,,ݽ9ن x]]? >ix sIH*/y{(, YN91u2# tV6Mx-!3>5>'cYgK6 fjv٥1E|5k>?'dJo~7ʔ0U-ZWMIѧ1tl޲+$` ԭdQS"&`vC٦[5 Xv,2sɚc Yo*;[yk׾qcjy>??իϞ^]m@&nUCN?x͗jjz[5@$ f͉&W/ڐu. Ԕ*K^!=$Hm A &]]~f7>}JN۹nC宻vyٯ~&bO1rՈ`2뻻1vodhy#;G E^Pa^7y_o^10zKAr۶kM N!?κ6}w\1Uɍc֞]Vmm[H;tmRz!@4~5^? H% aƖuo#" s6(+[BG`Fi[)Qk{_o^(ޢ-v6V\PB */ϗE'y!w<2n1G:qh8#PA# MTd&TJ B#ܘĒvɜQV^*fa|.4u%#L|/,n 6l3UiFt _\^C0#3 nZ -Cfkm0m " ^١ y]eV;wllۜכ(iNܰ=.=sN@Eoԩܮb_8*.a^轏CX\S,m=34vTK^D;4z^_N g{j)c!@ 4F^E3=S~K."C[% i_UOف$㰆)!@4`R=ǛY/VOR.oF^y/C驀m*Q3l76MY'B#h$gl&7CS\s`i}Z]Iϴ7o*!@ uNަ,$ʩa𸘱Uz:VkΩazwCb;!@@CG@_כĨx0%aVCy-/}ۂ xxC굧9(EBTrUYvg5rGgFK\ +\|7#mC4hEwŢC 2tde@gJzƑ7&7DF4W85nG=<[D1I[pG!P|'siH^2.ؚ̚r m kqۣ'mYeV.[]*khNό^SZ~/KPe6GƼG> , KCctx{&3qy z3u` t7=aXI# g_j{]`EgӤ(O:wd~R<!@sM>(W R|q^NIa6"h;,UuU߬c"3a op[ޣܫGL㧍?f&Κ>?3̀C륏7%3rDAϒL{^ЁCy>yEpaз( B J*b7ǘʻjr?Tl1`c<3򛑂n[0z9egU = Ys!'39M ]<6RL+C!PX-$}BKӎto$ו=yGėό@} ṻBGNl4/b`ZI)e B R #k%giuePS+e16nV,zV]EsyLKxyf 3_u|=kwJN"]A!P<%hZ+V`aQKSІ dTYN)Slr6fذacC[0jd2df֞㝓F2[=!9S˓ K.M!/#lb6͚KH60.7zYy/[0CJy{ΫSҭUWnX⽧/3\sE{^z#GK{η5AɦSTRB /.٘J>FR +`ws8p@ 盕v$+G||v~}}眹H BIWfϦִ~kO̲:R' 3qs-%B(zmljO"fEv[K9ɻp)$!@!qiJ$=+j7s\vVC⵳V-:tD0繫pѴG"|y'!@ kϥbz)~pFFO)*/9ӱ/.;!],D'89V.eipr+dkϟg( [{֞ =l=S*N:@F 4Lӳ.g{Rek%rt]qe"@r,:\cz~EHp=7B#B` 023ORV61eĆJƇx}ڃjq3eʔɓg!73.g_> jݠnJD{.ȱb€U7)L!@y!?;0U't+^LL8e334ǒu7UгLm8N",$aN4y|={g ,eB-ЁCM6ԭ! X%m>e=/߆HChX-}&'H-^i*bsY.X}S>#Ggit?hP`YgA噌iFpzSOJL3M}"ǘxbR_:k|;N%-gƒzwirべȀ˧q-ĥ;ta'0h6۳--=MdJF=8; v`LLxJD0696f3; .gS̔)2:NcT%]r,Ar!@lJhһ&7 JO>*ukǦcYìf}Z} >&E?ŦgJ\5}p@d ǔO6\m)l>Ehj>1LH,cREBц`35OĶx5\ wX# DZl=!SmE_{pX r#1MlL"dcttkbC`%<wLqyLKxN4&oH.6e)RvN6tj+ RݖqLTX]$&h\F(`--=30[2\ZI|sVwE|v?*f5CSuWarC3#`ʔ2E&:3=$LTgH+vPGAB$9KG|}R]ES{ WJoO8=ϙ3}Y]AM,MrAFSTB\y]7\z~%mN盌8ܣ+H9HIri%Lq@ͼzĢ;:J>(6B B hc%TAnMVQ{:cuȸ t3O"ֳ)yufX!@!ЦтBnYl Ӷe76g㽄S5+SGn1ꈆPgje!@@1PgBvKk/c;feˎ^427 7b4'C ֞/9d BA a^?^К,-֕bVⱂFOFb-o]{&zNTL:uT(.BhDęu[PlU#e:,ji'2^ [@܈Z9 oXy!f{O_r gN '2gvt++{ #ǡ};;`؂vk̔aB zraa mAN 3mkͽX)Tl0 '*M88Ͷ9 '`oQkE$FhHTJB b#rp1{)1(7dP;Q8p"YnQ|!@y#J,>vKҰ J|ܱ̌vNcUisM!@l2Y"~i)3=㏘zKtT&A-dJ|{&k}"!T/2QkmQ65qa[{ XΣOۍDƉMD(|Er@ƪMazGlD=X={d9!IuDeUs=#oR"e\%L A4pJO,[_'XH+=1Ö+CX3=7F^<7+-; @ԪPz@M9MWT6#јTs=muz>n=o^S^wbl8^֖( 6bס=k>1kg=;fGpq&+wZd@Bjp'zlqs%ܬ,Cw ;n]"(V;W5,vDk||j+019Xq?GJ\^ƝR@iHzn^/;evh)S ֙!7;)q x+BܘuB.!gE\&C7wseys!W Q>R5Hcb]%Z4j _LӲUaEg E\JDM4++; &5M6{Ia@8b҆ {sϘСM݋VUgBShz X B{!]EFZ{GZ92mEeܐψLߐk_VM,<4 4"Vd+L:c~gLZ>4Gܻk-yƘ~,v_=cc`ks'Du>$9wEv2DU\@Ei=Y'/a%(1ʖdҲŵԎƥ8o,7V`8Qnm=\y,9Ud)vgWO9#DWYl }yzr,BW?[ Umy)swر)37*,UNx'N}nNL5PԲ Ib&iB׀-YaQkæd.ѺV`J5NDex)Nӂe%zn *cUu.ϛށPՂks38Ljv,\F޿yބ }sy~vפW!pqLբX~v* bbwnZ˴-1y8 V,-Y/;ggZ`UY@]WS뺍b\lL\{)MXVFax1jmc)tB+"8v㎟8m=KM\Y~].'ejAa5\>=/Й;UTpճS^yfØ({K$W-?[W+$?졚q񮹽}߃GnC,Νx,Eo6-lg zx~8W"g^qCP󦾲8[xBge?o+8 cvS)JEY5e] _ >AF8iS_ϴ{O NH*uڦR$/*(̉ Jc 62JDKT.gw=N降Ѕއ%}۞w7,z%%.LS-h a+>vpPڑ#JJ&ϜE.EzD fWs+OM8[NhIjɂqX ev'O<~u 맱>`!#o/RyaBT(! m :m)e,E;z0DR"KϊlT4֔#wIIvi5Lvgfgm/H xUu* +Q2<{+0oz /;VUdyW}VJN1N9WKJi`~53g<=5Dнv™?1sv cU~iaw^W&@u]W*u>Xˊox'orId56,4:Rξ ZY"Yk빂wWU.$iEhJ =ȕi6/~O.ڳdsK<, >3l&Pڑxtnj|j c8Oӗ|jo>_O;]m/fnJNyn]yO~ua!JJ>_Z[XQ!w`G"k9/`KD Pq-y+h ]VUdJ59D>Sjl-eVaNH]d,<‚C6x QuIZyRH,Yg9_=rf>|E&{m' 9c닮pX䁟~abh԰x}PqC#>?;9Փ<"^^[flY%{?2MSG3r?bB|Ϯ{0 WgvMCnO\Ca})eJ#/p{xh̉Pv>'']@^77TEN zա(3ADgɲB.F9ӻfXk 7;Yr -Kk0}Ho=[+X^ӴP,"7M<ٮ\;~cX׺NMSLu7jj*\u+k k-eڄ8Vv*ljyy&f:RM4l1Q(z&5/R9z5%#>6jj 5feLJtv#jsLÔq2 ,pX jy-aK.hDMRpiIzn:mS)cf3LƢ0/v1~[ӰbI=7 ͕Iͅ|^6NT #ͶYi *{7-۪l-;pѕKT>s zNf:mS(063 Tm޴>K0Qw%dkB=߇ϴcxӾ=PhcUShlcU*0"@blzsJVz`oH-캃,E['6J:4 *vpr`cb#<!Tfc.J*5Ak8J"4#qk? ,NgI͚()YẺ.ހK{{&FGqZ> z2S$)% zX؂^kP$xZ~FkҶ}k&FǒԻF"BHg߳ΈӹAmA\.Պw 1dceF31s 1զcIR7H!PXde]`m<)9<=+4ԋ9K;lQySuR^B >4l#[Y_ c7`F#/F/Kxo-j?` Ҁ }M [xAʠLq2.( ǔ冼uq,=p -P ME]cBN5 X`ek؃Un)XФK6Z+c}~mk g8Gz@N~mhKB`S!Ʀ›i0q I'Gm-!\<wKM6 6lxڄ mhX`Z3e 1..-c+0Y@ks2tco?׳ڱAԇAGzUM Pi Bh} ۰@*mmTamiv췑; [S`B[-DRBhR l}t ̊裸gW#iciO𬒒 }S6\I7P"znR= K@E@"cS$[[X,^?!{.I 8OVSj/K0Gr3N#1@rb"4w\ǟLbĬta%D?S92rb7M'XR!8=s!yeO?_+z7}E/EXl~DQ7klXzbr֤1Odn=Sy
lA}3|ᒥ$*g>("XkչM1Ƶy>Y8嬡Sfz&螢q.XR@y:? #27+ʪ+0ӰB:lUUqva,$j,օ+rS1UacDe钅ؒƑ#`Ly%2"TX4_Z SLRUuB?hCŸUqϪu:0WB&c#|=e-QnwY |*s11kZW-&k{̅=,d:5sTnG2,-ڒT)&C)nZ^c6Ml~.{2qKu&\b~~ sFGT[7-@M s)63qC,hݶ5aB5SֲV^Z 'gQ+ƒ ɥbMWٶ)nVWPyaa7DA=qD&;;d78ÇF憣mWfn|+Lŝ%jkt!1w5,C^=@%.6j1ømzE/3`X{fW\]8LbbTQ#0%(2ۤ n,yUDO){2sρ^zbbL~u{k/rRv6k1%"B Yx4]FVڮdL<@60o knQ5[

xUP BhIҮ4- ȯa?zUA%hm2k_l]yz63<ĩ<4d7!@s`8k1ZОc>[ѼuV}vFKڔEnsvCEwEx7 q5wbs-LjZfp^6vڑ{ d r+* Y{伓a-.BPנ@D!X}扇XO{O_r g晶;FR΂14rƊ"<]ш" "AAT{.)$ dFi3 =3L%Iu7B `rBHmx-N*w5+#Tb,"ְ_mfyZhg)[}{ lm B %)C̵A66ٖʬ+gKhKkVmRG۟nț. =%sEB  -+k+/A Tx6C4on or°ܬh5l-)m!{1:IZX`r`.:km1!@@s]m3&6xhm9ITq!"A`FofV+wU60י1iX?"|'B  *ků25,_b ZTR,spTȥV@L~B ߱$8Z24Re+z9tȗww-[k=I@qcUeEo[hW.Dz3Vq=e B R!֚F^֊5,jkg5g%^ִ#u22-0PHB9U!O!@u`] sedYCD-ZʲZѠaJd s QBc_&-TblۊJfUH&zFQ!@EX8]^FXJ,dHMZfqPTب!r-?DL?;@Պg^O?q3`f!m$هGL!@`Q{h\`)udq( KG|uQz9 Eϙsϯ3gεQ\D/!+1M);l)Y,BH!)QB )%'B.NJGԲ޾۲WnO:KJJ~>Wf~l1I, /tHRerT\>@|tLe M^xSoQgQQ𢡊UOy* TQetC*TѺC)WY:$_ 0&{I^ &p5ۼ|n;:e\{huhTsttmUoygH𜊚H'5Փ,m|I/!pذO'`3E 4/3&|ĉ'va%Io%%P+LܪUcM)zsiUek-U.ŕ]$f4?0Ӆ@M097֐F]zE׾;:[ <,R1X4p12]KJJ+ҵ _'`I,YNz6o]}E*Hj dCF4zjki-u?Uw#n-*x"՜cV.Rز2Rn!D|)Bcգ7&"ogNB$f }3l' sڼ-JYF@|f$eeobQgDϬDv]_C(]zIpyYw!"гw޾ɟ3d Aތ?mms&S+M1ZIhʒHmybdhV*[+T*ϰѢ,=ЛU>;ɔ3k֣GnXӗ\|o=/#}扇nX4!@@B[bƂ5,یD.9dF(bnMI{x`z.f'\෶LQP-rJ\P?:ő)f!\q͘1[mU3U~սܰ B` P# XE*/ϛ )}o*M=A!*$Pdڝ7ګpYӳ^0V.x)ڬaÊk.g`}8wĨT B 6:,S,Ao YA%a֘q9՚h.^F=ae!~哌%ث%1GfەA(,+#Q۬Y۱mmmfnR3 ɩt5+9HJNMLFn7)*E (26C'.W%h_!@繙 \J*'zY3/s*!@@"v5+u^֪cMJ6Zf)Zf2֋'!kؒhq<7+ְXU:Hlq #zF!@@82 I^LUW3fbcR&鞏Sy#0fRD) y!BSШ^+^ xWJ_i'lܒ`%JrU~EkMjÕ^ZǦaVxgkS괎(rB Ƈ@aOtUEgv9Ge0yt:+@.`X1tu>LLgsf{z عaC~Btg9T"B C=:fnȵ>JˆC d2Cv'v5H B %HQ\>;$*q_13k"iȀVqk5 IlҮ9e!o!@u2[ŅZ3"H58i?%b˭9IKZEL!/R[sW,_`i#$;F^Mڈ!eAN \ |o <9Q2m? B37) f B "zAH6Z!ҲfB֌]X.Bjכ=ܖ<큉9C^ B +LcIkŐ9lD&i$my35@RqHIn቎ #zF!@@Z1ql$o%FEhEh3q)Lly Rh@~ۇs~|!@&]ʒ9fcUHlkl1%+kNzuY쐬 '%uDu(JB B@$szƦbFB Z!R.zk5c%O*n9!-mbdh!JHKsZ&B(ad#/LJ0^{j1[1t}g {M6TQ—g( B 0hu) QZoV\OKK ZJ6/IbFEB 3ia:2/2#fK|fNKO0Oc m}kp1C6sqB!@kY"u!@!HB;fe#K9Q+5p[E9Ynd2Tَ%Ba$DӠ/!@@ji(:tCV]=!UR6|gݝЪ|S!7иZEF۵oW!@@-[#jv"%zϯV- KzI=RMВz5lj/1.=\&F B  B1$0abreN#WH 7j~Fk;f_*j't4REB !@AI!@HmZK bIT[x9ˬA#$c>c ɶaŹ4=#ns=ږ(!@@1Cbݵ^qEluԮU3PZo+04fniȌ</7b5. BH|34^}W nhK(TJ蝍^ n1!@-Cܐ+D~0G[*+SpuQga|C#D=eQ,!@@Wk&&B(fXE]ܫ9VJGsmd߳Vqq} )ɍ H튢!B(^dqvh ǧ2Bz1] A%Uho 1l& nM Bx5ZPkג kYƃW-xŋӲ8iJy&±dy#WLM\E1!@@Ȇʶ S AlCd*['.f :=9s#B 8PaM6Vސ($!@!P4܍$= Z BP|jj#kQ&G H ZXx6BtIqbY>csm!@jjw'tAF -j5^EɫqO !@ 45tGԉXHb« I*w{4Di"4td!t6s#zF!@)LH y#BLxe,r*\8,P;YLe^;#_$ip 1/~(YB  ۀ H]^sd.Hwm;d$,uX3 5- Ϝ}Joj5!@&CIX51r6DyOvhh۝*Q+lA\-y%D!bR~05;YݱpߪօOB=:}, qz !@!`mvܶBoLRB h֑{=Hp#:Ѕ$cN#co4c$${P$Zz&nj=K@}B fؓ>6r;C ] B(&Z6ǚ #èxI,ㅯ"oXI)n^%qL6ܵl` B@BTeS+N,:<9k?"JVS'ѫJ̭mLa# F!@=37Iu6L!@~IX桴T-o?VA;;b%uȀWN+\GAsRC!@u@ꯟ0kb6ǬASx~r99?7!@@##@dۛm3YᨔJ1^NH8@lN B %Oz~p<8B6SG1׻vQ0Iϩk<!@u6#zK)nB ́8"11Yv8HYT 2I9I!`[2YXQ Ҽ):v"6W$=E!@9׻fe+ Թ  zF !@u1q)+|+!@usCL !@4 8r!9sh3TB 6I+u>dpz"zN#BKljǐ[nV+Y|Ws[eR܄!@ ?iʌB.-uNyU&$=#y&B e Bh7FG]7GlÁ:I!8U'1(Q T B B ?ՌՖI Bnv~w``88릙Q!@@aF入CB F|#^M{Q*Lύ:*!@@Gig2;qs"+!@!P_p/D3uѯh,6b\_ B`S ȒDIZs#U$a BFVR31tK!@7BJ !25㸤I]  B(C+)h5:d2vI$<'#F>B  Ik\<"T~ :&B 65 G0>9Din&$l bSHṋeXGDϛQz!@MJv#zn B )uFT~."pt,³AU2p~E\Z% @:!tg8V~/l/am񪝖E)%҉)w*;!@%:˺nf5%QP[ |/ֈ ʧpY"DiRBhbOh*Gg G TZqm}TBE*FrkuH 蹉u*.!@ &!V6#MXA!@^e2.*>m!zc9!uA 3B zc/.'^PDf(ۙ /HF"ClF򷛉U!dPtgMydMќkmƑZEnВQH1yk*8!@*:,F4@!b %!@>0Ej4(."T@s"ZWf92L!&fR6Ft6~g9IRBhR`T[m;b8іJ1t0ZV JUc5S\f(26T=7O%B>#`/)9+1p a7(#v}n7B $Ƣ*j6Bm|nU؝,|ؚmKFoQ^\vXNt p_ KS@04g8#u;w蹾5J!@M:!H꬗Tg 3na$ԑ-7HC.:-,rȹ`)!@MgM6\Rcl#gEX: !@l.` 6:#31X9 @!Wz!$D|ChA=Estg,)]B -RS[@}+1 h}*qFU$[@>,"ӴP(DVD!@w'X؀Kp^ fIXVK*kFFXpPDdfmGn@lĊ(.B <v9o9s8Lk_-CkFD2 fK BmH@s< lvB,֟"B WM=,撌-|r!@@@@|ZB[4Da1t)-7<䖋H]f 2|%2o\}S<,L= sZQ+:D+ֆkeI݃AoȤZi!SmA* Q-V$w&z-U R Bh^2#C[VW0ڈ jqVs^+HBaR[tx2Y5.úqY$=7N"B!,tfgɝC)^)QYaj,,+)jFeq?ѥr#/sj4 #Ԟ)!@4҈q|g8t࠭U~)YB 8\t\[,_J5& )ײ3wA p|EDEa5\ 1#Q.[JcV H3[iMO!PyK~6CCtnǃT655 )os ^}Yr9ZE0xoisB Bh%i:Of T6*,BD!#2 21iP\>k,*ύUdM] B`s +u-gleQ Whom \9FRC:r\|r{sCJ BXrN g)eoaH]H0!@#`M- YTj+֦v1i0Ƞ / ;2l*-cw 1]ODs«B!@4Q\0v6geGgu8ٿYQ3vv9Gvo3W\٘Oc%[[\ -3좷GkS2Ep'٠1beZ,IFGQ!@@@qXUTJtK~Zp)#5ZV+ALfEXz&rN~ !@@ЦarҳL%fa.E1Qnے#IZҵܻmfC2 B 5Jco&xX^dL\a蹠&DB 6 ?Tpl%`-\ Rʵ_a XT֧ՏM\R,מA]=;H='E B06_BqA"Z)~8]ŵ^$foTWר·)vQ+*M6%sͲ5IQI5B 673=>1JQKRE+\W k̊ Z."S؆d%++9J:嶿n:(}B Ɖ2sY$iEˆ])dffmt"fЃf]-Qm̾r[UW'K3\8 B`s#ࣘ,e&m] .vaVJcƄfmi 3}v9o*ݺ[u?g֋G'伓pcN)}B  .0BY֖e!7-Ί7x1~2ik XoJ.J) r,mXH"4<hp_ 7w>Z^!8d˛wTݩS"(!@8znV.Ns-%s:{\W]nF$,m7+6冼{e~T4 B!!02]ܬG̩ ]/[Zof?[0"Im\m&e#@ׂYaQܐ B!AFa,Z>IXUfRK}Y;ZsuIJmTܰJ#R| %7 il}UZzr;4,e B#6^'=k,/1 ̱~1g2k H&@c|;K-mJkF簔çZI󄴙y lِ?܅ CKy&3J B) Zn7la3F`[kWP1FdTg?v=J_nܲv–Il9rCUn\!@[Rr^]Vmn6+1cxoDm$#?O 47F֛ir֞")QB CF'V-Ji,!(]nm-I.h;A{[HJ 6` 9KY;ğ&ҒXӗP"B  -gIZff^ob0 C'0W=.zYe1 S5wrW,B+\VQ81疄%=YNۿKB BA Om9X/!_w5ZYEpg4^@#,- bHϷYfG!@vhwn Fvd%>Y,[Zv ڶI]v!u],c6$=$ҳ$h;] p> @&|!\*ZQf"hsaʲY(*ޔfOpKL\[e@!ZV9UW57ΜPZ+oj M'5ݚO(vQ',[.ThHM|F:!o}bйZDL_ .{J.ڸBGQL٩}S_MΣڡ>jAډ6ʨ>ʌ}ojD5H\*Äi䪷q2Yd$GMj_Uj1C[ʎiM^ܬ X.Q{, >[6dM$9ۮoʭOO?FǮⅹQ?u-8}lBhO4PonN?+A%x3"lhqѣhXf q#]TtDq*ꨪ1=2iD5O|:13>U 6,}4,=V BD y'xuᇏ;vÆ EN=;+EB!@4j{롇V ׶4,@ڦE B F@eeFb.E,YnL B ~b DSTr!@> 3b!t-[zf+DED"Bh0zfԼ.tՕ,0$~nMD!P<,{)c_F*---[{&.^RL!@ &"?/;/{ڵkWcI>^c_3%)  !@@cF^`b%\ˆyܸq͚5:1ooAǒ4Ae#B,lfb_١%b'\z[ne٣U"F B `/ Vc{,H=S##B  GrV?~wJϚ,  I!`Oz衃 jٲ%gv8 >;$陶;!Bh 2ӧN;gP3HRz BI#$-պukY%4TxB BMp|Zɹ瞻7=!B \ZhqW~G}1;cժU%%y飯 $[cI!@!@ǒ"B 6rL7C%P!@@x>Am BxMÈ7sP!@M4SvS7ߚġ!@u0cXC~qz3gδW[oe|u&ό-4*D&?l@9 o$r0L_'8inÂEϨ7{@rK3#8\=!f_} ioHdx8/Z< )mdO7#겦9E4"$U57k^kmk~G|9wwZ7'Ogc3} b Aӵ( 4 WĢʑe!xH/:xżpm&y b[8% r$}yr.+Jp1˔%84Z v4=oN3HbF|!s>T.7 yO3T"Bd2s=ѢEz JOuj1q`X]N9oU6w>\禾j~b#!\،r02PhjMπ^P+3pw (+!:^zMCͺD;'4 .wìB/Vٍ0=Qg`#v(9+`eoJ爁Z}M%̮#%SrG.A"Z+uRtJYOL\PDi*,|#^M3z:n۶9{l[ҲUl0ܺuLW7|'K/6#gE]#boZ9t|'*<^8&{2vn)BYN|Ե;م%J)L ^"ȷ$7Q 'q j(5 R#iؕf_{RMl =9pGDx`%),pV&ܲku7Nc?{lrgܾ}~ (=ۗo UY N:Y qMƳZ&^J[UF<˼̲R"(/)kĴ"*)҄ R2,BnoA6}TئT!f#}bNbѓ/66gV OB-۪ޗXpݻAh|)zҥff`SrXGQIm!Lʢ5o.P1HѬ}uAb*?y@:D?ԾZH)05t&Gچ籇∋4lLy'bt7ֈIENDB`filius-2.9.4+ds/src/info/img/email4.png000066400000000000000000002112321506574241500176140ustar00rootroot00000000000000PNG  IHDR sRGBIDATx^] QY\!(FŨ$"xG"D#Qc"!&1X<oEXE_ի陝]vjۥիUud?eք1;c7E"`X~}i6m6oޢYf?ozQ׿7\'T'\rHpN+c(GIqBw\IZ+@?YkKbLCv~7=EAτ]RMAbTSS.FV$ A'OSUKdxtk=fSӟLQO3f=:2gQYɔջ鲋ggӟXsCǎZ;wN~O?J+-$~Lԯ<< /;,xҒڣc5P 2lD.Oy^|?􏋻5*Гsp@T(ZQV:%6UmM-QZdsU7:5E.0EQ/=d%Ws"bpaK(V[[ez`w[" s*J$yAm['C7#2%L3R@ V}gy%1d$t%?Avy]U3ųOh4ѳz2Nx7nPoYcƌ{t} _ݭWk2( Iף:D81KW"%͈2qA)^wt9ۿ:M2ϗ/CQ6Jr巻nֻ~>0#F;d'e; p*# أNv-C=Q=UOLI:"(@@gZʼnn1PI{YH$ I?8;ȌfD#N0|P'?ipuQNرBNlxJѶpl=75Ҍ-'¹\t֏+*+]lJ.e$x5W [(~TCSv7i}E_WT5@1NP9mVNEL_J䧥lv%],/GF :k467jĜׁrxWm y^NU'Yʘ#9eWJ˜288/;┕JXyicqoΒeP9qI$=^JjzXy.wK/O-}g?#/;ll>IGXYB.[J ) GN?˘ zQvLIuQޢZ!cOym#BΎFj"Ȳ!W|]p3_wP/#)l!so?wRc-4%tMzk{&݁R4y(o AHR ['4*qG\]'gǓ5#T:CQ(}$+ZEM릤r*$풶vp.wcUBEZT^mLҷ]tys`ϗT$IZkWW*)'h a:'-=^Z4^V^QVYNBeqJָs"ggi8>JA&Lcjʅr@C3 M%#8;T7Pj{cי}cg / XKZ+BMgN2 [%L4-QW]l-ySV^𮤎iZ#sR;1 -KdP4 {NH=m4?\+n3k guNko /7Ք\k:\] ևCz@ɴX$]ߠR6n̓HUAKu+[2= ڇFC)u? )\%bOc8@ 9OwLFݬg3]>gೖژ"!j+z8l8"[P4A!3CFeNf|vK1j[q+ SaՔ</8L\RЀg,!]_ޔB[@ճ)i>y}LF)z67bDl%Q+H?|L BA)8frJYjRYyå9roٴ"eîȡi~HÇJwJ {7V q3< T&uBEE)H[$BH6v雏nǗCJ#)G^1pZVcRj6b|`A-Cs ²_ ˋz "PltE,.I jgӡtB&8O[%$1mg ]Pދ >xd2+ h("AAn.!䜰h 5reM旙=Xhcf{B5p"ARA%eSx-g!5۠ uMwlHq,'OJAe4D9 F2Pқ@:e '1גI̺;v޻i[$ J|t]GC[GJM/}J/7>z꬚<9&@HOՇ[uv%( nZSCYl2Is`rQ(i ̼f\R2*\Efu3ʩJw`ǍLI0"Kv.'u<% |G@-H+#,ܬ_ٱOh|opLR:6 S(5VvDƞ?dڭH=xn}_RƫnsfߺtI#_BaiRڞ,>0ʖ~T;04iN(}+7}?=|>s'LR5Qu?U/uP{M.O.Y}K/=r[cQ#WxiuxSM[ƭo77O5z;(8 E7.~Cy[rѧlř?ko꿾'`ΦjGS o|nZpk߇'.t㾅s%];:A#9?@6F(OnCyN`Gv8Sܻ:my?zDcs.#-e{^0ҷ}W'6[ chlH4aqz'`J_ Oo +rܧ'#rrPV 5K56_jp#dޥT=I+MD⨁ [%Us=\2`HVɔt]|ZفS3A[ y\$-]Did\G7*IS0j3hZ,v(yg?}YނA:S`kB$seO=Y^ZHv9m׆DԶ4\MҾ޴ t9;V]4% J0gy6NlWz {V&MbNfs?ElvםڷKyB0g%=A߼۾h/[ȬBy}jFSuu^[<۾[ 8B2 d޾MiMmC_db߹_E/zz)l9TB~)(+ڹmܶmN۬˜϶neEn]##ӱt䇜Di<&]ھY佇-`&u 4+ ߫Gl71yONmZRU!J$b@AƴǒfE> 7`LۗH|9c~FA a.JΝCcJnsHڃi(~Ep&v"@ԏ6ji ڨaQCѸQQHƑM" oii+};0qSĿWo4jDwRoi$ e~=' u-=\89߯;ҸSQ?7gl֋.p6MO<W}dY?⬥Jv(U;ou_hǁg'뼽F0jۼ޸rǁ/{㚗am o+ظc#ݾoK%4up-$mʵs_^elI ^p^xdeGܚŷoy\SvˁO7'>1ry\Ӌ;މ~! $ƟݾGxH$ cL#LNT$*nrS L@%oҳIݦݚVQӖa%0 "F ł>Dꕄ;qDEPIZH]7wR6y29݂ΎTMҩn{DYyY\.5S~O sQpYawwL& c,CT5mѪU 6ͷFM[F؉Z^~5iфm,ҨiQӌMf`4kA;Mao{FB&/4mm4*z#EA74z6<6,}0mڌ3w]hizB~ʵб&8^rK&9Τ f4W[:CXY&,S. -Þydk{ڍCl%.k+P#G~r3NXe=2 |J7>Gr[}sz܌pf#s/`L:egsy-o'x dM5'켿u5<7|3hBIEx^=f忔ssD 8f|$dn1Rt9*Xw-܌}IZE9LHA_*|z\N M\wJ9tɭFR.=h*^A,6NҮS4z_!hWtSHf~!K$7A3t /[~ u~gҟdd{{qi=U/-Zm$yo$EJm0"kmР^f~fZ6=yzZf޲ey mAztqe1>5%E@--z[F[C]ZE6/YfzNMJoe5kڶm۪UN8O?}zͫ[س#M}FJ`崑_RC6 7³'P3`s<6rnn Lq۲^szg$SdG 0 /EU0xO}/Cb7;]Ms:+&:!Fkz}[km;oފwՁܱ֨@ ;o'-mۆ8kK=lYʫ_jVbfvoZELUm2S*hŦNXg@߈{uA2:c 6[2Z1MtC"W]y²ٰKc OH6?gdZ }X-K4nԀo 7h(~Ѓo{C({ YacC;HbloXA-Zޠezh٠9!QrЪe}؁[hEF[{=Z ހ ${ I-IOhkܢU˦ޥѳԉ">}?{ns֭O>͛?i?_wﴟ8$Fc?~P.~sGi{ vWÑ>2ez {;móʭ5ow5s_Bmo?1[`⭸\<끬kun9o]0mYvzL,e=o/raO_qr<4!s[ 7 ?N^I׉r] uVv[?g6y }ΏrƿzG'V;zloxP`'s:tlǟ/"gӇyw,~硳{r4\v.A%~A> 3pzK6)N Te*0?Sɉ`q$WQ2k /^{ _0֥"i]c+pV,i O-vnnL㽏^`O?vJ{ >7Q_݆s񓃏6~:{23.Xi!n&}<73|eak}{J*/t4́GMKL|[tɠڊP+K&)\2 y^ZЬE)Nb(Aa4yv&jz|2]glrq:}FQ8H7Oe* Hg9غe{'NKvȔg i<5c]'1-.>VuȤؚQh%0Jv&[C$8\zf>g4iu09Co~m_ӲEM6lذE"k ew"[xE/BHIѣ\A Z%-aDѧNҴ1iIRmf5V0(QdU[tu#hrA =LOlQ &ͧU%>7W &W҂ I EJFzOo3VHRi$'I 7Z7g/>[;}!35L^2M,ƫR]{^T@}pz`9K0~S? }&vxri|znɉ˒hdĎV|O:^\-;O. ^oH/ MtC3]΀Hvxi=+ofSSO3E a;z߬}Db_LL<-hmRP7>).t&x7HƗ/VbiJAGǴlUx}+AӀ=h 7OuZn%ŘY"l*gq!Ko&ӼQqM5%Ħ68qDzdMIB \LHϞ)el ÄP 'hډMK:k3zƪ\FL`L |͑2##}Q㣟Heb&24luf\Q%egczR&E @Ix=3  [Rݱfda -YmH|Qڡt^|Zk}b&KUwu¤HJGʻ6m);3Xg]Kz*KS ^văSgqV;+?nu6J~=q t_d (JGPvfdbT!B.YˌjNw9CH@Ohxu!g$$BMy5UZ#)zK':GͣERфbi65WݸFƋ\j1#hNIː,]1[HDί83|,3[FЌ% iV=7qn|yte=XS?06tW#AD}UKkOyIS#=I: eOd`pn~[⽲b/=.RDB9'fcϢR PN8:Լ`OX։(.SFYru$nf+̘QWy< CVE\OhʎZ&2%Oe3%% i| cA'^uC'8Cq]gfECoݤ$M~ϋjJwߜhO{_CŒgPEd0!! ŷ L-WD}[Wދ裚4cM)DgO{!}s9K9y)Y"$yҏf14Nz@7^j+ǜ1&y\Z?tczuvڬ9#:xP_SQ 6dF}2i -Вم1 L% FMg8ϑ9\HR/;.}K4#s^AN3GϨ 4E8~MH@+"VeK^b!Rl|Z#Yܶhnhb}ywonR^NC=H£0%l0׍&ɑX0]T5}:I.=kȗX2s4pcvV9- 5}ɬi|sBk%?&QO)}Ez7 TEԘw#aGu.Q 9Oa$fȿlMQEhD:j3 \3vaMFq|㺩[Qٵ8? z&1Ԣbqӫkn2:KnK~M5?AXNPLE&RB45Q$HuJryɁ83S3iܸfV<7 \VTgšўVE_;wb2njgʲ4z>[%4ӕKzށ (" SCАdҵX^}{ma$.#Y*x Ia(!E -:Š?DV0RI Y)p/t2&@ږh\i&vaVvbR}{+W H2)haYߺ tievs=LJ-^4^L>H%f0(&yǦ 0Q5Nx *dnȚHf؆2ڝdE`򩶺[{'^ڽMy$?dQc6J|> Mр{ɨ,$ MY]It몂ѧ&;lR^%`?vۮ4*{Skݵi04"AܶE%(Sa""Nid~jK o#YC ܋+A cۭ^Rusz AW"tw$B<5Yžb! :y|% Ʌ8ZY73 ,@qs$ >L7A4D/%՘En^f {k[bT/_JG/S)HH3'pW'tO"lKD 3_5n~?o,\<6E"`X,)"0~0;l0λz#^N6["`X,:_u_Zd:xTz u_ѭџyh׀Fz拮ᖛS+E"`$ΝE!{sب/q[\p/VmĠޡe}rN /=2fX,_}K>9Y6P#8Am< , "̞n 3=BPސ䐵-E@`aZBN!z6pG} 5qJJyq8CORY]5lAI:smE"`!>fq %UUfC> q)+s =b=@0zNE"`XdM- R01q*`O%lIm{-7'fX,0%p-sU@8] (9Y~B| Kay,E" d2mԸv)0eA 8y\^<2II{BFKƒ$J=t8p`ݾT-Au"#}JyySInd H27cN=F@Pqf`~43a6V=")Lx%` qB 6t;aa ``Uh:g &ܲ1Mx0DǗ,_t|ڳC5s™ϭȸmՙEQ)2Y8Xl%> Op XĂc1/kt&GeYA[ $ʭ 43th~Y hc1p30jm ۾}{޽ab՗_~nicϐOS,8cH5= gLw,Ƙ]M|h9ݝr-۰,?ƞ!~O%;,;^`q>}3{c0;:?{\(LZAӨNrD4mX,$QL*?S5`mmX<'vp1A!%xY+G$:2nU-_"^moa`LRbՃW߆b [U)%d[?`ٲjS:߻m]xY-Jg[hs3b6p,^ (i,V_Ul%jژ RЯBgӫ%TF,?ҩOL$JY;$ֳX5,⨫3_w=Nqaٰ4~[mUɶgUQLk7 2} $f<zڵgϮ=ݻ2$%֓]ƅ`G*~tR 2D@0Rqm7\9miE"`8XS߲=4KɔǡC8ߧXώӭ*׎,E" o~;|uL/=ּ)hUY1>HHJ6E"`X^!  BT 'X͜ۅ["F'10!E"`1~TɪDu@0Bi1E6,#my&KpʭmFP!:]z{N,EB@{YcKNA$HגH^i^efjBs.F= =36v)E"`X u܏7rBxY~ȱ\; KWg4<{Z5][E"`F=õ}o"k.p_0~5!F3Xg|LEF Ǣ  ^DXRkDJX,tl%8UFMƱ~ls3XVen>D$(EIqwVPoYկfUc/L*fH -4/+HrD4O3:I.5 B-LP-UI]Zx,榁`J,Ж ;{DZ%i+En&VAV}󨛮KԐR3yK϶Q5jxT TҪd$N)'*T U z降=kx3ZX[; EmE `p$K Ga~c/}}n50[O0zBf"`< LbɰY!&Xt N $T:v,)Xj`fX&ˁ`)1qwZh"ae8z!]$@0s: NoGaip|ԉ'Cx,e5K?=}g^̤7o~us[`z*/x0&Z,AR]$4"'u-񳏂yIqG 8Ҭg%% = %fEXEYY"W2ȟƻ!einƑ#GLQF·tC[K >㎋F6*e4[>,@W۸5Y,4Fe&o 0UsYލO@9=uK0]V&!*3`׼;(/-/;544cFAxEYzKoR6SXj L8= R"?YZ <ٳڗ SW+2See.pP4g>]UZ~?H# >|Yf# |(x) dΝM6ݽ{wRP<RkH;|AYIEZh|{iӦE-[ZL{&4*5^x^jŭUO|Z/SU;)} &)PyZ[n3\& k|z/ŪmI;"N!^[OB]{ݠA}l) c~ܜѸq-[t>)5}(6ߦLд?nj*k0ӓj]zTUبѿj*{-UW+Ǚd:T]M)zFg.Ie*;R8HSHxa%i(mcU)\e?=܎cM6޷|#G1f2w "vZ8HMON@Q /kCGo]_;흯lj$+??/?O?U}ѧVB+?X{+_{?Xᄋ,g鲂%.]ΒE.Z·zSSJψ=Ok PGJ,b<7zI%"r5օX0@0ȝLpaY6-ꡃxc c۽ÇV:GF߷w͛F(Qz.гȁMӻ}{rMלN4YHigЭW^~ivꩧr)?z٣GO>{ݺu9n]v:t钙٩SX,.d"[4OK uZ`Lẓ0mP]??c6iiT<.=Zm-)䁛~͚:xC`IW3kl֤I͛7k.8 ֯_yn g)E|,H $)}[u K;=sÇz[Kc***``Ϟ=[a WF(b%"n,5SU}sHYH@03|Q?GFh~CWK{̪ax A`H=t#\0S hy4o׮]ǎ8XQW'foߡAk=MA,bmDw_<[%7h.Y}(q|IVew)lm͜aU:-oZT򾫅CjU2r9TN y4^ԝ۩=mPQݰa# ;|X<'CftGsřM6)A6O~B8XϩQs蹬GZņ6>η.<|ĈOhBKϭZS|n9-b鹪/?囮 Eƣxrߛ\(0K yTf! +zg7|H{ E<7n,5+\w}{!lXɰȞ{9~`~p膟[F.bOɎO#}dl9;ٔm䪦?xaьzGQ<Ò)+Vśgy&JE~֯{0ӳh w,YK vG+ |b^X0 K%K/vjO[wwرn:]P#:t0. *oܸq۶mFECwm _}՗_}hѢ{;ѥ犊ۖ6:˿-ݶmM3}d^@M6@UAe1UN{-V`Rjy(0qU$8kKʐC?*_ Ư> %UļӮyeI~_,|aÆ(_p{߿3 ipjwl,K4l ܥk UkEWò$5*vdO|ZyE"P<|[*^>+X*SHsIAX%٩!M{$6O՛X$u ;,KҨQCwŢ^Fx,^z,HR̢֬gpzCW_}v6Cw z/Fj-7'E"P#a_pE==6nH!at?zlb{`z~XkQ/̢}S eAq^$L7H,n՜˒տOuxJ!LmԨqn=SZ+xHJQ">vGȜ6E"`] кuXtoDhFq),ESlTAge%ЋI&kz޶ʎݦ[ES1l۷y#r`\K`OuC063Ȼ7S `X۵kR䖺4̿{9FC13ۜE $K>&ggٽTj`/pM7ҳ@BFf֋`Odd҈3:q)춍MgBzfM'g ~f^ /jaBpYC`7=Ql٭arηx fIÈuHT%G5WR["`T5|H<.-h6pd׿z^MmdSz^?c`=~<7T3e9[3ϕisWO?54}% /o`ΓZ[NVE"`H IO*JύgCυ!0Ps3%zۇ4\mW :iݿv>]|-Kor6:cGO MTzQ[,@j^ݥHlWqHP&2 eGj@DqyZϬ; T8pmz2g~"5}s;FM~WO>RzSȜ6QDf =x任1  `E"3rA=x_\wRPyMO!xUvgMX"#bTAdA^FW=+"~M}eB'yτ |fh~=^ǁ}{q#{=rtm3;o~N⌟<{#~<+uᴡS{ }5m(H?ټg.]-&'({{,j(7|^ʸ*YE͚;ymIhظ7\{u j( i3Br.ZA%I;6oj(dH}%ZrI ? 9D2C~t" 6EAr=kaqH/r3/zw(/w_>5z^v þPvJǴ+g90l8uFY:&)HK,/z_?]ػp?7?kE+Zj7n8<|{bҩFR'Yd\'@ؚf" |=8TEP$HER֯wMxxI6'z׌Zfq,s'X6̦lmIr4gƞzimsbUJt0uY&8,$FҔ=缺c3ُ8TYIU+J7ɍu|NSPyg>uIgzSg]ig_0DX/_~'c"CGwߨ7P |ߟ܇/:󙟟(A3Fz3T&CK+xKubZҋ{8kX?c}B̆?gOxv>8e0 ͷ_>T KsZ fKiJ6"<yzIn` !bYJΪո•}}cz'~y C\iz Ւm_-ワ-͍o􃙫ǖOO^\_ܗUs?[5ӌ⢌hhkŝO*,5MQw79/-y= Nq5͐),*,8RoRe}'{ޓ1Ȓ /~xuŴR#eK/5=}󇗜zNҴp98͹[X ǂA 0DyA'^MDN?݈} iJ㡉>A=Iy-"7E=ֶyMb+L~9>wU(i㲷#O΅q 9;N=)Dzp/u\猟>_Z >xiYn? S9#{Ɲz8gSZv;e[rFl}Y=Y-Z0M9׶ym{s8}fԴ}ƭ:4JMc3+) 6ZτfznSc'3q[) 5͐չۺEEݲ:8)zfʌMsw gZzjpev4.) @i nH5L6]й" 413U/]UL&,8|KLlt8OԊ]+2˖~mnDv~Mfva`.YhΈib׎8 ~=op4`#@+!hqFGz1k<[S|P ·7(t},$g}YHs !AU u[:ndvbcCUQ@_I;}s5<|UJz " Q`4̗ +vȤ#SCdyW2yXN4IaeU(+C)5U׾Eu41sJ5Q0[,>#^Dx*3v|؟ӵE!+x{:vhuJ)GH֗(g1Y sϲb΢1Y^ZnZv9&?5ɁO)"a/Bi,F]3dT- D_X6n= n. pEzӞ?^P}@{] )_n䳘4zAGq5%CwO!ZX1'|N-` lO)1Oc0ӄ|n8)~'\25RKm 7ҭYwk﷖nݺۭ~n[?Zu[6m=ekƎl=SKoQ+nMMcI(#==02۰wYW/F^LōCsq}BWNW`~8qفBWseVlC~~0cntY_y~eIttF<<щ$W4c-XI ij m8CzqкW茡oY)^F"lI h6ŝf{'wp]T\oښn?U(O&)%t51\YģSw"5<gE Cσ.Iu[{9F=jzirsgR6I巙gtGvbP}@gMWO$4BbNq9/M=R|@XcOCE)GG#FE*:bZ2ɕk+L)ŠqCz =.u:cb1@zoU%-ltP,ySZ$'ZhN7WwcF9>ˬowQ6}#9 N\;aX2½WCoav$nո,Z@0O` <2[Pʅl.604~f=\✗UpnӂZKvڏk |WVɐ헞z` Іir/Nm=k&yE"`H?P5ɭu:e!p&dAz68m2eRbG_GvJj]}/Enwl{nw}\ k='uxپ$lTFXq)K@x3U'|Y0,%GyiP`tE(V,Uc+N_~3d:W,='kT[Iou gsmw ݆7ٺK˗=:H> | Fn=o:[7KN [zN-"`&Ϫy/I7dOJr39y~fxχOvt%b=͝=#3 #x1m%,@-'7gLsg/<)/%17'9Q;LWc7DYgI{gY=`=2@Ϸ {|#Z϶'X,5 fW=~rKWiatc gQWy=ۥ@ϻv-=$$7bZrs 2Ev#`2K$%A 'b*|?WI x-+J`XkV?vLS9G+ J<5dՀ/p\_2#4?aiϋ [ܹ'8YpX$4:i0T +//o\lgCGZ,@"`=  y%gI>"Iu7WjYL [ɔIhW=Dn'M{6levN=h4}7xz*s 8U5k=+8<x>j\{a᳼~A[|ϩH.sn^^4izFVvp]Y -<*Yz-[߿3΀z`h[\\\oWwh>Ik>}\6)VzfpY)LD2C' ]wyWP]{P?O" ESzfԮWlܱc駟윮];W\x|+/W7)(܃/+_ 28 :m~E"P>j@k:V$wO$țAe]7[#c+$2/ySN9UV0\QQA"YedwnSaboao?OWs7HG cX,Fe/EXɴ9Q2ՙsWPU\(N LmZ[,uc6 9"4ir!e駝zj&ض/eCVvOot~a[bS+o՛ q)uBjnY,@<TrX9UrŠq$CbڸJ(f'+^ $!KeƵ={\v͛7QYdn;݆diw@aR6O2Nz8d;肏Ɋʖ.GEH3Nʬ:e`e[E"`,ЬK4TKˁ%}.+}'`p]ӪܹU/H ޝ;t萕ug8.*++ Q$c/UtQQW_}{ǖx`k]O=wLtYKJ5?SW_D-7']ҿ+ ah ˩-/-J j(|nVXy|eTH=u*pfr3<'g.y_Ʌ 6ɌS7LkCΪW`<Dfy P} -SO)d%l׹E?Y+ k/fw߳w߶m   {֭46Tu/rѢE}w7UAc+q23q^$qNiB xPhR{ OD%p-(X.ye錜3rc3g gݻpᜑѢo3@1g:B┼™ç2Jy+#Fa+0/ 2.0y1gussZ-#%Nio`l' I0F/ Mc̠%+q1`/9"wށy-Z2d4 E^|3nn>֩ rn С.d .k vަnpc-h( X7WG}kUt=QyupG2?i`Ṅ~#?3HsPw^73 #6-[\ΐط^4_‰l=_eKY 3< 5|̐7X 4d%=982yu2[bqY L=8G8B|C^J\{wfَn{XᵊM=b0Pe"l葭w ]Q]{)hYi2)" ø-ѵ 1oX6qq K\$36NY6=9+Y-n6^_@0B=YÁp/,.ܽ(q t5rW"D׿td+q©'6vjZ$u{L͛3mJXA*G'Nͼ2,8[URr3VytWBcj=IҕFS(0tC 4fm:)+~r *]`@ :?/Ʊ. b/.i:+HlhXWM<ixz*r1gikT$P 2aSc *#+C{;5[n| mZwL-En E`-eU Yi4q q/ k):,8<޾Y4nE"`81p.CFOx.%V'5 Lӊ{ſ`P%4LN\ii-UsjE1a̓hvif :4a'nlݦzJ׮nxdЋolwKEj?ShjS̋Ú]vAb8W+,E_/y~Ɠ\ͦ ŠovGsɮWgJW-yꙔ?erBϑ ~Wzj5"o\c/V-RuֵH[@g0bSPK|\$%MkM4F[:!b%2F_#=G54Jϟ~њc\`5t*Zz>*`jgx +njLXr]֞KvO?Rn<;;4uY]

=#{W=_>ZӼk&/r*x}>jk\zL67 oHQiUz pmPːݭ'{JLɰ3un'tkW{m<2'VK3;F=k^ ,@M@z>R %[yTg.C8=u(05IAhm1MV6=KIUE`:8عW%e83v+;oWeS!/j<^\0nS' f )70򩹹PUuԕ} E"P{0DkyDt% [2T9>RWdа*$OiNߗEKKne 2JO}Ğ_w( 錚3>|~xRnl1/X֬="`h6ǘ,)5,=5=!fDmk.6:ݼezBn];'Ri41˛5<'gu}syN޻⹙9:߸dtbT:7w lHޜ2zTQ,vs"{Y,@x&°}%Tp/a^ 5>[-l?0JX<k?G}ܹMgpcC-guzyxW,p]:'2Q=L)y3OG4$?0{6Dq]8wdvpsr ;P-x*J;rE"@ЇVIQ+vʻʺ(OyymAX. jM3WѦ |WLg6 ?YӚۏ~ɻyx M:9'ݷ^4_5V9|f$ %rF"1iX,!d,ОPч"sq5Ǘl gidG7y!L1dUWoԲ(s휛{R^zor,Ũ=]^ݒY} l`|Lplϟ+FEG <1oɼfBt͙̬ /qSc s uā](- K'L=AYN7Ubȩk}:o,8}y<9)9?7XoyhЪc>w󓁨s˓ ytJ.斒H4IXPym$gjKވ 0euDz$b:)fWAU4={x2sZ]ww^u_;_\Aimhp__sѥܬiC _(֯0|3?샕mIEZp!:ǍUIgc>о.] s.  2#).Oe}CuLV^9|X I5#ǐ1}B28\ 4d%LQ='u\OsWS=8N6VEj\Vrm1{ *uL$ٖDkQkoW &e[7@ң a[l!_չVnFw.0NWސu٣w,ܭz*lXP Zlvw奃:Ȯw޸~wuU+ܱg23<!o,ؙ Lw MC϶R@grxFȰ4UHFJV\A<97&iswnʐx'bFz _dVX+U7Uksk m  kƴđAzHd%zYRNPPBM;uiҥKӮYͺe56ڴhpm?rc"Ѧ[59Ḇ96=zӶpZǪ<&ْ1 L 35ݹhΎ{ZƉ搪,yy?kkuE֗X iY&;X4œdgA9$?$?z|Jlj&gyn% ۪U)_ lѺn(\>Kt)Оg3YYj*V',FWJԚL^7OlpzN݌=&ϩɨmׁ4nҶ獻[Vebˬ:b>(3Ypz8s[Y(͝"E1Nkb~ ٪ F6!TvGȆs *0%w/um2KhE7U¸Jyb >jaԲy`^S*.@0[;'Lv9: n Py[ױY סV#"ƞ>,X.l|OfqBzc\x fbߦu9ɠA_E.kv좼)C?D1Ϝh*^#.@,$ac':$d91R^}{Տ_IYu?v% yvՐMNw.ecX5/iBҊVi5,a{81y kĦAqOl9*z,ZN^}rEqG&soVP헞z` `?z(|45fk\AliDƣ-#䠅mDO8 -8|'c;i\g n"34bcY iK>'HEWU5Ś]d|3 \Š|\&V̍yf6f6ArBg+r!j05/…&n]LX1]Eb;_ s}sZ{N,U ;m7Z, <>LsCW2-joU+%9rt)p\$8˷\m.Mq.Kt~+:qW`﮸vWw^Y}î}wڽoמ]{fv]e5t9jb0!@÷x5L&f˲&! ].]9ZE)"-TŒr; Ƣ/D t=k.[wU>y펱XvT#˹nX6bs;rǥ.4=8}U,s/5{0I"%ۢ7͖۠M?Nwc2{>&/{6s[-=BQqȯg\ٻb̛g\YКXLso=Tv5z]\2HܮʶX,ڏ7r֦K1%b'Ԁ*j "lϋbԙ"'/Gws9bR5\ Fnm4hE C$IZlA)m93~,}IO%UbGƪ'5}5o7B۴Ԉ!q?sgKiъ кukJE U4*'*ngNF2 Uw.)91nu0R~ܹdunW-`Zxۭ"P%} Ub^'9`O\Tsk1joͻ:ɽ­}\mE"` E:PFbjt}a/bMT.C;>?S!*T8mhXzdX,"~"9| 钢ilNE?.I* b5dSKE"`@5R8-Ƭa9%lwhJ'X,)lhXRp3а4hEؘZ#jD)40v Sp&фf%tKLӜ%ȡO`Q0(q= $Uu[z*davY{! mvRf5 *9N~a dlB3h6I]{wE"PkT.4I`k 1~_ fnLm?%Bi9Uƻԉ· K2E"PMȫwTqn%pmDބrJT#Tl9QGX.UDz-͆US׳X,@8Y%31VUn&VJ=XsbX,@"Ӑl`lo+'4 ӖVPVo-b*[VE"`yDk{I~oBLJ|6('<- BKqN؂E"P\ Nե Qcz4ZKx{R(??@[Y(! 9+X빊;W/'m_,9qF)yq4ͅ$&*2.7QϽw>}}zO(8HzPY >3XXeRbʐ(0DP=@LaPVՅfsz cmRnpȬ䌜3;u(-/y>Q93O[O+t Ϙ A(H̎-os_[\d!\o~ee)5)V'KOТX )VlZ@Yp/ њ{lhX]؁{pM,prF{M&"k3 Ģ䙑?!'+ӿM_ Ԉf7蹣GCEb܈\|9cV<,_xXǂY4m[L/pk&qJ "=&=̭]X-[솭7ZV07{_7=E62adQaey")W<ٞ`B4IfN|l2-.trY4Z6hLgWgz1tu#d:1i 0ӧfwaכ.\_ j:y&,?6`p"`뮉)H^\TaÒ?c,\nUS`L ۼ zN>ή㼹=aDHuPFPgY²G#Z{?%ns`c-7:`gNI+rZH߸)]gdp\ٻʎŀ L@yh~ ^m^~nz0Ô] qJ֚!k`gEm]B0VN 2f8} ?1Z#$wcf" 3Y+E\3̈RaYD{91Q`ESZuOpѼ)ѡYV^my?h zˣQ~&UTVԝY9fz݃Ƣ9@J4`y?%w9 [(~~J {t"A^kXgY%,4\j92`,\ONNlV^ 7/F/>Ի8/akz]ٚ[Ӧn6pI4N<#bیkak&\\m:6lL2s! qPbk\9KϵK[-@@@"Z527%2Jrf@7T-&.AゃY۾4/O3 sضEV# k]_jSrg9'c+DJ8BR+S(Ėyȳ߶i6r; Z* %6"`#%=Nc3ʊ9qy)Ӎ3r0l P9ȡpBd5VZ6dt-+J<8?`mKi-=D+ƒ@֭-*;*yzLﲣkjJ3MsO̞2%ӳNXL*=}=Hֹ]{oyGvY aԄEMY;+[8FJ>uYR)0oSKUˬLE"`H=0[ 6Fly|Ƨ]ZC_LkNJRv@YPI=י…6d:kX,U@rh+j9Db^+<#KpOdؙkJ:jX,I jĂ MTK6 f,=ґlE"`H+(RT-̣l+k͋1]vAd*m[ޥGxi'Zۍ++e\.KD@ q,E"P 1M=?1edc0 # I1FCR:YE" FXl51qkg#+Z+8Sai%̀i<,Ѱ+[ᦨ8?2;T籙,E*2+Cȴ訟w>ggi^~gkTzve˧IKZpNEJ^ss!ޏJEO]0۱xu'h(zӧOx2@mMV 1S Q`o9: {axHb[];fXkY'NJXt+y?2+p9#̇sF:3J-yBH?Ψ™ç-W JJ-P3 ;3Ls5DcV\9oa*?!ͯ3ZĔȆz#)en˽4V> bNrl!%sK#:}Kֿ]ӻo%{pM,prF{M&=k3 Ģ䙑??'+3PQr˘FS;whH_~h4?˧BΩmOT%lJXzȠ$Sր=Xڕ?Ez{<ȡ@|rܸ0RHŸib=bvPۦe wuw> 2w64|3j3'Be7JviE~hX ,QfTUEƐX\T#ʆkZT\3`B@]CΪ6 WksDJ,)\?$iA.Dd//Aқr%_sd?ѓZڗSTnsD7Fe;y78jE(AG4ld&W[;mVdCRoL3Qb ,Սf?c3 ̘'@WL J,fsu^!O+=b0p!ˍ6)LsI)\~ MDLUVސ.pR1]ϷawrD !;+b3[:bUk¤tycOc ;.iR%G$VWU=KGWZ,E@aޯI2J#ұi>@-aASYTD{ZbOV-S` 7bҳ+,Eh#%׵n+\RMKCcjמVVsP;&/:gMˋx+^qp .q{~E"`D@JGU32Y_m°/}L4Z~X,&SJ|d+ -<؆IBɾrskXuc21 7%HN^+?ZpԳC{7+dHyz7~jw>)kw}`ƒkUc/VmQY/?p-E[=?n׮]GE7OY=M1/kvՃ_ Y~Ƶ|XpߓG>Ca:)LYfNir'u+ucShЪ7uk>pK]K"f'Dj 5gPi),,@j-S)l7WpAevCpxzAFUekw/N;dd кu0l@QD̗^[pL 8 7ӘG C!I dV3 sbi)eʼt0Ah U&ODkv8s}s@5 `tnz0.Ʒ[j)64}`dzdg&fUCoUX,ENzV"xq JPʝ'_!Ŋ1dq,O쎋P/h mX,@z.ѧ+'"kg(Ew{;HI/4 Y>:WjX,ۯVY}^d81햒HZ%w:x=2%;18+]NNJưces@lf!S+cϣ?X50% Z&ʻܕ^;kv!;FBf  b!FnxzFM^2/89!\K ?KL=f:=J陴&= M-='u[zN .9 àT3eI_, ML% 59Ϭ0hpnNn5 Lmn:Kǭ8ݤ׏dE"`H,oDJ C̾n#Z„"hacweQ3%l}Q1m2?\HҩٽIrz;/:xb>;i~xɋc,FcSs'Gc웟'/f)e|.Ϗ#̸&{zKFCÊ^75n]yvU"堲BHUoODp=4,\+A-yl6}ya"h6 cPCUx1~6Bh$$=GAҩl?F+.s5aQdbgW;/QaޔwDBg ߋBL_ei/ț"|Y`8]5V9={3d~"pzJ%3st_/ryC-x](;N |e]gf~pm {֊g@h%#kfN9p`^aq PR\7p`hYQ9Nehmb υ_!~1nDn4fRPק̩xzO(Hnn1=.p浏>)!c{_2vMI]ZqLC\;3LݛnN{'$mZ@rEFT<nz(ޔcйy~y&UhARٚ؆ڕ(q]BU>A|.4XU:w8EG+z钕~#0즍s9Su¤^`™ç-]oZpѢ58YP@]7aלk{K毉)H..*yaɟ1x@kqnUS L< K\9± ogWq\Þ0"X$Y lڅ@P9P4yyE%aQG}㰶sF";*;&6٥+5v1 #c⹙N;kYmZXd?//XGks`)7(ux0ؙycsmlZ`jx6Si Ø*`C G=FB"{7>fm, , E0 7ZSRTX 4eik 4[\ӂx1~$Ei8Fml. ؞S@A2n|s(vOg2lG7RkF`0 5FxAKՅ Y9fz݃Ƣ9Y) Qg,`y?%w9+[T?V?q%z=QE1씖IZ]CŹ;))ӻ^nKisʢP"c\W]uS]h,pI@x8ڌ|Ƴn!HoVqc!c/T&cAL9EYo7U~ʀUIMꄥM?͹EC{\s:D-N9b5"r 32#7nIUϣum7;ȔLKA!lXkL2O-c1eJV4Dvh m)ɣ.]âIm@pO\Px6˰@0p:fqd4؋EiH! }aJAxٱ$O8!@O<&cTO%g 2K\꫱$x5(0)`LܢyIhg571M9 7Žm߇tw -d綨W&kF6ׁ)]4]KϢuёwߟ֊8p5,0''E9ySlt,p)XO6GBRԴ=.cKv&[ `HqN黝0l'͌cĢ\ƞMc/lJawh/1`ZGn%˒|o{>K bUOʬƾ ,u7Z)pᅵ[rg\BeJR|H˒T#4#Pn4CSy% 3)Gg=ߕҖp'{1̦{gBύYIVܕwsVchXiUېX֮o;ԿA]zE߫w 9[aE"QTlmi#Q69lfSWh.]ٲzi&9e>yf 1S?s6^pqN7ei=вE"`T ֐i`l\GdJh4cVx6']_ n>@EyAB$fT^UwDz|6& E7,EJPfP/le$Bg0DgpyYC+qt$p,23t[нֿBKy gpm[v X,02}J6+!ĢzeIN6³or_9ǣ,Rm]}WDnÇB/V|T[ ۵kQO/V=py# 4')KӴ\QաHp߅ +>b~S|H.=3"'oұFt};S~ّR,=ͱ@֭׭Zz3ĪAJ~QIZ|$H4MP ubh9k]IWCR:;ncrܖV/VIʢ`Y_nP7C-Rr˜ɢhw(Jv̴rtа4X,V^^^VVvn:xK^Ӷo>90JKK+**Hľqdb+`nx3E*.tpGZ`dFk 涒2mc];@ yԢg] ,g1mZ 6p\K~[n(7W n/54Mg2{LwbD?JܮRz_] 2225j)_$̙X6!'>!Ż|0yFׯܧvZ%+ -E~ eW0L $hqj)Efh4-^*(ZH$*O2D9 į. ={l QOYj՗_~V`Fa['bXN+¾1pMQb ǤqZ@34ëlPM`LRo-iX<XG:EJ~c2,< DbO vmraÆ`F7ԸP1lM&d 0aWXld  PE"`AFGfrOR#Я>;AQ%px'ІuXvEy&_P5:%See~g}yws9gu=S SO='`lO֭$egg!&OLXNܹ3D=SGT5VE"` b/.FnE,Ϥe& ) 3#8]v ;Y(+tZjchpGCuG9BlY,@x-cW%ک4˕Z<.g,cfzme,`7Z禰mh]S:L)c`HAB, ",0ƥE`'ye< _xޙlIE"`H#EI}Cng?׭ l|GF;g+.w*̪Z5lѢE۫t%A`, gΛ7^{n/78oE .\dҥK y8a aw鶜oPXr%D&aګ@{eX,iB/MISUGOkyyL4Xѻw)RpR#XĵS9'k-H:~E_>5oSӗGBZXg6ns"?~qc (_ԝq)Q>UJxu{ۄ=a l{Z6E@ 9㊝Nn6DRTC:Kj6PFɮߵ޻ۜT fVOCp54Ê]\ս{gqF޽!(o߾?ۅ^8p.0~ .~~/@)( @L_GAd KdPwV@}ڸKpޘ GΙ?؂` ql,97%ӟu' ,?)~N3ɤ}"; ~6)7V8K axj6|fa] mWa[^V8DƢ՗>J`[jrb[6թmRFntE5r- ͙X 7溸]VVu*Ki䮲 B a҃E*=3!*No~N1 cC0݂ S8L9d w;fffr]v7xEF6n~~S0o$̾׏q /PF`SRGS;wh/9>bDbSs } 1d, F1CVE⋋ {dwYb&.U Mo@5}pt`1y.H7Z^|Iv Z. JF5;gA~|BD_2/CPUYfܥE;Na7m\UuyfM{(g1] -zSr+N"zmź?Yx^ĕ̹m]UlV1iUH %&+d{9aT6Æ WZM| T+eaïL‡)am(En |WkX :wIP%KWj>ܵKR9pD f]qB ˑtHX1` XjEK9U ń-bdМ0le5k͋W QJzg\vn$O` %6l؈#~_uU?/bXRքj fƘjeqbR(P `ZiSS&+0+`X,[\Px6h9ۛ 3[6-[+oC\$= OA6t;ĖA0+OHf,11ma2hYM4*)u8'J$Wմ5Ш+r-ʵd6}RR N/pN 0 *` rFݫx9 A6Y0t>51ys+[m)_dh߈ItsȰ޽v-9f`/? a@`=Èr \xmR(PF^Aqd3nd2鈽WY@贬`=sQO| lv3X>:C LgEUjl]b"Tf@kpqudP+<̴twNϚ.eX3LGJke)`,7|, *슞acX8qL*C/ C+)T98' ]=]5,g+hL0z c?L)"`Xm!,9˰0OaFƈ'RiFUmeY LgBa.0Fa֓>a@!f"`XR@=yklʱȌSD7PpYc*g|uW/O˲$0u İ&,8SE5~w"a fX,C}1X)x$u`f3,q7&-}=㐳Ύ=WE ]Hf#~zub9>Jfj*q̡j9.ΨANHN^|Ag[D^'oҤbeSz`w|,Y/?p-QQg=Z\\ BX7;/BZ!mOA+X]vg(3_7OY>y펱XvhpUR-~ ""{ 4(Qo>ԬY{~ mA1>QFMQ>bg9)20f`i%Td3Ckg?}>yҤ>۸h)msvzV%(z N 52}0zb\w.)KnI巙kG!r m&W]pDI]~@bDbR(c!޴thLRxd͛atgϞr{sZREn*uZ,qeiT5$XnznIqԢfLGcZ#Mnj]2Yk -]QMXS VB֓*ɻ)˽{2m|$=#n-bX,ՈAM= \Z`xV T vIz?)]VeXmF 58ɩVOҲ]]Z%2nGnÂzd3iª6\43%9[}"p.]h벑umXj1*I 9y51u+c2Y^q$^q݀k93[Z%JCh6k,n#ȁW۷oA& &¼S"P/, /e;Mv֐`6r^lv5dFm>p.n25gS'[c_r69 rZBմlƘpfe b |">;ۂKAEEEeedj!2m'E;v쀪 6^d%-='`V^X[ZZ^C ;*z>eV9c̃r UpCmVc* pyVB9=$dDIN`cҲH +lٲ4h0 ~2mEXFz>嫟ƶszWeQUqaM'| s`N0gTqa.VE"`1"ZI#EYn(fL~QdSW"ˍ Xg\)?T}p6Q37w~,VuYYٗ_~ KuYT647-jX҇y.2@6Y&mLhC#SJ6φ/,g7 t?6ҳ !2 %B7K n?SNI""`X>%*B14KgjL'Q9*[5}%ZUӥy͐1]6e",Qo{Gax83¬4WVE"`brڇ4ߵ8vYkkDخq,1. }YwwKV~ޥg9-@&0m81C`7CDfW{NB"E"P ǹ 37@}cZ ܿPaz8:7tm%iODieA`B a2:sg3YnNiUl)1`5bdKy[7AqKl4fr+mFςt5ݴi,|s'CHt#H eU2UK[)ϰ#'|M6@Lk \= o6*aG3J~! &n ]4, `q_ b} >5L)y3O[{s{+HD2>%tt7̟?d>PS XGŴ@g:# NV\h mcWW`+e]0al mc{s1(ls. $.&L.izXPIūVM11] #܆evsXi{ 6|q͚5|0(q߿8 d g@@w~,k׮pr`:پ{dԢEGI:1?3~Ȧe y}!\5R3#]&׾M/=tMWg wCBVW$3.7nq2r0-0_5zr3< j>f 8t7XА3E<9GA[E"Pi'$J=)k [f`,k-aLXs!/w}F?p@@@z*|&KY~}X|`OQH-[L*,ƞF0xg D8xb>t0%@h:pQLxxEؖV{]:.=YOjVhHsF5w|֌`.u ;B,){HCϴr ׂp;z´;>+F>!>^H[Ϟ= 6&8^azޔcg,epAg≦ndf\xnvQZMM8-]e߂K{tJ.ڈsZ6ǞGGa[E 5{Y=uXQuZnwOM7iaW4a7 ~W5>uF8 NiG7I't-aFt)8dݽ<ꂂӞZ7kV :'+sˈ|ù35hFk'Yv<[DcBQL備,‴n`?3*LIx30c5ZSC>^vG~%g즠{{il{/K蔶0+>(Y@u ^!eUVFn0o/7E6E"`톅JX%ۭ zeXE6ЈIfWD1zf$q@0 ӟצ2(-)'yff 'A~ipGT{-gX,C@LMxЇI2&dnQTQ= nmX>χFrq-[IkxژFDu3S0:(k=ڋl9E"F8 By$RAF,IM?8Hfn۬us%,gR9dC% .nX &#W^dɢE)n]hƕ!X >˘Z&%Sݬ(E"`FVmS/AW Nd@{MR#Jϲj?iu!=TY>]xC>j(8uBZj&55и5L]x1,3F-,~mC_^*p&R:6E"`X7 ",})Dn+ {z#G$fXZf3X5(f7p925M8ŁC.#%}$8[*ܮD[1+ї]v 7f ,ݩS'†(q0 >| g&1>ʞ|`CWg+"`X,iD@gcJ*- غwްH'[?oE7%a0b 8!`ƹa?y Xٮ^VE"`T A%wTeG)BZca8}I,<:Bx3[LᄂܮV*04a4-zk0V  o`t8 E aÆ?{'r[s'&z0 6 Faƛ l=dȐ?,=WC+KcaQ6r[\paIҳ(2#2²oиNρSc߈ sYq8\ѣ8 ?  o.[b,T.Wd%X,HsrK`r BR.V(qtX>:&=k};uY=koͮiVE"`}&IS= m[hXj5ĈqydV"+|1NI0)ô0@XKSX,BSYz"ulDZj,Po.su0[E"`"ZЖq(Q^x괎p&GaPJLE"pL ؓFgr[81F_Z3^5kĴr=o8%nQ[ǗO͓/%/(cհX,5em5"U,w|`I™X旻igPX,H::L6Qf}+cϡvmQ9Ne` Zr嬻hqbBQ0bB^ؤtz1Tr ]ߏ+OO ptB޽',u XY4"[bg2yו0iUVs\mLzLgܤ~+I,9CؓҦ,iz7-[\ hm}WHɐ_YwR}'1t}F[ F2?i`ṄOn3 UOuA-FK sr1g89 $&D"ׯ{Hf劋N|STL~O-*["GP6$"&$Fmg2LE?ΤPƶgOG<΃S4qYu҉ܵs1s@VOE~OLնE"p4|4ѯEh0R=j]ܖڅ* <]œd.~ŅyӵKfSwNu`W"8?]#/RإzaD^Jn[,2K< àTDE`BVhqnC&F^!ohQBʼY9 @դ,u}|p;VaCu 'ZrU&_f H=o$٭ >ȭQ}ԬBN_{~ mA1wيa.^,0JM !iuS<9-(PffT<J+H }Фɓ>/' s3[L`9i9 ln:t^!`Y\:D&r/ :=*+Čy_Քa^nE!w^Xo0u$_JVE"`Xj`gp^L%64tE"`TX2Q, !RSj-dX,ZUEvYZE"PY4B28&wU;+fe-E"PFuJ%?yv94T6E"`3% 9$`./gl@-E&# O[$s_@6];&zNXX6E"`X,𪵞mX,@*d捠NN+d-6q'K)5ZZe,EX@'2!]ia/Y3U!~i~#i? Z$1GEo? r׶/VDu_2e"E,j7*SMq\݊QnK:9t_!ǣ+%.HՃA_b |+y,=' <5FdK3~K"+4ҳ#uS5T l E~2ߖMQ 8Z|Hْpc ryF 1abS=8\0"m@]c##`9PczU3lOJ1_w|-r^D/,~ ډUUݽ|3E.0ӳ{S+,3XIVoy z*h+}Vf?HZ%>RVŒٸ~De3 Nr:z/ql9K\S;"N,=M++<E.qn[f6gAX >Wο+?1~nl?kM y1`cc|*suLѲƋkC 9H6TF/ʸQd<!q(K:͕n*b)K'='.HJ+UZ4s8w}R2ӕLϩADr sו3|Zӌ)7Kwgjj<FCdP7`Sv^+G"ؾ` Ī0 <J#;7OK9';[( 7n%Ga '\d'{uWQݿ| H%%b|](MVjY*VZvهQ.עPciDQ@$ J|Z98{]Br=3s~g3ܶc{*zt-AEo타a@@Hs%\VHKJ qa(,[DR< j:m<8mxv&&o|uǦcvn^{/_x O=r^{k)e}aHRw*bodRju|#3w6U~Unnd!pNߵ\⁰k~ѳO\8KЧFwMYcco}~nT=&λ{_mצ3y."ggs`bׇϻ3>b~qNqCݵ|syȐD/osnplۻYϏޱ^1Ɔ@TϤ0<p @U~^Op$3rƱNDfzL()tѳ}XϘ3@[3':oOgIȧW^zq}qA{)սk^˧k2Mgu]kUo:y?9xAV΢clr3)8lݪ:'O ݠ67(u DYLfݵ|Fo=FD@0ZGQV]־|ؗQi.+P_zfz)gA*d5G`bßɅw~w_6>~gmog:i-:v̒EƼ>ws:mf2<>ٙ{a6@<]kmMNvKzz7WdMyb,@à cg>cƤ.tߣ=ollq~t^vۯχ޶r G/E4:]"sa={l% Z =h;46ȧws^{||lߜBO ~M\8KX'$iTvi Kb}fg ?t2k__[w0}cK{w6&wݾw姬\tK]LNSg_}eUS}:Qz6OJ3v-;,m_}3_n|wkwߝ(Qw-~ћ*fj!Do>yGwu z.+g0@c/8zZ{h׿eؤXGzI,|gQS44tpU.}z{]u٥GԵerBlۻyb{v?]^Ngםwe!']v E8a|~ Q"1A[y/+I*3Щ]p֚H鸵={dKYtĦM,}{ [.ŝݔ+zF\n.o="TyK;sWl?]p9 DmD3ң)4kJ^futںq4hGfYu*r|-XwًrcI* 9ú|`󥛿سe/^|E܆=j;kk0/@$klxdax3-f,ڌLIbVz#XʃMqLU[2M.$m2XEpGjLg3ynPwIqͿܺ%pF䀞[>۔y 1FPқUSڱ7vKv-\*-e7hsО ^AD#3 #Ѓ0 0@Aəaz. h̕[YK#s!+dڪKK!`uTl"ŗ-xx-<5&mv eXstx[ݎG>3ϸS[!dW1U9CO2/kN?1OҒDf}'(N ϶)3V@ 2@g=b̨QG@3hDՇql*c"dm×;DHmld-<Zi?6Vd=hҍU]lôשՔôBM_<;&>\;}Զ CYf*I,)2|±?qF{n+@`=[tH'6OwCφ%r2Ųݛ*|K!fe:%&,{H"{Ae@􋀶MūLkc-݀?K"CMͤk96gjj3ŦRsqs @ 0 *gIۛISRƜBB-@*+ -iv)s6kNu`Y۹j%6mw~BuȂң@Yޑ{wr)".ԮcyՔPnW3̫+_QL,a;w\{4cy+$@c.g:KYDVmp@Ɗ晳]z<4&1rz:l=,OL 1u H9"ly`~7uG?ɉ ln+C;^'!-Gh،)#*@Z4of%Dկ,%E3@`t|ҮXgtԗ> j-\m nv x3W{  0Kڷ\:C7AFkB.ĤJ>Sヲh*at#~C @W]jXX+dk-Llmr- !:/*Y΀3عA Q>w&]MS,[ᡞ#+yzN ~Fy E6d*y0uy׸ 9|hb5l-_V-mQ]:Mժ oqkk;ڇ-&˺1@`h)vXMTJ4EEH>#T%􌰢gnjI)CYP@kƞ ժs)lg)`Ĉa@"Y"|lڨvuF0( @E ^1sQ1q&=*0Eے{Wu9wm]UGd<ڈʁ@aԃ]0FPp f/*os&s)Zu.ʉqmSX6se!ѯhs,0-C@ zYWs8u5YμHC-Rl$O]8X#GTK< !e4 0b$D.h=ǼĦ*n[OwM<]CZz׼Kd@L;Մě=V6F&&p Q 32pC0_$I)~.KLnֆ"ʛ$9Gay\*:"nA5k3()C嚦NwS')6>C> IG7{n@Ch*@`P-f4QWPx͙Vj%;}2~NKEU,fҳmTU<º ĭDѻuV`PhBGv0j.LmVb;lGkGPj7yXt@8hd Oڀ&CV+[ٳ"c7n\&\;o ޮ+m_BP,8 fsnP[7&ӌz*"M1|(ls~61%u$ ,LN*Sҽ)ye7$ QG%yk7ocX|bx(j}/|  "}#07,qnfvnc7 k,BJV wֺ w\E O=$ I sAϚbq^Xlc3l>elEMMr)¬,:;+"*$kX/Yb=W&H@QG@yy#cUoqǿµEr˞EYӾ{I8MX0J)Qeg>Z\sizƋ G~ @-윭i!*ӑ9rSk*zl#f M]eGϞƒL!5]{šsgsghNF֪GKD @K9KiqZ-.Y6v.q.G>˧(7{q-kUK%'85ͦVsKMoD$^|w۔@^  0#"p(3+\L;.,*+Y"׼%wnwힿ<U 8YCrq,Ґ._-TO +oHrRF כ&@ 0HhĂ)hV\*Z|pS`Yl/G)$ X>~ݑ *@ ̚Uq̖-5IXۗS"TkZ݀N2i@ 00륇Q M*1hM,_v]U2%P(EN]-=lh1V zF *_yQYmsR LŽoqJ_XPzT=Uoub\_ycЍ?Wu@F 8ھĝD}]`ײ^ԥ#6 7%)E#+[_ F+2@ï@`mLəbJ.KqŜc['n>#L~f&rӘ1ggqavOpcd#3aC?@VttM#nUW8&Y $͔5e>ENHQFvd@F6XoVMw7mf[bc٪h@`n! 929 )#JYZpΤ(nvbU112ʚΜ(~~[| [ @ B[YdlCWl-643j vóJDl&Ed[HPj~3AO11_hŅu;vN XckN8ZK;=駝qvB [4<zK,~cM.пzsΦs%zsK)j$6 +5QqF^%v_ݿ zG bCm|nz ӂ9[ǹC?VkOޔ0~P/U{mȟE瓹D}5`A/Ry6be̯%8v.Tkg8W>~qkv"TtN+*j/Axҍ{ yQu C'‰Z= ;vE=Mr|}(8!0[+n(2?"`m n.^8z[@ê.¨'lz@\rCYL 1AINH&kj/~'Jb|pzs x@GS[o #4;S)P0nd9M|{ԠȝC'S<2:o(g< (g-P.W G)tD& ѷWyA=$i2+MNѣ3[Cy?6X%xEG\P ƈII=&*[l#9b e_ԚtMhMrD̪Dfiȅ\z^S !^\W g224%łgJDώSts_R ?ŶGgMG[ dul@"FDy|<:n8:HB*UJ$M)Q})4djqZ'􈐴ڔl4MMZYP~i(^6%*SZ(0Fg"3R&EJM7V*$(ե/7T"<&ӊ P%*-F^4,=5 \hiY&wGJ6dR$ ec99Vz7amb+Z){!~6ŹI{x*lmWPJPƴ LWeXzbs(XNJA5) WMEʹ2RnPlOq{Ҷ`IK UZ%-{~4B?,XK.{=#,愣WXgZ@ Pl ֮]k-9/>ꨣj 4,gy5 hŋ]wum۶w}=,HǧaU@f V>îʧ~pt\999CDg61gs|h} ޽O?իWz衆$cOd4DG;Գ e @uN>KU]UY%f})H0*vS 6b~̟?9=מ@ NO+9W\BÿCd@ <_~CsJ|L5\O4H8s,O_g{ٱoܰ+64@ @$k^sJ, Z@ /2=ӆi,:. аȅ @gbz 3(@?B?,/廿@i @_9?KBQhPr<1xs׋~!ϳǓXOY^,($eY\]4}#8FͩL׮A8^C¬dkJ8V/So5b"ݲӇuΐ6=՜Jt>SRҼHȽGFVMzBv %8{8ХmLvxOen{˗{_'̩c4e Ty}=sߖ Ы)Z 7IX7{$ ++>B%2MMi=eWtBvh;.˜v:Tw=_LLF_R"j8X&`~} mDRq;ZM)n?osNǿvQX#:}F}$ҙZTgUbώ}$x&jl##{n}bOۿr#[i2{)X]WS R u#6 zS=|/'?@Z`L°^U6]LtJC]ZO&GJLw#'boaqR>?$ k@muܮF팤*%gz+ovӹb nR!pBDC$V|3Ġ,yAME_\k]NV?̨L3+$^榺[.[{r20ct(8r=rbdmޙy;"P}|&*J=湚g(Z,1 qIX mgC1\bʨ蒗~-_0`cYs֯7vG;Um;zDjgVΆDi Mn}lڸ{#+>]`Y[-jJ ^>>=ݒkHgx~'6usWf14N8 fװ%VV`ȏN/_e6- DJ8`)ꞜstѩTLY*G޻&Cǯ}{켫?? y3>_ߤpF7>dʬ`+K˙\3Jp2:>Q.C5aJqW<.MZ䍳6/:Vg篆ԡ<}1!n>?ȶp?~ 7/^X/rԋ^ǧN@X.>ưE~~T ZIENDB`filius-2.9.4+ds/src/info/img/email5.png000066400000000000000000001725321506574241500176260ustar00rootroot00000000000000PNG  IHDR sRGBIDATx^]վѻ&]E,XQbh]㟨{41&vD *XE,H?k?Ζ{}Yٙ337̙ټu7\իVX1 B ChٲUm6m֤Iz?={;W S^s.R:e;qDf*b 1z!_<'XmX(e ۵*Ul[%?rd2{é0_ȲLFl οy8K!y ??2sq!.o8w<<𗲊ԁסc-[='v#;aՆM%V<xoWt[lV (< ZiQO,J9O}ݒBBj L(i<})~7*5׫F4l v(C/tUj(,KxLoUU?ԁBh^J-s]#/%o@i0:duڛVyi"Q}h G |0$;g(@ԅuًMoƐ5~n "efe /9\ *copAe- ޣus;ް4 z .8݆-\x'~ 8 BJO23 3X]KUp:ͮᢜ  /OuWYe;k9c:tܦnУ_z fްCK'"iC> p,8/O@p //OD(I̭|;dX]0eҬlPbUB}B +)3iYMq],GHHO9䨪2+E Y=gduvKFwMX>'YZ!~5S\nQGrMVW|U'Y>S<Y顑bȃ?B4f+$0$g)oن(f<-^EsU ITS:=ԭpQ K2~.GetԈ1ߪ mU!GsHAQ_h.d9mNL# XQ*+4U [ҥ[R%%t\U߰ӹ~ABA&t +*9.ZSrg/?`yI֛={'ެ VƼHBa SS<۹w%3 ~gaAf\%%8oR ? IhHa꨸P"G (&BFb`賀/"ApԪB{s!2I+ɶ/#5-UE̞(}AV&[KxxNh IvŅbl쒴M m&mCE 1IӝD$I#v[ru囬)h t+Nbo 钒 8KKҥeed]QzOE.\_$s"G +tဈGw"Ew6[5]_~[ _ !sx1AZa|5NK`E٭;aGz"*E1/zʀJM<0sFT ϭ t407$@=U?as,B].h--LK1LIתK,ٮmkZS#Eݭ}8 BKb4@V09KKnD%mqRYcdJ5 %`#SVyz&e}`V GUt?^"yR\o(YZD!T-5ψC ށSZ93bY L\]{ڳO/S7N q0>cnhתYHFIC+2(34}2­U_\aUzݒUW;K1"i7[{5剶{>p0[Ghw`޻yG˺uœw6R9,O)OO7%ȧP/aNifvD '׈A NHy^`pG,1rL3e)6z/殞JYnh]vڳ/8ӻ%?~gF1WݐK殘17o/z+R>жCO:kp {nֲoˁma?\ CV3^M x^g_^~Nˊ>Rw}j*v.Y_6@lSkxlfWWJ" !`aJzZ5E71yESy;;URjYbP#$!+;KjWX6+> /U?C[dIlۓ9F6-K3h5@DҊQ#qT#^T9fa\gp46esK}/+ r,ċ?j '{L޻|ɟn5`ɭm=TXY E,Y&-S[%?T2㋲_-Zؾo|'&e_ycԉ3Q\غq~^!6 ][1Ef?ԵMcv> f9Gڦm~2a( g6ʪSm0?:򏗦~~߼ |r8d|uWlJu@~z7O\aK>y4씺!-[:)"-oX*pgᾔaB i?e;'%/ 䁕cS/7#MDKN5}ɆnyܝK;.%u'"1EK ]8(AAwg7U7{0YGspe&8En,|wͽ5VQi0b1*CKVX0dH(mh2aMz.gO҈#ǟBxpMw;j5l HRPrd58EKFl߹/%Х3[pOHlQKDhиٷ&s[ 7+dkaa^QaA&46k8AƍRMפAhj g#q.X'\ޠ Q1Y|HިQy7j%,|C׳гֶ~ɛ~G_>c{׸u^Q#\7^=s쏟ܜJ|@.5o^bR7^X7|͹3-Ek;#S_-YL֩7̕jb1{'6 =w> kؕOH7`"U{o s=FFZ_9=Cʃ?iaw_8;Ҝj2psk|L_ou񗻜㒏hJ."0tϴU(pq=*$ VGC[XiF!4fz"fIPֵW0a#=C_*Bz 4U 9={vJ3kC1j.<rԶ1`]*z ݽ3mԗKgx7RKjݡ\i v«b@[a¸ckq5-!PK۴! S&ܘ[{=oJ7}zhv?*B鯦vsX4ںWYMX6YϿ)i 8"ŒtGZr Rv};$ kv`9mԱyV|DJ=(cKfE=q,̱BlP>#e۷!Rwb.-S0xCʅWՄHʍyXJ5lPO?EQzEYzY^}`V8Ec„S bEN/נ~pkޢf5e$.Z4/xyn~p, 9@v6,"hmغ}Ef-7.Q'Km^1g<ȃ>fXs˖-waM{g̼$vŷzk?3ԍ}xX̀o}!fF'+_Bᕆ~vǓG}-w+Ti?:j:ݮɣ9eOg]~c`2bxel7c^S |wߒga'*a7G\?_`V97?@\67=yTI759(45c;~mlMzMPߎMM{fPc)u>toW܁@I lQxVʴxū+ 'o؅\c?hߺy& 7`̘wZ lG ȹ`)@b[W@Qaa"О5iVԴY&M4mRIz͚oִ t\FiM ߅L)9,m&7e2AN -Z4nױu3WxV=NPO"]ц#>'<><=OeKqf3Lſ ò12"Mk /D_$ϼve>wws٠Q#÷[6ݎ~7 ix-;[ 2ķe^rd&,V t<*]AyJuS ·JHVZW_MT%f-NuFq֞qP듀bb&iW(Iuqg/FXхzf%2sD A~oq >ʡPRV*tu7x;qm;tF(=U5'1aD>a3nTO[3+l@ezh£W!mr z+6L=[c ذ^_Wu| W= ?Wh\5 J3VYSۂw&ëCn_Ѳa;c>IK|ɲbx4 z[V[u ,[ P` T-y(z~5f,'S@Z {^_W>ے٫TlٸlΆ o\-;ϚDS7 od!aaG=NPDq4=ЁE*"BOKfɎ*0 aȒ_L7!)zO *WZB 4`5qܑj7 m A܇[VB̌KcX\$I:/մAAƅa .}xrLELLuIJGjCن-hSXzHe .,Pj%S8#FTҎVU!S&\vgY"ܘ F84cF̃h )1135xpMΏ(o5H4lw,lw| =[sz$ZG&#dhMimi#au^L !EIB9]^:UF"ÌHG)+hE5DIί,UN} ?Dc`G8]G]uڧ+A{dz;@2ʊ%Gpvd+>;$ T O(Y˼9Nj]o8OCKET?KE#H_^򈔝|JԞ#YFʧ0G4Q_W>nWN,- ̪_\HmzBK6Rgaae5CT5ݷNu/Cz6)K K#'| )4m Eڢ##;XьcQ.|,byJ:SʮQ~*"f7 0+,E@ߚJDM.T^x$06u=Ap1]$vW:$6tX)2)" F6Xw=5,F>F-+.A\PonR?LjrÅH@5!3 @sHHn^_}C%'4Ƞwo-it?!kOƍ1& $(V[%%`+2m[ kuA'FGd'$XN-[6D mѳ5n~?t|bR,B B{đGa]0=wh^+yxhrQ2B B ^< 7%ԮÓg`H nJ5ؓ;5 ghNܜU_D!@!{D3ÌǜԎ_S*?\U'18wh^sG%7.O!@v9澄}7ƔB]5#jN@@ÓD3|nt GgQ;s6 i43d)6!@@@='4W47a)$Ėx3LRYӦg{װI|ɵ$SB B ?3D-X^ef8R %%>s7ȞE=_s,p$!@F&:[UMĉ v=*CȞɍ w͐"!@Iopx;I8ePp!= *EϷߞ9D!@ԵʢPq؎~~T,t(K↉h,ws+E\:$GmܰPm*)-m]ꐕGUorp>@} I'B~aJ>,օ-p (k'a9W>0 < s*2}I"牍w S$pC0I.cg9{υfϛ5}Yh \`]8YÒSxF\ }Z$k$} vM3ITE.G7h}{cx=wc y2`}j\ pNpGpr`w2Uzoj3~_sy)_?M͟hfM6hРLnKKK׭[w؈qz%e"6]@` MJ2d]??"Ew|_5Cw.R_ؼ"#k1:v2kISijُR廆iZ{N=@gɖ͋.ܩ߾k׮=ztԩcǎڵkۺv-Z4oڴIÆ @ߴi~VhNB]3(I3#vyĄT>I"qe,>]B-IrGQdfT${),e%מєoѼnF!L3WLñbŊC?7Fz3g0Y~I^E8/obYvŦ?q+;ޯnH$.n LeW9B*3xmkP~ҵҙa} {U'NL-J`̼ Iκ飇=@?8ǯZ qXK_sUO+3.3S+".f>/l:thˏ֭[jժy͚i~E{$/)K/p;8@} I a'܅$śJ'Sy"&$yꦃ.S<}phkg$K9|楆O<*ɚ ,\Es'`ks h=~b{/oƃ)[n"tesӦM+Wl׹Ng=r*E7 I}~u;㏦}铻g=>]q 'ï}_\E\\֞?+>8ޱײQ.#gw5@ICf3M]Evҫ3~,p! y9"M^/Yt- ">F hϾy,@~D=dvߥ꘭bBe_,75żՇqy% A Ɉ@J"1ٵ,KY V^ < t-ԞT!0(@} `{^M}$zSj D80&??5QyFpӽH{υI?}**HF}qkϗ(i~{` f԰~ێnذW^=}m]v#ss:!u =?$uqc'd'< yۨtEEE/vMBdqq^X{⯇^wq>{>,U8|g,tI]r8+R=I]EiEt.DMѥu+=] X@t=l_=I6 a +{oh/ M.n= GoJp gA1/}ćYNDcܪW)0|d89r.Le͛-=,wn y8_*vV{sgޥ`@*K;o|偠@t3c;?Ǐ2esZYtؾs*5k\QY #Uū@9Q*Z ׬n[A\y0IXHY~D f.)yTT`!.qX &GUhJay)i"V@5A-4Ji\?g(ϰ=)WV^Opk֬K.)gh ,6rN;ɁӦ-o;ps8CϞ;o]:wNN>呫wH;@]Xs^x#q^(,-q% PANˆ"jW\(`Ѯ<{PvMtM8P>zuM-Z'b6҂(7ki.\ }#߰]Y/'ad*pҧt G 3sac%y$cOWf]Gqq1h8@on׮h%%%Buv\#s?p>)}"tm'_ݱLƐ@} ޽콡_͐VGa byf,MsE  U:#w Cdm;C + a[4R,f0{V:43_v,`ߡ'аp'l|Aª)?\ԊMX ?>B;*R?}˨;:?sɩW/ߩzN!9l,Ə M; K`z^۞rAOӪl=!#;R-~~fEŰ}~'\ջ&``+RӞoŃR4YŬDةk?p>@} Iu=CqĜ~y9` y@WfĝʇoInܻ}}{V= Z/:"n&ƚ1[z217;s=#; ļſaʍV+kP[frCMӠ=&2mfq֌?xιޓ:$Y^uzt[еp DUt  z@^u 2NB`b9Ͼfn`[1u}?34Eϓ=>C)7r'-c%b T{q#g- Ķ$I=/KSW"n=vJ! @kDo$/z`\,,r5$ c>L/+aǗbVit lA/yC*Z\HMjiy]hTa4Уi>;kС–6_v 6.X0"yU?</·_!R۶_M_=[ @ě D⾚髿fW+Z !s!Xl+ z`9b8LqD*<;#X*s(:s[O ubѤ!@=LeWWE] F1틍qw;dܞaPWa0v=_VQ"B BEbR`/lN8 iLM'W<vLusϼ}siE!@_j/9& /'}#VQ$y(n-hK x=փ!@ /ONbtrky#cbss_{dĒC(ުfU3=aEpY'L7E#B ɶгÖ:\06r%dqev`IXhQ0H㶢g R=K1&V§B B`+!v7F[o *4OI]Fp%9Y &[m(B G [EڣA *5_쐅Q,RHr=ǁjN@uA!D{\GQEaJ4@ώ֌} «=W^H Bp'lΖ z**#fVcFd-sT";|E|]l]Go}y%_{v\{bt-]sUڶRնT0BHC #Y, \{%m*+帗c)<6w J~z_S{n؁= }-I zF{LljXH*!@dK?az✔="F*Dpp2 r/=˹g"}zXk;JE)!@?O&?Y#¿yss>_}ͬ+R9)RԁU <4"'X 4Z.$T0ꃒ^ڵY:ET ??/g, YVV .iZچ N=O?>{y8B~\3{eW**ȧ3xOs!I+F\)ou˭$`$O MdIpvӆj 3qnlKi{ BݴqC*]vN( qVk6AذxU7m,/+-+et.xݺ% !Eɖ@-ioӺڐA.8p;<`vک;c~;ӧw޽zɏ=zt޽[no}׮]tҩS 0dZ8#<'q! õ'gёAE/^ 8y-bZoj. T/9<7K4|e3f͚ 3X+V.] w/^ܨQ#xC`-7nܸvZiӦ7m_~o($ǂv*?fmT 13#lؾ}Fa{1aFZ90 =J]3F"O~)pG7 98HFt3~3~OQDVTcG 3nuf'IY-m/? ưȩ~]^Tdp 2Jϛ7"4j&e!h^0 ܹs|pv}<|eW)(e^5=ӭ[y?6̟R0oҬ߬|qڶmirX'x_*N^Ξ6m_YgiuD|oC6ACFFPQCĒ4Sdm<3@;3 HbGnG5,M6CosЋ-¢[6Ú*Rlܴif#v9nE78]5n 6RpWm?U TYA$vJKR%̿EȆ[lZv>󬳶k&R W^|yxZ [,R9o#fH(~݉)2Y?t55a'G۽Վ`Qh1 wv"c;%%u˒جLkxYے@ ٽm7nTQ7oUz挙֮+^`Űl4v>7_R {0ϖ-[ .bjC& #d_,pz7V[`a w|qۼRPxw DŽ 1#Rd]w QEt?=g5#Lםș"VzeExiq#Xh h&~j<sp˒m .1Ϝ9̧`Ɲ={6ر-[(/N/[l9?O?G}8=l[z.//YNlB’e[/k|Eo~pΙ203gΜ, lv@UAϥeto+g H(~݉)2Y+ͳ{=O<^ -[٫g;}=:vsQz;uuv޵op'#Gwsv%نeƵ kwLMS7H[ӻ{^fUdħcǎ0QTF?8-Zh=ԩ+F\Z^Y27{֑sBjQq7w?zj*)֓lvIZLd9hT'O*_a#R5BS6mh1c=?27^ko6'8i&0֫W4u/Y[W f`$f-u +ionYPlu-o4歀,Ev Uf FqV,wkXBa调̟͞5rdֳg oq)w3\>XU1QHɬgw/yg<7 l4&:2^7|qL-S=E^~zD#!QTIgw\^E j.)YW|k9\uUZPW-xW(ujd')7_L-YE XAoBp ζѪ#v%O?kFY)vpȑ|y-Zo#< #:z!:fA[_|9he] =<:ljWmXv_q 6N0'o{~4Bus8zAa: SF_~뭷L+/՗YT*מb(U'#tk欹sgխ7&dγw<:qo}u*f3>zn3|aN96 i^Q7o>S\{Fzc?_t?o8dړg #bvŇ/VP3RPqSs`(| Qyv4^(ts1C*5ǩ~jiw¶`$uZĻV%#nEA{ueTW„$%d_N: DN0aJ-LYH_O(Ofͻmm-oشe [pX ˠa.l]UJϰppszYEN.Ԋ fk;r"z4@D)yc|_27_݃7tuU^HMQ?=|>ɑӥ~?t?lk_ߧؓq>*:wa(̜L{Npw5sxYA'fF|3zeN~}z܋pF@,6RI^(tЃLCY91R(^ z* Ygbj.QYX0]ܓ]LI=#:sX` gΜ{{p~3G{;>)S&/]`V+agocVlwb[,\ݍu YWw [e!#0ZA \ J NJ*ڰkj21)//-Oev}?|+?}Eʖ Mm fޥ fܥ;ԕo|zgeʂ.tX!Э3OF<|9/* G[*:!ϟw22!D<+xIf}:O信vT L EX9=3(BRI PH1ydK%R%l L6j]ϦjFDR}#,rqIL`5>39pzv"_`2x-+iWf)apIg8+*b>nN`_̺9E_aX {n#7 kۀ=ĉ'M}@&~g!c,W+INVw߯Ni'XCns>rN 2T a|G6{|뮊˄m8[j//T\6S>̅+,OEŸY̲y/\}P[hϐ˜0~\Ϲ'y`3S*5/!*NV_PB/a fH*sL+@QM,!y[K~zۢ6]b˱˖B$Xc/f-+5Nhv=h`u,a_SSΩ?zW93 ,qyrFgVnH ޺+\{ˠ#_ZH uk`AfYDamvK\ON-xׂkF3]f>[<7{qI×6|yyhk-lEѩ}=kXڔg[vj׈69)1;iX+5y@V/\Gl팽1_G٢'^x/Ӫy[cTŏE7^ >A=9GWɡ E5"k^*t Aj7jՊL!(h*B0N&zǡuF#h7­GNrfܖ0nKze6-ь7`ŠW r ;Gڿ (hXh>n.j٣}aݱl8ӟƿCzG$a._z)gwyor ==?ܻV[GTŏEݷ^3A[UDEU eг(IbK `\ƒ|t*s#Ygƒ,瞵gmܼc.;ow_lB^^âgw?~߰ 䮁e6/{,-]+=6<_\m]^_4x(R#Gn;dp6n>quѬY`ʢ0jy>m3VsEM X{hǮ؟$4>'Xbq}l='l,|| Iv{}nةo5ѿyݹE]Zk]wk5k&wi׿^mv^vڳ]=ޭmwmآCJl0Zv5YK &ܡ.Ǟz0[f!jAPo5c3J=Ѽt2ұ2XbA dr3@Ay+qZq]\zjr-WܠASp+L5)bkn\xcTN [o4,U\bJG\֗~de%yxYWjи}̢F$ +W>S]Wo}@%=Xp+TKgZ5o2B"|(&h9Gi߶]" ^3d ׾Z7&?l(%$mp= 4u;4z䨬[wɆ',ڹwCYSuI[2Aϧ6NC\7_5&ʺRSǿ3tPUIװN:Osn+־ؗ3{'.|M~w?ZiugMTl@kY@D>G03^G0#"& ws.ɰK8#R2SVgmч&zf[)e}mX_gR8;Q?lݺ5lfٲeKB:Mш π,V1-M`묋J:^ک/!NTgl j!fcVڈKı@ߗ~MEH;)+j6* ',||pۗ6+JWtʮ$~&v3pivǏ5X~9u͏q$?PLj#a6|>x˖-(C-ki xhXȾ[g5PB [=c}k>XG;1n%q73J훦)`^0ݹyqVl&[)"ަI ;4ɶRۮABsT5,$ 2B5n/]-[Z[ f@|f0}+Qy²r>"ۇ^,bnWŦRfikkI)m3O Bf"dFv^e%ʎYkG2 YRm3p?=WUw*R,B)x-FtQS$ g 6E+\;6^kDeWج{f!.S#hU֝J% jQ\~d_L(_ 8EYk! ET9/Լ]‹/_\C-%kA{R/$,mҵ skV!@3b%˶ -Q b30 w} zvi;@,B𘞹%=W=g0nto UiiVWu^$ Յ~z^S" C%@YQ%5{s &w(f1nkVYxns;nJOܼ1eA@]A j1,|I}wJ,F bD'ⶾ9IrG@+)* 8< TOB Z}5x ^YV^ޖM_J-SyikOmZU: B:!m{x]e+  "a:]\]xlcOkl",SnQK,++kXtyeSo!@T7˻'$tL(>L鑤7(9J;aa?OlZN5oXxAvv&knv_|:\=Cu/v{|!@ P0^̴Lf1zrĊ)?-0e@9N~F,,Bi#{f8pz @\y?}ձnᢼ|%uϜp7`vPGSخ qok7UzW~t`-#UKN_|IfB2whB*[k_^}"{YPT+LZmǯ rvPQd!sTltFlϒ]-;-Zgc-,vEQQQ^ztnSdJz$+-/Le.&o5 ZqsL(!@S-)i{ɟjMvKeGJ [pe/ ~9w*93 &uF,99*V=o=i5ڴiF;ԶQEŲҥquIMkƶq? ?G΁q>=1Ns%S?+_T=!};SxI+UDZ;ܼbUɒ3W^@O=?Bs,2;v{Ok;XC \?k஖ѿ8[F䁩H),$yF5Ȅ!@v[Xu_Gj$ w׃k}ktr*eXϱT+W~mAAA֭gўaݻwu\1tn{tYJyj$ՁW8eX"9DyC?[7.{>k{:I^>/:v+O _|'hՏ}YK[IbHBC{~*wwS7 wPHpL ȗjh!to|#n1o}jԺ;tЭ[w<蠃JKKAETqarz֬Y?Kye0M {5pW?ncLm>vA- Ѹ:3߼w0_4?bVڅ]"Hر/+u.\ڥ SxןӫWTFiqfttQ6R۟Jawq RtuVCnjc:{aX3G-/NV,%d^JcV&n;hizI,L !~7S=,Z0@(mla756nܸ> =7k#SXqq1W_}𒒒]wMn޵xc"eT_xM`N^Q]`38vaz$08 ŋ[84mGNXJ\3?|0nhtl2jӼsK/%5$\<(DL~]a\jsu^3ϻ||Vy/a1v2I.(N5˱j2q{%:cG0Os`N+ՙ:D$[^bu+Ŋ?]Շ~wWpf`piӦ}7}ݖ-[ pK,kl>kQ/Iouw ϧFYAu~C[a1X.%挙wT~8c' uZoHr s77tIץs:jMu4d w_?ܧԌ?\? |W}v Үd2cɳ'.yyC?qIߚɏu=~I,Ҫ!5 Luk1U 6R끯Å%fYOΐʰ:DnS!ͼ8˙/$a~g!Or9Crʸ2դoX4_v:5g6=ds=5r ML78q_\ԭ2eyZNGyɩ3Ѣ*<թ Sv<(o2ګ=sU?>`n~d3\ZZN`&B#~WfJqG0uϕ}A{1hٰU6-[%WiWR+,/g*CRd}4sjЙS_IC3ys3LDh7Ϥg6 HV@N7BgFOg`~^)@&w}Ȼ)*laڣIS9AAW9Dan<L[]?8n)bf8,8:bEyeqӑ~b_g{棋f aVm0A^^!k@ Sw9 n-\xr \{cԯxCsEwr%w 0p(5&]A~. #W؃w,ܪqQ5b-V,Z [ʓf:r@)!@dR/Y%eo'Y4()gOv)]«)t++7[J?9).!@@]A 8bc01ّ0$swG[[v Pͥ‚ , p ,q6{0xS_1eQZӧ~A:eӧ0DۆV0y<~X^O8gc82.I#X2)`bM:FT9Ӣ[mw2饵?Ib@"p &qer gv q7zpj)*!DҗUR!y]ѧ%\./#;s$of5VnRZP^׼~?x6ulԩc4]x G &?JSW+z6W!w t^]Wqz{[hQE0Nt5,X컉dw^pdDEBEϱs%{Ÿi~EqvE (2y*)&:&rqiߏ\w9ԃ kbA";^~#>8Jr5Ns+\ٮxl|#***FܟΝm(]D |SK)i5ñ-9/N~Jn& f:Э%%TK=pfOyvUCR݆SQ1c֜9O?vrc>#rvbVMqHjD`'c#A;PM.O})IkgoZ&Hhzh^bJ԰t.Sqgk6R예%=iPWl<'%RigqԻt>N&i]ʍ6aÆAVyB5>YGTjyVl;0*V((eQ d_ƞM ̨2xkb<*L[i8'[NT&g=6oYƫJJKJ0nѥ Jv걺v_ϜڕDw/8ԹOW;Q}VT3zذ5aCXP!uEDrЛ'91\?cs[\=xm ͍&PUHN]@Op1ӀqK @gm ;!LG =~^=lA^: v0cnm?j9waga3iHZ.٭g2׷MA*սkg&0"WCf<~&d:g?loL!nn}=*f<[w̰ayPﭒd ]0a^4l _C߷/c#w1kbܘ9,p;nCg;_|i]Poa>07bd*p "yVO%2q¶=L#k7:UMU:eꊽ]00?պʤ,g=2 dy d xFׁ0!Ɏ zvDLH%K2Dܮ-0-T5< oRb\ubV" [u(;u71Ե)YU n9r3q$c>ɩ)U&˘k*Ga;( a 0^ r aӓ_XSY=:sÆ]Vw^pea{YO5{c!py]N~VF7gڷ+3.݌)/6yl(!@ugC]M&]'?&5n+~z_y}QQQ^QzVK52W;]ZsE76c= pߪ36HWOCޘO9+6ylQ BC :z)QxYf! ]-۱;2 e:`ۉfvhk~Y|0띑Ǿs]!omSw۵_EaTٺT ?en:Z^q톂ohޢUڎirX7i9sM>9+c"g|1O>rA҆@uh̔IF?8f eZu"rث֓8p^Kxj?(09E#B: ou?(XVjcfĎ`S0g~#]&ץavmܽ[ݚѬm5oi3uj>o!lѨuSնc N!Y_lܕ[+ {ATI^ٞ=B?}|ʂg("V[ZV{7 됍6j5-_>t!@5bIw/շck31[ۍd;D^lsئ kѪcoI!@@M@ kX4z:ds7l'![Kw@|VK>(Y-޾84^yLB eSKXdzS3=ԤN$ 5`x3L?jw +GSmjvǡ!@lB]^a%jQP! Gc$_ѾjJ ]"lL:yZ`M)ˎZ]rͻw͆^zM%5X͚=WodեG N\B  H뵵[FU}R=k1E͵{䆦h?r[3}#=oc‚}a-IT]B#Ѻ7܀ qWɛ1;vAN&nY݉ZC< 랧ºgu kpOQEmIjTsQa B@f܈m"0SW$9bQ[R0nS%B Hg+ȥ+hWhm~p^:Ʋm/rа(g-$1D!@T?l/KŰl1!l׶X"-h+Z-ƃRۤNsT"B @T#XDX)EXCpԖ#VDNs"(!@@";lٷm1^Z~/0q(SlH!%^!G^ea=Wa#ф!@Ip`6N9;]8Bm7 cNq zNs(!@@U"`יdݶ=f=|.K+ܭ2{]M e*&B-Ŕ3&csŞuơ5\s-vFrǵXdy*s% %%B:(3!"|Ǭ\Kq2s2?MK H2n'Éb!@TE:d9҉S4iڻDJX=:U" x;˱<=W]w#Ʉ!@pX򫳪Xړ3l9OY&hvyڙiؖ[0DyIS4E~]Vۓ9!˵)3=Ua"ф!@D 抪ǫh;X~^t&d=b!@Tf8dvf| ANxŔ;alQ9 wk Dd=4%Gx\B1%zF B !`]TNrgf.&/{ɗatuYA4㇈9QסH!@[BWGF~^~[f }ŠK! e}yk7ʃ B 1sUN^ٱB;;lERc㚶е]Ӻm֎l֊T۱m[YjYmٺxȄtHS=W>J!B!!v>Qe՗3骏Njw2,YB|"A_}bA$mvbywZ Kc@5&B!tF2vL-bٝҖztnOGa+ ݨD8Cwkv v} D!0me6+E2dSZ`tFvok1-Yڡo:&(YyyQ2N H{ǃ B: Y)=/7,̧ #q4ufG*TuyT2!@Mk=C73pVI!! Ih́g66кrNHKZz8ۉguht5||'_{eIxh]z5'F_~ w2%B Sn<=6mu!y/.S[-vQ…+8+: LG7ydWergE 7 W|F]=RlB8ے0Z_EYI%T.mnfLFW&a[fTgss(7_ ' 'ggZTQB ,ǂ~v|Ysp 8OZOC._"uoyK<^z<섔 BE :GauW›ZZvek'w etۺگ[2w^U(/L:!@!PYOKL^Xg12#ܸ$N3u fg9Aʶ'B #%P'/Vf|T[4ǻkA3U{i.WZJv [6m!@[ J1 B ! !uƆna!]-G Sjn:[k]P}qeEv ǃP̶$]Ԅ!@"\/<iX1[$ZIR[K꼴&鑂DUM {{gSO"hz^B 6H3 GD2D[R#eLw4cn?+F͵ۏt`SϘ[B~w{&#;o ^X2BB>s.RI఺[Dt[!)H(Q0r 6/.w܊'i@V`͘\+'Xh j@o5h ʑUs&QCRSj6~; Sg(=a GIhzs鶓RSj1dȐԄova 4 $h$%!\" Ma8kvQqBg\5g%ɐ](BJ^װ\^e>!O2#3S;-';>&uaMP((ebjR"I]蘼IV>u;تLdH SqcƊӌ?M,Gpgg=JVH 9!B՘0K0?&4M8Mr] VRĊD^k'2ׄU)@5C@[Z,&]I M:Lk&"u5} F=:8k14$/5[Uz; ;\ /02uJj@߄J30\6] w\ք˦:vnU6L$M;a%y9{ބ~l'\21ERS/{LɈ,込d4y͚`HA3Ex<[`%;U@~lƙl/(м8%(VpIc$S\ACJ [ĩ*,kӏ0!u˟4zWR9SJUg8}NKX5Z%ے2zsw2oϘؙR fROvU]ed=QF)yHP5+"weλ^{_)$Yed#S]>d+#V)$DŽgԳN 6 ӑX0b,n;nѮ%U}QkDD~ݜyx?p+#jm$1 t3IA`\'i=OH%(_Ld"M`’IEz0ޜKZZF j:w[8 Jb~ sdf xKWh"NOP3b718VQ7c"p2LLB9{dXF (]ĦUNl f.xc 0@[zv.вX%WMɇP`C(> Ќn \:7OHYKd;ZATW4/b2TD E$I\)継UU5D4f30)+L&{rq'q+mJδ\Igf |Q%& f_Jc};\ X=\tmwEj]YZ˨Rzq^~~X3i.۷v> ~'_{@N0v\? ! q-WJǩ*XB!| Q(k} bոFW<4SE]]Ws'X_vIҔd!Xܕa&DKbKJ{kn֟mܴOwlija̙3g̘Ι3g޼y_˗XbժUкXǏw!8V\ cŋ-gφ͚5 25UP[ro!N^qrrk˰P8g)ò%;jDI2)mhLZڦe*(sy,*//o֬Yvڍ? vhˏ6mڴVvh-[/~›7oޤIlܸqwK*!kE wߐ6(jVCĵ4mo0bqұ7sjڙqӞUhL  M},Kۤ&6lXXX,ϐX&L ޵{rS# TN1Vs[K}QZUdFqbࢄ@Sah5@8.@蹤l^asC4؟'O-)/0"P#4]ŵ("շt7>I BSDI/ds1UH,!@@]^sY[̮[ĴrÖ,wCۊ9ٹy9ﯿ[~^A~}ZƲWk֬{sjUX)@.(Ū:DJh(v [B8a~WR!@4kR07rKsTqM)b,?L. kF)"1-Z})EݶܕN5ª۷}ei`/ <իO>;.2h p 2dȾ8<::~g}{o+.  g  |`1؃  SnѢAiIA!-&_zXLE3 L#N.\3OEx/-ahӳ#X6zZ.0W3~6䬌sɪA7Ç *u]+no.zN8 t z0, g81P2 ;vإK]hadCm-{&M@F@t+>]SruZe_Xu3h"h_nS`M͵@]Z F`.ۉy_= bErBNاq"^%  )!@!P8_fBzV!S+UGpr0LKf9l8b@poR >`( v"-bM6mٲRAaPƚ!@$F@(#eTM!YP_U@耢K_h[c86e{4~gڠ=Zzndە_ fo9`0Y ޅp]fq bѳ>Lhؠ rqWF9/e= .wPDB H九j~Y|ye}_8a[7LWg7ocם_3.?ouevnےDηgN_}\-|"m0\!ڄC)08K_-Vno^ & M3{(6!@ LHRV*Zɳ %ǰ V˫V@JQKG )M@?_/? zm(^pm dW?|ԨQgugq 'uQza2:eZTF[PuZY$@Ϡ BPQAb*g얥ܼ7r 㚜_ ƢYdioGJhpFJ\=ƔNsV ~`(i5ZAd S5| \3͂!@9GL+9p z/t6ĥON:g<" l) ;,9$ B [hZUK,m [:.b-߮ nh^zeQ[@جQxr6)zIZ% $/7|, F$8!@A h:0&;;*i}-e\لDBLJVTk|MH|L8r~w " r`P&2j*LYvIx4 …YJK B  ۗ&Bg'θOYo[$tmhzJzhGV=E@Ba.PFW!7St;IشaC^$ B28ˎ>hƒ5E6kh:_{sP,Rm=s@ϰH并268XhGNs%q!@9En V91Itv:9|YL9k4^EBGʅEBUMZe{ }O+E²Qkۨ,PrGU6BVʁ "5W(`^%i,M/^ E?I(!@D@-+R|lsa )%hZ,$`T)_m&öum27=E TxZ熞 =nܸLɹ6++Uڵ"-%!BzfBGθF?t K_nN!cW9 A 0" W/ʂw+Iz3(R92CT}B0rYC8<ܿ"?_u6z]'9J:#OrK@xrEVӧO}^VI͢Ϯ[n޼y ~>lx*:  Bz 倞a.d h(Vj;~1OJ碅H!@u4kLYˑ0x03摰1(ZuE4+*Vp1f7$Rua|Y匞a g/]tIzYQ5|Gk׮2s;IPB ;&찚^ˣ?xK ~[`BK<^=*$BGYX{hDANZ@+9h%@$ 9T9NL̸]VVw>` {>iӦ}g_5°Ȋ+֯_]2 u K|,{pr`:޿yLP|B B pW*|䆞aրA8"[ 4چ9i`Y(|fꫯ>s 81yN,ggQQQ=`=ӠgY3O B ZpWEY;9[u;ZEVnu"`p3G =j̅H7}pMj >;뮠p y!dG~5 ڠs;ba( p7hs|!@l%y٢F{ O4QLHΛfȾm]˷gB?$;3zs`/0t3N h@ 4Rpa\@bF~ PX[X[ Լgmc%=<.`tYY#/ mG$K5%3Yt!@2xO."3{@zkqu!4 b133z~Κ$]㽁#ֱcGXmvmٮO, dewM|PVr Ӱk…Sa `%E#B4(̞Jd&=Ӭg}=P$m}hȚ<܄m[ڷ=ݶ^Ia?KSIh!-#7.]n'OQ`n;۞DB r,W{~gٷgY1%fEs۠1Ô3ߎ;桗,YgįkoL0VjCGEs!C+MBͅЅ#vv]4, /:[!3VB_viܮ̊YРvǰԩS?䓏>rҥߪmpg1Llcs|,J2%ݍD!@$F @u:0Mt&Udnנyw%UOAK>p#G<3=\}BZh*55b`k ~[`_ ę\WkfC_ NZ1 wH!@T-Z[֞b?JjѲͰaav '[1݆/VtVЭ͛56Q%իcmЕ{@±Uo=VxtB طVp͖݉3eDZkհ*סlaU:t`w駟{P6ZL!z̘1'tfAN͈S v5loªO E#BXC9*1cVGdilΌue1C,ܕal`C=O#4h8P)P5k3h Z/L]_`Xȁ# mXAM6lFWXO!P40׌9H˙A 5s AdI9J4_v`'C{gyAqƍA-[֭[rJX /[z/@AgаM7?1$c͚5D))!@@0v4(C-FƹҾB!6^Pm vj.|8z &ޝ:u=@%4԰kĉ0 6P8L`{!;ׁ3-'B = װ8`93ڵkau5P2`}xkmm>O1 utKHB r@7/(Aa6{m=QQsMا6ֆe1c9N\Çc8,tk`np]Yl 5k|ak3uߌ\ۑ,B  D#v53c1K^ $߀}gܮvsL aF>% ߇k裏FþK fP x}R+kZUIߡ!@T$G挝1ԹpR@!%0q Kf'x"07P8Xl„ t3" !@9F Pm6+EpYU8)vҳ$|J 2n SN9s~ꩧ‚*G!@#aF_C1B C)0\iRyj)-&e`.<{(*I!@!P; R A~_^DC3!@lb7&{:.XICy='A!@T)q3Ua>v' n=s^^RE뫪pB  J7acHN/tːifU2^"B mi**C*'T&zBI4!@!i˰T!@[&$[%m=Ӝhʓ B!Pi] BTi:" B *ά92bH|P26jf؄!@!->9a@S:B B :{֛ p•L\Sz ڎ@5\ bUArB `1tѯ?kXnp'M(!@!PȆWI{cK@uG 2 *ʙI aI3t ӱsE̪RݛG!Pj Ҋ͠ֆ>#HdS4_l=eA=)*!@!P\Rm&8jp,XU{(B xb6 %ӞX ZYmz&9Q B @ʕD'1DB tǘXzUZo|wM2[߷ŅB Fkv2]#Q΄!@uXsM-⋶Z;i rq$zs.=ב^N$B!X(YkSNR_~,L$ jQ=bV&HٍV2ā!@ao[ֈ&hu6-KX?1Ҟ B DծI@!@9*jm @Rzo@am՚+L<N\0!@58# ЪgV;88P` OB8A?RD5R Bmݬu\%P Q4Z*Uh>bVlшc!!@[_#a/LhƸ^!^}k8ܐN^< n⦄AАhyD[Q!@uҞz!PKxw]DH5]K)˨xv$1 Fu\*,!@M6aoX:kwX~/Et0ŴQ^-=Ⱦm hZөb!@X4!e7T[*2q(ZL۟ϵF6jzpB0٫DD=K'B6!ߟNUL.2z6\X=s3,a/f_*;iQԻ.!@&H;$cFl\,&B MHn2 1S'3VfR((k B]-\ХCk'gZԃB m`,˱nMesK0A+̾`ŕKҖ ۸M6ꌔ-!@uM~A$a;1^G3F:lk{tmEm3ύXɚP TuA!TP>(i,Q0FNo+u,W/*O Maۨ/R!@r˕m<=3ĶqP!@eJafmaX4-AS֫g!ֹd4,9qD e[Jz\;!@AY؅1)iԙUrB"f_IJ{YPf<ˑeL3դR1B;mw=]%,2 g2Z''+E]5ePԤ=O&BZ!gWSUAة|B$t?Smƪa#lEژ]#zCϤ@WK!BA2r_SV]* B ׎QŤTѿ P=.fZv ~j8N+\ÄM\Ug-I-hS!@@C`FrOP$0\+RרԊ}{3Z[oM>M::qDB>ai5S B`&v6.]z7jwjǠHg(`٤#1"b&us:1öP;sT;B j X7^[c]]H[5ŎyC JõY,n2oq!@zBUIU+'d0I0d:%jTAB m "_.6نx.b+du2aoec~KAC;,jAm#N! Ne6eE!DkGM$ʛZe!Fri`3>P<ܬ4]aMB m;CtSaX<C+oDrB!/y9K`K홶"}$BX$ڈHf%ѐ*U8ױdemuXI{G B"^շbƧ枫qSB+IY]砳ڕ1lõTHQŔ8 Uas9n^QE P7%3ٷS@$B:"ۖck;["TA8^?h O{ўk:2*!@@мX4I biUtŵF]GCorjJڇVDosҮMΕM$ B sC1b#Ej3m.:w4l᩹gFdT)L N DhYP3GR!@U@TPܚ!9[OE=gy B B4=+/WUܰ4\5"fk2NXe$W`AeĤ=ׁ~OU$B =;~B>VqV:ؗ[+[<'ǻhR֚W 7skukۈY*"!@uki ~ ڕ|Y͵#1&CJ*ǻ[I:y2qt9:/[ hV:穊!@JFm@8!A9Č+'=1!@U9t{m;ڬMҲm@,gB: ٓS `$~4Z;GgAxˑxB D`U.[n_"Bp_OAhB#6!LdIukZ[HU# \Vã$"!@"\tt N?̮k5YOi/HmAuA4CD@}mgt!@D@f>?,FxZerjRy--hܪ6:7K}˃[mA'p\v$E!Pl[қ{Ҝ]HU!$pT$!@wںc -S;J*Z:*޴6nk2+/jKkkU0Bvg]l݊i\ZѩZ!@  m@8@$T9kpLZY F}@$)kP!Pм׃J ^W[y QaEj=%DIbSkLQك{QVmZשj!@b`η˨۶f_eTr`\YThJ/2ʽk]ٿJeM\S. B *79]` Ir^=!@@u>+J06Fˢ/if*gPКп\kR%sB#Uy#9 B k2;˳6.1xź]ŵKQl232@]@y,2mҞ+ӡ(-!@@g-nkփ9bXW6HNg/nNgr.fYd>\Yɒ?dQ=cB @N^ƄTܬ`2 cFzV})ZŢX'D*H£@+9˝u9=kzRBB B gTRuV z`d9\K0fDU*9C!@FPOѴqth!J/",Mv ۥ6$ۆk-SJAw}JaBL!@6G ^55S҂`k9tc4ImzC?/L2Y0mJD9^i@ ͌6TB  eW+Z t)hKWj](BGl-sψ"cKJs87TIϚک!@[}A|\RwEu0c+iׁKEpBd@mٯ^+, = m B [F.]Iܵq0Art2›;!EsCd2ncA!Z MҀl ٟ:peRǓpyJe6,lu.=ۺ(B ]KڟK6Jk46SmcmV\@\ųkTkUþNbk&˔hIͲm5Ik϶ /!@B@9U+c(f5m Z!RuP-]Y)ͩ I=HB.ܷ%B`~_fQ37͟ fYәVJ[;qykp3nc۶J[7[|uqT;B mb"7\LDZ*;( 晄oo'~>m.^w ;}9'mբ/!@@MA+[z5.M?(l]4'B5#.ђ?/e,Jl |`r4̕i}knkY3mٲeMTNB m@ =u1*0K ͹QYٴ%} F.Ypm͚coLqr3NZAzAύhy63N!Pp碮Dfl7K.lli_0%}RYLK#N͐ub=Ӻé!@$\DxN|,d 7Di^Xil66P;,[/қ[SxH=׎M B6!v 'eXFc[/ 'd("smTB j&R 1slP[lYgOftW\b Xc>f8Dϔ[˨P&W;K_05,e=̮L&BV! =KTvrhJK QܽMcS:=>b2K(_'WY {==aPeB oﮊ#Ygm+uX%墴^#\nHr,ufI]R+ܞ|a'װ;,!@m$Zdnͅk4殶cc hk0l'W4f$u~W&-R{v NF-|ǐ;R֞h v옙.E'B |[h=K5Oo!WC]eQFVZ S^mD)g6m4``c Ul-` 6| C,]([S25+9uPx[ݣ)MB BA oW+4 !Xuٶo[plv*3Zѫfb{w0FZMVFooz=?tuβqv!@@M<^lPFs6U2kzu5\O[/25,9x!f)O _2፬0Ft8 *oIՁdgywFuãQZ{"g0=iӇkDB]YwkɥbS!E@Bdcj|v9Zwmi}C hW誾Y(o@GA`+%5.i/*V7lv}C=qs""鳻FLx?[2!T"E(ĒABI4ϯ_2vCj^aOO^9y07DAUT$#nFϑ MMg|pgٵsO;qP%2$n#WI]@՟tLySu?ͣ.~]T?E|@%M#aKoYzUd et#hMBfT׀7N8eF 07`q@jMCCGd!(^=V$" RBR/Q3#mϕ,PLY*UrgJYQ}E3Y q4=l8!a@8Y%:ju-dRSfkt^)PGRt'7uYB^OK9SepˮNvʊ4YpzXMrbBC7nx"|`(Ig[=8/IWԾ´NP"뮄BC [m-$WU˼e(4)lӡ3!2U'yKU\>:n/`P뎧 C(3Ҋ6n;kL ]ھ ;6a!I( B rWu &OB!@Y!$Q"B BD9!@C9wX$B B Gx]`װ5,GB Z5k5nة4h+kXr B[TTTvaF:>:蠛oy99!@-~W_,+++ ͤ+ %'B j%%%[BrXA=Zr(D!@fccƍR1IεPB a M#yg*,(B Z3P_5w&!@@H'tYqs0>dJ@!@@E~w?I&}w_~%nhڴiNpےL5zyǎ-IN%!!@{/***QWZuwxmIn,|PᄏҞgPk9T=B B X &8vmÆ MK;\qm] ¶ۚ{ߢ B b/ޭd}a*ƠȄ!@u` /Yv-а] ,//£3oÑYo>!@@Ex78o|B*]&ج1$*+1`^Ių9'B޽EVVŃ`Ax2^h7g{i%ے7y---'Q*aiD:jzfIYf DM`yy홙UV > IڭaPorrD"p 8g{ٳҼ'Ѵ"@  =IJVVV 4$rp|L 0D"@$nV)m{cc륃-Kr-KSvfskzc--*I D ˒rS=$0[K*D"@0-õg4]tJ"@ ǭa}i!D"@J03MKD"@Ocía׿{v[=`O.UT"@xf_xƯְ^-{sԉcCCIܱWCvYKEѻvrPxwq@? ١*z t}i1fmݬ5yzTiFhC`[/ln_2巜R2)K>)z^(jzhT>v+W0JGF#n&޹J2%A^E2¬ݺ7g'j󯽾x/xbpRNA+ z8E+u͈kِA9_l#ZI m2u{Ж4j{@r{|+z|Nޢxe\/;`)'em ̾Хmh3j-4")Geᇿevٕ4[Yg@Z7zN~6zW\,22%P 2*NQ$<*cr{7TUnBd4(ڡnbTL`WVs 75P3ǴYFKLx^*`(R>A &dC6O5N䢿5i8HZiw1t]p J4oPW0RXIj1y =Uˈ Bz4 ? %zܸTt9p{0Ѽ]A y;wen+qf-]Zoʳb]ž_r돬"h5x~Ut (A̓8VUqr5:qFG$ٶ|qoMq͚ K|].v6YSo⋢yɬm}ŀK%Yk{`MD#%LpI'Il>&خ%HA(t['H-~C{7NKº (o񤻀 xc7An'^*Sj+ϐVNAŀZ00B7Te ܐ`O0^mHa Qͣ0A`Y^\y !IR'[Sf!,XQtc-Vdu+H3`=mUt72ݽȪÃDňrK2Zni{r9Cn'ӑwae3˱2x"]C^5"EMA92 6@ Y;\n凜ۙKX2oAnTnt ϒsft uW~7cW,q! qUA$ Jp8#Up~=bl|;h݉G]͔Wb9uE܌e`}z6mUaOdAx;7~|q5E}~~~AAA#|}}ۧ^|ԩSx7}o,]t'k† 9"` >B簏H``HHHHxxHDD:|ц%Kw{ [nuʕ5J#G ;K%>53úi~3{ͺ + Q(Ō[.,\']#s ijk^iӼ2 u2ً̂2WeEd%̂[>-+_Y~..1vݻ~n|ֲڭvke#::[ z5g}n} a D ~㕟_Lnwٮ]z///|wof̘gQn}qiAH,FrQ>*jlz> &E ~=yuWڻ{sJ{ٶ+m;sWڮ9\9cׯqwX{={WV6{.\aKa!|(-h|s"?MƉld5LymG5'oT3g:D$ŋ;(ѧ!4mpP%*SU(7AhLE2լB%L)3!, WfPJxFdL/uAdV^a^fVBY>Pe,a4ED{JvmzdGSzA8z-!^V?GidBW_~HD:Gp Z % */@ZQҙ(G&U -C^Y> PG2rHbE:Ji(U< FY*j&5%]/O7$-a!9t!ϼy\$y@}g;Y _>}#y!B{z7!4 :-Vn}0ثR5(5QV]4WBe1@mCh-:TXuT@_FZ8XRX >IAJ)T)O@``yAn!sِ=yB3 3a~ y/G <y}(~n3zV-S-v$Em;dE B%T5 K szԴژ:ԵfE}v3δW݄v#:*a|NvjРݡFCN.*# iCo{ z[n)| `}<wUw [@ō_o|S'~S.H@!V}?ʍK}=s}$(v܉h oI\7J5PKu`kq fWDx#+_Ixhu%]e/Eי tD!.C|k&`Pq6ˏik.UDRaD *(Ɩ3|턯G]u%N 4 #«pRS %z s#uf+L@uXo/B(0.$o@A/@j= @x; o#|Bx3:LhuG{Zkj«˥@ =8וN!xSD౗C3xW*$j& v hq`#|-hLj8LaYEI'VN5UW^2—Wz(! D\_MSM{JbáVK?3o$|uP@uh%›Yj›o$|%ں >8)%KEx%/#||1Q`OQWjk"_`Ve/'HO?*0S@QJ-߈ __D +o&|5ve$|% :V\8]z(z_:_Lx9z q|P2:!|B@:ŀԢb̄o"|#og:mC h6 hz%jtwS@~ sgz8WX 0T~~cv˷;>! 6ttr?rկfSo _Kx+Z, hw _Ix^ Wj«d)zK%xplƓ8qhݬPRtR@ghpԳ6V[o&|5ڜ8\ c嵄^ᕄWէ>V9'0 yخ^@*#߇d"KYBǘo"|#?.(Zj oYN3 o"KtS@«׫" —t՗!GHHw*k"u?q5K/Fz!( { g >tQ@t 8KF2HJtu'է ՄW^s} — ˡUIWnO^IJ[~ŗu#K.ñׂzG3HVNz_ഢeALx^)A]te'$e|>>(֔ p6IN; o#|k oEGfWg۫'ZkXy5UW$h4PT(..Fff&z  .?>S@ߓ퇩8bcԩHi B[Eӄ!E.]Kx+-7Մ7HJ^0Чe5W^Ex%/W把  <PRoЄq":n;?S@h=Sq{a3** AКhq5NzV\@tRʛ _ W ݽFW@x=_"`@Ex%h?FII\UU%JR̙$:R_a-XҦ`Œk>P!db8[p6sMrDDDJ$q"STގzuX} 血+(z( ^Gx-4WqiHl6^QSS^/ȐW@Cwv7IRptо$PTEx_GZVz-pR@︀>#+ o :V^Kx y*GQa/UYy< jyPVV& ls rCcS3Էtӭ:3d>U=qe0&HO pd' {F^tzjo%fWo\@? gUr ۏDΜ8ΞST*RGrJIXMT_/O/[揱lo޿Bv&p~BF9a5' "G'ġ86&V ls[Kp*(Gz  pDu;b G ?QDI"D9A?Gebd=g"?޽_#d_ LQ'DpjG!:&Z}xx88~'BWt{0έ@2n8&4Is@&;"&&F BѶ>" Pq;nxQzzQB\gr 8t 9A #Z8<Ey×%h^ BX"bNKo勮qTgu?ŲEO>1~9/L:0$|It4G` tb/ f^?碀]5 @8v BQiK;A!$>#↡!U 蹓L,O@)^Q硫9m]ҋb%qIs@ BuHHUVXv7g ?]qh\ *߶3yt5س{͞={"$$$$\$&GB!nO ƒq {\ȰciY{ْ vɈ,^,ۼy;xS%̛Gmmm_O_,`IT͆r 87L. SB*ơCwޱ{sʔ);)O]#:].&8&aF2L8ƄB aS2ЖBY쉢H܆-̛J،Sב*kU>2q˘Ȏ]|dGc8?$ Nq0e! D\Q/"ɾoq>n٩y=_|ǯ_?~S?dOt5IENDB`filius-2.9.4+ds/src/info/img/firewall.png000066400000000000000000000201001506574241500202360ustar00rootroot00000000000000PNG  IHDR@9~! pHYslIDATxz]Un[gLItI!PH՘QX9JC'(P75I^snEb2bHb[q;kk *} {?dYf^?;mS$4 ZkhH9sT}Nz%ϛQU:7Ͻ,޹lt呗׿_17! deU3=*}p9B, r#¢a?P *-~67{!VHmFc8 1oA5fjc'_ &'5d)nbBoaIߕoh9?.xڱ7wS}ƅ'_}Ҳit5}ݓU9(# U)CopÑ @0,(D@:Pxm xgKIJ<( €Q.|ǑqԾе>A6šp&ppct\'^I6|^%3. 8EE,(|M%Pߋ2A ^%lE( L{o?T ,-^\B#7ٍ.k6 ' Հg-6c4T"5xǟ%>-O{I|wa_}lzLwueLwL8ň(2N@(1CN$X\>Js9c_Qjw'F2XPǸg + wԂ!W3l&&A^_L2=dz{6YKb &: }wcA.J;(! 8g^cnז ߆E$!.J)4L"}&rM'LǑW S&j2UP|7o|i\HBĭ ,t$c,PLT.}5Ow+2Sp|CLr[h,XvE^yx?Ռ~9^ oACS[ Cbvl*uǿ7[)wWWlY1 5!eNE {-&?_{ ,|$XF^$5>TjO1s=;\Z`O6-X%Ɯ\(ZC!~_Aw-[7T2!ZCS;g3kh}$6$$`onΛ)gyr79e ןgUh^uB&G@d3W{A' VO?)϶ʡGl5=EKQGv`*@`Hy*i F̰*C gBěZ42M:ׄ2  PW {-(#Ihv<]p]F'!449]v!)T[&)0=D(x8LHX k;kÏ2(CpANP}.p'8jA!IWʾME!(A)Dj0YM a*~ùCx#χs~ ȕh{;o(ʨȳ2Q G0 º2D$j ΍Fv_qeS1DneG8Fʟ) On{SFTW`>%QEe bX#2 %)䇪yΏLİm蚂든(YdMyݛUЗ&ؓFnaW|L߬vbOJr鞫1jm3:}ݯklfFc{*GGRT ޑפ"Ĥ|qɂ HX+ÍIǑŽ Y"Քk9lH͕8< Rj/44׏i>fƂa(BeޜBqj/Q[oʖc>6$5fv暟 B[~!3gΟM+y]t4߱7Hc+Grfwv[ܺ.=+>`v(3.W\pޅ(Аڏĸ)r"w@o|z@a0)ήff5[KDwīt!U t(lJ1SgX\#JJ^,k,G!qaԯK>ojq 6! -@AȇK@iG"%|N315:4J{3A8X%l ~'6 T>VtP RK>){+o6$Ĥ-\u4Vhkf%U(|f[X(V HȧWk$ 7 61@U|+>d* ?5+HhטטNZ&mFgDUw^(lB5ֳc|lC5@N@F2::k]׽v=G*)ERJ(]emxh|6ykma cʾvOW^ʹLvPKxqw]~I7}ª;~ِ_]\qg,RQ/<߳r,=c98a@FyVwCk\/{z7?qSR\6i07:꫽咛zeOgi8?PH8і޷o`>Ɂ/d|tvgG[0P1 Fƙʨ.ٚ~&CP&Ej76R y/LrbuܸsW:'+8^P4 @ 'zNN|(¤TՎMe',[R{mXt率 փ H k=-Sf]'ll.̙W!lyU (tՊI&͙uQz_a)?-%q/|CN\*fJ*zgלy.E2/kPZx`ດ#P1YfF/t)<2NN~?rYb-t#`E(ѣ6j(! BV2IQi{Eʪj%<t_.xzu KQPEHo-d2hp0O40,D  =Q 3?.ep=-yL}UC# mm'=v[NMbCȦ$SJ":#PG]oyU4"߁A5GnZ-%e6$CD]⋩ g\sQgU@]%x RF9G j^YWvEZ=c)sgLو9*;Mſ0$Pr*)jaoٰ $liW$.Q' !u$"Da :pTo#Bp J?DQN4#Da={뢾{ Zj}fDv)J-(a{v,*#luO}@U"ӠR2aOX>! ?Pf?[*ÃW-ّ胹+MFʠ1aN+HzBBVLz-k$i .8t)wރzkŚ#4⁴%P: puDI#i @`a'2D:H%& "aTy-?[?ԼI/:65Tً.9؇_"vE\xh^ub'$O(mntƋ-u@L  6c FމOD[P a@qopXٝNLϤWlLŤX1SN!=mZl*UW=ͳccS3Ur)mqUƼʾDsЛ JR] ٙRekr!*̓Z2m!3N|V q~Kvw&vsc$a}*ӹ]pry J>=HbF)(.s%TvC11ӦuTquzMxv5d/dX4Z ^9X먍2 SdDcRުBAbYH%l|xԮ/?Ti_~>d!{ڋ/$=%qFtn>S![ GD2\3HKP6&TIPjf+& ()W*XM҆Wyc0^G5XdUǁ`{.EM#zdW8`Y֍ernTc Ѣhf)GʵbR`$"te.P\1J'b b@2Zꛉ$~jT$}Lb1 œ]-橧ZUgmU7U31b&r+G"*5@MB&|K0󛚚{Dژcd9'j2 3$,ID4lgwڟWHhy5b1Hbwfl[zc0P"c-sk^ixٿ;yF'tAqqe.!H&>Ɂ F IC25u|?vn{{k׻=8 ؕK՚ s|1b3^|6Ă8 ֥͙z.A b¸heJ֙g,}~gWss(3̟Zk,Jh`y2}:5uTU 0~!3嘮dzZYj}_{/XacPp n!.TVԩ3\ޟ'?/M|}(ߔ{PKgtB* q«aG~NUŁyQ=Ej*&9J tI@(%Gv]JzIENDB`filius-2.9.4+ds/src/info/img/gateway.png000066400000000000000000000251011506574241500201000ustar00rootroot00000000000000PNG  IHDR]csRGB pHYs.>)IDATx^ytTUOUB7!!Z)$<g#`+' (bk$3F]"@ ^!3ՀhGU}ιVM*TխE:}[_V2  `fĉ0A@0}w n>=r6ܦ33Y!^O4E6k"""Ο?a#xL sC :hV,Bل#x|y#" %A4 f 0hV @p jW7t"V@ `ohC Ya~` `0fYOzjwfp"Per}4G ॉG zORTvhL<#t+&߅cu:<欹*e{_,یLb&5;УXcJx`1Lpİ;p tz#jx壏^1^xy\ٻ |$eAr)w 5B'P}PIR|8Y|n{yCɗ] wzdK︿%Fv|%F#>)fsC Z(e|M=C+$QCώ4%b;g㞕͵Qϡ0)[-0v #Μ'xO^ظ,6SӤtO_LVvzZJX_|Cʏ}xl=ƋIUCDmI\Z "]Oc9xE>!;w'$oC ƨ@6J]g|K[ 6G@|HCp YmjnÛ/ @ ޟ1`op 7,@ > D7 'ܐ$ }w;4V'222"""fhV,2EZOBzqژ DyDCO3@ͯxg h_}} @,oh/@ @@,oh/@ h12WoѴ@E{@Ǹ -!j 5؀4+P1.@K1J `77bdْ2os}-艙ouAin_,v[۷رǣOYEu$cp]HGy#QU^OוUIs4Pr<{%?$-j*dIFqc*R{l{ݏ˜&#XU$O7h )BWbz#[]xÜY$Gɩ WyjCujr2o3.]$+9#9zR/?1f<;uKs7ӻ9mM_.2eʤIqI&-^//ZtoP~\1mסýJq ع}W~IKN\2w;ѱͤoST;mv͹)H!6CƮVW۱OƊ+סFšC[Ж\kݺqM;,[ɱVLQ/)ǥz>үMp،y0*6SvU9eBTifF VxO6\tZA sC.Y9V )@ @Y-%;@f:h)}[6^-@ > A%@ YA%z1*P۫A H$ 0@<%@ (e==kҫG׶c:@u{<#tju<$ekfuoۨmut \[unwspYc( !y%RrZ%)ן),D<#Yjp3&$+eRJ|Ͼ߻4ʰT/)ׂ<):TNPkQܷ%fyv!+/+ٮ%-UྜY%NYtᇍɶ 2r1NsS^R$HgOhZQ?]XɢOg ZYֶ)}y ;2zۗ(ۻ`6ĺ{.Zm؏Ը8rцFWp\:͙JrÝ;-KǾ$G?\Jvz I|]F('PbQ;wypS?Jҿ̄d MúJ:ϋFޙ򳔡CS և^Ij6H[:*}Jw>d،g\Vg ˊs|/v!c%j~ Kv2#W-+I,kvP-x;}2~T C) R NQkt4SEK3Ȩfj6Wxq<9ԛ_==/FjdHΘl^j2-li^d*5MNXc2[z竒d]kcɪG}!x݂@S5ŴMܾ<属}GXd 塃dlڦy)[,}r*i;W7%֖|6+^cƕ0HJtZ-J%52Z,T܂i_HO/dCgM}>tCҕ/8~w#. Cam$U;Km;=CaΘCkY15#LRa&@A,ohoSD-c0e)0H0@ n/*lӆN̛|x㕈@{]\S'bM|1|@(PO3f'V+MWo:yȨ(ȇmpm亭ርrUv>xҴ9Y>tagy \[*@0:b~ij=f抠_ ܌R+?J.)YycWFx!Mcb 61ڊfu694LvƎaVt*JjNMlG)Lz;'3SɩO\LtɏS2<ڝBj+6oSo9rf- -U\RNJ.$'ph #6]hm¸}j9 ^.qȐ_$ђ7fmfO}ꈊͳ֜1E=aebDX[fLdyq檏W,&KcoN%1ӛV}B($j:,;ӑ3)ڶ3 5l͜>^T Ui\x+IpTqtwxJ.*eX+?VXG1ݵjI蹈=[[,#fLKu#(:f?L-՗jhC=4{W ղM=pvYQ)CXٱBDͮÇQdr$4{iZ4eK/ٺЂD+gxȡlZoK/ͲUݹhMٝox=(fr*6>EƊQgOUQ,.ޱZ^>r$>4Ms2)z\n5RɜȪ@#}%)s6Sg% K?όl|6},um6?b+KG^i }0N[rFo ƅALMXv]7nwq4HXAY@D?rmMHW(b @A<@4 ejT#6XA,\ %q@808ٵpfFƀ}w͛,-O½}vDDO\ 4˟1p 7 u D gKVh"g֒l]FEXgao@sxB “~!<A @p1e00h@@ xG:Pu,@ `oF@f"b FYaؘ*hV,"aDYVOZVFB{ekf͑nՑfs4pmS׹rd3{QB{m0;gf`95zi꥙/˖ZR 󋵪8WҬ ivQ0)Y&Lz͟.UG\+iLLMs64gm.M|JD)/8qb2*[4WK"? `{yZ`:(iSI.> bRu774s4 ^F x]$Mtctr7$pUuܠt7՝B4|g):Vl2RT4qDS|EuU˨)$%ee~p8wb%f+9W~tU$nJy`|)/Ai?L <OɓW[k?5hV6nHfsy!CU,*^VUaӆ'*F5S-.ed1.1ټee:[4 J_R$Gul/Ju1]%I-* "^PΥE9Xz~@=6M6Y7[~S:zx9]d"DkJ2ϟ U)J{%\ /;FM7w.eRj=IҦhԡ9wizX7j͎)eTM?x=DzTMo9SkF.KD&*7~I[]$m^Z*1#@ɔr eMKL܇7&Ǵ)RY,Jt:gX?W-ǻ =ŠYLٸY55lSԔWI-l4b\h8l43Zn9lA {wC?fK׮]"쯿gJ$  ` ~  Rxځ;r  :AK_ @uYkf7FhhV@Y@ZG:~/hyc4Pg5ۦ0P= Xy1>!N$y2ӷՈ;Ʒh6 ß[wF9?....*|ϼVZ}i+&|^MUUg44 _ ;'xOUve K h`TTX'V>bZ&H]+V:EɉÅUPF[.BDe.F'HI=PZ7f|6׳?H2.%Xهɓ.vԩ3gOɔk.Z>uZgiO@ ׁVSOŪ属s3tdOvHAHDXϑCcfё{2VQ]k2Nvf۴ڕX)d7J+9j+kUis*>ڮ7Ml&ڂ#y1a<:Wr@lQ$xMYF94AC̫B㑕?V͌JW0V] Rh"%XD_..~{eLdE4>LeE %NЦp%N%-Pn"Q9Dro2P 4C8G2=5Sv3'W 1$%9mO%=ŧc۴IH_It&LVeMME,JԘk%XQ9kK*x $?@'Mׄ8O^)T١רLJP){j5 nݺFwwmэ>ߥoE{m|wKA @k1[0:hW@xfzc `tAh_0UÖa.=&F%QWq@xfc `T,$_A2Xx^u!0L+lX83#sCWV %@ݱX,ЬB`FqFDDD`ƨ ЬV)@ 76YvIENDB`filius-2.9.4+ds/src/info/img/gnutella.png000066400000000000000000000153771506574241500202700ustar00rootroot00000000000000PNG  IHDR@@iqIDATx{XTDخcT J,+6D@Л TàF+Q4M~pDwoó3Yok}pAN/d aX)B/?v7Lvدl~iv9E&`?3;GL!L^?A~ v3ص`Wق]~.`wl3axn 0B؝>`wxv `kKVfay/X .`$PShU[b3rn<{/d._4eprﱽ<={S?Vg6xm.fr^IY% tusvAAڮЮ .0 "},ӷ/?b:%[C^`Rl(:,*YϿcoGNxe3f[4T9^fGxg-SQV7L,Kc,Ћl"dOxYSb_m,47^[rA}CH9X?FKl~no(z<¿+)4ʐ}7f}VO|MKo!xcXn%)Xd 1G'ӤCKFcgiLV7o\vNON!V,HPw~T .ˢk\`YԨ }k#^C:>Kn  . TX땏nOc&IC4~z LT2Rm7<-&ҥK.8.~E"rBhܧtK#K3)D<,J= t(6Ԅ~.m||}j@@Mp 8oF!M!-cI,XD`-(uiލm]~hm@ZY^q]e .HSy;T9;?Oo!wJINs)kB k_X^ B)>S$PNU p= =l*=Thiᅲ\ Cv,~VT4O>mo<Ns*W&FU|Rv#` {ubq˫uI N`ŗ\@WK}@M/u O;}5E*K*XVvثv#po?R{Tڠ46VA9<%O,ֻz{0nPS rT8[H|G%:\fo.PF +PB AU6v@LVf)?4C^~O=+wV NE@~ܠ*=Uz$ʰ&k +t׀ t048j-PecWQ2 (#\b2O|V[=/9E@nܠz>e{{$tW}k6tVW1]mPb]m㹢"QH\+edIˉU?z }\@>,z#:qUh5\ЯKdi@LY=Zku;_hB4BOAcy9T/rt5|%%e S t(R n%(;!-ŹGcZ to#J>AO7( UAqRv鎏"hnzB@B/2 h !,슀g݊ݠ|3nookp<ky~47\]5hml>$d"iPF6MFv1X-rSRڿN;6₭5޿{<}.,+dp]A 䐦Uޔƶj4" 2Y}Z޶:Xj_wTBD&Dxgэpؼ͵T!@Jbx݆b#Oh4Am#YA;n$e# _4"Q|/k222rm&C=!AMQ\2>=bft'M/]Y\ž!l|K,h"V8bLXaE nBU$(޻i=-$@+Ep 63I|R ]óZs:0"3Gc![HL񕧧O7hG[*Z2^b,=|Xf%~VHs6D:i₼Jᆦ&uvbkxAב]iG T𽔃ؔ^B9*dVM6MK^ftR|s2 ax,J/6ض,Z q_SKKu/e8k`I"L^1*rp4 >O^\?}Ok`=똾ūϸFx{{$8WEwoVx3 -/>6q U;A[2"x: | DbZ' *Vv!`XR;#bXY} kZ܌NAZsֿfvm!8H"`_e=2y@ c AB$AZSw:`t樞Ǽ`   {݌V -b\ c '  &~h;Ҟm0~NB;p7.AQ}rk3g%'c?N)l<:0aFڍs1Fp?#.YC$&m00]6T.aV`nX> d=o>Zt/!Pس0ZQu0j%Y1|7-י "`-$V SD'1&!镔T(Mɛ_v ؁$19lRtBY{!L)"pœhhj0}C[d8RK>)B[t1f,-; "6~7 с~T= M۱t-=GΓ1O _/p!c [ETԼ/LmxG"aq;fΜC2xggfi:K c! >v^3y'I kbXN_X4Πg+.,) #'aB0tQ vc1Q s\Ɣ`()dJ:~l~9tУ&UFO) =<ǵd|=s/FΑ(! 72 ϲ*moځvNf+!{0#);(؅"^9-|r.ydS;vi!r2YޫF%pMyڏ卐3g, pbjvcJDy7kɲt1XpN`,rC؋K~1KTnկe A[H[(2"Na Y׆ Z@/> U$WsoMHpH'|) >gU^<<|v DzN }>{Eeո__MI׉9t`-q lU?XĂEcB,|)’W]W^!Pl#7oIbOP Z%@!򻺷l9P`[|RZ" U.]"8H LBF5PXE@6jc0X@ehov7Hx)& V1zmʑ^ƹr*IHl&7yx[8nk\pV5 ״.#`$R,Q` WtFS%l#,Wpy'`/ûqh+LR8&{R{#@bpB $ %`rdLF$$ Č fg3~Vu+ҿ_ӋAMJP MhpkFK=J+e]{sjwx 'w^208K)؊f)ݾsp qL>R^b092&ur,#1?yy-sZO9=654g1 ͓mBg4%H#ontɽUmdhBD 8jRD߆-D*X$HӅp#c4 ӟ\H|}0)'NYEEkMX/3e)+ ʜYh>^@{5:4QD߈ztuճun" /n]GgX,Df~#j< 3'T4#D N?A# 8XzFcgZGBGsH|"cb3#Lb)y3ux 0ix5ۈꤕeՂpJk_V*BNS-m[TH,Abě k \' w A.dN&µ 8>J1wN8PㅍVo<@B8˖cvc>bc.L=:ӯV[hxA\PXG kGw^M%Ҭq wLLHI(AY1~.,!%T;A X4Gg W!|ܛ~ PV[R*Xr7$$t =PK<~Q>RLWx|%+߿w&$K2͟#ڤw%8s vqG$g$h9~D`U~`W\w]pLZ%R!W? 60{7 MEԇ ҿCQ&M淏fڀENs6HT^T~T,th"9WM@h y$E˗- f sֆ< 1NHWꯪc>c )ݠ]@=Qc G|Sɞ߭,,E\`*2q䗯&o+v0!K.HaƷL 1iO{ ~Q.. n8y;`c0/՞! BRSM5_{]po)\ v]ȸ3V3 uۯ1U׼o焧ìhO%EBq in}y3);7IKkVpl,QAy7ɸ<_/h9˖cnf، Q蟢c'^K3-8≎x_כ R5 Gf(}ked|FK&{I210effx!oyʛ^xcIP,8 kn_YKK3ߛDQl[vZ'0:Y8˳*0G 61+;Sla "=+>v|H  -|&&EEP4N;lA^V1-$=>U{HMjW c/I)lN 9"fwu[dQH_~VmL)%slrC4;W+ڐ^b dڣ22 - u1L t3p)[y-a+ŗZ)OHuܤuS%UdzYiri^orϕmwFk PBXIENDB`filius-2.9.4+ds/src/info/img/groupfield.png000066400000000000000000000033171506574241500206040ustar00rootroot00000000000000PNG  IHDR((mIDATxՙiLg/~4U{ZҦM4MmZP5UJ4&ZH UTA90ъr 1{1{.:::\馓9~(xN zP\VBUM6mZj́>`7rA|t xNQ-Ս6[ Z?]]pD}WJ`g' 9A䡃 vNmnTP,-rTf!Hl( N%$kR P}Ww8IPxAOČ4ޯ T`s8HVl(aTRJ6;U"=RAn+F!%U<%--~\RI8sb k7jg֎?oZ9̀iҙU/ؘBm#\U) 3t)bx'3qJd䒛fR\64M#үs uY'6`l53"6w*FN|OKKBypRh%> yw[E|MG`Y]}6_Sk$ɂ0[dfh$#&/Ǒ,X5:$]4x_ 'hlI(QVD <>\&٫=IXqҀг9uN`u~ I%A= VK(j0\Ղ)zLٮ۩;$anc@d Q$D{ll;||ļGu"edp\@Pk%lA#|ᇨ!sHLWI;g5۠֠Y,HFoeYJ5ފAv5"ŒL'Io;;cg^[G1bB0iߡRL QHzˉ> j:-nB~qшwP !RFu w5{cHk؀#G,i 6$oF&Q z8V;0Xfj*hDwEx.5,:I0b'ym`fZLP^ $Yדih /Qb'}yς7Mh Hpex |:u yXtQ`⛍g O~>jԘ{d2r/$Xg&[ =H.x[.1.ēYzzX_ / OniDOg)F7zIENDB`filius-2.9.4+ds/src/info/img/hammer.png000066400000000000000000000024251506574241500177140ustar00rootroot00000000000000PNG  IHDR22] pHYs  ~IDATX͙KOW}cP(vA 1"LTE&B$ߠYT.Xuꪂ J%GV:T$H5..'h,meIHxpXntN:$k'IL3lM`cYU :H^*[lHL[dViюbUǧU[(+TҦŭ&,nb2Te#_[H,S3\e? %ZCN}Sb j",;_@@YzH,z/`VU ]bJ +@Y[[eS9%A?X,@ qm6kqu1,HJ$ K!hjݐxG?ኬ]J!}Xh3g$vT(}*]&\VvbZ{$0&UUDPfϰ3S%R_[?_i̢eNuTI7GTZDFʆ3KW CZ%? ЕRr l-ijI͛y২?Ұʌ>IENDB`filius-2.9.4+ds/src/info/img/ifconfig.png000066400000000000000000000212441506574241500202270ustar00rootroot00000000000000PNG  IHDR? pHYs  ~ IDATx{TSW?gYr kΠ/hѪm˼U+ޫ3*VgzRR[UnZ5kE!@ߵSs@j[B5k };gD`#'_\$IZYC1 `Cڜ8Ls l lc V@Zv V5ۖ/_r Qm@X8n\JYK~ &ƽ~[#q7]]1lx֧L#ڬʎk3Y{f OJÃr.cv8HY\\>ۺlY\\ܲO5e[Qc>[WRƍT6Kg.LIY8I>_U6Hƅj}}?OpaFYO+Ip/]O8oȹl<]sݹ LO/o۔C8q}ECΦccBb^y/2ݾ-DjSߺZDp=}|ޟlʨ'"T[YYYy!T\I+ftS^~1sys{>UrDM͆ƺ0FD chxB 1i38e7WW!_.#"ٲճ.]WΜ5B$\J0ٷsuahi* ;YID4kCeRum|ٷ2}\q𰩹ƺ`m^8)#Li[O^j2}qkt ʫOzW`;i#NW8Yݖ^ADcm y{DZDDn.=.1F.k;f-Toހmټ/}wr<~;A@Z iaP[ i+ۣr0`[?̂]qvvvrr< g}+ {c& v15`c|ӌ  V`oV@Zv n%HDMIg5+6d@D9Xtʌe {Wţxbé5,+5p8$;&="WƎ.D$24<)}ᕱcIhw-qtwg&Oi""]'5hr[#݃^ʜ+UmUO+T*U7jxj6T*U;kd;ͿwuL۔t?{Wƴ_nL:Րn_z3eQ{˓zLOG1YJZƎu_%*2 x>RaW0PGuD8fi.<1Hx]#]; tU '%:;<~Qb-zdnxZ1ƈcDDryh1cY*Ѩ8ed95㉈ MLj-j( ܗix>84ѽ+djd1{~Oۘ8Ƥ 3v NkսHDkZ|c5ڝ%fw^{szCh*Ho*44idngT($u7Q.Z4u5K.Gu|O+ڟb)kT߶tꔀ:}ušQNގb >ocG%oE$'.nRuyK .$1RqGMgxsfx7)Ǖh[뼭.TYxx%}08cbF&R5|x#&uOTلkHAҹ{k&L`]t 4r&.3t#]{v6ݰz}hI i+5lw#e0vzmS7U[ 9z67zF5Wi3{p\ID{E_|1&bbbT lZs{.=NE TY39#111iݘ]-vmP]㨑T1b07:w|ߎ#nho6tu#_j"V2T~}e? f=\~Օ L lIu1YݩЀxdv„?I I 3kM>o%Z}Fs'\҂P"K\$kH9GD|&ʕ3dlFdRw0`ݓoډH u~}Wi? ӿo|Kifu9jط2>h}p?vus\bGy VDW fݎC|U[A KDZ4LH/k-:]]` c}v/+:Ӈ"FUukc67qܹg*3BTyhىgϞ%&Ξ=w\iGѡ-vҩ,|*xS"|6lW\&*K߲.8TADDiBѳ| SR̊PW `ߊH[^ZGR*W|BӅԬ:٢O.^TЭIc.^KChuYUcnnP MVvSgi myi&1$ijCl?Z?;͇V=4^].`GiUEVy)\-UbqUilR26Q޹Cb"cE8}. A>///uMwx9|G+¦QuV (AHӯ#҄-*_͉*[RʰT}p~Ui_Qvl{ VTN}*RaE!M%u<0^̺[6M-yԻQdѮϛ5s;Qܹn6n$cl&1"] 1_@DԪ})#q+>_H>b="/U[J?xqLVvh4rl ؖW$v0`c VH+.;@Zݭ1vmԄY+o0`{vNt9ط0K9I+V lK[[V߉oaclj+@Z ` l$0r82Cd%3 awI@?N+~)L j~T.a/'"uϯX#4_}g>l\8nРAdC  yixڪ6FI91FM9ƟM=.swxd'!c~~)y\ P?Rヂe\5KS&'ϕQSΟNҲ]k=7dL#cԔsMvxzK]6_DWjM\JEH3)J K]kIJ_ǩV=hzjpI NӱlRޮQar7" q7- єu.v7GsʈhjHz"Rk;Rx$Z?22rMNlԯDO#ɅGL*&mYr -kƍ$c$4F b{s"~ZrLtJcFzMF{0ӥP}ƪ73 ՚& 0jTޮ=zBuo .m7qsǛ7츒1FT8r13cݘPxF_xE>L*#.ʄ C>y5D}8r<5$ Ĉhdעe^S?/&Ady3.0VAPH$H$ D$$|:qD5x0cD^w⊍ 7Vd,UǥbqpcGq4F`zEo|,bc_ժ4:@ڷ"uuu#wdUk*Jc;VQA23Ƃ}<Q{^q܍ӧM6eFH(qN)Ƙ4Õ)ӧOOa3fp\mqMwN/4=ƨ'7)8ӻ ZC>?dR(=ǖ"Ҫs KX}0*l(͉JCX ӧs7`[6Kߝ6 oVH+@ZGطVH+ ?y¾;̂]0Y` lޣ٘9I+V lK[[V߉oa`P[ iH+ iH+ "Ԋ0]B7}S+vbN+Ah8rqʼuM|T݋Ӫ0s+VeYiURy0FD/`DpjZqANq68 5YHXXXcKtWHHHȮRS{1ͺ8Հ',U+DR +ivJ*#c{YX)y^}bi|23j wYu<3k11N=zq_gVA|Aㅦfr2:ŗ*j$"ͩSA5W |eD2ߪ«5A݌q5UUSBV`%.:~f5]8gPmn H3g)ØbLn Uf֤/ ]^3O"+h b^D:2K$ET]G޾ss ~CM#cX~ھ~/ø5Q}coƘGt?ztQ&-^ x1 ve7`[6KߝyiH+=A@m 6V;ֶXl Y+?0Y`lQjIENDB`filius-2.9.4+ds/src/info/img/install.png000066400000000000000000000072751506574241500201210ustar00rootroot00000000000000PNG  IHDR@@iqIDATxyTug92q4OMNe5`e3ɵ ])m׫oW_| +k>;ъ#-&s6L N#SShɉy Zo!a?^{VzǓV& ma z6X{XZ)I~#h4kn73O-ݮK]l':4mqz8y|nFCi!·9%5o|JgͱU3GQ_g ޢޤ) Ѵ;Vp1c%RrASFqeDw=TPAׄ%y 'x' ^7\}.RBqJ?`e%); I 7k)4N8.N@qEHFYG1WӯD/m=5(@}KYG|N=3^t2/v,@BTv% 1$ %Z咳9-oPAy"Eeф(@ .љ8Dkע1(8p$fȎE ' a()*髨X2!ݼ@nD5$YaVHK.D%ZKCDR@ZxN@Ez28]M((OȪhDa)h2;r<~}rTL +ҥPYU! `UQ08(A(RلZƁf pHd~pD:@_^Y|cx$2#v 0@Aͣ|La (G/TFQFxLx@#@=`,vW#5@u{ ;7TCa S @CWE_->#jխC^K?\ I,k56``v)R?*(9 )SCKASpMBw\ˑfuFg8 fȍ?Gr7's66 5 /3Stq1~K,rBʂ@ ^NYՙyN(C(D`D\N P/G꫙f&@ׇB<,&.ԇ,Fz~ท},Xe0/$bl@ú|uudAp65!o3A&oI8 e{y˞7&]4 r0:g4JӍjUTRyy%UW =bբQ60}lJ\`/~ԚSkZIPVN&E_Dϣ2.Wly"'ӂ6P2WQQ,k)tmv"ghFYٙJ( P^[CO Y=ap5;rܹs<]|ń@^N+9++ڵ!E7']3y2& )lIQ&AjՍ>JKGZb.&(uQAK$3VkE0\iHJIMJPƌ<mujrY DFERI0kf@6j T "HLF%&㡇&`j*,*ʪ&ENKf66 .>Iq=2:E\yeXFk҉)7/_k s'*3\י]8`ۖ+lcG Y$ j <5uT]b]& #AR\crXg()^3 D^Q 5 >`ޝϵEn/SeU2(BEt2( CC^:KV^f~ 謯 f>ZF5loq\_~R׿ֲ'@8JnĈOƴ?:uy}|/S˄@r۴PKWΣMꋱ` uO]trԗ} CPXpϋXᧃ#4O{R@Q m|gOK9gC?}yF}+fyޔAX żq&xI2@DƤI3uòCtm #n/7ĎK|.H3?q^Qcwo;g΂9sg϶k_~9svd ԰?7/uONIENDB`filius-2.9.4+ds/src/info/img/leftarrow.png000066400000000000000000000010171506574241500204440ustar00rootroot00000000000000PNG  IHDR szzIDATx׿Kap[ ʱBAiqQA£!'rȥA$:C)ZZ"Ũ2eRO 3{3xy Js 7pֲd vHnZ0q#'HT)5U?Y$ w9xaQh'9@#]?C3׏Ϧ,A*@33ЎC!ާQt5iكY : '<mǽ2t685 |KnM0{}pky%8'|cp7NY8t| d}5 b܅$<{{߻Я/h曣=cǎݏ<ȣ_l߾}wmm-`0BBB~~GjjjJKKF?a?NAhhh|o*t:=(,444W 4$11 \;i$_MUWWn'o577L&|饗^Ŋ?~RtA/ZZZ(>>U6G9:##f`UUU.>L&VvUVVIG[V6>uzU̙3TTT /[ql_}tNN], >wt^s III4 |f,¢M&x^h58\ӵz@ttt|_S-/]\~¯%)F4k~Lr t*az l= ck0|=Zj"UEG"tS%o+ykӐ燏#~v؈O0«$OXoP(.*ǔm=!/0 ܽ())X w^`1⇎x⩡KێMeeeB|GٗuQOs t!H'3$c;eyf-$m/[٤ywjߚ0m~uHLLH4nܸ!\+,%Y 1Q9HWsⳓZv$I:z' Arq,QS~v߼ `ر ` OH+Z^^ѝf _u=P ;PĿ;WGN jHƍG'O(ɀWhZ!2 Vr W3NWJlgtxi!^d]q1a-\J2Y(_몛UѩV,+ L<>ɞ&oKE"))S̙3)..T*pฆȴ5T;BɳsQɳ ׸M԰PqgI-;N!adMk͛G .7Rjj*DNhTW@g {ajD!]T\PP`/ɲ >p̫0^iʕn:cՔzLC*mTZ8`ge(`Y`^ٴ9_!ZM}=8Xt7H1Sa3pVI{}}wD0ҾFb)?{o=zZg h+m? Jۊ+~6_+V]MT=w-gZzR`kh}1SNrL2\2I Ib5Y)6!8'8-HKKYf5Zәؕ-ǂVx3DSTpDFQ"Ϝwɓgttⴖ5cf:QEAg,<b*wxFk׮SA,%)TTlVMg/(_.2 WۼB#{c%&L-=CJ'-7;}RHOOTd2Q:8UɂՔ7zڒM}S_%ry7m{&hmJMk?.P3[nƌr& \xCc d07qH&K3M 4z.wk\Yǝ_kjY ]..Ts:mFPEGӫ)+Dh>& ^Wp;6rk޾N(e%ŠJŵ\ iN~gmt褕b5bSEۆXyӢ'}aP(l}9m0(pli"Ax@*Tu\kX<N/rRe8|(g 66:rDk6+h6e}̝;ԩS!L5ے՛hEl:Y6lkh[᳥B+ElQSTT-Y+a4wYR4_^о r QQ KإkR"r<H McZ-p 8gٹ yp,`v]N-שG\5l%KrgyWi;|)a_:#x= |`^BgϦO?#xJz#l= |"ģGxxCtN .boBtYxϖ#x@쇄/?g>}0$+< I 7D1i^r` oXK_jou):S.!10hԩGI&0ni%H@-%7??R&n[zѫmքVc0O6>=fSԕw? -w% {WF}@,`    bO0H<+ZMЦmW]>SXJ!rGEI0@x b<V囔EGG7I #"`4}QpQRO4#@ !22R)u$A?@ 7M##!UrnOHF]GO܀UhE:-^yWfh!-Xy毸BˋExHF @Oɑ4.qHH| ld6ƖGDD:6B|(&L3f-''N,}衇K[mg=-e+7m᪢nF ,>poh#9r~py^y?Ɇ udÆ 9v؀1cps|y\IENDB`filius-2.9.4+ds/src/info/img/modem.png000066400000000000000000000056431506574241500175510ustar00rootroot00000000000000PNG  IHDRP8j jIDATx{l[WOcǏ88yI&Mj^mI diWhce@k:-馢2V*RXE?!`C1P%^nH_٩}[ˀLk00Et&[+6FؑaMkre͗IH kN1c5 J֡o-,1& NREvKu\:=U!qVdT7UWe5{YnLh?3bճ :F@j `bdd$XSS3X2-uwԲrIj=`;ޛ,dz:6+ Gc Xڷo5>>|TO";X s׫Yl;l=يSV(v:M Vuũ)!srrz $.w6 3 =_{gN8NGYN#iXX]]=?66bѶmۮX,Уe2Y;^~՝~=pIַx6ӹyiOVYYt|VIϟ ٳGHDUUU ;-P"78 |c=|4kt.7r+/x:=T|ұ)v{:;;$"[ t,011qv,..>%źB" eGΞf/˺zyof~zuL%kcumݖ-[Ák׮]pSaUMMM%\]TGGRvvvZ.\5 U 0L{r/Nνyտ浱ߞڣ;Rѱ24K6n! !l޼9h>aaظh6}j4#LJIyn`˫y=֯|dszWzwc}ZHMʌ]TTtn邹qwr ***u:CmΑ,\_?3+YonOKw~n޺x:~c;ѓ;n߾}Too螑@E\}xxxΝ;WEz԰F¥ %(M-w+߾ruv机}`YO]lGC!EEtb..tr.\,%<ɥѝvԳ؏o`|2[Yz@x}zs}_JnHmmmxdl%@.\Y[qpiVq}Or +ט𝞇MKn G.-^d"pzU!v"cõmڴi^Mkt(q;i;ͽDZq.$XA$+VX,DEN3e`(0ژ<2k*@%Z>rPssjm\QL2T?5n"~A$#s8$1,{2 *;s L2`@U;NjBNT8auX (A$*-kP0ip&G'cE$l@١&2 !+cRʅtHD/===k D!D\C "^~qΨ PZ65@nG3q; B) F& ~ a޿Ȝ?Nn8)ʕdADWFۈpM/Iĝ0{@)V@^ 361T ~r5XmcFGGkEy.!"i>=]UMpX,ذXKXd]-Bp8Dp\oㄱ&U *V*M]]]bˉh u"H[ćBXiu4*p0ԃ6Mj`X&ħdh0,$,Pu0 Q5|pW n- 10"ݴZh *D%@D,DV9Vh$kE|>999l@%Ē 䛔FȕX` C 4豂R {] ET8 ΏkEkǽdȱ NSLp[Q CDX5Z)Ij+ykDQ/\HdABVV"R> Ş<И hxH$PVCM-P8r^W/D ɝzNnp&yB"▇\.Jr7X̛_-rV CK@ޡ #A ?%^?CvsssE<1gO- - ޽{E @y77pԻ" SKW0z= y H Ls$rP*"M. ]!ҿ(̰OP|K@.Ԡx5DpNٽ4i/ \J ~ !38堎 @A>/@CBPfi@}{ TZP(rQE-na0JbD%17l6[br$9k:deeJ c6 f#DIENDB`filius-2.9.4+ds/src/info/img/modem1.png000066400000000000000000000314171506574241500176300ustar00rootroot00000000000000PNG  IHDRuDHsBIT|d IDATxyp՝9}w^lK!aI!E23ԄT= 5VYjBR2yxd2!!Bf'q0` 86Ż,,Ew]ɲ,K%Vu}tu]` f{8H@0e @)# L$m۶Mqm'5:NWnL  @)# L`$@S@~o0wnܧz>{9~{ٱnù5:7.;wwPP(a444rJV^M(!pbޫ0Gu]c/ /r޽z~4g{0f{gt N~.uӱ0;Cgg'K,aҥDYq5iJ}1p2x]֮X?ĺ ;8rݴd"l?@9ymȯk~Ow{c{k86cdQw\UNJCCTj/#@9yM /ٻw/pl׵qp \ l lHun/a`&Xgrsn^\ץC"^OE[-'q|nziNٱc۶mk]\Opsk<w (J|!oup *ky| Dk[W,&Q*B[UV%0똷r!yxrQ=2>Aږo#UC,&D\&sebŊs}yK [la۶ma#D6-D?YeENTBW^5\C<_B>ʮZg .@>>-| _ m6pu+m ORpl@|M|,ݵ*>TH!.]zri|#n <76Nĵ˸e4 D@i %twwrƳkU*:UPe۪CTQRq(mdQ}c⑈Ϭ`Mm5EtW\_ub6eaYJ]5ĩ墸 .E[YO8VID2xؼyxذa͂ ftż#_l߾'.ee1BqK|tB= zi.m1`UF$_ Q]ʣH*RqM)xnFͶXY6'nqHj 4T*T*cv !*%rbN&܋.C["a"W^oJNnVĔ0wo|>0N"KB$}4|)# ó0>oH$*M^HîwZ}.&̕Mi%}+[GZ2R)js_,X@"ZnX=z kD"3B$o&=oJ%F466{ pwruם]9;wncǎƦ Ŋ\e )A7p5T*)Xׯ?ϣ>??uQX[M?œچ8v\{ _͛gNRa6o̳>S荆}C4USe@Cb tO#/yxRнmˆ:#jYƵ#v*uG+Rc+,6Wp}Ag3jFE* RbHPP( h&_rQ^O0ܳʂ%$n)pP$$j͖-[xGp\tl68w,(uuu*zD"?q>OSȼRap]r- ᑆp}UL5 OeQkiD qb֮F^"}+diqK q,#[@3jFr.[]_ћ_A{oc$I'[y /(]]O"E$>sIXuFQ*!S__a<'?^//ٸq41DNUPEH=q5&A5I4bDFHD~øza}7H DȓL0 $%WiMƵ Z@`8*M0Dwކ[ 2,ی~8q6 Ƨ`ӦMsGtZ*||>O*R)?~xݣ3ż!GR(8ԣ2 !!F^b^nUW!9 \; V "a#0OjBj5u_>.4Rf )TH0"^zshGm=<~K:^?ƳnB\PKedplgjM*P*vTݻٽ{77n殮匎=YJv㖲@2_A$ 0\V# =쑇A& ^Qf}L5"#C\4*Ŧ 43GD@97B(OXu0 0p92k_Dv$.g_io1˜tl) 8C.#˝d HC lZ=sl߾oO3>yC R} :Yppq]xo'O!n# a=#8PԫbjeQ$dU3cOe$+|BӪ: BJ!62@䒽" Wbq5cOq̄C#uuuau\.Ǐ~#}Y>hļq=zR?n[r;)epKrŜ.4[㔆q*yJz &syN8qҔ===+/N{o&uP(OOl~\Œqp]_2t]i==)D[06#fxk QD(У%PaDW E|&< e?sp:_{woEQCCClڴ_3MSH 4M;7x#{/555S>ǼPadZ 5w,p5pj/ᑀx YrW+<8.pq! \K 8Gs F7B6 /s;#9d~!͒'CBrih]hI<3|{XHm2j˸UUQp|p\o^"$u}KG\B4p=C@-$UԹݻwJ?2=S2]Sb,?غu+?{^Ȼ ]F_ w۹mmf1Gh` Ѐz2B/"Ǡx$$}>R[PH ^z!o>~bt:lt`:Ǻx}8 x/0qSK]1*灑!tE.?spee#l?q5 y#-?oi]+ rc61<<O<~ +Jd9&¹69aƄu]ym5E;~mjjx'ޏ#$ˈ PPpIpl\W?ڵuT`"Fߦ !L\M=Dkųgᡇ:mW6%ͪs.uuubrN1T*5nJBoo/}}}*#rŊ{Wx+t:>r1Z+ )Sh -ej8Zȸkڈ*ۿ[05,;T%rI"} r:bQ~3>yy2 m#`<,ZHM8Baҥ,[+@nk]*-m/?Eh^Q!?fSsJ&՞)V+uqqRrF֮>x]Ȍ ݄$ILb8cc.$ jkkO[Z1MW_}uѩs@,bppRaX͛ٲe >|W^yuq׳rJE"FGG_ogxqX~'TU1u/]j;kJϋOD.[ +U EÀ}!aJ҄RMӨRsܨ}ϵPodrTDHRb1޽{OX=sOYm B8x ?O SSSmsN|M/_έʊ+FZ[[Yh\[o㉧;~D!\OH!fYUW[ C(BŎx;a$ 4MT*aS,r;BPSS p8]DAcc#B۷N=9G 2?.hr ?~x<4MchhrL>X,;b 6n4.MMMɟ4qkxK>'Jh" s rv466r7sW2R)>ZGMA Є B7 eY arU*~߰gŞ\^l&HA}}=300#Gp] РHT* Hʝ644\>[ef20uÆ lܸŋ}#&K[S82^v}dYzzz?}6 f gd9Un xzTLCJJ]k-~z׳|rx3qmLi4{O`&}}}S[[K:&777+I`…DQX,h",X [ hllT!===P[[ Bֲj*ZV\kognZh[Ѭ*MF]a3*3 ~%IBi6vd2OCCC ;񖦦&֬YC8Vq@m6Sv}۶m\ve 8p0BO?AUmXtchh0hiirѣGUI$!T*yeP ihgmIDAT!,ˢVEcu]\xᅣWw^|)6•Y6eBv/}Kb@NwO{b?φ ꫯtmݻwrɴr9^xv L@g۶'LrBrꯞ,)JYb_yצ\͸b !xWo~泰,KTMӤB@8]9qℒdhmm0 ,4Mjkkill$LѣGijj$cET*tuuF466:b.pfn6.QѭnS]+Ȯ4}G꫺ίַ5> `Ϟ=&ϲ,*ʤUj 4xcK<gժUDQ^||K_9tg@\כ3ɨ ["'N94 \a088HMM a: .t:MWWlX,\i >jH>e`WdfYr-jlVZZZٰaHdԃɓ`:Cc \uvH#\o&n};v}iTkާC Cn7YuE---oqF#q8v옊Kc~ى-N&F}}=Nu]$ .V`ɤ"i(L&áC( (oP:Vh4𰲓ضM:X,8-[nrɌ]_GfjT]{K@vP(i&|IZ򕑬E;"OS<h4:8?G3 D#:ܩF|~"x˗+͑#GA]]Daz{{ĉHRB!r(5\M7ݤrsta>]w… g8x'3E J*Rı.{x;p]?clqnI֯_VŌfJM諼e2V/_~9_W{.]PrX,FKK irv/VjL__li#ioo's=ʾ}hiiK.aҥE8@?mmm,]-[iJ%UNT,d2{޽{K֪ Kǝhd>Ǎ7xʾg:VXA]]Bjk*סPeTϔvR;xycF%˲RT*bOeI#B^~e~i:n&\.8h4eYZSSCOOCCC455qd2߿qD"*.ZZZ(?~\9e)wАr&IRmss-D/YF===e$f%44DX$yټy(Fw"m@kk+qq JQT,P(aNd8|0tH$ªUرctuu 8B!2s 7ѡKH(D"V^MSSLgy|\.7c򉯶"BQ}7+^^,QrdRyʵ[}S{9nJ6 /8k֬9$9J!\.i444AUɂ H& `6uuudY:;;U@[, 4U*6#^z(4Jm,Y$}Syq#J(o|_Rw}7w} ?8k0iե8555hd,!tvv^*ضMGG7|3\sI}_z%6oL:Vvl6T)LA"P(pqJTիWD*&?466N9z(-R_v-W]u555! 82b^~^id2'Isg8$j2eY344?뺣T'#Ic'InE8 z68d2UF$ !غu+۷oW7z-P( uuutM|il߾]I.U>.BeJXl*+Ӻe6ncc*܌ib1.R֭[뺊jjjU͙7ӟTEG WVH&s=|3s9*r9@Hʶm@&Q7X$YCI8o?@K/SO)D|>555*bZdehhh`ɒ%|C"))0 jkkj̆a ǯ~IE|擟$~9UeD"c&*$N,W]u{/6lPTEdIjOM<禛nbƍIyW}OJ:*Эf%Id2z{{ywT*,_\Ep \}$ JlH$BMMyhFMM )?q6V/CxSkllٲe <( 1mVW*U ,R*TUv9x:fϞ=⋪B_*>O|㹋_|Ez)1IĶmTZ[[dk+*KYY9;08|s㷿-k֬aլY7d1:ȺbQ麮G4SUܯ:_2[Tn$*7pRaʕ-dr:ԀH$ J 2)M#=&w @;Ux_|E6m4Fw_#eri äR)RT`Ɯ B >W'XL&\'"?\}tMqU؉'7Hڬ1gDu4BAI$mX, cT1i*ۋ<, @s@Q*lrB|>d(AnOKK#i\V91UƲeFM @92ڢ(---ttt8---DZm[UtUsbRf NTVP(f9r/2*u\x'\wmsmT*J%c5)f^Q !D"R)-ZҥKYd ,\k`Z~ǾdUS6\v㞍6IRbYL͵̗뀓'?&;h. Xa캺M~b2j\Rc $@2 $@qo$@@J I c_mL $@SF `x߹qguqMUMwl鬓=t1WaUl90pV@&?N6ݱUo(lxɎuH$2fĈ:޿db':r &{3[K:M6ga# s&x{ù8O@̘ 3TZ8St1[t Lٍ[،L27V&|hi֤=㋡,IԱX4?ZȢFO(V^͵^K<Q+ ُS?5!pptѱP,;߶LXڬSTBpײvZt|"mͶ"lB _+ H@"w}7H9tԎ`1]T }1t,_\kH&\dQV,}Cvm*u[NYu@ E$ hRPSȑ#|@f} 8u.V):ѫ cC(5퉣-oHV6 p5(Ȫ]-I]H"4-JM󂺕P* PtN}\g)Zܩwپ uVelqsql߾??cƍ)穴M{qqLwrsbhX4&h7ߨA;gU:vhIJןs-/Oҟk>tjڤ]۷D Vm,XukI~\u]qZ7>5sWmQ44rV;苚1 ]C˲~;d2E֮]{ZP(<@$|ߦcw&4L/Cg[CB e8i[H]!j()]t eZZZ[)RI˲, Cމ3+lŒeseh Ku=OPE~vs/^̗%. b8J O;w=w] BQi@:hB3@)*;nh=b c8'ؾvjfez"gՈj2Ufe. 5?Pztvv"ZF2ROSD2&6lA,mOjJ.MMM ?Oygg?O).J OJMC@xa@HIysi֤A5@@ea@aa =EωJ`<\뭽"9]F |Zleq&uGV)qǣ>:n>#xfåF2QNXX,d =z({/< K&s%СCJ%wRC(ĐHi'^]R7v3֜҉fD} cj8Bn\DZ%齖.42V87Ǒd} @HU0bvXz[Cڬ<~$H#})ۜ)WK{:۶r ۷|.L&C*ֶcv;ikkԽs%}GH^ՊXnY #vUO'N[H'17j`@ڠ+ Faļ{s}f"4xY"Dn5 H$B$pu܅P{œmi$'_k0ws]B@P8nq*y455O?Ͷm'??)Bqrվ̓H!R  5zizb(c{s֊4lou7扠t|1ZGRs -I3Rj~E VwL'S ǡ, `nr@[3~O]]];v ˲ݣo~{=᳙ȳ;fZ8fVT)P*ۿ[$^~o"g =(-qD Eކ4ۋH Cӷ<ˆ ]QoѮUKy7).|+8g<UxxG/9ue?fNOBR hv{+_ O LGX'AP `_gs}:6nP εS=39Mx(ߤJ. `)&  $O<8~@W'J3?<.^h\%S(N['"9A ϼy;'^S_ řIWA݊U#p}sG*N(ҏ0>~ғH HOuPAl3qvۿnȱra2DƜ ?f˖-w}^ řIAУ 2 ];QI0qCh` Ѐ ) !RD`z"+sR@xQ Iq( twwm۶{^\&͆sTT2'}lB q]>OdՌHXa: *޸5@,GұWG8~#5y*#ۿ潥槀D];(q<<3{quHӞ4h'+0LK'IR9ϤǙR8sǡСC|_祗^ @:UW򃿾+Eb@Ch"@D`Œyq\#Fe$2 3 4PyI߫z F4 qmiRJRs)"~o>0?&&>dJ5kd8APi{af'"38[Wj3?d2uT*il߾7|bZG,\%L҂B#G(J$ R¤riDQR_=֭# BPط{xe,0zU`JCT*R[GyQAϷatЉD"b1bؤ# ρ,k\1 UB9bu]7;lll$H~;zzzؿ?LD"Acc#B0(T*!t:ͼyrb6ֆaXE__\A}Q{9z2LC:K/6J΅uvZZݜGUazA<'Bs*}r^Ub!pڶMT:nЍ#Ȣ'̽y.Z)t`hX,C<#xs288xKwոpB.B֮];+g;g@yhjjbXŶmxWu]innСCa(EXt)e100@GGjL&.2\TBWWTT*FDu,X@V#ƍy駹innFY??}|w>ް-t(',0FEU7AxVV)$vUbEuT*q1ZdQ\[ŚOFNww7]]]b$` r-~/lJB?1{lLd< rZ[[d2ʇ555ׇJÇY`X^:;;j$ V\ x:tcǎDhnn&Q*T*CT*xygk9`%ݻyN~x'YCsu*A׮:O8=D"4MZb6;,~dcB`rlqm\r .ofǝPUsm300eYaX~V7 ;w g](q$ . ]H)={6͡V*L$͆a*HXd`` k4^h4ʚ5kX~=6;9/I.B̟SW]˃nAaT s?'5]˸V|4U477Oze#GLjrڨ}Hru}_d"C8_>η^kb444@J)y&%Wb̶ܬϣm\H$=P(uuVQOTrL?D0 !<?<]tx<ŋ )=== `mmm̙3jʡC$G%L"v2 twwS,À#G@48Xl&J|+Vpwp O(_|y} G毆 0* L׿0r:?ɴշ+f͚Q Fnǎ\r%^UU~/&kc=c \j~bX`|uҥ|^ΧmGcfgdkAZ5,0G#^y~O>Ƕ0²,)JD",X;v,Dۙ3gaٳgeJhii!NɡChmm<JPP(hiiAu2}}}ᏸH$¬Y۸꫇U7ys)VZOqr|$ N't˿˿sΓ÷fWLv^4Mu]) 't["`bʥ^׿0RP q Ar\-G&?k/~ lق@;v,,L(y2 aOCCa:sΥl6Kgg'|x܂n[[۰'GwC3N ]aH1M ۷o7 H)ôl6 Hv4l6Koo/RJ4s%J>t: 'r߿RDCCCMf8Xpq,ru7or W]uUݻGx-ZI0u>lVRģ>#<~Vpbt]ʜ ;stHG<"G"a%z{Tr˥:OD ņ?33N BKkXepqµVD j^b1=i5RJ(ϧcǎShiiH$Bgg'466bYeFrb1)Jat۶inn&LZz{{, 4iii0k֬aܹ,MOuIOdΝ{a"3?.Zm&8MMM8[eOq-2xd2Ikk+ht3 _җ|B1AH<|0]]]Q1Ac/2O<KKKKX'mjjbac?iTt]ݡ8p^2D???=x b0hll$L288Hwwwvر0-P(d0MB0,hڵtMam8p;sNuV#$ *u9sdBa^wo0[ZZH$?NQ(|<u=_=aJ ϟY{zz•@`r$ <ȡCسgmmm\q,^r޽{ŋpB,YeYT*4d2xlq[^mŊ|`ҥc Y)jpQp 2 ,8N@_z%6m4eǡ9siG0 2 Z ۶1M0y\.ǁfDQ/_^YÇىi$>ݘt,X7… CZXVFX9|IS(L ?ÄT*a]wݥ")H񻺺J"t:s+駟f˖-y.>r 2˅sMADoo/BMhnn ^Ξ=t:M__H>#,XiooDz,üYfЇXzyh4Mj8âE4R,Ώ~#2V1888,":Ng2NΰTB85hNgggX$e0t5|D"2hjjb֬Y%=Z??s7|d2IRVFihh B h8>{53g_~Ǽa<q&LliQY9+ 'JVc`` \LR:Tx<ܹ^x=Azu]ǧ?ru Dj˖-IT0*6X+H&P(sB rBFT#H v^G6Gg>.pjJP,"HL&C&Q Bq9_b1,.fDQ8HdX֗iMpzP~#ᦛn"H088&;v, J&̝;7P(9'RJJlRZix3dIVV#cYV8y={t>B89gJB.cppRDX RqX,, IOT| Z5YTv,Y( b8{N:BqPeY&ai4mS. maOh4ʍ7ʕ+s B J Oxx* kV*"Z uqqXx1zk8s+yWY#jkF^PEgDQ2 c,Z 2w\.B%5RF\WN}ȿ ԣ<>;vs3a63R6M?#o*>+5PL}}[zN6UUPs $\JT(N3'`QPL/J IXyY{#hF;>f")CA* B1 ʂT( bT :)q>=ScNTs*+hscz4NB(Sb *>Fr|fJ? b&q 'RrL$ {{Nƺv<c3{N&i|x~A?:U(i ҙhʉ֡ jOFT=Gvt>фl'J ŹzLP(UUP(QPBP(,HBP(FA1C111sCX BbU( b@* B1 J  B% BPHBP(FAyq̬q̬q̬Pi BPr* B1 J  ,wߝn(BPtttCa&\uU,[lu`Lw B. {Vl޼o}[̲T(f2w}7H9tԎ`-ZtR|,Ik~kazEMj˲Ї>mvVD*Ts88nkE QCemE_:]mt}v~vZnC139r9PmY W :ѫ cC(5퉣-oHV6 p5לBRqH:v-{9VC쫀6(v9q|E1mBy~yX2_"k׮=-}P|yx |B8.Rad)]/ój qyТВĭ&mNy)Ҧ'˖-n;#WQ8RI˲9G{'b#M3"5,4ML>(6n[oņcm1B}QtqݬfC - F;8 w&ƼΪtYQ83R CJn`6mSհz;Bv"` /B+ĐͳѮH-k"r*ٸq#6mX,N5k̞={J8s{(GG 0Aq]HZSge++]D40aB /s} [o2~zj~oUyN:B߄A7|_!rHaBYoUJ]!j()]t eZZZ[gP45=im߾h4J$Q \ ҇cзZG>y"J%s]7qűRp!z{{Wmذ~l޼yJ۩\;fZz`_u6Gsn)[rJ?EZe%""DD1?#Ab)D,b)XKy"BR_#b)5$C5_24=AELjg10cD@xM! ?bծSEsҰ<16LݛDcAUUC&R79T;.X`zbJ  @$yzq/PXw1emQzrzHCV4L& {dž x.`ppl6{x"* ]]]$ 0 #|A~ӟ~ןS$;=qճ@.V'^(J"ÅD40H; `{oF*]EhUmzi$J MSoA2_~{^oG"`kd,7{8JN4>}ql6D"bL&OVѣG{yg>%\2gy޽YpbYyFX*E7;=BG/va#45hƼsmjLQ0}!4}+ul?Apq̤HyJ>6eաHBg6\q6ٸEdHRv;X~=wy'mmmbىj)H!?ͳ0 G3<̡}cFV¨!Јz.U3_ ԫUu/CAB^?fi$*cGcfGD"bdT*Ecc#Uf]ι};+GwJ)a$IfDžlLN7!GP(w'|oG vp-Ȳ.e0B rBGd}\Gy'عs'7|3zSr9r\i(;%ld֦X}!OxFGqkCZUTˏR+% /ת(Nqf l[jvᯉ4D&UE㠓H$vH$ρf/N e,f\ d2|g|{;kIB#GΝ;g ( 3::z\epwS qh6y]z&? 1wzup"wlmvj=R-a إ O4]x^Hk lkSQ|~',v ժxkqv3+zR?YSJ˲@6"I'Wx:֠g2B۲PlSxڦᄇX`Zqr*lUS1j7Tn%ZE^1WA D2//_N0DUUbȸɳ,|3]|.+U@ @ X~HbkF8gk}XQT0+EIE9薠Rj3][J9ޘ&b9bئE~PLr*jMuh*:j*(/gt@ DrB9rܬE! Hé*o"k Z/: @R!LR*0 ۶Yr%Wf7yݎqw+4ṿ2]gűPũ{LZܭ|c?rԜy0MltDXl3oG45jXW>ks6@J$'(A|>Ri֠UUijrJ^e)IMӈFu#ؿ?*jLtS2id( B''vm˗j*?2yjcG2ؖ">&n|(~IQk"ZLEQA<6R P&5y3U똈&mcS9iB!TUT*a655Q,f(B883 ;ժ( DB]HRgy4i( o?O'H`Y`YK.eٲe\vY/_vcNJhٵiuwMű")LZS,ɞ B0M66VMc׵X0$h)|Ѡh֦l6t]p8<t&SwCK D]קYb j U-^ 1:um2C"'tqV#:f2듧o( P`0xB"NkǕr?(<i(ivlvŮ98ާNj:uRqHUJ JulM#F-e0'ym"믿Ȉp2c6Rt:mN\AQLUU$r|>OX_gʕlٲ .`Hp-Z|gb&REUPjNEPϱ"˞ZcϤ˪ rHJ"H+碖J膓Qt@ DrZp] ¬"Y(=S\2Պ0~8?6UBκKT]יj*֭󜒓4MƨVB!^/{7 |O,cddD)Bgg''Ͳw^8@"`۶m\zS,X,ƍz2zGLfPPkӦkI**G%=&c5&U o3i*VqSԄӃ:Kw|n@J$ 0 qϪ+.2a:uL$5M:9癜bY\8AnAn:y_2ܩ\.b14/H$$ `ttM0 ˲0Mb(,b(<^__l۶K.dy4]^(</MQ; תtNX9yj<$'LN>J84H4xÔJ%`YѥJc?Li>Daxxө0Ղ=ymsZqfY<--- /޽{xvB֭CQ,  mmm=ut:MP.=6m]y+ƺo?/NT#X{# 0+Tj-0:A5ɳS^;uAѣX,ׇaNmz׻`۶m,]}%'eYbͅ;::3<" dYr)* (hN4ij14\Gy~K/M6ńExXsW}`ql;j͚2XLall OPrI8Rd(ضMSS6mbӦM477OyVy~r58Q*=# (nIӯf=,J\???{?&<$yO_| m<O@g{fl7:vbbnm---]'aݰɳzj>я͢EzcX,F>ȑ#|>^/lVJG]%LRT+@Zȑ#R)|>x]) LLLPT( ~.m۶ 0 ^y~=ʫVES9^ =>%8tkAww7wux+H( <<# KDvuv^YVtTX\fbbBX x* ?8_d,"&<ccc>|۶Yh&]+JuO2;|d2)<ԇ5&~."lŋ<<{>Sfh Iu< R^Ҿ)..??{p)?;iDXz56mbժU|>|zaxޖ5(]T&p^qo˫{'?ɥ^zߝ)/w>a44=BOaKëK0 nz}y<:::hkk Ek3Bުwb ,iϊ ni.]*L&46)9rD$ ( tuuH& Î[e``d2t]'LbHDdq5k|+VLy~Y|W ocIEb&mb[&X&׾yf}s  R %SP(#-eQj/f 8 D(BXT) ӃSOn ܹsj:%}YIQ97˯WY{),j"mYxi>A瞳R %bddx]vHC3 _7L~ecOT3gQ@ (\.pmJEQhmmqwLvtt/~˗;ua6\t:-j_: ~3oߎ9>>NTB4y2 X MӘ iR)d2;bm=VBmmml޼.?G_fGn=@"m[`ZqMZm1C!R"Y`׿u?@N6>5˲T*3֛ 455a)^3 ҂a}-O~R7$eqQ8] /?N__mT*E.ESSJŶL8D"Fع8O:СC MR bF$`&TbeYtvvrײqF˭hsyl/;I/Y e;k|?r՟i[~= 3wx</^̲euMx[&pN@4vD9vopwgϞY{Bܹ@ 05iwz|kqFooKR.T*D87xW^yqzzzXf XCK/Q(΢E(˘)Dq!⋉F r&&&fŊ,Y]ׅwk<'@P(D??+#G_|9y}l)Me"Dd2x<-G@׳(UU塇SWղ,0 ^Xc6~ŋ8oJD"A[[>~'N: 0 B"(jK"}{]tSzG|:j[|>)<Ƚ/ @є6~a)됦i E]wq͚5|gʕgb=h8Lę˲H$"3!ŋE^Wx4oe!H!ikk\.388(a  dRDQbirs׊KnNX7aA\&JH$33<t_WyDž}߰})"\r _җ>FWWUtPB!8b^{}1>>Nww7+W$HϞ={HtuuH$fɒ%"Dz,٬19F]O}SlܸQ}  [6.ºHχ H!_rD.7nooK:銤+nFW(}>7oo<W(ݚm>gllp8, :%Վ=J6% l2Yd ~ J4H$B0}4MM{#R"HeVךx,<`/}CKӽdw)HW,Mdddr,(W]u]vmS,d2"|>\D D"ܺbQe0VI&eFf #'mKK JUVqmF)d2~iGuBq."<?r9Ϲ0@ @oo/]]],Z!V###"i,cƍlݺoX,VJCܟ}{W­*k h&/e2 lT*%6t]' q<ޏzm҂H$my\ "]\8ΚlEaΝ<#\qlݺ`0H.Ab3 сg^I@J$I`6BA8dYQN4 UUR/l0VR!PVڧ{M; )DҀJ%4\B@>n8]ERpf]'r,r$]]],[L)H$ LZkafEhnU*"*|xI@ ۋ(|>ZZZXnxl !)^x}s~"<$>bT*ETP(P(˴NźtX,R,ٽ{7jU3_;,q87W$$,Kd)B*i g]-KZn`ݻf֯_=#m dGG3W_7z|^8ev"M{c#w8TUe޽tuudzzz蠭&X3,rL.c||$/2n|>?89*EhݳNN'r|AX$ˉ GG#X9+.+L>vԫr"眭|צߟz=s\]әW\wߙ>Wf{>m2C"L[}6Bc3[VY{roz|D"9Y5ٲ#wSѦ?]NL#m R"%.gZtNz3Ye4Ne)H2CL[6'sL*H$^)eqS9|gY>M>t[R %;zS&֫P۩ޱNgso%t9? 9l5Z8?r9lm6l7l0<$DO@J$DR)H$I@J$DR)D"A D"H$u)H$IdGcG#X9WLɥRNIENDB`filius-2.9.4+ds/src/info/img/modemconf.png000066400000000000000000000202541506574241500204120ustar00rootroot00000000000000PNG  IHDR+XUsRGB pHYs.> QIDATx^ pTEϙj+-!O&Xn]IHL d%DEPaY\@XVTC\<^"u1`#$dn*h™3g&əɼ3}=a"~ 脀A'~M`Ī*#}%zt>=5b@|⎤YKEիٳg 9U彡Y] A' :r  E z%@/VYz]< IY^ @r"4+2>OEQ;~@ @ :4Wr  ЬPPǘ  :gJَtz9W%T\oq̃ 3#'^<lEJ+^~"&;E1*:f$4=S'(a0I >dRzDHwHVɡQ3Q<3*5@:Vrhl8uC̖\&N}4F&,۲^d1JHFe jE"9wT ب5, dg( |?kPJFCC+ELl tYIW+bM/~Ňq 9n] CoS,Gܲv\d?? ";kB?<{>uBL1P쌆} +_ 5d~edMoغր%ՒEXK[7}n8[XkqD|[xvnYS aVI@ꫯiENo .E+7ц~@:&hV @@D/q0=2 ~"VH"q@A ,Ь`8  3x?Ք?V6@@gp _ z$pټ&zw ?Y> .QQQz W4+X1[~23  pCc?f&L4+1 @@N@?A0,?f3h@ @P@X]]'S0 'ͪ\ ‰@g8YGk"{& - ^pgm3SƛmG .i`Çv)oŃL̳[6o|t6osJjmFj ս[[֦/pj<7eZZ?i-c䗖ƝzG^{tJ3FFvV; yN<:-4Skj灳~'^zv#;3׻h4ZNU4{^&AjΧC.**DnPSҍCYyXzA0D{3о]rc( CVONDʎ=(fy+ObvN;~@n rkA"Et1?usi;$%=A0S헂hb~$I}'t-')..e)`١!`ךI}%K7\_l;iծ]-|$#;h=4T0yHBV`9]A(57Zew?q׮ݻ_s'{.Ov[{.Q8R_ qJH땫ƫɢ tY76ڴ]<\]Xg0d>gSe Q1ڰV9?Px/{ ;<ڸv;>j [zPN"豰I4E#3Rf-8{_Z&W h6>#[z ;fވoɎLrOYN(╫ڗ/B3xn(P+OIf%&5T^e;1K+/\xEEjYԒ^'veaXѰ}gRb;!籽j} J% lB\= o87Q0 ~%`,kqƭZjV/zcƌ{Vkf̯˜LgE.Zb.#8KZ_. $5L.pO'#&  "3xt帢')$ gĂx$+srfyJVQu`k\q򎼬T鶻3l7sCZ.ehfPz⌜Rz2Rg<8ag Se=*ºҥriRپ˵^s 2ӭ5 1re,{d5ye͗/kAlPtf$=~3ۏ)H3BbY3R.Z\ZbjFr딌lR1oK: IX[R{Dr˥{􋺅Q-/\nw>0HYկ\㖞nHٍk;ˬnMfLeɐh""@:ų_좥<:>y ώ#㖛M}C;~"_DZѣ r[Iw6Dr+ECztU;'/۲2EU/WTԞuj6 WSR˜TXvdBfPCű$Q_1N1Sm~cNAS5~tE+)a'Szr۴̯#=n:U(ڀ(gLDvM]=n*/ |p@.d9Y!ƦQh.+o3'KN֔69GyNϬ26/mwk݂SRoiѽZ=zA/K,hg֖y>,Ƕ#3=yF0oJeǬѣO 96o,ʡ6!EoLšs>X0W]~9C;t݇i4]z'ҿXV+ x|VuKw r&1;zOӧ y_][~w9+'e8Y"a@ĆY [a aO/%۝_(8 "vCӶ ^ Xj%v @@fi%v @g!>Y>|8l%3 ^@n52t!hVch @F !$ 65ѯǠ^12ŀ"4+a@(́⬀ ;Osz#0爫ׯ_5Y&wΟqN S ^z]t Zh j]fz)0>7YB[Pfzڍ "_zk9 @ X-0uMݻ?թ>5t.yc&EC:Sw @@inhu)H(9HR@v@p \^}N(x&Y\ Xs0p>K|[N-Q@ , 8iwRy0b;597+Ű"5c" L8!W;YɘON-Q@ , 8OI-4?9XW+qjyQ#Nf k>+W>$9N3&# &rOe|pӎSdS0fg Z(r퐋"*ۧ>+dSKJG_a7S8:97mo&aƶM. # !J$>7 ŖbѓMJaYK8֓NtNtjPX:b16?%Z `Xr렛S Bp~L L n @1@DՂ ,=fi+4 {@@OYzZ-  |߃~N\\2o{jwa %@ի,R ^zp' % p '"ZzD6Y=4Ko+A @"{1{hV @dfEc 7 mܩ7§  fn^6l i;#7<8S fQj_a@ ׮]AL h:Oo.zQ8 7A  BY;;h4K{Gfu3܂L f "x}IxpZA8 E{4ͅ&}B ޝis~'IU$vռ 第M[⣦ϧ[k[> #m ]P6?ZLGTVo6ٻ f %i9S_pi.ex=1--ggԌMR%h֟ۦgS,״~43-gnȽ/yV΂@dZUw=@8rie{G^2>6~[ʖ$io?҇a$jq^MonI?Z==euz_?loe8) O/);|tXi65f)R I=ipElA@WYώ^OOJI>6rUO(jȹa+SA^:c€h-{b{gFr$f6d=qXFo)6Ȇ}Ϋ YvգT9n]1+OXnhٺp`m7+k!X{>WVV__kIrܺ!Y ӳ=0l˟Pzg#2Sj,m /E#k ` +j"qQo:qA0n3*gҨEepmrv&%jVۍtlSR$'<<]f‘KlpGZV" R{R){p%vʢؘ︐S>54KO@'ĖjmOZ{_|v h!,.u uK 4)6@Pkrj1Y*=_N.Y#G@<`@Ggu BBgdqV. p6͊p >^R@'FXQIENDB`filius-2.9.4+ds/src/info/img/networkclient.png000066400000000000000000000057651506574241500213450ustar00rootroot00000000000000PNG  IHDR@@iq IDATx tUC!'kaUV I!#a.J zEAٷ,APj0,ͅJr#ߜ?{oyμ)))g j*UR1K!kkWFͅlD%DR0QiGtVYrTdddQp-F8{t dDXXX [nכҨQqqqۨ诣1*T"Ex(]4- #)﷢JN N#^X{F9 666@ɹoj֬y`(\0BBB#:5ټ}f4Mk(oE7ϣ@HHg`G@Գ[-]2vP7\{Fؽ FT\߶=1-.ЪT ,ŋ}+WĚ5kT7mڄ;vzߏ/GAVVΞ=ҥK i޼yHMM#ֻaÆ44nرʸѣGc̘1-[`ԩڵ+o߮ڻwo#7+]+ ݆[jcȑ#1p@ݻW]k߾׭[7C#FPyo6K*u֩p]dT$''#33S=z(W-W 2FB^{nE~Νضm6oޜӘkbժUHKKS=|r A-CM>] 'C[);<\xbC< >> (Pʖ- yXQ%KD-TbcDnAөӧO4xE Tɓ[(Aթ[D0ז)(~'\{]_avt72QbR&⛨OWgWX?\)3PCzz:8-OƟǏɓ*|wgk@CJiϞ=A.ZH3`߽<<0A 1g4V8PZ5uA3;1Hل9z8]7uYn`bxL/l8{ }IG0:Y4シfK,?Ii?7ns)@4䴇?!9Gc]vZn);o˖-/`^O<iHbdCw6ט5>²n5u<72Qc:.'p+tC]M$ϯĺEnf@~7·i̎c3af LvYn00:!~U. 3=e̠ɱh9gLXeծ][Msi3A`3 1޳gO^|I<߿4L9zTLLizEﳃMMvwi{3 '_,8>κsɖc!Nt. !ޭ=(JûW#j5 Vz>9qvusd}fE;ClClﰆVQ]{RQ5 jhx<5=4rR!A 6Y-Zp4zR?^6^gb -n/J~Syx\\[TohZF `lOtlIn9˼33Qp9Vr/7FE ;LĪcs2#6aLZH/ +/O r2z{\F(6V 握veATW[T8yc%?>{6g?TNx8x[gq2:=&h3fXH?%v)Ձf+:!4n81s O#zdvs!a[LQ" /j_j3(C4@%OHS!q2QFG£bc -RHH_;RlѢ/+M _ dkd{;p-L5k뽁<$U_ޘt]]M;*YӺ+,)ST \-A'(_Z)w;N" ب3xtY@a3qt}f7fF)#Ѝq !@4m)ѴK##v 3yiѴ|%4-g85JYYU${ȽY 7sZ\!wW, A B,=n-S_ 8*MW '/q?sdxuFŧ.)vJ挲vx,/Lx̫Ln<;չ,u8(xNlQ36t!5xi)3Z\JM8a{@agtWGjgi,ӁS*%IL@g8ۉ@||NQo7}؄[~ ỌYY%h4e?.ø`09X({*Ch^Q&ig~1z,giQ{251䉿RloJ/8kҵ =hla /+~9BBVdxz-=0?RV)ZVn4vɓ grb7S(vDqor~Y]\7pR(4GaJ1lDI ʬ"mʤR!aǍx1~.DҚ9ܔ̕PCE[ P 7A"IENDB`filius-2.9.4+ds/src/info/img/notebook.png000066400000000000000000000155101506574241500202620ustar00rootroot00000000000000PNG  IHDRTPŀ pHYs  ~IDATx|kp\յZqw[%Yq&IL*+TJUI$EϤjUS3ITHr2b0@,K㜳^c] )U[Z{!{OH:o|R'k{"OVJyk'}G N~z ϕ/L/ 'Uq׆!y Zfp#g.߶CWܺJ{z !&,8 d?,9ql{;kǏw*D#g9 րL!qjTav:Ç%WJg(vm% 8K΂3d-X$=UlwW ?yra,YdY${5;-iOt0 7?KZ. >Z{%g`凬!k\|_36֌ q=nGuYp{1ozp1:K63`6 ޼31w{(| Yt|NvbrAdcxT[m9:Hp ΢ x5 '&*Wk)w+/~]wO~9}Ok=?:w` Kr} 06"t\iz}`]Ȭy΁h%3`-\ 6^y s!&F'挼2j`K 8ŽKr΀`m&"d3hKȝoߏ '|#]@D JBr1:Gְ` xEEl&>l&ZR諝x¼ (ha($ BB, "\ 6&Ӌ1X2"lL&"D)/϶;kS0uꀂ(J BP  EPr` T6JR9Ġ"4>oJD-qYsY#}+(:5PtBP $J RP+HRPdzDZݏ~3ZP=+FG 'rEoϸ˳~.iWv6J!{"@j IjT J@8#0UD&&QdBd&BȄh4Vz-1]7Xah!N(A r^ ~MJ CAy = Lh"2LJHz C0QzB#kַ֯.&H   xo3IR2@y'Qd|00I&@y<2!dF !֨t ZtlPE!@=G@jBdFx4*R@y`4bM*Ai0f"]0Ю5r!"(HH {LxڣÍ#㡊ݫ|PuH`B| Rq6P RC\( "Qz5H * [AHFh=XRAjP "B*j1(45BwuYG #O(P &p{}>6"AE}>ȞGP DR^fIR$DkSM GJР|Azd=>ʘ6 ^Sk*3ȥHB(Dl>WUՁF2`"PZ{^/^hA<P@@>Wk-}%%"@D)|Rzdb|p>`ۃ,ؗ"1Ԡ|Rb( TZ;y*raђiG!@ɵiƠ6@< k,C1l>x"T>h9FZ{~j23DQPp:Phӟs9p9Bp@;$"!}Thrd ؐU@6&g9$GHsBЈ* A 36D00D S `Q^ Y# B@oľ=I@Lc?55~0s!zyAdpҒsH<4d  N $XI $!"@`#">o8^#OMMwK/)s/2Sr%܀ 2 ^XI^3k :Ks.€J@ s,[ (%bLB"J=M~vsΫɓG?K .Uƕ)bC Q u`QZUW>!I"?+)6"ZR ]q;w7~8Vye `J @Ƀ-e9Q@D@+B""#?م,?" !o/~񋱱[oX,:9um۶w_~;=G8.,}BX bN!",Jg/aya7y>s΃n߾ƍ7mt53/Ï-!@DW]tGf:d,ǸP DA<^n+#!WB(WZF n`͢ԔBd'NxG[R,..crRj]pzdÇKD}+ bR>𳇦^C>S'xvR*LZvȑ陙L&AǥRiiiI)ed2RJD"tR*Ç_{f/,wh%p ĕ)-L5(eo-*={|v"fB{ĉrr^R\.wr9o_5 +Oj|"%F)ZК?ߴ> xy>fqǏg[T*YqBqR 0EX\\,˾m|o6t:EQ^Ð8pf6rX_ [O%z"6y;.Tx&6N̑#G`.kZiP=wDKKKJ%d2n:xvvZz hQm ]wÇEkkrLJ{Jh{; R'ϟ~UwϰT(y䑓'OFT֦R 6 H)ٳghxxCBk]V }RplZJnq<00J8dj={:~!l6O|=M6'D<)Q(@12;<!k_2ŁL&SVkZ" r`qqqqq錍 ;ӧF"(TV?^ausLrZg2;v8p`q]Jo8PDaOqEN|ojJ)DSOI)嚋%\JJE)nݺ0 gggt2\XXHRX*s\R)JVKnݺD"|RB$ * fjsιK/+_֭[9Kұg^01SD4YY-ޣm3lDTJ)r?~GY^^fq>gR^p^kGFFRz}rr2c6lvnnnvvvpp0 8_,(k4AE)e)Q1Z󆆆nk/ܹs>/ Ã@+?rN6jwݖrifmOx' RX, VUY-//irtt4WչzH$6o,hӭV S %1&'8y={/ N<պSP7*|/Ѿ <>H$ZVTP(lڴ)L3g2L[mvvRq###(B~zn;P ZA-"}.\8uTEbqݺu88}VAP( 1P(R)cL^ZZXk],={ԩS='wۊ_Y??>#Gfffsbsn```\ޗ,f|~zzzii$-[Q<{lRJT*l6Kar,Lnׅ3gLNNرog\Y[ .N~}g9RVRF=1&HLLLRr\.J%cyN'"D,|^/,,j5!ؘ |t:m]^^ItСC7pƘv০hyyr)C` Z- C A0t:}J7oKͭ[. Åc J…0r9k]nBpaEѻ:vZ +Jәx[ 6[f3\9xoaSeYW]~=z}֭ve]klZh4hppF!( ,Y> gY RDb||'%R9744IJkݽ{w_p Ā~{Zt:-`633J(6l8|W駟~ᇙ z; Cn ذaC*j n7]z饜sssjZL&bZ_~K,cV?SSSιnl6W~655Wkn!N7DTV0gPvQnAsΝ={3-L&722B9L&9~0ܵkׇy҂oi6\ OM7}3رclF\v\6rM6iYwubhXk8N$w޹s'qܱXޛ7oS3at:WVLzZϫ>xn?tg?YnW3% f+(jfCԀK1fppK_ȈsjqSpR >~QvJ)3L"=yѣGy\/9H\,VWG <[Z!3|6ݿu]aXո uć[zk\rps09937x#+w,̷^sih_׳lj+ c'C/}E VtA*R^ bEQG}\nݺ;s׮]qsP(Kx_csGDjm&nd2l6y^#=y\rG!0bnh6HfC1X>GQTպٳg+#M>]\`eF%J#piv_BpC ?쳼wqǺ;}r5/[d2NM k_l x{)g:5e W;kR[uRt~ky3Km:Lo;沙IENDB`filius-2.9.4+ds/src/info/img/p2p.png000066400000000000000000001046251506574241500171510ustar00rootroot00000000000000PNG  IHDRU$rJsBIT|d IDATxyt\Օ9wIU*,˒elc`pIӡWX&'I܏ tK@:@Gcp'i0`y,U$y0 ]UuOսT_>)DP(m;P($* )Dؾ}1p{{シg8ζ5՟w8NV|ALӤUVxB!݁3O>$vb,Y4w bQ>Ir!_>ïx\!^}U}Qx JtwSPLR$p'_{;;9&o&4551|"4XPNNt:M>w8 L{b٢| YCICCMMMB8 @$xWxxrƓvi#m X47`gc&aPWWGUUdrP(FTJ$<M"bIDIDQVm ÷-^~}޽_~?:61NT'~#$%gi6-Um-p,b0ʕUc4770R(%'I>o!v O@=a-{v_6W^G.9xH#c&˗/Ϟ!*SՓs=2PC]_o *i[.hC3+nR(B\E FIm6oί:rH*U˳Z'k:X%W|M5 *\qXbk֬!N dQz?W36ڏ ㉨m=&%EmYϊJ'#`͚5\qbB8I$O(N{HM}'Uo?S.ylUHab9% R/ 6P]]=_B8*$:;;34 M""I{ DMV[Ehz֢c|9Z-Z$OuGcn~ӟu>ʹq̬ǡ,Փ駟K9д a;HNY |k:V?qjgjv{O sˇ(7/^̆ Tu,b_ތ!4=L[\6)S=g[?[W@+rTE'Eĩ,/ž=|UP@--- ūv )-00y~qܜTbCJ w1TX%0Jcs.UT'Au`yy:yn:֬Y3ߔBPJַEG>Bw-UB@h^%)5fJO82<ճP]@{]>_DZs ]gY9k*ӈk..fwj+BMChBA3C7@&H魶rS1M hkw-T9Κ}"a &r,MЅ+\r ׯWX iFMO?o/&pr#Y:'M=-@79 R Pw)F1xVm+A/PJ«&ˬ>ʶmXnW4RNжsN5 9M#Gȑ42e 2A޹ N>;֖]q{TnU$'"qD8ޣ)G$-E^[Z8 xsWmQU042ʣ>??c~55R5># qy o/TM1 շL k_0Bsz(S1RU77 cV P  g?;ߤB@M޽{Ejw/77@ƄT3&&R^[(8 !0 |? w!( zJ&0)C\Q7o4~ Ż %aϞ=LI#4RR U7&Xx*RD!剭r]Fi] ׌\1%i\4LO46X´ܝl+i;n* p\"'@vRs?R(N?JT=㊞mX{rRS ,EqUmetqO@H0tSHv$ H<!l& BqQzWjMT* %qhD6ёoe: Q鋢JxB]^J,R1f}seU5\q$ACY GQӟ]pNERF*x٠Y Ho)xADh` أ*RKK`HfG 5 D?TӏJ:q]a*/u)##p+Kz S:^`Im]ivY/VecySd`PX*#L=mGcfGT\.Goo/momvڑXX5)8tVQz%U:T*3faxiVAZ4tu+{;>6_BCMb |˲Rh">l֯O?϶r ,~IB+[{Tu66w7[qiS^@ Fj.pt'vjq˵>i>w3jQml6h RJ:,/^|g/kG&HBb(>Q y뉬Z‹ /jbka#5gLxXJS]+###d2lFAKK /gÆ TWW8Ba477fM+/Ǟ}d:ສk -Tj^  ߒ@d R"q0 ,b˖-l۶~<G/_Yxq RJhlldN^} ܾ.gez{!hW*еXq~/xSX+} %B:TjPL'QmaFFFBFٿ?<B!8ض͋/kYgŵ^ˢE&ꨯ=eV߃]Z2XFliA@˵\ݭV/|~YMEVsb㦂544UPL/RJ'L&>"455QWWGTbpp;w}kD"+ 0 "|/V7?u[x)CG]Z0_tZx[A# B.W!zGa|_@xU)%\!t]'Hi?<;wddd۶麟R8Q]]!:;;%QUUL&C6\4M3~J֭[G$ B"m// sT Y,·H@PYbB79w/YhdÆ \{K(^18N SO=Eoo/#Lł~hFP!}}}AR˲4 àT*O6Eu8dK.+d2m !8:BA̭ @*BsE ,?/UbyGj6JR_~^x]׃|[[|B@SSUUUϴB@2 ;O8fttP(D8fxx۶E,B)%\y\y啁+q!طͿ˥j/_u,,2ƹl17V_UB1ѢIRhF<4MZ[[y$### 0M!jkkttt0o<"}}} R,b̞=RD[[tP(DEEHQRb1 !ìY|V+h^h5)X")OA I7o_צP(gZ RH$B(X,uVvXCCC`6eee466RYYs!̚5+Z}k4M4Hp8`v522 J%*++)++ L&@8&sEqUW1w V+ |u??y޲&ܺq<ՉdjOVgb͚5|򓟜EPpTe2ڵo}[<:t(𭎎8X,XzA-C4믿N"sa…,[ )%}}}Iee%xH$D"A2T*fIH);w.H)'`ӦMy睼k rs3ggyϋ,Z=B1RbP$<M$5Φ뷚{qq̄3RrR)! aB~O:a022@ ϧ Mepp0={6uuu ڈb1(++CAOO A.kOO###hٳFJ%:;;D4(H =_8d/ V3㏯霻[8D6Bz:6:=ʭުVS)̌U r$ /֭[IRAOo*JTTTy:dYLӤ: Ͳw^JDjioo [?X,Hjt]'OX\ BֲaV^=a@gg'ݶG]z ̪>;ߕt lX͛7O/P(3cEUJ0L&%ii?0u=ՁxJuu5UUUfI&A*"c3g***HӴfF,XMA20B! ޲ ͫT*/aׯ碋."Ohmm7[^frg]IwgVPs?ޣŒUq DϳuVZ[[R)RtaB TVVit>3gD*//FAL&Á%3k֬ Ŷm"8C"m4\qkaժUP(}x<3Ld86sm*_!XQ'`~qD4Mjjjp8ܹsd``"̞=P(D{{;TTTP*(Ja2 H*FGG`hh˲ ˲RiTWWs饗rE1gΜ ~זn7փ&~NP(`TٶP &~|=O?я//\ARD]]DQZ[[y74:0MJ\nC__K./'sA^yrVbѢE466/4I$A@EEդR)4CCCijjjhll$ǃ>׿u~pB>K,kFqO556-՞'G d&xxIw<XE4e޼yO?m3k,B`YP\.GXDA?ͣl6KWWLMӨ'>ծ~x۶IRضiA<窫. ߿MӸKS(3ZT-ˢ="%It]8/k=xqd( Al$ Dxt#>k&ahh(.%QUU,"HRR 7v֬Y,BF444{zzp w/fŊKXĶm3g4B UpWVLbxMq=\$[[[yX,y۶illW\qݱc[l |fi}P`ϫF|>O2dɒ%NmX,Fuu5t6/[kqbHgg'hbc_ bfpƈ*>V4_߷줔?P(P,'e/2Q/~m)%tBi H$3gNP!2U__8:t(p!Xv-uuuH), D"eY8^{5'?IjkkP(GaFTΎ;={6s̡4O H#bٶm۔J% e}Ǘey)%\7M>OH$( AٳgNك`o+{쁁o{rYgQ^^ΪU;w.m~M4rw}7[lᓟ$<ȴgzɴʹq̬q)U`K5m߾~L&L}OWkP(?|Dlǎ<裁l3FFF fdGGGb* F5jDX,lPӋ IDAT4~ƯklfժU| _n~ B1+0/_4ˡz B4O<>l0/JA4V4ફ/'Np'/~A_` N3:: "H$,>QWW8ʮP(D4%;wW_}|BqThQ)Al6K>vSFt׮]<3A!D vZ/h4zEg<3 mR(hyy9W\qk֬ v~fYH$rR(۶ټy3[l`ݺu|+_ (;BT}\.H-Woݻ7!`ݺuARu4qfAJVXd?)//P(~Yp8L$lҢK/qF2 r-,\ ļD/L "hSY4p宇gxG&\gϞ>1?vP`hhRD(" L&I&G vm:ԧ? )f 4(p7FB )p:hzeqWv0|eee̙3H$l6w}7=m+wB1 Q)UIBǙ7oAR,ò,4d2|0 )X_Xmo 6 T*ž}df͢Awr7rmٶmLmiQYyGTM|>O&axxQFFF${%híգBӶhnn~[r-Fٸq#+VxP(\U6-R4(?{BL<6nRSSòe˨8-rz뭼KM7G>roʻRTǓHyFGG |>yȎ_šrA2/_΂ Nl殻׿5ͧ>»^T} Vg @KRÀiA>ߚ_|E>Y|9iN\yNl}{7| `)oJT_%m3DO8TUU;wrmdXj7nTªPb˲r u| ֲ ǃ>.]:=?:---|d2VXM*wlJթj۱cD9sDSSϧ ={6Hs 7t7m…l޼:^z%nf~ߝ΄5՟w}JہI`> tww|V.\wA2n)gOs饗8eV(ީx3Yn'cn =KTZ->9ZZZ۸뮻TBqHKXr;nʕj;ko{+nׄO4eӦMTUU͛ ř)+W Mw79z+uVn:Q(f2gTω{:JTy\lrwp3>dfZ8fV<4ɥ^ƍ; Ŵ8O?r((QUed~@*)ӇU)ꫯfR)~MwwiCq7Fٲe ӟ; ŴDUqhll#mi&]Jam3?mH&~wVfZ8fVTJⴰm66nHUUwdxWSκu HRu]ozt7rEq=*oJTo 7xcS@KK˔szjok.>ϰxb%Ugͥ^mSꫯ|3"6n,Y½6^:JTou;vtU&3 _|1۷o XUVqyhmmn`ҥJ\3%s~pWZK|ҥK˨5kְpBLСCJ\3R5fZ8z(;wdΝG9sWGSS]vmgu]v-" ʍ7Ȳe˸N8֟w8TJm'? {/\p֭|駟?J-[F$9{XEkk+566ww\f IDU3<⽑HKRQQ1{߿L8'>)]S JTロ￟x<ή]d2 `444Lt{244t466կ~U {eʕ,]tQ8-lٲnl6{D[CCMMM1yZZZ?j}qIOqimm4MXj/nM%Vnv~_L8/ŋt]]]-^x!<)fZLVx|y}՟T*om6|MTݻwgy戶9s0%iP`'Md2ɢE&җu]7*&G:>QGJ 8Hb٢| Y1p>p8{ZƥDUqRdY~ !H$455 &}]۶顧۶xO0$)C|477Ŧt]BJɫ D,J m`X/cKor&>tQ=&ɳ>ʕ+'<6}vz! =JA8fA200oAwww nG;t&;) w8\;L|.BiTfZohؗ@DTxG-VVD'%ѢX9"Rζ~n{%h "swOm۶2FW_瞛p^uꨩubxx4g͚Ŝ9s4%/N'Xi2w\jkkuM&<5MG?Ѥ81?iooՌMcSLT JHc!-ڲaQN>G5k++ ͲqF~_555̞={C*:)i"qn^z{{p+̚5ڣ^u_rTL UBH?Ux>TҸst_:.yV ៷,ޝ0Y1G?8<_|16lx[2*&pwӟtB񆆆) \?A5 1o<R))Ũ'O$[y橈i=Cj+^̧>S1jpSl_HGת-"- "*1jyK;v`ǎ\r%\\*:^z%@XirYYٔ988HOOR uuٳg%BOOOV4PUUEYYv(KK<vjULxHm[M樺x,*uKr%R-Ԓ%Imٕ-+cAB@\/^̆ NIu,%\.7vJ9uuu5fzKn 6 2Q˗v~~9˖3H$:>(&o7#BeMFRUrVgVOuע+V)]a]1,Wi9/$N}Tgyy)W%*"Hi*W*#N{XhYYsΝR"߭000M}"G5MQ;@2vlj-M/Pʵ2&R2/pUrV`JUia6ITUUk-fǙwu8TA:bH{g48 rˆ!"eXKE+Dz$"R$ܔy9":H^Zמrt?ap6~q-ӟtRcT)UO~8$hFee%UUUGFg2uG߭0:::|$AJI(:h+pXo|o>}]=~ s먮fݺ+c߾}q>~)Uqtv=_ BM!4a LV[9" M]*zAb`q,ViW*`i"D]K.ן0KMRʠ"82N)%Tt:M<Qr#Jjjj] ~KK}KhS˗_H)9ʬ n*02v-[~m\}|&US?o/&pr#U=u4OH5{[2@n"s"AZc*_(ǻOUpۍb(V*^hؙWM'Y)}m۶nݺ㊫UEqBp'x덟~=pSfϞ=i8t_ ce[__~bq}NSTVVnMwr.}1::9__8*N{"$i4S#=1 T3 fx"k"ua0LE=auRC^pUa2,zaziVaHț#DiXSGeǎlڴSuǎ\xb1#;B!f͚5ek/200pD:V4%$ 4Y9z1֡ ?LGBxꩧ%Hp7reMI޴_Ve* k_\5B'Ƅ*]p3"(Ut!=U/mz !TDzޖн?ys|JTT*… 7of"2444j9ZiUUU`N\.G*:­`&r曮^+'~<?--oi\wu\s5i`޽"g ooMP'L= R^[hzfi=OFJ0< U+-ڒ0LLUIዪ"KRVVF8fdd іNojYT*p/w+8@Y}jt?! )% ;zѴ&yg,?SUL={p0 v$4WHU~ؔVaVCT!0)vN3Ӓ4 h'gmP)Ʋ`ǪDUT d:_"aP,)uJ, 8d227M3buuϟφ נ&OmLf8,Ţf ##t9sp 7z)&G{{,2 ͵J5ߧj( w<*3oZ! ! ?궅>늯 '2A%zqOTU k>U7,vv(B `}㏡!IGuHoMR'[]]͵^G?a^^_6W\q%~}\p/28巿-NP3APgq*fJ|K`g,2uUiQ7Oec>3qߗ" ,27!d~Yp0 d1 #q75Tf aFXpP"\u0qT[l9ϵ m DXiUFFb1Tp\-U=06#Zg 0B3/ H+?.h<-=>vq"`ٲeرsFH_b>fW3O0MJfϞł&R,gw /222B"+s_֒L&\ K1y9Z4 oHQa\ `)a^Ba7?(qc%*WFaDk 4tYn,BCX:ђxA*P5\ó>ŋ jd)KneYtttH$>TOX=ꏏϟ?1 NEp//LooopB֭[ٺu1=~AC2 jVVV ǪŪ8>Կ?3t RHOU,.=4/hbYMԲOmOo>4Mcڵ\s5XE. G?mkxxN<jB[YYImmsj1mׇ/V{ӛFP՗V  uVSBs,png܆~VgVxm`7sie!LuYw_ [KW8%pTb  R֯v]RO0qC8*·m/g,YBKK_hW+?6W\Ϭ[gQ*m;@oZ]]|_~hsttv~FGG?8&I$uuu'ExxxAoORzl]]uuuL]]ݔ 4Wl _Jyysff&,!ّcl\vJA%;دbI)a#K~ 6E `AhY欽tofBVuswOw|>H%8*JPk\S{os\3kl4q lppqP"IB,+?U\YڵkO el]υ|Xo|[bΝtvv2::,@ߜGC{\QU]x˖g+ ( L<?wƍOyW\,MR8pBؿΡ( 444A6eL&}1?415NSe/=T vW?ʴqJ%#R^D*MG\ZqW %qE$$QqOG'@R-oo9~iiiarr>*R*6B}ZEeΗCc& ,_*>8,郁RGѣ"p|:tvvreNWWy#Zߣ@2j.KQO~q I ^ӱm93u Y2WKM$z H ,%gL,SɭtUzӛC@NJ˗O6J8EUVv:8X+A<EdY殻M6qFQ`֭',S4@Re~w| _u8t萈lg|dժUtuueYdY әaѢE,Y;̋/¯78,m" 2tI׏mVM {둬$ɢPWݗN]])GR5oV*m'?IK<nG~dL$~G4K.eݺu,Y]gId2^#~RkJTр6~">իyz,~CQ,ihh//?T1[m:"I0q}m/Zp!XL~DUUϟOcc#r>8h!b$122Bkk+dIFFFȲD" ȲL$!399)YSSSy2oٲe|SSD>lceyk2j8xm[4pF^ f˖-qF/͹W^y wW9?.r*U_fǎLLLj/o2 j ֆ(FTU%rA b*룾rabP(D]](Xr90uuu(BX$J.: lڴz"py<տ쥫a^wt+ǶQr> ([l9gmvJBgϞYO|;w?>}g& {t:a'j?~X,&SSS߿!jjjlVpTC-"388ѣGȼy?%\BP```P(F$It]')Ek,Çr}^\a,X_?U/>32].`WT] LyQl۶xbH{{;w}Ngx;>bIos9G~\^bǎ> iR[[K,4M cccd2^{5kXr%MMMضŋJg'7:Ķ MG@s;G%Lr]wq5ל:#AOE~xo`Y\ufTKπ8|$/"۷o۶E$955%ԈJ 4Mömю۶m.R顧bH4媫cǎbX"zZjS\z8all{_\E8tvvNCCC4553Hu?9> J+Vp=\"Oi0M?w GԜ@̊Z(d߹Yj/۷o'NST(D"8|Çc&H6bTQ,:֩),ˢz"ǎC$RmmmTWWfVB8vhh4" sH$iʛoI{{;^{HAb1V\I]]HBDX`۶m(m~AN9|fX,266F:&L(p:>z?ZeY>\JT*0M]Yp!xa^uKuL&áCm:B8OXṟcǨ0 Ld…jЗ^UUUL&,.nIu])TUUg#&&&{&s}qUWzWx;ȮN(  #ZAV,d'''_zضmH UXyFdYfhhUUI&T*LeKK 555YuOtP(ҥK4M(* ###hi°0":7L2Pe˖Q__7T{/|n:;;yεk=1_Bglrc3)N8nL֙N;fB!͛G}}=3>>N\S~;^r;v&?ٿcj~ b6ضMKK /ƶmFUUڨA4"|r,TH$¥^J}}= ===twwcY+WOOF3<g-8tr,bo̢:MMM!F8a577c6ǎ)(k׮Q4 i>TUR=GT*ڵ ]׹emd2&&&HB"Egy^Dv3_+ ei(TWWqF>Q__?\yA\@ hqL&@4!wGHrXECCaDXb˗/qAxD"!A"83 sb׾5^z%"_.T~+ R ?ڹs'v"R'U'W]Y~=z EݻwSOluGaR)a]__OCCbTGG,X+H$"QUUI$DQq~A@w6qQO~J.}) s}q!׿ReYMJ:淿Zܪ_MU4ٰa]w`)_}rm}UUt:MP\}]u]4[nQHDH|m~ IDAT{A>.:RC4jkkꪫrwCqw311A{{;_iii9/ Rl6KTT#ȃ۷{N8K$Pk׮哟$Hy֩[= WT},Q*z֬Y#:fYtbutqIFGG\.{nʶmۄ¹G} :;;/&ṐuOQ(Bj֮]o~|8G{>O 7~_W8^o|vmTUUJ/Q |+]V~}s/Hu֭=ze4&˨JQeDӴ}G&aҥ|["Lg { ߘX,D>Ĕt ;:'s?>7o.r\.a躎$I{:?.:Rݲe eL#e|hoj`.\H8>!)VX׾@ࢀ8\.  ""XLajpOF^{-7n$255%:\n,.*RM?3Scm!6mҵx 7\vt 5⮻"ɰb ²/@38 Q@#W˲JH$"ڴ$;H$jU$IT*,a\~󉋊T_{5~_2߿Op,Ac?bH766R[[G~_6XQ*d2LMMQ(Bd`[RN*8LO6e[ttt̉4'/ Lz (%ڪj>+ĺ+PUU۩;K_cccx_m=rAMU$UӴSF# PtuuQ]]}>nHG?Tx 8!ѱl?R61$+̆ET"i뮻U*(  { HxX,R,ٳga˹K] J%occ1}^*Yg2W/翯^D\)JhET*|_ѣ466U<ض-VgM"ajnկsѬ/߳gÂ/2/_*x .T)ZN"GM&cHjp5*H;^9_`bjjw?f߾}J%mK/Y-[fj[ǹ{D"!4551|$htoW^رc477s-OIs_4O?Ν;ybcJHm/jzQ9]L F Fz>՚¬X{}v"> pa4M"SSSBG~cBx<ÇXv~:hH___RWSiOgTYj푨e5M`-irݢ0k|xx;߷  ERWe>pV$  }k8+sG~Tj[H^j:exlYBLXѤ'5HZM6b @'eYIT(. RʯTT鿅3Rd$[1]BuL}LT.KS蒛t_zu@qQtS=rnʲT MwfL%խ$N%?r5]b1?jL˪/ko"% Tؽ{7wfɒ%lڴ鼹c ARUHz IVDaJlRmLkVET7jt%⒱:V Gq# ˫| 5@!. IաCk{ I #G] U$F W#˫BUH*H} W!*$B oE*!C$>ROQM\\?jl]Opsz.s| U?Iq#Uod$I\y$!W7#W2C E~n4p_1`BWU"Yf}hxp}w^.A]Be$YERUwST$YIE4pofGҌ4KoGYQ!$å"&(KWfDpqT%] ${K )(*(:(O8t'JՏV%D"^%Swnj (e*YäƒM9SOOs 7 E >R(? ;TIR@D4iz*Exwd8'No/esǨ )}q5i~_?ߏ2@g|:00Eo/ሜ&EőU:udUIQY* jYUs):(^䪔Zq2QQ4Ed߇}+sVnNɮLq<.hR=x oFp;ݗ$o,OOeg%9O 3={^t$w\iKs7_B澢nnU*)!o,N]E*3e/̒[*\.T [MsLN;{{y癞tpAOHle *T8(.:>j^JֽWC3{KQ*HIB"i!UxIuIK??#꘷:L/oӼK}}} +8 HL$M"oA\ZH reRZ%Qez/cUce` _{GQQ$Np>p|twd~LGvyS^lK}}}Lƀq@r'U? MG=ɏ0)_ɷ,v+5 TdOfe) yѯmcޘ$G]B$$ES.N\ZTT*a&xb.]okK{3_\++^>9pnRz Wx%%GJ, Un%Z'YQ욹Y8nrLaqoo53kܺ>欤*ϓd, I?)lڴ:lq*Iݻ_v$.9{b?ljFU+תaiӆ)Tj2+Sh)'+!$MM}{Y}믿hP(Duu5,399I\&S,ŋٰa_~Zǡ뮫Xy׃::U璟$KBw*IWWi?VZ6v'YՓlYփ\*6] R yͩZE*0 bꫯ_|>/ Uk'IdUU󌏏S*uD"A]]7x#WosOٞ oo-)g̢"QI@U]k˵z-~wVڷȸ8p⼐8dYb12 ۷o^p0allLXT@$X,R(PUp8LCC7x#Voj^=;މ"rS[Yd1՟i:->yzMU*M&WRu̪“,Y;\?#SI8 2ƖGEn~!UL7fD:E>voG"{pjTbbbYhFoo/۷ogjjAI&yuMr444P*p8T*(0 HNuu5pBJES%I" fnFA~ ^د4 Λ^[} $:NrSn>GG[[{{0𞐪iLNNbp]שT*ر}4122eYb1۩AQRǎq͛'V?4t:M8& 199)dW|I aPSSC,Qo&   Xr%6leV nWp-s0qUru#U_~呬$xw\}5kp @9N~S4B,~x `ttT4LH$"r X^&0R,innf޼yضMPr9"uuuBI0<<,XeYmH$pT*E>G4K.eݺu,Y]ׅ˿t_-VeEZ`81sW>;@s gT"ض먪$I`׮](2K[K.\H,CeFGGDUUϟOcc#r>8h!b$122Bkk+dIFFFȲD" ȲL$!399)YJ.۶me˖O}Nitt/gkw-+mz e!9^c@7*p}%Ua0>>8iXo6r 7o&fHD<Ge>Loo/'ͲpB,X@>W^׳pBYxFD"ضMTbTUUfa``]K.,$F!*sa{w_~Y|t-nHc)`,yq4@4zKFNgl]Opsz.xǑ8LMMdm[h@}[>[ngAQ0QRWWGmm _xUUQ&IfD"\r%ȲLPGb1BNmm-cccBeyĄ ~zV\I(^ar.$8Ml({ }wLm300 4HK/cz{{qGH4SSSNkk+555ȲL:fll qD"!\~dj(FEL&ѣG) q͛'\###^,"355%eN)ضMss37tW]uX:\~9˰1-ZUlڴo,0Wrd "]\XE"@$,p۶ ôD󌌌`tttFf=zqPh4 @ 达>JPr LEQ"IaMshp8ŋG?*ܱl<7E˲;Y$x]Xt'#VYyDZ^gY mihh 344i׋8B!ZZZa||CQTcN?TWWca d2ajkk) 0MZbiR(0 4Mk+W4˼СClhc&*OHu``aNJ>i©֙^|EoNoo/mSWW'VHʂZZZ6ubbMrTEQT(DQ-Z$ǎ#ϣ*b1AQ%X\d2)')p8_ƍB_5COOԧhjj:;Y+RahhH\*pB /}v4D[I$X,F*"JaYͣ]4Mt]X,RT$T*E[[dYd2ȲLss3PHTq,bbb˲4 UUlذkV[};#G 2\sY`n]i"R2DQħȥEǓL&C\:p8,Hx<0s%:LCa۶pJ$ ) i T*ERd2eY\vetMB!A/B(TUU J{n+N IDAT7o$͟W:cTYVZ_|3qQ4jhhAeYlۦRN:2tvvrWA:f߾}dYV\I[[,X\.'φB! l޼YXF"t]ghhH{UU˖-;gnsil]Opsz.8++"LLLסJf53jo~3Y[j$US>Olݺ*[E{{;7x#_ ޽m۶("Z%R~Tioo'Q(T*L&YlPH14eF#NGss.֮]K<(_ꓩcnnnFU{CUC45_68r?0G\.STfmikx<>g-8tr,bo̢:MMM!F8a577c6ǎchhUU!GQ֮]Kcc#㈞YD4}JCCC gݥʶm2 b5 IxgxEd7՟:k&>lܸ}C"^k.v,XBT*i$I:::4M}iKmhh0 "+V`8#H?H$(˔e HH$  ̤\.3<~ǯܹ]vdꓪyꓫ_[oݻyꩧD{ʄ|>O*444_1Amm- ,+ hTUUhT\lKfEԪ( WIHQ[?>jFuu56lA 'XUUI %y՗dI}}=r ·շ4D"B!UQL vSe:ZZZhnnEQHӤR)!Q?3>6oLGGN5MC4dYf͚5|_eÆ @zKa|n5E/q!;vJBuu5,|/ˢ?1I, F8^̒kܺ>Y7UpTb(D~EnY sƚ>s"r#l6+$YJ%K?r,-a("D?@9%U1IX$ˉJOZܯj5֩}x'g\ϟ͛0 Ct^rN2$LS% krł_nC>eL8!j]f^ƍFLMM b1++@T}8CP$?r,KRD",˂dgD0ۊRf1 Ctoo2 @N3Q*d2LMMQ(Bd/3[RN*8LO68brLOOPB!Z[[8])R6}\.'dP>|i{$I8WOB?88HOOEqJAkk+MMML&EZ,E&''d2EXWY 08`\t}FGdl(J̝JZjk[PRh%y|>tv+ݮ|J$Z+O_|;+sɲL,Vnlb լfM~r<˻sF @HVE'5cr8$"N^e28@Ⱦr_p(qKRdY&˥7`(/tQfl+W]֢SR$Iy^l 61 Z@_/-UyyOyRr ,EU QT"-UΜy]Zo5+۷(z?w ]u/n[Opǜo7+nta6n! VQا} Va:ቢ"B/rA Wl;/.R8ˊʯkeGTj$?}pnt>쓂>Ȳ qd|Ui'#*ȾTՑݒ]A$Mţ^X, #f?!#Iͽ'S߅p5/a4!F!Z#Hm &~<ϳQo`/GirŌ:QJ9Vt ጝQ_*} ĝ\JKO+?꩓"$SҙLYT6gJs3!M(V=U)$~V}pp&lj܍LvJ*J1f2JTWVf**S<[YTV*2U*Ra.YY A23K3U\#VYf\ n)ش}u[dTԞ3 s1B(x.@{0PB.IQTjs"`n>_ QHpT1Š54YPX(8R.~GF4l-iXRƍ%e2X>-r1/Q3*iNb-*ي |*+UՕUd]Qz?]2 /$ IErfTGK}tq^}o[%cp'd\Ȇo5p3 /C1OUr%6cS&-/^ ]DU;^3iz|,,ER܋wz o鰞'3ds3EHfT e?)լZ>BB$2䣟bQ"j< C9󠩿/n; í>--:DPXCo#Fea_[Lgxl8T-Q elsi;d Di]=juـ&6byH l%a yj0< BUOR*շ33ns׿6mm"4oVt=w~7^ Fkסc]z8N9?>@Z ,lp 6dvMyx #t>,ܔiDu>EAy%tϮ/=umMcr===a@!$]ն|Ot{zX߁/tu6D%J9w`I+Vm8Yw}-UKHa揔LCp<m)U01 .\@e8kmS5FGUA|,v#r-Trߋos Q!iӜ5y{ \0=9Y~V3)jm0C #"I(I")Md8O=QI]pE6 )IZ̊H'ZzʟNP_+|׿ظc#]Uiϖ%=_bݦmNoNK?]ͻ`PKjYLOGj8dҘDd|r$~dkj^M}K3G=;tlPZ\s$%rWr,\˩NZ!/sr˶\ K̂/Q{cFKkruVٟ˥ql.E\m֡ђɢɵvE/HҰoj/Xm |] qeȑQUy͖ k_݂M\1 B3ÁȚ"{Wē-wK4niD 4BQD5ЬV?~E=qZzS#id7wlZ*ĺxdiKΌ$Sۺ/tŔ19=Y_߶jG$UU۶Wl^.>g[y9|{?**`9;ܰ><^^|*@mU7m*Mߍ[اT?Ftk ,p3܅8:H!7VgƊMGC)+H3z{iO/C[b.H}K3j`%59ƴPoEƘ:+)IS ѾW Z0qW,H-o?Z1ҟ}a%>M%ZT5AjbZ|If[!Ă<..)Aq$+10o}R֤sӨENkȅiibN]2HȩT|%'ThBtJ $Ʈv^wZoIG-d?Vþ{:5>N]to\7S] l$O?ʭ趚EVR'WoYݸ"nIŧwY~ɄK #}NIEN$̹!׶g0z!/'>*.Ye0eNVOɁ?<!puz =Xl'>z˶%äl/ /5gJozēg,}h{fm@;_OMg7_>1֕5(0+흼*ˉQUֳp]$E7 Ww)?+&\5*k/oeԭ{k"p=WKV,kϰܱ[/}w- {tOosWl.e[J3#_+8Gm6Xy`/u5W,1)rJ3eWi,Br:yIp ׳g5 bxgCÂE7uXzCdcwnWxRJ?lњ- ? e7hteWuSnճj˫6/W /.SW5[?x뎸}eƬRE;E_& V;S"Zsٺ൛_᥯> %_3;4HC|+|b IW/=?ԛ7cЗFwgmc~ 0Y5=Zq%|nz'7M{oD4Q %כqDI(\;9R w%+, 05ՐC4P2Wdp~K!~k=C&3qR9T5WLn,FrXfh}Wt&e2 Kw{ l&Vq`"UPk#>+-4dx3̀3{kE#xP9q#cX/vnآ8;@3SV+WA ]m=w)_w_L1dQ}Zn7=E x ˾|dü^!Z$K^A>>9+?Ocqױ>ui]s慯M >>ﳧ>x SIo} °L!pۢ5 8Y1/Xҩ'M>>Yvمn#'*[#,Vm}1si^OiX^xMj[s ] =UdNI!N.m^ r䥿C[QD`8% 8i4v*G69l]c[ۊubl% OZD @r[XrJh"[%0ꗦӁ:fuZ$m$k9ʓDr?4FlGNԄOt{вh F㊢#/^,T-(Mo+g UDK&Y`/J^q9gJ8L5-z}P喉|o_ _nb%|Ğ(A,"Gy+xb[U ޭZ1۠}< YKFݘUS^+_ղw'9oj&o~vn{'US'NIέvW kI' [?8RUR_r+ݺ:u+$ߙ >5wɳλzk.gXgղ٩SuV#b/RO^r@ז%T==}UħKڦ OϋI;BM?\5.Q,k:+9>銕41QC|Cًiuʨ~sHB9w_bXH=Ps6/1 TN|p?RLVQʦ!uH (1EN(ʝ&Mi]ȳ\#qnqCn8﯊֎m^D42fcᲸF"X$,0N/pkkJ8g$JzTSiԃ$ $ݢY-l^ZPZ.mn"]>f-R͚J.Rmv'ͮ'P9UZ?, K^"UҢK S{qQ,l=ˇTpk_H:=O9Kn]ө/2ŝ-~lY +k,~ԕ8z`08ppRigAVoYz+_~]sD&z˿FK?c$m+B::G1Tv뇧x_n[zxd`)mP+ޅ(r =}K{؟_iTC|3%%Z(YFzi_λvbi+кG`%|._.nb^dʹC}uVj1WB+E#.p_̝p!o`+qe( A[y*ASZMRP!JYr_*uY䣓ZQEb45X~ Gw|~ty/N*,,ҟ+[EEiӾKO#۶,bZbAnQIAIIQVE䧸MSغuQi6ݺ )h>Esbk;.BC ۴J)H޶MA&uۂ6L>| BoIv-[{|M(Tto^믻tҮ]N:}'q^z>35| OθtEW@u5[&W @uEuw2Xyf_UL"k%ųz!tUڝ˸C-p|쉭xă׹&\΁&kS/J>.}OL}|xZ~;O.2Ի!:Kjዩ}vF$?6͕fu9h=IϘa SEӨ6V i?mqI%;npɦT#'SXRFIi,+!m&-]3MҭZhѼbYqI3K/J|Jf hLXl*nVTRڬM;mW6mKZ3e_ڵ-t6݂vKOOSOɲ(/mY;u4oӮmKotD,ܼy]wuoذx۾}w߽uַ̿bc Ք$]^tS?ySp:uw3lj@,z7Pd"K[.{q ?U,DEDs{q?| +1!5ٕ\l)WH>9O} 7VMe-FTTZMZfq܄S?epqn}Sjj(d>YU(Y:W.6S^>=t{R7? y1dcP;榺_S&uP)w0P~~vg O5{ٶnBc HL\ib 1n;~$؍C^3t0,6I g~u93ha[AD'%8\q ma\A61.\F>$,zfo6z TLOk>1uTzU%C#^c:[維Z5oVx#ibm9, U둫$/#⢢fŠ=jSܺMIV[j]Ҧu6KX|@e Zi֪Uq$) m[ L&iӺ,m׮ewCbu =d}nF*ش/u'xsυV5z?:Uts1aȡW¾_1^/:e[~j4ix%hg?ˋw߻|3Mv˽>Bi=td9jksԪ K|?1tfWMhߟ)o^؜{jE{O}shوחz3y匭o3R}vGtY+^rŠ=vN?kc)In~v/+ OG{=ƤS+g^B%Q])=txvom:wjʥ|o }px'f !=bE9_ 3Ֆ*5R >anq\~N)όJkQO!nTЫ DW:b.cƆ [ӳLNt06IUs #,nvD*@W99ȭe>¹[ˑ Ӿy)3>teA"ru4:B ??^pd [W|"n[ di6Oو?[ʟ\d0EaާPE[V-ܲesp.llNb2* ^ iP3+[#MAOG}s *KDzzz4Whpw,Qx]vڳ|)YӳQ73Pw؉bfjg9f:}, =sCp eHwP@X"*<cBx.f);7K5f~23& pVf4tI:j]ZءeQ38K_v#t.7K,+qDTaYDʙeÈO?oq?Y6oE5lWFa̡@K?A268I]J,v=Lvv]~ùaPZL+#A)mɌߴLZ1[\K(O C݋P3cJ9v\D\edIw̿.B(-qOi͚11$N׈}Ħg'0z-/,, NLthIЊ7YR>`PS,8CoE+I{ i54-TTTI LWz\IYXCAc[pC?xGqA]%qEM=,bDFy=WU[ʇTBoR`.MNszPŴF1Ԣ \hsFI,YA@8Ҵ* bH=m KlX_ҳ,Q<Cb*8/+ب4nj!f -Z{[ 붏 -ugmFXOeRj|ٹH}|VVvs*ոZVsg^v54 -ț@֔= 6<o&&i)Ռ"h,n!q(؉=16g yyZK9Zoͧ8{/&4ۉP`cJA-DKs 4+aqRxfz$ [j~I! \ 1}BBBCpG@>垫>[֢bS*љ)Z\aШLd& tXK6Ұ-9ŠCoKzxnIxiR}>h,JDe ˇ- 5goT)I"C0I5ZНo/WyÿKf$ &][W;c$вimݹ) }Cbm3U2v\$ְsih?|;hp_7JЪU a_5`qdeU/!l";[ AJ 14 q=C/btb@}\fE*6a_7^PGVUO2=!\ѱ2QV01O6~j֐]3NIUVdmlXW.nVj֬cI;}9'oϤz U[RZRjuDßtQ0N8P>@N,pxM\T̈]7t9.[ VoM\ZC-Ij0̩I,SX 8 O ęΩa59zb7lP٥wqAڥ- =bel9OxjsŻ6?YX/,B8.Ow7TѣoyÓÈ.z}-Շ^2*uD>^*+0vphx} ?$j̆aYqay4"Fh>l=3Bsy+ya+ehuO3ޝbbWn\FM2p3Ѝ9{: ўwtp=Fspp%fк3nr ~zG`Y)w"E8G2eH ıJ=.N~}_S+ZUldCiA<є56 ݱmKV9xeX9 ngMzpfeudaV^瑙Lfė'˂.'8ͫ|uϏө̒=7 ڄQJO^a;tQfN8P>3kҁzp37|QYd!)Қ39 6h \Hnwi0`uE /QCC&>Xu\{7N3ͯAI-XpaG.\GliGd.Y)Nޟq1WrŁY}L0DR#y漻R=zMwo<)}~6gw}wzK\r_1ag$`:XF d@ޑcڳzwn ߝ8uޱs-WD 60|<+;dm8|mZw5<>S_9yPcqn:"1 MYX{ 8E{ZrY?\ҭb{NQZ7o\_[nqlu5h.zy0dY06C|Gxa; QP0GrA,Lb.{ظ{aFBjV`e+7Cp>@} !zN7a0*Î᚛A` e2U_.X&GUhJay)i"V@I R)p)qa|T{yC iܛo~~6d~f홂_?~1CeM`޽v_5Z=&»S<4?ۑ:0?A?y L}yMfu=*ldXrkܒu4-Sn|l(0}'Ph}tґB{6m=p;yQpe{BoiE'_73ሯm|ln^Cy%WlY j[[y4'f.8J=k=WgΧmg/Kp@XyCs._?w`L]zɇ>L^oga*/.Yȋ}٥OoP3٪!cžt9hd7oZ ߗ3NbЖyC)KAKK\tN8P>83k"n&wuxp $/lnt f& *'!e5JvZW[ҧzd1-֞/>{?G-7'kl]u͛j5[|z}rU#ܻSW6ߞ߅ҧ<3⺇.]:ImGژK4|LuؑS B>?1Ca Bž ]s>bԬ,bݻg7N8P>w?ԣ@{[LGZB9u4xe|쥠. L6UfnácpƽI#>[*GWpۃ(>A▊q~1;-oQbU|Iw;?s!' 'uʎ,ed>Q %DxVJNr4XT"YMzj FmV}RvΦ[5xUU۾鸱p-٩ :% aQ$gؑЅ1-fI@+@)B;?zNiI?Vq Y 3B=V=S)԰8eC?-Ramض>W/vTe遥 DHf tQ~N8P>@p4E|rf߬ \yoz)|E`Iߎ\ew"-X"T$P`(NP˔p\Rnh^; B p}@GoSfz܈TKͼסJm=Rg!ꅒ6=ZCcw ;JD!@<=vԉaYg"y gNظJ npjX찥"(#Uj%SH쫮`IX4;+ &4j-D= z>cL,P"B vP@jJ0F-8O0tG޴yD{eXNŸ0ỲFA/>Ѥg6{ۚ0?`$sOT_x/3;qpxRr۷}Ru3kDπLi_ ԤMގ_sLfU>df NNW~Pr,_XM<0VRNkk7[Ը%ȇXKј^z+VQ;|֧]_vl,55]q^?ӆ uv ۅr}N5]ӫVm C .)I" -:y&NNMq3J.--w}::ٵ緋.W:Yqӯi^mӹͦo쌕B I3zjHomɗB*Qpq|!ne3%rӞ_-/=]]͆ 7^,}틋aɤ7rcQÛXmE獲6)_` 6{t&by֬Y +ק㥗_ر#(`n߾}۾Hm_ZԬy`hY>u@k֬)//oUQ!̐>Q_Z'UH+֎kas^sxyE_"}a-[Vʬ\YbE5ݼ9ӿGM[F?d /^|_zJOۓק؜^fvf,[V%>~[ٱc׿nv֭[x7p7|5nב?1eǗʬ4|m޼yokΜ9M* .X4\ԶoZkղgPɫ8yVϪ\hV*]\iС[n٢{ νi&M!P$om]EA9V/-;j졔hZk% %kqdQiN=[.-[e~M_3t]W˗Kb^rٲ+u[-2ɇg]anj:|][j.ݫs>pЉ.{m&zWy}r+01p3X3 3|wy瓸k޼y@bÒ3\`ׯ{Vض;@WZp-iڵkq<oOm)CwywK}/믁T&@X.cF\mXMciVk9G rGrz:7<ޑMW/g_[uaE*UNWd-L´.[|Cʪvs7TXt[}8|Zqߏv=f;7nrd6|jٵKL6UQ\&5gyjT]z=IJ,\0v`=ױmy&|5+V}뗿/?~߼pތ^_8yo.>wX̵x%Úq6-Խ{uС1c&\{nݺu^k;N8x)u껏9C4/-()iVzﵕ]-r- 'v tYu'ۊXvQ-cێ`^Nlo>ܮ 򶒯7V޲ع=;4fz>[[>!ϴVgZmN/ߒZvZvYuPr/J{ܮeѶͩo֤Zڸ5RQa աS=?tg_ԟ2bIw!mPҖZj[RڼYIqҒlzw[+[K[ nX}Sً= w-iڥ{EK#줂xZPft2dȐA ڵ+81cg Cq}6o)߾y)P78]@C &psOc( unhVkhC7fdjU-=ƽ8zw,[ؽu}޲ق/Z^r,Ho̬z劊E!* W6{:,ʕ+W߲uWnf3Pr1'}7oCve'}%kź惇3b#gĈF:187~HC/w>r﯏ځ]l?vRk?]?++p7n5|†/@wbn6l𢢢Ν;4tTWgg}k٧{~-HDB9 -"L$64 :\,V(1 Dx#Kb;Ś}I9UMqF9/Ju캿^o|}-i֒gToRbSM/kV-|ŦV3=%#7}_ >X~m脂ſZ*>07=X-SRIUf,Jn;t'yYAYMK`Ԩ vW&\]K.C[o[Vm[f)ڽo;~PzgW=W۪[݆_W( V߉ª*üףOO`y7{Ν +p}7 n`Aݻ70ҥKaAφCRTX"S^Smޖn֬p.ӗhhy6&0?yI  +:]s`G? &:YLM "W//X#XKws}ol6"yAMUW~6r#ohcvٸr՛7o^rY&[Y$[]]|S3>Y_l}$\ KPr :4| o"`X؅;)S?_fꄛ):*,\|{(Y-ԩSNf{5N̞_.{f>3/_~ +͝:unnЪ kg%f۶mУ4TTf++ +>i"%`YPpM>^<a.P&G\W9(JfŸsԭs kqw[wȷoh"y#SUtu2 .m{b ʫRS2gqlL W3OʍeBfO+A^pKW.Z8Oآbn RȘiJSOn!z|B姲\34_om)hg*ʷΔPN:cӁ\Bq:񬉣NU(KcqЫtUs2o+/l׮Eyeqm" a81g͢E*.y ӡL B>]ai8Q579 1С!*Ѳyг <W41dv[s/-dElץ@J.阻fހ#{>Zw(7m>ظ 4noYӡb]7h-СjYipgtߣ,j^^\ZQܼ0%e *^}^QGnAx{tW?5ըڷkSPQZfj)ө-ו3U)гw_}Ʋ{\kGAmŝy?W{R׏w9JWQYm{AW7KYn{L>2Bn'6µXNQ-s4VIG槅}:vճ^Тy p'á%?a0Xx...`N2W_}%thxT*|? ee#hpZ6jyB N9`$M;r옶HRZSKҹ`In%G Ͷ%o94nC*((ir2|JQQ|:EI-bzvmnMؒ޴k*[pk?ۯs~̙e/>c+ߛ$5zA}]P0#:2%a3}-z^o/O/Z\NG+vkɔ5fkϮ-V_lXڶ,m@yこncV^ߞ @c0^^Ra>l5^>k6Zn뮻F%䓕y pS¤ӗ jӡe[q&-zm[nӹk}vѫÇ]w=mX}V$ .`W.VQQUQYj7-i߶9QkN", ~[51c{00{8NR~Wk-jҎ67yQjX3f#Vvdu"X!ZHU o"$u+3& 1n#kblVT.}yw7VڪbS,U(ֶ̺Vꖭ*Ǿi/ 2e `hСj Y 羳a5U~}Ό?/Ȋs-Z5ng݌ЯMήzwWw(3@rɷ#Srr<\/-^T7pdƹ ,H a5}g9ޕTlkら[.x٪bZjp=NbĖU{s<!{"Zdt`?)aQ0\ 2zz9x^}Dxd~T{ إ۸jQa:V0?}ы?ӡCǶm|}.g D9X&2m&܈IѷwO؍rXI M]:BK~qrzariҒ!agӞ9s{ 3ܾ Z5j@4h`;c:&vN3_E}|JK˥<ث,b8T3ƩT^-ܖS0GaaFϖ^w̉kW]Fۂʭ6kV)SYZXnݱNų{3Vxh>-ڄ#QR{sd2cg(M}w]vIvٵׂW_TT Bb~?,S #ѳo<5{Ov hλ3^yv}}o]{gX/@fx5hH_8UdPa 0%Xa#`u*bS`;(ʰo?СxvۭUV@W^"Akޣn-<NtyvIQF_@oRR@[sp) g~̳P.LlbO2Svׁ7||d ݫ_3;޿lHQr&Vs](wۂV%7 3:'*tP|6<_xiӇ?"<+ :ݧ39})J}wpy3RԖ vоN^^}T˹~8P={'´>.81YX{΂ J3o$}}e_ɬ8{ =zfJUjyL7oa:juJ9Ed5kѧO[/^$#<톁:Y ]oDȇ_?d lЀ7qRr{ƀ//l.L Z2Yz}jTզTfk>[R-Kfrc-s]K ~mx o^das|!,9ñbp&ܦMXx@U f f p 9裏 ֝|8a:=v?ZoV>z3KKonOW5j+Om۞VOaiK@V+7WW?(p3^2m֬Y=9۠@]XO'LKPqs= F ˃T0{uE $ /.eܞ"fRLE)::%Ͳ΅.lζQm[0}V`nӦCISYHʷzw[:ٹF_1q*AK`΅750 :+3@/F6옲4-,-[زlQ헬a#*,=I-W6gU=Ӏ_ ņ6wm}w~Vխ}uu/_`Xe ?b7eM az6!N|Yl&^)r 鳱Ģ:3d#’ʓغML%[ 쌗kSz޲pU}ӗI]wk𳺤>U&UG?MF q 0 #FuQG>a&ҾMɮlQڬݠ۪۸Ioז<2-ǻ6w+-:t;/kyֽ[7PϚ8#ÏHt6'2$]EtB|na' +GowNۺd%p!]5U2Z]MoK 7|2 4()j -^]ueyW/zel+nj8O{}H׌Y{tճǺUe6|n `ӆi0sAHm($Nw[|.5H6*I@wRuCvᖁan:ͷnW3z]  o7xcD :O ۶e oU3B*7fc_̟_~Bps=FL B`ct?I! k^;~Ӣ+zYNY{`6Z:q|f=W=Q;֭W^C+tq?ڭwo=f\W_޶'1@Ft_xS۶,=)n}oob^U8YO> ?0:?dؘ7x`GaCFoTwzcT#NPgp℗<ŝ]&J\`ڳ:djzv+ʷm.I+8k[wo5c pXb`֭ CB5huq]kdQe P-u&rnW"5W&$ǧd-ugQv!`861rsO ݯ˃{/K{_x{v9!$Ooڴ nohhPyk]DZ'L~jfa;*EB}t{㶙[4@$`a `x+;zo88l]>bxtAp0ä8p8CkXMI ׌:n,FX&m9¹җE0KB `VPmTai^1 _`]r~C9a6(r7i5C ,4WdC|B,tNo~UqeV 8c%W=ʸۢ~t@E80w/z P=q5'x"p6:3(FT0BE@qJ`.'xqzYp5L.3|2{ZvԗЛ.s6nt%BN"!4ZA]k70QvdlhrdXoKxXy>&! ޵c}Hz^_C Gmj]vjyp?JRBh\q.=/S#9!jMyw^䝌/y5gнz )Iin zsONgp;^))BZ ?z^J$Y{nEkzԐTB ƍg v81luz3`1sƵ?JYB}Evt6ODs,tLi۲y`Of/^WΦ%|йuEgОwkOgb7gjG@""Ԟ\VoW!Xq! N_K{͉X,HKOƊiJ"@H_İc_̸UֻCr.aˑhB Q%Y!;&#v= JBr6ԶJ<,QNܸm MP B v =mUyEz"aF`\*fs3w5n#՛2gjy;R.!@”H. UѱA0>,Ȫ)Xs>"=ApOp ۇK"Y|0NOhnS* !uo& ~zf3I~$1=o .=e7>M<7ݻ+-us'lA*nBh Чȥ)CtK" ȡlwDL9>zv-=GCҥ(.!@4*jH CHΚỮ|琄n-rg ;9g(Ҹzs, IU=Jz0T ;s7$8bx?Hj^Uz@Zwұ)A!$*TT/H[qDf0˸{8oSZr@ϗXEk;e}I\Zʰȏչkz7ϝm?/Kǁ_L.`2ȥmw}i[4=Rڽ} Z{tpXC!'9!7ȹ76\ ;˻Y_zԗ:Wp>;E$YX11[?b]!@4M ]Gε9t֞+,˗1G ~ECTQ`at/~(:NEQ B1!PYI'bZ:FgrZi!srx782ͺXe\Co~Qck-۝5"N_7[v 'OkLS<6ʪTXTaeRFZ:ͭ3`? ġv9)-hߨ^FQ n. !v$ !PYI\*8렡UՐpg|Й.c߽ףxat bK"b^:.d,0s$:ԯTmC;ϻzyeYB`CVDI%]xO:uʵ@Ԩk=gF] Qy3`[֌y 7㭥 Noo9Ni_@jە4s'$/ ق$ KO{b747*-pu\.eޘȕ~*36@|=;Q&U__gf\d6Uî>sò %+r5ȱB ۿwKa-+51qz;8a@DAD+Rq֞`ʸF,A]%gJ{rN1- ٨6C2wͱsOj}^?f=a㝷ϕ8(@R#r)ɧAhA}Qy}Ϟ6m:v'ӝS}{;tԙPMj'P9z<N(?u…l@IscK KMLX򙻇 x0&41U4X=0׻bx)b&qqX.I*F ]{fQGMf2˫:X/~O}1_ުyʝ>4Q_]n=7e" wiaIWioyހ{4@ȮZٽn%~?}J?%ȍ$gXx&h}TYC$>w3ot^k!TQ : s]3NrX#TAf ;[3׀a#L8+ݫw_։)tO~KSQ3]TGupaOϼ:GXS1r( t!\flI"h^=BC\Iܳx>GVw7k.0bQ ynj̞;cy0L|$Iޢ _-WO/s]-,۩}zA~|j eg +Xt3c@>e@`5IlYuS 62̼$ְM{^G)!@:Azko,d].(#O=]i,ɜDeU~L u2eٳ+.}@)[$D 9q.% ,sOrp f.+,l0W&7$-?mw$\buX{[g*k-O+9^*x@K./KIZ'Ei0&w79K ^ۺK˶dR .fWo`ޕr`%l5YSsz򘐓-\bK&Y ,B{N̡s1G0Ӗa]SG@'Z*k7S_{ZHzH6+{;tU(Aӳ^++(Pu@y3gU8owyz(7;0"v(K<,ݵq[j7swn/qx֞Kp빋7/<Ģs#I|)!ET 9w Y!|?,wt,rUgɌ>Y|T楳f,L <䠞!psג% vsV6It8m1;p3zo(ʱD:V9lkt:ʂn r}eApؚu֢(,YXt (_&mGL9kJT9M 8X`?; AaNȌ,SlvR" lJ7J8ZH׻mfCs\|I띔C W\(p菾߇*k៞5n?슻d7l葩'yM-7i2yKAOt=vuK(br%Y(R|zIRs'oCJwv\{öG]m_Ee1R7 )7BP)Z?5Wā(;i(CB ~:?KcWC(V ^Deb<"zWE!@@8%K#v8^T =;$pW,묔h{Qw`PV!@Mke׌k3"k,\hoNSqו[kSY{n݂N!Sy=$I9vT^<!ΉM-ڵa{Pʜ &@}; s{,ai$`&=P-pb̍Uj2dU B %F阱D0`\^6̹_֌GbNA {G=h,*!@@D JEZctp;O54PzmR-V5MX32'l:[UO&񿓫qv !@G }JIguy^yzy>Xۮz1`=pv6 [TUe蹢:l}6T-쭓t!@; \QڭM^U5oo2km8QļΕk;A h0gx-kTu[*Wxf|B 2`/hz7.v@y9pe2Qʼ>VqhXDжk8D , ,/MP쬉F8 Wƌ3qVEvcIbѐ%!Q [[; Oh(1LJb?b# Nk%;*w7R%k]uwU0 #W|m[+gg6M@zNs\Jo-%h4ifmRŽBk>cr>hF;tP T=Q%,>MLBkϠ&*.Ч%_8azAA'6UDrPg"ILTt{ G7GDNUIKsD"'-Y= {Հ{ygk` ,5At`bm*M ىU>~sM!f stIr7Lma h E;>۟&Duc73y2} >w]?*Ɍv3z.}sI"\?f= ɧMjd&iӦ^|u3% ƌ2wby[i?_}FU뗙w#|[yFV:1~6rYYeb8QvR#51^;WOߝSZzJv˝hrv`y[2.{R[vL_(In@w(3q铔C@Oĩ˲RFB:Lcd$cF ' =?/X &߷gNM +<9W: e2\Dc-ab%b 3/xqSt3&MtAӽz:B ^L2W_(#-zcuϦsH/ 6&bC%ئl.6,a#5cvjek֘%=cŀVc C]3ҽzXaʱ7vwg3@0 -.]@Mط74z2ڝ0O7C^MxKUGOH];dUA`f7*;y AYY8h̜j| }\84s qp%XƉ#3"w ӇahݎiZgc_P] Ċ,5m(6FD81%ѣgڳ0%@N['w GCJ1)0^kX%3KNeNW*UÔ5ʹXBqp铹ze+ eg2Qr>.;H͢x*HĆ`Ǿ~;KλwkqW6y"$K/qkm<`UbN B sON^}7L14eZ& ~bΗVKG/r^ ˎ(q', GU@1:f#7xW+ @!pPF} B5߈X$+$f,' d靑 @@G :zWh>kF3Ǚ]oaY+&.Ҹt0nEw" B .iag{!4g:lG%UBn Ze9{̊qbϧY(M▻ AH B`!# ػe̓)lqopUaƲc9bړ[cD,O2zΦv%5 0|ׯ_f{ jS `1\ c6`,Xmc[ȍK P!Ɲ rgw0&ˌS&CTMC1b|^Cڳd+!@ /Yػ1EhdpMbG)3A( !@@"}NAn\c m|<˻=ZؿL?QiPSDG~"_k4B 9N<Ao:qSsy U3V3&E %CI1 B B xWKۜ*v>P҃N֖=FY%3:j2zdm\rB u$*pQpz&C7jS>H8j4:{ص圈[$Axcuhi*mx(,<{o}j@*o}=*;PW/uG1P찄n8r'vU܏/WfvjfxWFW2%BhRʵietĉOAف]"\>g~WZ9 Y(@%r|XY߄ <0@>8,Ycf'r>ee G9\ Ys3T', &^EǮyDS|\U@/.:ig7) !P';&S#9lD{Q>oj O`JORNa֏70ƍMl6Ghe_mRqWɁEI\cOoD  `_^{cms=ٿW@/.nL vxx ]@=C@3aެ,ߚl[>Gx@F ~ :cZErz #Cі!sQ ]׬R>e,QXcU&j@^6Pٳw_@%%;_= pUfZtx]7h$>#)5^/~xRxxDnPt 17 zV^g\ `{qfS;Jr9` ^R-bAgz{h>?MlЁ}:XE}鷰6`}7`l)t: h&2ciugkҹ>^PtF\z0?pEJ3<%y`p9s|JoHd4='0eXN4۩`jT?8߽OPz:w,{.FOH]{lPpC5,D\(m!GfF8!v4oe6>؁A=7ـPfS[ܖ/5;~U}puuzzc2"ZG Uϵ!=P@۫bGkz݄ڐ) }ťS*7r6uIƪ*u |cUIc(N\;AG3c; TB _8]6$N|c_ǡS@&m23fﰫVy?rzX琡C͡!<d=PnНyp)mTuQB!`봶ީհ˲(>3]KEХъK4֍DT˽yUC\e;WzfOjŒ&!pاXHZ )%}ۀ(Aµ ދS"SN<+؎r$@&7e Vh!ayaIڡg1]<]4hGhSσ²y~uLs.H.'fq\ϑc01xJWTB ` }aoM%=+Z0qByNl%j^Wq3޳aDQ=fR!uiJn,ʮ} |L]D ye<<aME!:D"Pizm0q9YoL^N$%!kzbnF U@=a\` z\oe &gyIj^6T-@8˅J/Ps-KυR@D ȦX5 ҍ^a.!*žh:#{AZLf M\/{j 5:, e7uê)#yJ+̫~| Ӵܻ_9FPbBw op[b;UI8mi,: ,[cMH Xő{YI]79ٌA='U6w@>\$cVh1tX=F>1_3W`Nkj AՊۗE".'%o=>@̸-\;Y?bi$7ag0~.)K,2| _tA ]Mrk3^,-;Vhǒ,(t%#EhܯھxuС=XZmEp⎸w|l|y2/Y7 VB\ __Iz;}8\KPIB 6(]6z2a,3=> qvhޗuHC~0kw: ]Z\qŕ{!?{7NzM}</;q !@@#p-w}[d0żpiqGV-K*0 OioFSbT#+1s|7||n=`(iu))B 3GNV,a; bK{<δpy4ˉNa6qcCLO !@@}@ rp sF@Drkv#װ !@X[PMZ{!o2^3VUb /qKط͂5~ )> iϱ]"!@1cv؁^ľ%5YcGn/qmsq<Ǜqd@&$nc9:nO!@`,gPOq*w 1ߠeHueެubw[ qZnʷVV0bhzPB v a*NhYV['ҥ<mTm61 2j1J+WqCZ-KZD3:#eL!]H炕W[EKZ}yDaF B YcaFۜ۶*[F F'|i7܃LhsYVg* > 6;u,`cI@+S B qǒ4Dmg]+`}I0C~:$!Ni$9*:7S|pJvp* ?DU]K3=өaSBĝvm+%aQ5>CԭPJCU%A+mޜp℄<3=!@;cF,é4ft[$-5Cd .E''Xplc؅e;`$\V zٝ'B#6T)hj5be=+ڥy0Ӳz0R5 sBFǕ*P#6s,B v: L۲67[S ZExq4`bԧ5KJ8"/0#Y0u`a[yJ*!@Fq ӷ…ʴ!sRE;pi7N+f=SA!,9o|crވO"3b1I\mruجW;6 z青 @@GYo OoS>1%ЖR,[+0{9 'znO@@@@ ʼ+" Ӛӱc M;~Fe2of_+a-7V-|GC U3E;7B09ȑb!!?z`B+=*p -\}o`jE-!Є% Бa.O_jN!VH>𺏉8 +JۮU0:PҞ#V;̫N2ߋ]̼j ƚ_HF)ə}ۀ(AuDcꝩrR*@ 3-Ao9i {#Zv=y$lZۗm6ٶ63p…s s7~ptՋ],7WЭ15ո6iFs9~B>H־^\6W&sA/Ls,)\UhA%'<a9GFwLP,EÖ.LxG5tN͎x# T'lN#8 !=}1}Py%~60g,qmØ_UqOs93k]nS$,35ԗI+4 /<9 L/9cxX}FJ7L3G X[@cIzV5lLfۜ&]Caۜj*,YFlpgn]n'ulY1SX(92 8M˽Y턺S!00\Wo;a\nAfud2feE"f=D֎j &ZVF缛'ѦG k@ssResÅF:@?VkϩOp׌ŕS*U@A ^u K: $z]8 }Ì1`f:W 6Y:3pʧF # c \/]6EyfH!@4Rl t0M[8dۜ-%,*2U<E\cVFpОV,,y^0]PoĶg'lu !b4(n1Jq#`% #+1+)]o?gMkeI!xgvj|}窍Hڶunb>lM:U#GU[oo4'z^9=`}?7Z.۪ܥW^ټg/I ?瞜|e7B5#BnwܾYI~^;œG$HO55 QHbuZ+)U d?x _.ߪo vC̅ huҢ$ B#%cmy$% <8ҤMRtZtXj&IuEj98 U{&ZZ77O5$B!kX~. cg ~O1x$~veC!@!8;2'{[2#  <۶Imށ}"BP䮿bmxlʒѐ8F6{wvz*.E1r֘mݎrמLM !@N@@n%i.m5~a ?:ʤ3V; 3;ZyiҵL2"pl#ƣL"= mA:Nh(ʒ Bi"}49pb*9@\Md8a 4 5!@;t2eʕFiP#rph-c8׶زri4ϑ-UyKF.c0+Q̎%yf|cWAzm&Lv,Nm=ʜ B"q,sTe^B yK ,[RBOS..J+gmo/>P~, =N|P*7!@SÞkC֢8̍1x41;IIH6 ^mכ+K2ltnd"s6+G1+wm7 S$mؘA!@!}5c+kfO<97[{5kv´u:`f7B16ܑg.ØR!@!`V7lŠ8 sz N[ǎf1۟pE"wG3!R,"OppsCФEI!@`'/'ٸ}v':y |:th%cbrah҃$CS9YV _G%JB3=!@;%(v2N[he>`DݲL" vS&iҘ|rt 5;į+4Hbֳۢg>ӻ) [A3ӌ%SmI֑cteCf%XZof ^Yx-.5³֞usz&FnZՖ zn5QDl\)ܥ:c3&'L+[(+. k qRn5 4Y!ФzDVDȵgҦ^R Bh]\U"35ӱ.knd&qBڜ/BM1b rhp!Q/7KS31rTzB 4tlܧ%S+]5^]Șż;Kaʶ5MpN k O@@ Hl逿]Qk{*YS Qf F֋֮*gN`ʘ\A.aEkύgS%BhX2;K^4& :6js+6*fhm3L`WFCI6clLⶺ4 sCT~B =zY4ҐAbNӞܺTvB 0nkfõo'1^|Q,sh. 4Ѥo6x-C=t wS BhXkyB ڝ,Y ]"VurFȔm,Zۧ({5 b5<>k~{Z3n/d0:vn*7!@  飘%fuY8ˤqN,QcZecyY^loZ{nݙN!hpňMREBhhfe\5lƺ^BvUP5rak7//V+1h\zIt! =id, ډvTH_YS&2J]ƄPf^5L'n M@Ftaz j,\a0æ5 Јk ψb"n2n7L'Bq hzrg1{c*r7SεW (pNh`bh{ $`mlDl(q45Ղ Bh x# F6Y=Z2c Y-ZfǟK>&.xΪ9۶ks1DύSBhMdsEGX^]LFig'&W4P"sTDB ?K5֌4Os#<=Ht_L9K p5l=7>O5$B l;vLr1bGY1{Ys`CP@S̬Pz*ԤA 3r)Q \O X m]W\#`{|,Eq;D B j]bW;׃}j4kv(8VcWryxdC V۽!@$CR`wzZmy[㚙/(]Yb۔P3XMn'L B3ȸ]gВ`B @fjGT"*kWeW}+I@D?JO/&zΡPTB B@3bPtg >6};p'Wl9?lzmb B O-B;^v4F,]A VUR[R7V<%#B ȸY϶Ց|&1Y%˨~Fطɸ]Di B :A H]'@PB Dط(vf+TVyypl֑fjM\;X#A'A BCoƌ%olʌ:B͝“$W8!@B BgfgQHY& [Yivs, =F!@D!(C-Fv,ۘrBJ֜ Hn@oC!@;U;,;!@!`#CuЭsyԶ9Cq B 7NpojV8[bLl4疳[3FR))!@E@rCb cZs8O 3ejK!@A H ^#os0Oq1B,2OlmL !@!PēL\-B B e qZTGA5Z&_hFR_ F !@@=F bXZ# 5 gd<.h֧;S ;q!T -~-pI'/4Wy(=yN-w_9\$B D'd_4`HnEe~&9fT!@@"@\hB @aXwj0Y%_T S0L B 6=F\ 2ܜ)K/)95<6E B[Eb秦\ȯvZHq,붧tB ZF slY<*SKb,DϵmH!@!PsЙ۠ӡa5G$!@4N];$%C!Pĭօ]*x8=ө$uI8!@jﰚ1$'teͼTkfu2_L !@FOv ao^E:'3)u$ BHK+I!BSYqn%!@:?$Huecl'!@!)L B> tMHTesVM!@IpxBO"!@ JhM6AEȸغՇ @=\9k xC!ppހ,">rgۙ!@#K.RFeUt!@@:H{XI(!@@}E!𫴀s b"ŢF\Rvr!@9RL^щ[PQDB z@nd%-#N*&PEN\O Bh̚&H-}FNZ=};)t BH@>Ҟ KqB vYPQMeKW=G!@|`ȅr s#m3w9֣!@ xK>ؖ+`'/(*8Ʈp{9nPJC!e\H4 "ӞX Z4U B :۫$j,D0Q$B @2nL,V *7Ŏ$}[\X,LUb(gB (bĚzXK*-Z;r>7$'z43=!@ΘB)Xs^"vcaakY=cV9!@ud<8s•("Z]Fm#m+[03yt 봗pB B A!@&L j&uYWنX ³l̉tvmv4E!PEhV=A2MZ= [匪X xbLB UN21[Mz-`fU*E~^F*EmzFB  GYZ= ьqUBImٖN^\0n⦄ATQP6_ RB @b֞لVZ Sy BhTD٥mx* +R1,ԺMsT B B HAwlX TWCr B  S RQV*wVlV PwY`|#"Vq-i5L/T!ZboMϱX4V!@40ft.'~IFõ"E͎*Y=<*N Qo兘_U*;;s,Z YùWҳ}TikV*.!@@#Bs%v҇lKp.)[Ġ)/B @5=.6Mk͎xxS+bƫhs9,aO he97;T2{:BBCu!B BNp6Wfx0z6ucQGךz`* B+![A+V+?z&vv?N!DSaֈ H:0\jq~!:au_*-!@MD-*5ct/zgZ[\˥jv^N':/U"M rdF2l_ѩ!@[YhÈ fvDG( |pV}yi9b=J#B"K[~˳-Zֳr"A!@+kWIe~g1W\WFkϒqHQ^]m:b[{g=:D!@7U[r*|c:P )zKd&3X^9f, |=׷NI!B "XEV:Gl_m4] P[fj4f\1H6 䁼l7w>v3s| ʄ!@Cj.}itv:mT. u4u_H/8 4,w2l5uln=>JE"B CupY|?,*hZe-hܪmi35@==eg#ل!@#sʛ{P/$JY޹Ͽ(%!@@#BZGdҖ-^$>]*4[!";QsNb'ߕu@4h?=7MU!B#`ň0G D!f7RZcxZѿ1M ]%rG,O oB)وp_!8|,uPfTfJ24:7%)k5gz YLqT [0P*4sT B 45Z2kjN!άߒGK!ڌnIeW(8y`~߉t!hHGښ]j,R2 * !@@#E靊[F9h,cJ"(+'/[hluz1p륜ˤ&UUkij!@5N^{){qm94m1S+!QQZ>f_{s]D;H{NG B  y Qm"TWV4bVp-Fʃw( ~Y'Ƃۨ,tC!PAy3vײ`J)͖煼fQ ]YhӔ9sQi3{^LMxCu]IPpB B bk]0uy8V̮+?ꔒ B" SP[.gUA*+ɸ_CL^H9:`(-J RF9 آNt9!@5Lfba#>VY[p!sRl*dB@ B1Z(H,1A$֝EIO*Y2-LZ{fjI ٲ<Ug ٮ]&!@@2mڒ\3,ʚ35_FܬtkˁK)4q\.y] y )( l8,#=)9)ɀX!@@$V%~*f t)hKWҌf Q9[nr,XQr87CDqؙ!B v Ӿ`V}-.mAF1AE&nɵftudжm,Jm3XFr3g Y,ȃ/;i(7B 2)!vw`rC6ȘEsCdr kO՟ 6P|*)WeOƊtmOJ%4/&.tOQ>bk|9-!@4y6ۨоa%yP?\-mFD.pEo{MCǗNmOIy͊ akF|M  1Llܵ,ne{IXhO`T_㧭hmTjdf'[o++9zzV!@4J0p B0E۞_drNgfI\Z7V 3ncp b띫wHS%qgX2);sRBhHXj bb-u] qxYֻ(;e B,2h!U0|$?=7OG@>(ʝak0[ҥj2Ҷü,vWݻqq׸/= öZf7{wy+lDƟysONg'v~[R B 7^veldpٟ-8N z~Y8?mas::DfgY:觍qjͣ?ktF Q{u95=oP;R[7@%B/P`J7 44l9msRRI O[~Zę`4?X[\7s]l}ѕ,TB O{v0+Ҁ5:Jm)ʿHVu%G6=z6@N&lJ9s;{T6Wg4n@'Bd*wI4Gj3܁KS 7PNRS[ۨ|fyj6n˙W{Zuo^* !@@@]{.c) fĖEZNAgQM:Dg}Z:9**,_xF3$mV0vhs*%!@@CCf}UQaM[XthvqL!XHFV0FlvTe?KUdnhK@c@@*k?ŷ$,#"Y%4nOڎ$bkf IQ!: ԰q[xnOn k1t !@@"Xh=K2'mI|% ;U\R/|nj΍g3 [ >ٌ~MZFU!s-3H?ֵڦ$ B1 Xg34k3-__o?7l9fVط $\]6Iٳ>==KW^v'wPB 1\nn zu 14/bZwپ*WƚMel-/ b֑oA?OtW-X(c QDaA-_ :/X(..꩞ U uo֒gy^qJ(_g&BldT{4LiDmh +e @$! 4Mz*h.t bڜ*ˀmV?GGς7vVm7ۅ1$L2iWߴ|\I+B֤Mͻq<6vU o^MA2"硾PF>MZn-y qs}0zR:)P,q)VKG"x ` I1[_BM]TYQlC0ٌq:D֏'͂Y!nݝ*h ꃞYiuQ](iM}》YRlbUz@|fz|rx"Y]-T=kczCL+*3("{ߘn $ OY#̳iw0ATT5WqDH* ihk2cSˎ oLݬo?Xze slR֛'Y-_J ij ?!ȸ<s%+fy*k}lpqKX,yea*}Q* ˻ <D!2lj3MޣF +2. p܅@\@,S (uឦҊ`XGU}NtQ[0[l5J|OV0$X/1q*Xd%a/Ni#QqBDϩ;$("X\ ʉn8։Yw \Å gTKΘiE ;RGC|h >'kKWҨ֦uuXkQ~٪ Vq?^']ϲԸ$gZA!@ ++Ra wUVʚ:VF8< %(.J>B$o$AYB+rGG\gv_M+5!j-/Yd@3YME%8QA8!Wd"Bt{+_WD70i&#G^5eOJnMH D)uiRTZܻJA4+jy-u7O"Ӭ.(0 *x Rbc$`B B. %H !@!@7nc#Oޒ(!!@!@f9vP-HbB B I#B @衞'Blذ{˖-+zee!CƎv5YQ'9j.x מ=ɅRLB B!d9SO=h:ꨣ۷Hp}9fsyPB B# /׾&O\UU͎!@@cGܾ +vW-JB P3رu֚"~FF O!@@@Wr=K B @@[WMg6MR &е!@#fO?s .(,,[E)%=rR6B!@;@E~饗^ٳgG}UW]պu뚔]|c̯֫7Ao  B /}饗1O8gG:_nM7EKbt,]ǒp+Փ BpF N.:vk&̸_0-};W)>!@@G@='x?cA:xjsb!@@b7o׿v+ <]Yٷ"!@Mg}#9rdii)3N*}ޓ@-sHyEJB!$=z<`A]UJ`}:6|Y]zc)УX!@B4jѢY%5ў iB B)'wytPrg}v( !@!Дh֬W\)%;sڵmv,$쿽{0aL,"!@@>cIӉڻ{,'JC!@J &k ]C!@=%"B  %o"͹&!@@bkk_22{VS^~K,"!@@> zcOƪ %g|FPs-K #Bi}wBϧi2n,mb8穅—.H?]_OOj&"^f ?'"7sUH+&̨?^{ ɽ+9x1~Tm"GOU-ra"{QJ ~D#:  %͵{%.{UQ]|ͦ onCMwoбSǠDKR&ZJG(s g(nGpVӌ< 8C=(C&8WtAD'hޑl3Rpaz3dSLB橺c4ߢI<!e{ *NW5h'~C~ kPV=|o 3NXb4rr%P jF o_AHu=6ز5$=g3;_xC9U(c: ҹ'(9?m[/`ʤ.=\=h D<9 {y%Vt YzLj!~?fJ0ޡhAtYd`Տz,ٞT0xD_Wȯ{ !DTKTWS4YǩZLQɜ }OH-If-E}3Ϸ [kc<(PrMz Ctѝ"DDe"BdsLo@]v-(m^ZISvj ,9_[:L%82clIN+'穹 ?^Y8:XÎP'e C6. o[=ϸc=BJa{+[e`:\9&!PU%$jTH[凱~E R|"U5Q {EaиZRF|,?&E~MhQd`Ogaduӹ=ܦM=޻yפ='!yt|=[ӇҥK_}ݢ%gG@*}ss2Ǘs,+V2  ىk:‰o}-ہTwPi5j z%^0sݨ~RucD$zGD1oT '[ z7>?9| 6,^xƍ|U$Y!@!ܳgVlo$IENDB`filius-2.9.4+ds/src/info/img/p2p2.png000066400000000000000000001677571506574241500172520ustar00rootroot00000000000000PNG  IHDR sRGBIDATx^]U~[e XP{Fc5$ vM1DM+HQAŎ#o{?̝{}oaa7w7ssܜYfիW\!@!@l_>֭[o߾yM6y饗>dNӒ w~n)Uti9sx=pGRFTC噭 >aPR k'ڊ򧑡-G`h 5ek}!Em^Y®,PO7IBa+%+Q.FD3UrML;4jI130G%\U UTFO]3~sզ@x:/`D"/B6NS>x/gV.U=թD~w͕~ʪMos:sNVzu[om -^5SnfMk(Ɔ77 (/b?so H7 gB>-BLsRBͽA XŗB!)ʟSB\ZcZyqdCA"fdDR!BJrR,Zk8ْ/ ^_{ J&YH=-b,tJω5Ԅ?F!1 _UMn!ۂBr U4T04\Gr kA34풷pb;3$[9xhZ/GđS&pɅ*(Rfo:ڹz+< ?C+Sr9!CۢIbL&U𭮬LVT&*FCEk1c tV]hԣ|"`,UfWٕbO3ġ- ӶЩDH֥J*~hSAWeD]e @ eh]Pm>0f,UNL` J"T[t?I$o|SIVV*+/ 2) :D Y$ IErO%tn<3a$j߿ŗcNHo,z&>]vpv@ RODLٔ%X"e35+N6cS7ŠMwwYx$,UziYiY'7Y# ]*nmYN R$:OSO1#xC1 Wl n=u4rd[#SW _!h!jiv yr1RK+4Ttu/3C`gsN 1fz 2|y)p,=# .ť RU#R;أ3n?4Vv6nC]Kq[ՉK_ֆuT(luډ7\qW\:.iw< a)cN$·9}wnRlfGfXU{<ڌEk>(.}g'ޜ^{G_9>+(;%$ Ktю <e8 :$ATH0YwKI6 Xaɷ"D04Өfr\b}PYcR +:<"Eq˜Ĵlx*H+zQ=sxɄw"6P̋*egFQ#L:=;} %4)9+͊uL6l\cng ؤJT8j{*Gɷ[ѫ$iU8XpUďk&z6Tl1=K&or80z𫺬iq|__fJ7_ʃ@8&|Iu&ŅO3~ݧO>Gt>oKܚv鴯yj(Oμ.-f,^CƏ[Ip0$d(~@Xun=>2]MϐuV> 4*`1?e:;ЊkifVSfnfU,3c4ƂVYe n3g-b0!15k2,plVVzd ⺴Zu/C$[&kduDFrشsQi-˹o ӷ:[YɗfH!vyUFǢdw+OuM|[**(3@B##N/ l}SGZh}p)FGn-P+Ul-'ȧB@]\}FOGߴ{/hz*R?"̆_$ꆜ)s~&uc6g=8HsT%G"YI:TVVmVn}?˷n91 [ym+m[o+/ʪ  0T/KUnFm[\q|+6nM*7m,ߴP[ɿpQſG/V5VvypM@U_`e |ʊmRom]T~* X zeWВ( e|QV| Ira)r^ z fրyT5As=̪:7NE0Zʷ 3*|MNo=3PQ {1w]a4: :Ak̤G]/i9OUMJIDr^22S8810nƄ)v)0*Cu[ٞ`{Sl ,2 IVP$zT6$o5iIjO =WPܯ:Diٹ@=/H T'wV?@ƣ<&|IJ}PZX$fSeɅTjIࢥ>X'kNB%dV_ r >bͿl_Xe޶3h=a]SԩB*rXoIW·f#VʄS?]~ֽqU ?[jT;j2k1W%G]0$Ѯ/DW,p~fc"wsfEU|2D] h cIddM%^J ^!D&fj'^Iם&\wtGw@f -90Nr-@N9j㽌F/KRp22M&DI3 5x\!'?m4ң{6^:-<3Zu 0|*>O'vYbTY.YSNB4cIuPDxiX(wL#x}$O*bV/8X))QL/[|ݥV5F E* f9Xf'kS|5 P\̸i/„Eս[{l',ۘQ-s ՛צ6,O]\vљm9-\\1cTϋ^TRoy}4$/[/ܗFmRRqf|cnnpѯ:Yg5t/ns'rT>XٓR}=q7'=G_kE&m/+Q=.~%N(9a4O0+f\[K̓%'cξ9z#9e[N2cy|=C7l._WZՄO=C&Yc4TdJgXpHi| RN,#^1KF+#BGj\gA w~F<ͲzЂkgN0n;W՚pc!22QEY2>F8n&cؠffR>#[\yEubmuݺs:k7.]\l".SoO":O=RUItnz?~ebZbq ֘[ﮅp&.̝ڵzAg7z-6s09VD! GLm!W ;~AUX 9劕fpf۸`PɅuh8xJLGtmc_ZN=qӞUCT.8Q!EYg3G&$@?t珿GkB,|%R6wCDA\cn5[ 7ȗH/k]#($*` !XNeĤDу8tX`W4TV~۞f뗵iܕ/cڤVeH׋^bI޾k fŔڜdݚzJU|;a\xHiP79ѿ!Vi\zFY٘:~ȼAs,J rri=:Is_M$W; 6|qWF}IϟG=3+^Lȟ}Z<-.F-Pa1'ZNDMx<(TW+$@H \^4 bӆPnJXEMi4^)xNZ5b"n*Q ~YP&w` K1 %5^uTe!5!>OW3Tepµ-{zY-MW (1 /Bğ  eܦ}|OfxJ>N*YD~AnQa00QTS\׸QnqDqQNQqQq8YTjTIA;n;U]}ݝSk pLXXۨ8EbE^Q"vӸ00NvY75Myr%+R[76In\1SGJv_?_C?~ާe13?87W$~=-V{c՛͸+qѹN{ԕ΅ڼho8kơ]<s V񲧚~1D}~v+g=5h[ahJOMLж)!3jPYeVl~ys[mG <ֳj`|UwPU X5 !5B R4˚⬱nbp%hX`|8̪NKU@iuSL@IT#|hYLVU/HGByR,Ex{f@w(ˈ b9EM7)o4k߼)|6o<͛4mqroDhĖ7Y-pBZUm 1sx$o,ߴyn3&Jx-Z4 3 7Ν۶m-Zi>[Og,W^ׄf~2#^󩳮z0-]=;ӑs¤2PwÌ-n=ON|g?Wl⠎2f:I@@z[ފ}#e?IT#0X05orn..~գDMCdTR_*xt|:Sm{jD|Ń3@13w7 p =J]FRVaWd!di%0xETll ie,*+$n!)5"'#BdkxTv(iUTݱ[M4g9 -q3?ւ)kb!&k&6j#X+$$u5/Ee`g!Z p٤i3[Ҹ>#Fp]~5 ,? DHh@bAܗw2:zƘJZG ,Jt/mYXaڷBʗQ3b,o7&F7_9u.fBkӦ%MYޯ,G&;=o p87'uȏϑ' zҹ{m$#{-hom:/\9щ?7rڳEkϱOuXW&5_)Wk"sXbb=G${T;ugTOK?F~i7j\?#bgU\ #RSx3}W>hCCirˆ&!΅&!W7f2L'ī&FIWxoCJch%QfU̼$Ckͼ&l+.dLߐs45AZsE}>ȼ!4K ]jҡJ[@W}Vn=ci۾3*&k}^?ClNpU[o©,PJ6yM͈|*uZ3Z8b p F`:3/d& *5E0sQ3W- w4L@q2-ZNC`SyG)6҃]L4I3$`rP4ƒ$m]S(›i 2^NU.f |JFaK1E!["٬f ~M722gDrv'ڌ\ܤ / U館 xVI|zLZSz/yk.I:bV2$C -4?ۗccesL7,b#Xd, =n/pvT 4DL"cfc(q YT 4oi涁ૠ5>;et3D1+Uvnx0]x#>QEDӺyM"KI4/#*DP'܍HWLon11~9 2V7E€9u}W_'#azNc]Qul&jرr^&1H\;zVM1Z*egMQkv& 8#f :Ҹp$gⷱ .%?-u94"_{r`Q5#H|T>0"'ܻ'5YBe4( lOƸmt+Ə;2Q2*bsDJGG 2xf%։Q) 2-iIeEIM\$mGsÓ 8#4Hn6]yD1D"$XQbV7\9Z@`マޛIXm{*?=IV}ֹ3ԗ&-iCX6̰+FdW,[<ꐜKҲUTkk車ЍW*LN)VA-1scU!OxR!3,IZX_V Ӧ V%*вA >z!]@$ г9-ҽ ?:I'HNbCƘL8,Ikű%H6jq)c"o`2ٌc#,!x:YMDAד\>[H!F`lv5ɘ[HI.Nd l^qe6;@hheg >[ZT' ?5ڡFI[t{J TR_4h+ qPuI78&O< @El*ҍZNEo_*!!@!S#0Aw !3έ?۩L'BsϸFomD}F#@7c=r?zavMlH\BB 2C _)Wѱ'aFop}xb z%מ7wiU sqcP!G $M u 5T"(g[o\,z~Հ~AzNJrDub#5,H2  me6I.c͢g'Gw[_  nhŒWKNje*Ro-ylwUNzsӶJ4xtM8P>LM7ND?]OS9p}Os?;n۪77(;Qx ,;o쁎Փ??#7?gC@I/m^54:v<kqSijُS%qstPMTVnI_[_(?ѨQ|sgϮW-Wm+oҡJk@(]} Y"N*fĮ΁Wt/&_7fHՍkk 5{g=LfF|3BR{jmK?X*~mVY8w?|Tͧu˷cSy!C Ewu{>sD+n R ?kK=氫_78Rwtt&DQaޑСQd?x6 +1BmxA`u̼ I.n8R?3?nR F{)v.~eeV~#1b2 :WKN ՞hZ?w؁͑_`l/܅TV 9"&$y~u d dX`AUFLo.R_*ZqHd:(Td%tT "^{?O^Ag8O[z9xO߻coMOolm6}jiuyOcSQxVWE_$>6ke=F=9ny Yzǎ{ţzdU>ABHL!ĊTn{8qqa98‡%жlRnxyH v> t# p8EcC0|!쏿7#|pq8Ox${_f-M1`Z(^(a<H ıNZa34$^)8O;8Z㶖_{6-K7ܦ{)l(ӭ׺yW:~5ǎ,asҲnLt~dfVVWpJ &`UIaxr&aw6m?~5iC?Sq%n:ѷ!MÔpJb6:tM8P>g|_`Xo|QYI )Қ96h t&* r/^$E'T,&/Uxԍ !\,(?1;Ǭ,N""Svnձ xJ =] =;۱=WV'oqQZS_wT=Vؾb[-Olө`[nڰ6ojІ^̧nA!# ۝BLX-#O=fޅTBX+F~5/yPH l&4" v>!D3\ nj:ބ$;~DkniouLV.KY۞&GXtpQ4c+ ܤB HSFs=s]暋9",Zȓ;yq疻lW۶-۸]޶>Qǟr̀n?i0CF~Ӥ?\`IO<7F^}xDzD+0'^?seόrbg<3:EL)/Ѵ>]&߰Iע쓆YشpuHZsH t'MG]I pmByWlfW2rHzooauΣ8A7sQN\gHݧW{YNof3f/K4տk*Rwկ뀃ԧmXI?ѶC'7s~-9IA^8 겜ՉxEn]AUN}?4h{nߛwY'zah˼Lq;ob-gno] v>]B p끛4L\,$@JgIaHYҶ!ǁ|Z4R,fς3.ko,|"\qh׏qy7oYeMvr[6jՍ_~6~m/\<@{NRK&Ĺo9FɁaN 񝿸g^|PN; G%-v1`qaf@ o_.Z/Bp>@}`gށӎ홍LGZB/8m8xe|6*Pryd*QÎ݆CrS9`Npҟ/L1rHT牛IrICWР88ڳRmR\ ~{7UVnmUU%M 6oRtGۻD dcS;9icN }3g˓5!Nuk $"ȻN_ F5@}/&cgM" T*;/N~'gnm{INZ L<֏C\7wߕU[o!o߂vj߳'ֈ5@}0(bp`ϖoh`fjyR$ 7P,'pԧک -"I' ZX s#TT7ֺzx\H԰S'wXḱY{Nwtq"1ϚٯKUTTP$BZ=5@}1(k?~݇LZwRbQρ*D0,Z$4D.JQ-H& P)? } -٩ajէ=_}2GR!@WwkSbz܈JC֞oݷ-YN cx%g-k>j%"B \{缗̐ w2y.O/;祈q[WJ{^z>>ԿB  ZQ@XHr7f=Bi)}rWtj"9f=(!@!c;Mҩ9۴p eVٍO ⫩N\ݷ-~()l]I1ZUd;Eb;9M'Ygv^I.3>]D}&gbf͚:t-?yo;uO|k?4.}{[hEy/Z^;-|n> ;LTIGtJ'ݏn2P¨Τd&$jt}78J{1eq=͜A9J.**>VZu۽Ess~6o̮nTzzFΌ/OoXG6)ٴiڵklْ7҇)|.]Qg8'W]v2!67M/פ儁3}gVS~|0`1[̘QECquW/3&裩LbG!n&nΈ?s`֪*x_WjtgqqqmnzKY^rMmŅF@S>Xj[7WLh3s >av֜O-sw(@8k Q w{ԠFo9UF̝[u+^xA +@O?]rͪN?7ƷxcǶׯ&]ֻ7; JYf-XVKt׫޺ukPݲeVss~ض QAuqMpU RkW^]^ݵ4N@ ƯA5!86-vH*)ٜvCL~2Zk瞻eM z꼼.9ܒʕ+/6%{̿fx'z몲9߿ݬzq7mQڒi ,^SNo_6ֶYzΝ wkj+w]h`hFōS_|{.f͛77訚;3!*2c&F #6" XV{Mr!/%/O Zߚq-N=gߟ{ޛ.emneUtie9]^M(5+Z}Om}~N#Oi٢WZ٬^ڕoӦMg~G}⋆l},{p[oII!hϠ:"kuլ[~Sܭ\iӒoiP*t7 oMHدJvYVUQc9 ^4 CYϴzqS-<4=˸t.YKR{{|}A%˖Ubk.u-z`{ulaw1Îw]'[I>qȉW.*2͜zgV'o|գG}>yvʮ\OskaNbsAq؀{U/u.S1nn[~\wsIpQv?WXq@K5cxVAK3u.[j| lkiӦ]tg}?N83ٙp-.-,lt5f5.%$\U+^'lO [B<6;a+Om-!K9Kω߯ܲz[I{>uvn(*ٳ#t~ɃXS>/4nmas?Xv$^NP0ˢ.(ZX)wMjv[Qe=,|nWгW}jܤyaQqA‚FETnV-q͢,ز~ᶍe? ߵ/I=СcׅضmlxZPf=m۶_~}+7tv޿#Fw"7CmaM˷mZn:IB!POcq'ucmW5# -(kmk;75"YWqEzS^'{v^ش~u,i4ѲuA?Ṫ ?VX^1WRUmoD[/_nk1ĴgNg+6XQ a_oQ!{vYo?gC1 1ouzsI;d߹Gyv9ouHWt^q/r?=gÆ 7_ lر#xA y_wmеԶTw5/??\tq֌r#"9 -"L$64 :|7XP.=HG0&vKkZGOkjע6 YiU%ծ_j_~kΜ1>k6.XjczbP7_0? 6,_GX#w֕#( dŇlےDeҢdf: pm٪Ֆ kg_tN8M-}ygWTNU`ŪۿS-{l޻Gިw=ط8-Gw.=bG=Z]6JϰawΊoa>=``bЃv dXeilг!B}UmrEuwGNglښӨQ#\?K#VİfG1wV,W,u,c-<`G?r &:YLM{_pG9bd60"~bYekҵ|V}0jK3Ym6rp冁C'?м^#[ݰr͆U7o^rY2UY0Uqiwt7]r\BE9P%qѠClw ;_걓vO~pN/lƂZW-a[NqKʔV:u)5w1tn3:sݠ(,w)4Zef|h^xW^o`M6 Z5Xa+VlݺzpdEe2"U^ZBU'%Ma!Q&EUUm#XUɌ*[]~QXMמ Z ['rdU_ꙛ׶H&أwɃ4WW%ط:Qʩ3CAqE* WCO뉿TSL".R7pܬ;A#UJk+[RA1/ḭ# \~*%1IfiنYx,۹y1h99]g\?br}Zğw~rl+ =Ҙgsu_䗯/;WH[y-Z4.,رyT䜼Dna0 wXœ.g?Lރ@]ԽVNb msWTS7풢M;n7A?s:A~;0 | qnYcCCT8Y|yi/^ ,?m]_p1OQz \\i`f(J[vhXQ!Azf ta~G0ig6,#Zjf1pQ3t舩3O<5(}肻Kt>s-@w6t\2e, qu9lh—,>w,MzX%O9Mvϟ6)<651jԲw7i[QZfj)s7ݶyvqحc"ti ԩԻŃo߼D['ĕOi{0qο 楃wAۺ-YsثKI_gBK$7t[J\3J٤6Qx:t')i/dFӶϏn|/Zҹ>{߸18vϞ=Вzjpv,<?,9I&` OwUXRUCN{i Z5|TcX*npM>$ iwWƱqJKq@3<ɬ;XZ)N Sl߽ӣr+ [p#mMυn~]XYLخ-9˿.xAs6.kH.~Ě/(/F|;gǞ2nͪw`9I<#?,v9?/F襮;+1xr?cYvhۡ0b"Э7_~ZP"~v_/Lj-ٟ2hp=P䜣ˠ#tmE[ᛋ{?oѹS?ů_8;&[FM\ڽE}Rq"暍%쾻oMDܵ{}N&Lp@3zOt }KNM?e˪&yvkשs=;uFlx1ƠA`38=3{7p-048oÚ477x ip[bYU֊Kl۸ebx,`#Ezx&f!.1GHۧ-┖dh?mINX3f#ki/O|"QV1ʷdٺU4?iQ6?8(ZKknAEow<~b-jRdiuXRk돛g_pn-Vjʃ=q´BI)@7NY0aeP=wes:31s׌E/,{d#eWtޭ>+RGFƠF%gً˾ {lOBNsfM? <:Qa͝wOTgKٖF]D.jUFE%r=/)) 6잂T >2e `hСj Y }ɩ/Vśw/K]p׸m֞sta36!3=}^CK^xuKmnzeExibbGи&h5eOfXO9%\ ܡTnWw/k>uɝ?񧩪Zov?Ђ_LaMݳmo>牾{A=lJ)>0 {xوkܔo<*qUDH/g; '/CM[wыV.IfaKү?}ZjݲE_}k`Zv]zėL/{{Wi#5Ze07it7I',ΐ /;9g?FyE*v?4 -=s}g }jxUۧ:ie9Ͷ#|E=>Zw(KgTw!P½mwnhqPf^QU{mWsJu9ms *,[XרQ~rsdeQ^~N'p*,pdk7_Úw[uicM Fl݉kaV]屩}fffSKAJ$`^:uzY.Pu1tX*ٻa-ufOTK}O'OgX/7_{lXFm]/~*ЏV;tH$:nw?2AnT%'A~Àu6oѳ+sTmj rܽꍳe0Q☇^t6`ɮsOZ`'6ݔ{ihqWXĚم /-4<'O@|hW`+15[ 4dm~Fe?,.Ob7߾Gָnͺkަ#81O];Ó ѷLGws񃙿egH V]Rwy=3Ya Ӊ!; \ȬYWרqnmD%+Y[ݺ;%՛eӘ;y#t!f9&1 qy99Ji'18k77в$o :֊d#YtLQ04Հ=xd܌nP9nݭk8s6OË@ 3, $$pˆ!Tt3jEً~^N5nDJ1ݾ4FU[*`̝jIkAjk?m@N'W]qۄ>x=Cg)c%'M.׍˄RJ{󛮝߯Z:cGU99spCh|aEgHYWYvW_|ظ1li٦Y=!n022JK92/ vB|mKn)YHٱڳ2xED=uNbQ[ 5+{s_qIط٩bu挒TϘfsキͦ]rˤoun~ܰngo>yX(-(Es,90狕s\;?t3/QҌfvWk9ϽocykSW?$JZ/ں-< ߼N_9û `hPPi|}ʴYf>ghtpnkԍYQp/OVDv rAWWOIgǃ1vf><%&k)*Jq{{^1Fu;tYYog%)8Hzeѱm.(Hk]d-]o750jiOOX3,Xx#xLYY-(*[PRY٢WtZoeW[Y|ReMϭjvnuy]Gsqwxxasf6xmQ ]^tW禯r^M5ob^ق/}ig|iгA;>ȕo50JZO@/Pg~kQ>V1j䢿^+t Ryܝ\L1ȓ3bJyw%0{|'98Ln;n[ ?k;x'昚yʒ-9ܞ}MF^zԗX 1?>ꨣ~!8 J(Gfw(,i\ԨE-[ܰQ𖋥k ~n_sqmaÕpŽnR7j?#ujꙍ BvD;9 1jӝR:fGz}@³%r1 +me϶߮̉+;oӦزxQzl¡EUzv>?W:Qd{|"SТze[R|Ks7P5`M/Y ,ޝ:viʲ7i4Nk]Q"δgqcw}ۆ]cm}?9+]Tp+٧K/<^xIkjc7wUwOE1wP}7+ ļa7mX: /S7uyep'$GkУuNs۠!6F,էr6,\gV5/Gok`|Jy74#w?֖eu =- :O~׃jR߱=?wp;N>7·C t_xS,y{˫mݔ^С+ZmUyU?쳰 | {f<2vo۷/MGp' ܾzcT#Nʤb /yJwv(_s[ gYch$ L|@u3f=2p>e uܹ嗷yv*Bbԟq`sajMf3X^uSϚT}TuuUņYw}7w0x;.6ۃ{ ZB^"ԕbjCTmu KK4jn47J#AkiyJK(4~b[Uls*!S#:4(H 6ӆwWj{l赾VŒE?/.g8>7 B;5T]m൏q.j%GчHj 7L09\-ؖvoeUӆ%dPFp}MV|0l =z@480-Uơk(Z# {f\Q٥8)DaJ֚u&!5YAcR {e`kx$\27;z(,<æ58A#G Dl"ap+󆲸hY˳ :T3$KoΦ)Uz61'Y!Uks708|arIc}Ǟr)~' ΰ tVKAD@qŊNWG y ||'/gOC+vܮvhG@+3DS% B 6:<Λ<^u61>*]Xy?~XgJyM;RNϢa u >Ett~a9* <2xZ;'^cEDy)z0=[I!s"B"/>;)@ϫcP]oy|ux]¦ab@͹ۗ3IYgoKYlE!@D#ݦLכ7f̉c&Z9mVY}ϊzsqg]Ez}p-!@5C`|HԞA-J1ŊH(;U*e#,e 'c៤eY7%1\i/b1=Xl!MB #`TIϞ#y  PEizBr6gيx<1E-‡԰Y" !@ Iq-ct8咡m LLm6"8wX&P%1}0:-<7GI@B LٍH5FEY5|]_()V:- ]/3z)L9wI$2\2fA!=ڿug3ks>)[)]`Q]]Ea?>DP zU<)QyEw: GL+L$cXyAӎϗ;鏾 ?޾bCѶXNϛxȱ]F%zmW_\.*!+#jٲ%ŕ'_+W;'4=JNB9 ݾbKCx=+whWWm(7sz6a`~v!P rw/n]t1zhrh*9E:=>hqְ[8pzVqmpK@ؕ[簍T2<ՅF07cĐ ktM;Bdw>Yܶ:dܮJaE)hܾ_ď\w1OuQ%3v<'$@.gOdM0ɬ8(rh4ĝ8B5c`O/Ba$t(K_Fįș],Ah+,l9ٗb)__T[vN0:Iϒ$ P)*!@u@eU2?r̴ :*A˥gfaL'ߙZ3>C:re(usV2\2l%Yo N`4QYw%:Jg=3VD5K3V(!PN2j+fx;fFu| ,mspґub&9jʲZ M<)`t֯$QbCҝtTU/"X$ LVT͸2لy6XT ˜1W}qw%[Vjcv.h!qQXR횈Mb4 G\ ӧO/(Æƹ,WK9~VO'DdS'TA$āK 6h)]2E=.iznnn#}`]"VOG?H9 we& T7>{UwYbg>^o *2ef}JILTV5dz<ޔFT9vA_wǾ] fTE?n2v9]fҎ7VHU7FneT-|«7ڡx ";}d.VG,N;:df-҆-(<$i<xJfЩa#uڝvO$0-X *q S$^(WKzB+ߥ8mLMĨ]tRЇM;{]O6}r[EMw8A{зMq/d,pWJ v(Yj-<28% y9 /Z7KYegЁP#lG =>&2 ^ЍEڣϷei/^[@`CXn\z8WLA}dnCF=zc N(!#)1M#ts6nl 37YsJUCo9Pߌ8vΝأb.x{DAvq4h3g1{o0&$ȴ̺2J("+ tk LzDsbul{3^ uLA ``AH`{$wP0wP6LЀoǏ^mQt,YVmPs2o~6;;b,]'v^1#R=rt v+#ى )݀3" Wxwm@|A͍"rus~m_;/zzAN-V,"-}bԐӎ9BLeE71ubC3WY(SXeJ:cn0-X,ܹK@Hҳ[M9;I *sSZ5hk8Sagg'd?3墴[h0Yr&|8fYW.n]` g@YngWͣbFN`>>g?)Tnf>[C &_#;'n;ob2qIs|hJYp$='_|C|83 tʐ#"XFϢ̷]4,ݺth,Z0m`ure(ٙ-Ff`^-!y_r xv3UΔ*v=t$bId1CPFIt4YZ~ߩ`V>bեo8kr9wƬvctQ=hװ4?A-C|~Kxk Zΰh (AuIG$\/YCSώRu̻dbyX29Yp]Nr=A"_g߿7k ^Xy1-:|.XzsAbC,J`;сX~F˰Po X|SNj^3gdqE,YtߙZAi;,9} 9_HYQ7+]:>|fB- U^Sob 1GuWeeeQ2~\#<'kI.Q ~Vۊ|[v%;L&FuN-2{ƅT75qYɘ^I!Wb3 ⻲{.bfZ>~%FFMS;w9ћO3 %M#u%LZ(od׋U,:mwUk*նy}g rbQv Fꛦ5n< B@FJ(~]Dδ uQ,[a!@_o`zsڳ m2֡^c12٩/|Ϟ΂q۲a= $j4.<.ћwX}(cB!ceJ 92zN~ɰmh5*:pJ僦"f'"Y Uk*%pM!@vB-ر_NϙYW:j5:T4 52CzÅ~)| lNzӍiݶec׿o(K4zޗ{V,Jܩ*l@@ :jh읹 蹪KT't_/9b+?iJH)V35!]\^bl]̤rsX`I͙?*Snj)>`0rv3p3%Μ:3;Ϲ>M>Z0͜ (w>|H\UFn`xG)v)GmfdM8ۮcz2 5>w>lh/} 9N#] QË36yoTk{Qľam}oj#×uedwrHYq3=ei'lεa@E*fkA,&e\{ Zd<;:J Jȑc'yp&Ynx7N,XHw&'R`XmjQ TkQ~^Zta6mbdžc7FHswr_ggVSceGWj͊#vS*v;,\;zT"g =+E0wVi&ʪ^"7ʈęqk'C` eT/a7b& af_uDtĚvUزɳT!"}ؚ Gȱ OT;R`XZL bY"e4~󽻰)tJѕ+xsDJej3rVk͵Xܲ]8C`N ~8`fۏ%gZPy! `t R7인e|h gf$lgAFeXQ[t;%<3?~1@4"tnK`rqAˑD P̻:,y#k% ړG5;[64{V^w[2-֭=}[\U~_,߽cFP^Ԡ E@;CkPCh85^`Ä2ܺ9:^)IO47;BxTr-zfZ3z=A!yЇ^:˨n.lL[Yk =+.=ώn)ʟ B!!vΩvY o,挝qضN3:- ۺט+`p kH݁J!PQ,b8(?8nb5g Rl ϗtX{gUulQo6B_|vҊmb䫮m[n|!d>㥃 hv:z:$q~]Z\rɥ:Yv|kϛ WGn5`L2M4=h!@!p=F.ۖ*? eaPPy֘9C=zpZ"!@N煒/_ Nwc'2r% =q kEdP F:1\{D!@iVE1{G,vcZu2c1s)bz%X 3s S&I.ę\%HIb B 2F KI{FO8YwIl0Ƹ-Xc~KJ@!@dVL30bp~Hɒ}=gMR&{mF1V:4#d:$D B DG@wӭXC Kfꈣ:Bݵ[%j8g:@D!@^|ljPKvy98a:YVl-kϦ"b?~rj5+'i.eIfA9H8\Yr'@Mdv\:+tgE ! ʾ=ldQՂ?;ĄۥF= TU~kwOR^wLzٚƩ{eʶ\as+J/} E|,v'|;-ՙ^> Hbv߳!r+wi(-1',Y|_/S3Ax$>rd+Jg_xU :i%f80<:31Q0˥mqw$+ݷ }A} iv/oMIH&meؖݍΖ)ґhPG0g`iI󣱑yZVe;GfeF]1Ơl 62ϔvIGw6q6qҲg3cPt}I2͂/k>҈l;/\ l)F2fWJXP, Jk ~AEQ'\m?c<ڄ VGԗ+*䎪[ ߨe]ۢ,똍Rj;I]۞Ȏka7^ĥl)`jh7QXB+̪8_4m//ÀiAL63V_|YYb:;ٻ* c*^f0o |ᏭztH珟mBNNcpmW|Cx4rڷJA(V1)Yu8q3S7 e4^ǜ78#Q='?1_n̳1WeѷjN{* X= s ̮}pӇ+0Wϝ+?װW ۹^/줒^b9I{k5P&K}ƅjkR`I1aMvU sg>;xG3Hʵg:0ܖkXtAޛE&zjz`nj݂4B@ `Q6za3X*$ݻ JH?zy 4"U@K  s(|7l/b@{C31clNlfvt=AP+3*1[)]; g5PԋK~z lc=m|BrG0(%x_/No!2DH֯ۅ^S =2qo\e%EƪЁ ͮVU}Ngaj )}PfC "fORRn~ϖ;v\q!d䫮m[nQI]}q-Z!@4DnASKjP\+eNHY=}|K$pP'CfeRa5-Ɂ9zcǎb CbmpP]=7Hu&B nOc4Nֶ( wOﵒa^`ىn﫢Wbdމ(!@@h7ۆf2Z;mZ[[nf6+c6ptKT:4;'/%cjъw2H!PcKaᶴ(g.'3ۗb5|X(:s%$BQ`3.E*zH z&-<pabXW#DY[$ B8ǒqZ:œxC=-ɉ>НPȈkܛ(!!@@P6mi:#fkÖZ-VKx^Ӫsksuډޠ%o䌙^kN.շH!@@Maۆ,}4%-g+cl<ܢY/Z c*\%8pCƢ#vЮ.v&JG!M~_>bmJq(v :sJ&$,(lXl"Bh0DKj :Â*ϙ!Ι"Gp̓ONŽ:DG@FqcX̏%±$1=өa Q!5Ҝ 3qY$ Mc¬3ìC,ug 9 RW,gҡsA&B`"`C 9-OΙ!YXU\6 ii|LboF27z2b,-;R!@4< bgWLZ˴ubRyEƩ`j&fM8Fa՘ B Q(qF{lrjևKD'Ip #D[:g*,9 'D Bh(#07f+-m(9Lۑ`l YZ9bysZ&֎`|ZznՒ z^BVGv)[l]j1'Dsf{kd-nͣjyHP!ktT B  oaO+U뜅C;ı1jӬx>kkUe۹=zKTEB XLncm\U&ee,kؘ uvZj% + U"]ڳ^?JA!PHeWWRy|t9]ܾ1bkҠtCTOB @ڽx?Z^d'g6kZjfik43@y0o7ׇ.Je B!N /FQzs%stlô)cW<˦-bڳ4[ {P B vf'hvVV\,nv]5LJJu?qYl B *)\qS%ImV'y5^`ؖkoj>ojMM'QQZA˱$'lY = W8m6o=M!#H%RxJ/:n_a>\,ILre"('"a}ĕWs[4vMDy!@4<1+vxڨZʫpzтXfXXVKxW[߲-n`UvB{GwX{(Ƅ!@pX>6O^C6pAu-~4}8FS#[TpoSÄk F BP6m,@L_tlyF#v- qvq\8i%glBWpO*"m<]1 !P`+XS2cWQv"׮։ZD |bGxk[Fx6K^׶z=5 B`G s|?~dG8-MI^9mREJzbwn݁jH!P?aXKBoj@,ݨSʴԗ1wKVmR~\/KV6Ytm~S*!@ כbs8Ul_D٠7m*WT-zEq֤VRmK + oPU B vhC\|,{Sc'4tacF_GYgs{ǔVZ8lm {FZj!ʗ B!"-8 Wʬ-Tjy99{{[ iX:d$D ק-BGbY8YAu&B <8L-,<^ǐ9v .i,I!@x=;O6pg9{K[(xcrk>w,$Xd6ez !@!Xte^*#yp1d2+OZ1-K.Z,FWFʴ 4L = w@, BhX\+ڳX7<3= m5L͵ꬡsv<ՆiK9 %d&>j9{La !CpܾOZ Ɖɱn_xv m"[\vHϤL Bh]0FNui+BGwפl2G)Z[)i!>\LޖO 3}B 퉀gyEeV^`_1 s܋tu*!.OfL vXLҶ&x-=ԂTj^#G]f"|Au'B XXkS>tsm\CLû[9쬱ez5!@4`C?*/nA܎>-I8`ZQV^tt-VqF"ݭ~J-BԪրU B^!U3װ@i?s zjX=׫H!B" Tj,unimg2Vv1&T.i|tX煒n=~,u#¶i$P B7d0Ndׄj$+N5Bu6b{wk{xYcjM車!PHgbj+FWF$xw;Ko˅q)mЮ6}=7ԧM@A@2$R D$r(nbel4f#7/ыm/] :[s7D7VR=ןI%!BZ6*vS/&H&Wq4G'͇'au63= !@t2 P<6k]ѹir›kZcas:vpz|}lz)!@4p r/[Ym.6bPLJNE+h0CCx ;^Oct&znU ,W%c[2Ռ [4a[,.uIYֺsdw(D_RBh5]+}lw-#S!.]vHh.8|5{^O:9G蹁?T}B ?!+?a@ v29(*!@@ %Jћ뮥ܯ`srԦ4kخpRjzj7ڳK[M\'=!@d"-AiZ>hRr*N^J)k6wqsא1lEi酒tK!]pt\<5ls4)ƩJiM:-i!` WǞH5.5i0otݛ)3B (,/BkX4zp1r򒼈Oi :.5s^1Ǵ^Y z' B> UIiqf_|ܦ[rIQʮ) !@4l|ǒguhϊ9qYvw3|yV-d7k\gthQ`mwN;qI{n؝jO@=F@3k1rT#6\gL%T^Rg8-'/-J Z˷"Ah\+ F&C]YѤO۳,K꘧5Kp4c+ yn7<Hߧj!P0ĆTҶ`UEu>'/+jRW:nP!BҤ*8O(=E፿w!zJG@@ ̇+_WNh'Zte$!B!+\"(T.E_eDK-YB=5ZiiL&Zn%ьhuZ՚ 6jcV; rf tҞwTg| B  /P C vc Y$FB kL6!Zr^-- ;+HG牐wYl:{mg1k"v͍v/F!PѺxy fD)[9m9lYEQ)Vk 3mfY!?-E\~DiB m'藓MR1i[j>rڰ1:S躶k&BOLoM4`=g,B Z! .$un.h%[w}͠ImzFa bL ~8Zu$JL!% Eފe(9C(:!@@+M^kjP'ZU>)M]ҿapTsr1c;VUuL9뽌!@d1\+:!ژ3ޝ,KY]'/{* yrZ14.=gڇ(>!@@%\;9SAX/4WOp4ej]BvX\P(<hJ6!6eINFB 2F@tIVjȸ-ь-SQdYVK57(vLl\հI\r]aCq/!@u@wGD:+`] M\HlB ('/e֞aڋJDQW+^,4olKhAR\;plƊQ=8 B$ke`$Қx)<Ļla&i$zF B !\v^IzZgqr(έfz5`[wWq(Zα0>}9^סX!@u*SRUyۺ] WlF!.*&Vۨ ˦ ӕ*f 0sv7N!P{4Elϯ>H z@e B#LõP3J,S-!%֭%d ?^3V6 4Ѥ!tnֽs$!@$jdD)Y.KҕjetY{v;kZ&90Kޕl=3 WD_RBhhOm{UӶZQ /*:W/9D򭲡M\:_{,^FZ}X=7gO!P zBE"B"+ar[j7KZȑ{P;FucV=FrQh鉞3@U&B!`{XpC l }f۟,O11qsLCDRqBhP)R.:iьC[}+ԿZ8 04a:nMlf N=7jM@B ܥKlk55Y̫F+_- ;*q8!lK{e~GyDIU!@ g!@eګr/޻l{9*쇖wP h:e*q(Øahx֘= x) !@4lc4lyZY՚cr-7/ M7#A'B M5bh㽕L@iHDJ^ueTL; @\O:& B%,mt,k9KcgKZhZGօSpT2-4>扡=@!@;]PBhx =Ҹt9[6׆Cyj\߶5ZKblVliƪ:UeI=7jL@=C@ԩ8pmLʎS:Q!+"4h ; 4`umv蹞uR*!@ HE͕TM5uYzsPW6't"׉fi|NYإUD 9:!P`2*O؟Yy^iwRUѬ#?}fjW2U~jg-*i=׿.J%"Bh&zn]M@}B覶\5QuˬCZ;2+Qqe.ZǿPܞ zOB@D&Q^ac*iѰC& 1r>u<s|ք!@3$EIbC :D1qcg$J0:g/U1k90cp\DQ671s=TB (pmc)JU;Z_y(Kܖ . b+03y3P8Ls(!@!P;sM B b!5Z`혚qd';fXBgY[mcҒ-e -~Ү*o@P$B :DYdh34PdɯqZw}%lɮU1mЪSBõh9d}&BRnCp)rWZ<zҠ.rޓG9nϡx!@u*ܖfJѦe[F\.mLEu'Rth@ћ@s]v6M!@G1Da!@uzz6"WG)ӷޓ刲Ml+le"Yr-EZ{^VDuH-h4X8 4NhXC&݈!@d( ޓLi#"kr"]U5wDmH?|+ʟK(!@!|NlB "@k9DJR0˶%;m3-ZШi]˚<,xy!BhW= cCٷrp" \FB bŰEh@ ޵;7X1:~Wog:P ؽH!@@P5[hH&U::0xy?QSi;¾M\D B B蹮%!@5A }[okAL(>Oג_ʛ4DϵÏR!@_?:avjl#Ih>SP*=g 'Bc"h|@UئLg ;r8s h=s<N^[ˆ븓xB 8D9PfDKv;mlN+N}~:eDPX!@vDy;MY!@D Bć9kp%=[E 6Qun[6.Fgn( BpH1;XP8Y؝1CyMs|(&!@@@@RCb cZs;ᤞ~ K YDQ)B QbJz}붵9}ґpH=A!@lgrt@NsPv!@qU`:-U@tFēkZxqcL) B cc:΄Ɠ"v`YmߐA\]2 BH^pvյ/ !tEOpUgF+I!PgXטjW&r_aKFwTxHKD(O!@l/4s5U ʇ B!w%) ҿJL] B> `[zc2bmߜ1FֿfjDs*0dTB #5H eR|~ڵo_mH{=$ B`;"F͙P,n xm鵯}$Dϵ$!@YF3;cE~,!@`Ύ<|n!@;3Vvd!ruކrk(#3wp*;!@;-J7acHNw1{>dҞK K'BYsO ?m`-I\,z!fcQsO B 4X֞I>D!PpԚq+u_)9 B#P7 @q77FU'B"73kř_i6{b(B Lw1gDH$,KBۙv(O!@dYJfz6+zI !@!9A#V1>ד96!@B`[h-O$ Bypހ,">3r 02nVS_BoZv9#m3w{0n-W]< !@aqDx䇙8jr3pöfTS4 B`D -fBae`GjcM2Wkϼ&>]*E@F [g{eKNXkXҞ !@;tKPibj5D;r M6og -o[mgr!}2%BpHkBE1\#@A x{!@ΘB)X3^"vcaakY=c;lK"L@gب B {HYMvvt:o3xi[^ٲA՗Q =kD!@!h険 !@Fnc1RhUbuخ*jʦe陑3-?onI!@C u_hճO(43[@hUN-+1KK}B :G f #-ZR03RTX;,n҃eim N\D B hVfQ*p*\kl:'*}(xDuЯH$!@@'S#a3BMOcTd##@KWS_n;v1a'0=gڃ(>!@A_}j!s; <!@@A(QE4zVe bekV-$i!BW(;6~rZsS(2ʹ%bqm*&nޙm?^uJ* !@ EQLIhH5mo֐6wHYqf jMz*੪NF`8!@?OW腒ƲHLޝ5Ss-\gʢxr}C(vDwRABhd,[rocIpMRo9)7B Rpq4^}XY`XQ"tnyeٖ쯉(x!TiB 퇀,B%x.\ue[e+l}Vib" !cn*ȸ%~r"B!!8C6DamUg"@[\ ?_.V#]2wPuH!@/ϳW3gqAthov2PFi#ViRYbYGþm:7~%)RiB j@ׂnQv O B @Z^Rb\A{U+ߨ.ȸ4".B4ILJ8l7* B]I!@!K2b)jvTmTASԮثȈ"~aW3ɾ˶8m2%G[/y귄!@9ɫ+nb.}ȶM11#3TXS9}>C\jYct)e!U̦mCXM^B!@l_nmi ?A_nM;ZRux-2QTL1\d>õaf689qpCX(7B P1-iKL ٮe1J BSi ;*k}n$'dw2֕-ZXB,( `!64uA[\F 'Bhpo)i:69ɅEu4N؆R4!2= &jEߘGRS qYcC=ذ P- Bw8DT?{¶`D$'m<9Ƣ0B5T@d# &xVaa񊵿%=\) E9bx@QX JȍRXSY$n#E0-ApY_PǠ!@KvF1|⳪Jz8T(B ]cF5 :\o\kֿi5p KlI[k k԰qwW!@ u_^ðWpi6}pċ 1irGiazX6V5OU%B`B@^E κZNNy"D&g1Mp6_Zh:[{eZFeMϤBT= Kc(Fl2b[ˠB%XܑZpR9rE*To.T>PϠ{Wiw!@.@ZoPܞ!A >YJK!P#m)Amn\vUUmv!ZzZXEUmY9S<^k6#4F@S"B B8TOL-RYAMS"+ί0Aqoh^xY.63:Gc;Iy!@5@@I$Zy靖,"v1uB!3Œ*/GdKr?/c%J{jΤlYk0%!B jCZղcףⳓ|`!@.mW_Aczcy6;ϙe`I[%T\lc&Cfp4YIh* [nb #TȽ4w˪-[*A!gzr>4LkHNFf`F~{£iUN`n6*sf Xo |WK6SzލI!@*]U מAJn؊N)):kTs;yN*[߸J٧2zWhzB4>Ճ B`WFqF\ۨhW#K+;miKXLqQB B =Ja mtƧgC6ʣJ+UdkjޤɞM+#) &QGqGLVRj9[EEY@D!@Q`UP?z_n KbTTMmEfj̵ ͪpcL3=7!@;w' 8gYWvژ汦kH W(E=!}8(f_ſfak. ))[!+Y*(YOkԡiLxJ]ƒe(/cKĪZIU1vl,Lkd^@F\3 z&%^I˕aq׹`-)So()*(^4;JWuMz㹝1G7p@=j(!k ]˷+ ~Y͵aխbzYLRnCi\{4!PaZVzQm1=8@ :f{\?5Ro[LXTFd9[<2DM#nѳNj3RZ]b@=C ,RM iNTjA~.}_{&!P"@u;ik:YLjzwW=y }\m ^>L-$cm]m1ühVJTKTV%]pg7]XZ $ {w&Wsa3v sW w+0ΦHZ@a2@9 VR(FLm34SjW4% +PH˗B%ҪEyP‚9^ч>[|n-ǎ[D=gUW67snYez~_Z?-$F`|g'S dֈ}M5S#iơ:ȵ^3IWKr(ǗoѰ-J95v7?lh>ZyDx1@!F9tk(ri|.]rR6Ҍk\ f T u9B=:8H0!@)5LhRiKRWUj2r,cTym'gȺ/>&P3!dWE Xd})6!@/2rQC[JܴƯq.R)'=4]mJEt5PiB &z8x^T6ʳt!:,DF4] R|ݵdۨxnzYUh.e־g+d&vC9F]W6*t~if_"MjI::`DƵ4SG(1.#mͲ4I852jڌz/E& t0i9FՒp8X,R-nV&tp#уuYhn4;@,&_UӿX!@wo7VAؐFl/Mk#jת_An3#! *M1/w=&pBoŻ;xANd637 MZд*aBfs̩ٗV_06[*؋mJOL!@4t,ٴ%G6ms˲tVT5oMhW΁>9Y -5fs6JFϵ]{n}O!PkMҘV+3W"^ol.)ܬD!V Qj;zV%Ѣ[{s.߉aӳmVkI!@!ɒ]1`7K\됺̭rDm8qthkg7gSoYHhrXI!@i[H΂u˨A?m̾W{YcHZt'JJ!֫f##I<,I2&~au9mb`cG)[h7Wa!ڮa<]߬^SkXvZ!@4`+14 vW)Xhȣ;d؟EBquq| `l~wL{ec_0H+wTfcQOot<jJ o7džN@@yi<"]73giKS BZ8$f[/6*ɾ\D|̛$#<Ü}\7fBIH, B vE %gra7iXq\ft*`L횲 3<=Ԕ@#ffsfDŏ1a sW{}+v!@@@`r.<FtB!I/\ce7XH; ͈3F6n׋ƢB!@4($Zd 78ܬZJN.wU!73&SeK5u ԟ1`'z#G{-ؚep5n$ho2JTb~a<):S[%7HrzӞ%=Jqv 1ۀ!@4+~&7W+ԼkvͶ};>|Ce<.hhH]s" zY>1Qkό1|7^}gi1!@@m{?r˺@.֢M4)" w;Nd\6mN֫$ېoΌߛוuog0Q>qNWw&"Xu>eK!RgB.ifvEvYP! 3=NQY[kZ}=Ce,o-#)dLVѶ%`0K3Vg&Rhw؍ r%0hʲ )3uBm?F}{#j_Q o!mF ئ&tmخap;ծdӤV70He}S{tP|݁ϯ"JbnݎgehWhB? <^fXC{0ugkcѳuj.q*kExW|;/U$BLnjPۭXH "0GLq:[(//iuS:)B4)׏-&HYzUe AsdoPu'f [jЀzJ+IBXapsT(K.;j\lU0TQEsK2o%0@\T۝tNmuwø3 +cx̕@2BbR1j-(2c<0Wd?,L d='[e^MKhvӃ@L mu3͹H (VVB+=]Qbc@DBLu{ΡEC`Nk0E >k8De8EB 8;S'-P0$@+ACFt7ӯv X8z8uw3%g̴"j/}?{< k [ҔǠMPpm- R&d|o]be94 EePl !%~E!ցX$H5KReBkuf4BąkRU\)P^!0fp2Skj.!p: WlapV&d?4虊[FOBTaHR*r@O]ZU; itzڡ;x R寊m~ݗTBG JhKwՂٽ=EcurY bZG$,iLSv3[S\Dvϋ|&5N+B 1yW>5ŃPoј`(Spmtfd?ֶdYf6s7\!R$NA!@!=.cI M[WD!@1lLHB B z+dI.!@!Pc=cד !@!-kG85ٯ+Laق!@J_kIIS)pWׯȑ#7kE>a!@ d2y1ǜviG۟:jر۶m *gKBB ve^~gϤIԑE̵!@4 ***d,tb$ Bj;6lٲ%[3Ev.C"Bc>Y6x}(B B`DysSs]N!@! RԙgyKV6r vVP$!!@ P_}3f̞=O?}/\嗣GnڴiVǒ*~$ _fcб$Y!@kF_uU@wygAAhώ tvo D$t.ɮ܋n!@YF6:" bzaK֭[gr U"G B {'i%O?9sj -Yzln!@@\?@q%𳺺h߳HME(!@@CGsGyСCp|V d5!@4d~!իO>bWUXDظLL]$) !@@C4VӸqcAJj=3bfmn7E&B'\tE|p 2mͤ9EJB!hԨ 7W_)%n<5k2Kv-wft̘1yc(>!@!@d@$̮-gqdL!@YF@ ~LeI!@!IsI&u2)t/m$Q^NT8.b53 a,:Egw!O ӈB"1Bqt?uU,=H0^I=`5M05lEmS[N#'sP?С0L=l~w --Cj:;3*Ke0^op뤛dChӢyOwߑ{6IJؖ1r~OODOC+ q h B4HHi-lٟٽݽ='sٝfvyyyyl,!)H?2źz\Y}q۶Ր+k>ȿiC0 L.RYMC @qěIn˜Z]Ĕ*R 㞴Zu_S5"&uT\e%Y)hZ#*pcd^?fVkrL#~IGFǣ`8<|6}9=={=?R 11;R\(h> g1V0G N|4%& O+$Uٻz UJAl ֢ B@PЄ fNR潓뫫 V73n>>>ad2Y,r:V6ͭ<:`u:)IENDB`filius-2.9.4+ds/src/info/img/packettrace.png000066400000000000000000002045651506574241500207420ustar00rootroot00000000000000PNG  IHDR}lL~ pHYs+ 'IDATx `Wŕ?~7_^ZBbͲBl_P^]"lh ]d DZZ vkB]*!< l4+?&c*TM[;93s>|I~o{f9׿ּp HS~ K湋/-r B)Z~]0n޽{$z|b (OFS6aƋ$@Y  P7bQ#*@AG$zug{V?y4|k}{7Qc?BB 1?kߟO~(d?_ZLh1I &G 8/\Esp>L@/]ԫW/*O0*H)ii%KI @XDw& ĄV7fMϼ./dF,!E'7C #O7M!g q" jyirS %xgM?[ƒg4uD ='ʮxv XgiTBe'!lٽbU wݵM?bd3O>{;..csYUXU "3i-+:H/]PP*KOdNYSUSRjb굪6gPelh ũCn d%?,*jpy?_^oTijO(pg+3p}gдϨ%S;PS-ܟ]_ d߷$T@))eZ\ `ˬjg-(U=w:wT0KsTAS@֘Q]]%7Fꪭ hSypxAK3/VoTƼK.P0_-+q*N0m7 =j&.H rя/]d.+j,W0Ȗaqrlqijѭ_h_~}%|UZK?Ari%\4s/|K˶xk=¸o@\bixMB_2?rZX4[5MK'߀@,3,ӽz߿4V.lQh@I\#Vo@*jSaDEEFl֒52o2%kX[` Vw޻ 6Qw|s?9-?^ߙ94(g-櫔4y/Zkq4[Sa庉KqZp%ab^WX;R-56Zr9ho_4ᣋS_jY4_<ዥgaϛGOS_η\l1_E 9 ȥi *vqZ>|xX!-RCd/櫶/2xַ$ dcg>E\Vj]D N_nzHF Ak'!'8>|XDZ쩴ٱF,}>IN҇'ƛ{UNݻ[wx/~哿sn+l8 8`?y}!?7O%4Z窯&zEr4ӱBi -f?H^ q򭳥sM$ ^ܪ󚂶M_a(1}_tl{gQ)QgǏ_EQ뱯b,Bꎥ싢;H`5Zro›XzEAp"'j#%Gp]$y~a *{5\2C;=fƀ}F-օnXtϑCuD-j={:b@,$pz WJmx#F52IGtH5uWji|M\kS !|4/%ܧw2|o Eu)84>ĵ3*t(4Yw -k0<ac{2vخe>zlq8$c5V?ȣC!HKwYN" V!=J=24X\A acKSUvmImtK{SVxEFV;nV޽vokc3[)iHŖnk ea՛V ș!7VPUҘH/*a{~C)ewbiNalrPbkG[V;FkW,X&gL s~ 79ivBQ Yvv,ImA5D0O QfCٶO} 6l$Y_(m[#it\Xk_67XV;;kAuru,@}^!*-xexJCYv~UTSK0㨵v:7*MQ(E_|5 {6Brh$,0f-l @Uiڜi_;]Uiܾd P3UZ~Z"ʖ i7퀬ҀcZbe ZiaGm50uX#x 0B06XҪ=WDDzlЂ+jZD-+>_/ 4uҥyQUCjPkǶ7jQxZi|` &MtQ$v.IO 4 XB<[ vޘֻ8'͢Ɖ{+MBLmuWؓKS!(٘iB 3+Wwߎxa_WnbqK K_, |MyhCP\>)%%"RSSYZ)ظi&.|dl|0ζHԑ#=yV9}zH+Oɗ&(u>՟{$Z&XEi6#ώ@'Ӈok&*g#woDgR>[A /Di7YE(اH^N?G~B}rJ +0 _{m=߆GOYzФ]/> l WE{७qa~&@ 7q? 0[[Qד#".4h`B+*K %L:~W[嵗RMt/]XGeWrtcu`&(,v8r%3*eVsaoy/$G̮QѓӡJ;M mG׊ݠF'r5͡Mʑ>4̷B) #q&:Bn)S+ECmy}iqDx۠Q=t܆Cg91mO ]M_ǒ`KNo62*ؐa#i!w;up ?Yr"h̶Sրlݺh3tbPa`-g%Gݡt~MW=t,盵4HE&!o6wב$ؓL9c}RJZ؜bC߮mHN ٠G,`o؎xRHH }"eRH ARQm;!}skyھ 9:amz14Vcө 9œ-rkqLd[kED<!a׾˝ʑ6b"WJptVbRN x+-XqNȝ"nw]:wm,F6i% "iH #k<"w(h348!j)z٨)Ǣ< o$pr6~δyk&dfjh9nk hnu:maTp-2~TxYV[;͊̒P,qbvvњ ;ڨ'B6u a)Υ)`pu(co qt祔, M)L=.͓ iλ1ۑjځPBpJ#BH6Z:fcؓaاmG7z9FCoyx.nd;({v;l\mP).>糲0Vx tDncOA]4l;Pc!tuyLrՁ{!8-vm/~w#?T2#?H?!3F1>0rD8Ff_+vDw͉]1&^ѭ5M;4C/ZSq`MB| +O>P*h(O`twxȎ9a,ҋ, Ku!Fj//)#ػvLOrďd]eNaivZ:t'&IYt|! FApT4@>_D#JxZghǂaGJ^ubKx2'KŃHXlu)'<|55554kj׭Z:4 PSTUmN: 'ڧ͇BgqQs@ʥ7GK7+Ѯ) zɏ'&vz%璲_:pǏѝ!Tޜ6Sueڜ>f,PfJȂC()'I))%o74ϸ/%IŐ뾔nsrςCG GËL8ǵp,Ȝxq*K3! 8ow{}tyXWsN_~gQWsKNuv <>z$HY~*; ݻM1$gW/ꅟ/ݡSl_?u󧥥%qiӂƳ1u7grÒN`9͞ y*2n^ؑϹ7<;A{#imH=s|Bgj^niXz:S's܊bGC4/uhٝJ [hI(mЋYT qLL]ݩ*<Ş|p9pu[︵/hx~?y-v1%2jƕ4\bKc:X-K)2`p~J+!Ď#⎕,dL+NȚaֽR[cݲR0*&~NQdo{] ,cPo+1\ҹ]ldyb{Zu[Ur͵0;h DI*rEi+r\)oyp\Rb oE9O52N[[S?],!m~ {;ܵ<< kq Zc,3/hΝŮ!`}Ewϙwq3&fN;5מ޸8wػwٳ|CҴ  (&Bwy$9;q+UFdBtzLL[۷['a2y粓s0F[Ho]>χ}}ֽ/O^6` c'Jy݅Mj|?6Mw'['ΡhQ/M3^{\.CM7X5|Snlyr<-vMLm^f>ԙ˄7)ZWbb %͝"bYE{9.0bg/,^L"\,E 3=}ݺ/P,omMQ.ك>'_7\OǺGm)f {gYfR fǟt\Jv? # 90uPp%QteM.`gLJfi?[-ٳ44/E0W`fC!8IHals/'D)xʂjvb垥p5{ve{ENa@*] uIOԝU׽KsIGGKrK 7nPdO,zUHiƻ fWCFJvOJ]VM;б91 Xahbg^z(FY 5b̄0)E s^[гdb?rZN^  ki`Qt]e]N,\\tr飏^:]e&/vx  p*EM#;˄u04LqyPNwueP$p\&])>M4a g\&z8Я@I?3ݝ׿]=tu܉*`xv5p:k4 ʣ'*~И̯x";ׯz~o#3S'0TFyf5BZiFE$ws48b;>YQu{(ː`+3 Mˣts$ki-N6g+-#S?.m~6I ?$1>LSG8V>`JxŘyPzZiA=l%S4XiuK#s}:pt(<;F:9=Lk _ы/ M%aj f(dLkڠQnї떍"Lk(/&DK m[>䋶(o/ 8~ Z+!u's1L#L)K ~@8m.ۇULs舿G.mjJCG 0M Oɋq;h :`lAX3F)cJ`}{D)}̌TSKL]IF=v! yx 3kI` Kg0n ¬GtYjJ$3Ɓfymy?/ټMct:Nin$^bNc4˕$AnQJIv[-VZ4]6 boil7+wwS*6Cs\| ^}'6Y~@1+IBŀ J87ŭD.+Mٷ{3 G {>t^]2(F.̛nt^V7twS(GPXh$ Ou`_ᷖ}G(#W%5kDPj[C;溈+?oC:\t]++Vm^*5i6GLgf񲋊p +` DY#FVw@zhe:w%9祱KgWv`bx)5>_< X|Hh];XNnמfgHxҘzA>&ҡl$72wBJfC?Ƞ4djuC)> w%6 }B52qsG7IR yyNG+d<~ņ q-OcvqV/f65sK*W V u|ɳ eڻIm2ۘ5LOQۆʊ*c1A~],ffj_ ݳ4|+7n߷{:9㺃8ʐbSfR46c.l6ڃ(D-<# s n1B$,GAC+s-i6UZ*y.Fzr2a:z^M+%cT`LG">v9_j.:ӿCfŶA`R(jE8uKj^NhsE̕ьKHZ|m>y¼DY'M9amvB{Ҧ38'zTAs5-I^`đ_biإ+ t C*l<߱t<:rxՄHshVXߊY^']\aKv'vt`DzgbcM-$%@62k]_Nqoxa6g;eN|JO Z(-m$a;Q av{L{u}E#|YWGATdCdɆ&(C1]hi4[1\lwH1IHDc[]m 0Џ2}~x b-^bXWQ ?4SwVPMMM=e A>ؖP.W'(ȝ >#Mk 8;%8T%_bIMMrRQ_ Kܔ pGJ"%k||NI)^sJ:-7lV4pm IJ@uIRX`kz-O(PdPzH_Ni¤VI@VZYjbȁ|2(eqY[$>*|trKIJW}k%ޔ9@VW2 xc>3XQ+'};5{8UU!ݚD󅚬o ũ?ښ2U (Y@iN.~މشb~x)ZSEM)ku]S(8~O,Hu_7h&mKo]pZ_ &\N4ormV"NY]OHo} kz1jnh/k2l\ 5 ;{I~=|vAـ'+8JtaX<{v?+w?z֖Vqv? 3l׭4I(țA@ Ȑpm0I*>74Mi { iʈџ@EWo6PQv3d 3 i A}95M?z_0>3K/|S9.c=w#F_>7~B > ~9%Md4ʔ4G# ڸ]yV2&nbm/0A:"g8R{ lO=٦"L+z)r[Sbt ia!,' l8q`JOÐjL(CB%G&BWs.٩slEAFha& Ÿ)-&|w Oǎo;Rw7]Q Ɠ~n[Յr2\M<@$ ' VS*K7[BOȽ) NIMf0@aL!b^0ak`saUP2gD,]{6a?|GIȌPj1 p.:=j9( l( K@.jy4Z 1H+1` WCD&C&A&Ē'0}`%YP…sOzW/ hbЯ>/K>z}hӏh2ZZ @._?3q?K>MXM/W-g}WӹV>܄(KS5V2B:/Y /[=s?9~s˟;vף#0gr ?byЦ# ~b[9 ؾ)1VM,ԨBb\2䆈Ҳrg6C3嗋0kW j.lʮf&À]2kHcz\&ҙeXe\ɬ&5+G-=m6R1vu|p˙i 8nI!`i 3=v%Y;ٿ6V1VIxb<d2oD40* ~JwGT0H֖m6A)QQK'7O1!J߯}ra‾4S0鿾s?pso$cǍ_{Ko?{whI _ӐD{zݟί@"!V ڸ+ZeTVE'EZiW(&<لHs EB04[-[-T}$ [>l֟H.Yw0Çeanh6YHVB0(+@/OVf9B"2T_Y\yTKPs#3]>V"4!\ГǫLwHd{ih(RF4iQ4VK̊`z/ ;he!%ߌus2HM &-] T:?s=}}a\j >秺\ϰNrg>p?/OJٸQcs.NxleF&p%):CD;,Nt'/iSy$zzHuuShbvƖ%&ìpQfY[ UU9hʴa{хcZR:J{&O-&32,EU2l`V#(3r\`@x:Bj`)j>bf6mL8ik2P\[ٴau|H Z.vT >5nme㯭;.8SI|р՞1"4-Z9VH@ G:%x :ViW.jMMT "ajvlbz7UJ'q=v]9i:Rc`ҽW t !8L!lFa!Z,z:X)f l;a&#VUPVrV4t ^in;+cj\ F3naj"\ 9Ga)]+ێ:!JEf `{rCN#g= ȄC",{ǟgX7mLxlṳռt1t5 >`_wWs^?+֣YBH"#b*X +bVų{.ޢY_#4{G?O+ͫg?>wϞ9,ݓvG}BbmSPgwַ?χ߳q`@H{_ܺJrcP^0"JEC_y Kۏ+'f,M*)=MG Qc%++iwW'<=y>0 écaS^׍~pa1'ʊGx?ԓߤ2aW}kOSAzqh|?~3ÚNWP~"Ee)4+{^(K7 LfYqH8H}tO32u^V˜́밽ʽH_)E-t}nՈȃF3#jǢv[}=I4ӟ/jFҁ+4F.ggfӠ+G2ʟ>: %|ImsRm7v/y6F=`1^ tH&%ÍPE]^o_ }S`UA>ENZ9dfy%,}>l0!¹" xv|㟭3H gY#%SirXPe|UD_ӈ`W }wN 39 . yɯ}9,oEvI2+|y5^?zh?QMOto/ *N yRe N Jf^ N\vSC<~A5Uu1'imYkK^nҤ'w O`Hܝ'[X)o[LW:#vͩ}m(4Y|6m2xWܻ/<q̲:v4Bh} zi9~=fԒ8g8~yx `ZMLyRIS@[8œqd2>$'h,RTDES^ƈ`ĩX 0g\Wy½9ҲB,u~x;fʊWF(k{OAəG۸q_ϯ*=4=mc@> mhun,MG(4>N\\η!Y1åS`I>lH&cƐ1ѣ2X<~x'IRC}>Xe}掇Ff K0<)OJ04 F7d$SF0M&M9d2mĴa(C9nO?0bh1dͱYKɴӹۛؒ"*ѥ8H݉cNpU"<7WҟW̸5텤Sn7g{( i6M;Ąh B H|֥CO1"+`GEfe%8IQre9"Q mʹ+>0Q04Tn_bOZ c(L$Zم19Ijn%gҟa`J SBS'%gy6qX 3 i$ -dfٰN +1&#rU$m %S`^%'\B2`ҫu튕Zgno|?]h:wӋ-.vt C3#݇P6\5DIuMhqg!'>|p(jK Qh>]L#b%`͆ᾸPU0/ 4ٟ:Fp`# IV4jv0x$b>lSWZ@=;>z^&'?OuFP>,<ѡ&U癆QpGިsuNrЇͣNCQkEy8@&ZqL8aO 8#C()[܈ ?D'wR 4>X=R*`6,|ƥ>4"C±ʸ;Q5h۾{*:{l^z9S$kę1ych|1ۏN_GFS:k l`iRKidOb-qcqt(/k!scŦ 2aqc3ə1&nvFkGl== -m3){JT(ži}"WN kZ}J\;6bX6Bt44},5p:k4\WK,m.j䷢bn+%ݭR0Hu,nnJzoD% Y#6>d w|/`S^acͧsg>TidVT&(<cf (ݎ6UP|.M`t?SΒbZB&iZ'? GQhS$4~}f>|t%\a]/sod+9?c, !;(3\ F.y?x /? uw1#-=*0Ї "ϛ8in:Up`*d;DP;s!S1N%:U!ro]>ᔙI, H[`H Z@6tStYZ1CEfPDW)jx^0-;YֈoXFƝBs2k8`Vi(aOt bn0ͫ _lF8Ɩy1=(4:l6 x1g*VB1FOmBXA:TևT| ݄7H>|t-BSӅ17DeiJ`>-qscg.L`Beu36Hz}1_)Mt<̗OF |̌p:!_ڶ4B4q cښgDhulIdm B;YO8> ^S=φ5_X^h)Nr(sFY.>$<XC6 lc=*w!TlBfUQz8ҶGvQ+֤5J6;Ňg Bp DX5c'gO0>2OW<F=CM H*Np6dk.G"UZ1O\KPc`ez8d rWYJcF QS.<$֍ `DJXAtQR@6Gk&|\/`ɇ.FH,v#H>|t%A<+6*|lLh}1 0D+LylG;1Ȇx}X&.µ\'B^ۋ XڦB%vLu%7iEPFGJ<;vEjs5(ȶ2FTxQV, >cr-Ayxuswn*`_Ew^y 0¢pw[0Uh:]6LʖawWkw@T3QQ0UNV0ndE!FY 5筼?UE D锊O,쩆7%R҅ 2m#('"66:)<m*Jod aEy_0,P)0@[zUPʍv63a6#+İ],P6Mkz? GעQh0Bq5/Zh>m#r4n$NvvW ӨG(qvBԊJJz W:V"up8҉: q&dg", W!쪸Iˆ LCСOΐc‹87|tL8vdNFGw[nu4> 'Mm>|zx6O@Fmdgfi`ceboUI73Hzf<̓AfD>|RpkcDDeTWdTDoyj[r8;%:%$A_^]^QGxSRJ!gLؓQPmh(ξoWIu ,NIɯ.8η賋J(0 챼OȇCi$ 'ofȢU8B Ҍ g좏:[PsYXYaQ\AutN.Bu<%cVP-AKXK%\[^ܐK86^ѩ3ĺb!B WS%9 1"honfыRIo&%9o9UU-h׋ ל wwD>|t^Jۇ]~tܥO/_66 oL[Cq}O]\JSNӇ%.7㠢Yf&A}SD}L-c{$.jjHޏ#X =vnxCqy|z>\O-TJSUULq~V)60JEg_!,h*4v3xTk7^?.֟O5Ilp.Q!|᠛ЎO,iث{Ɇ=L,>#_MռP e5go~KCb<8 +ӺKM]Llĸ2lKsLIς8$YR>wrG =^bo!3-Cpq0 s$e/e;{zJuNƌR,(UFSS!iկ*[ Qץc5cz-.@N?&ICv*cC[sBƎꕯTM}yhs aF#(~uUR8CfB|؁TJvM`P>zGqe??~ݷgvz~Vy5>:r {z|5]t_?!'g_: i T|)MⲀ W֩{)I|5 /.46GćiB!.`w3K*Z|[L1?F6?-o:e(zO)>4zJ~fggS8¼9ĕ^$gb΃b_#$ m`O+7AuuG@y࿜C+Pvb-,66T j˄^ݾC/h,a#K3`Be؈t`և]kCgSǰ#<_ g=sJm(}0B B6ޣػ6[ j=?J/@ʻFَIcaR#~-ڸo`ݷSEo<_~KeFڮirjjق72-Y-G?4=oI1+v>1ԑ:tZ[Su73 :MܔR9WfGb>|#)3()_~S/;[ȭrQmՃ>4SI/s?y}Ç D[,>O )T7lbkrY5Ĭ55,G?t!_ffkR삆(%cTX@N ФWD1^&^M}2ppAA v V-,plP7%b=E3A0BPՉ ң'J롊ӍmOHA a4 ɗ !\Ƶ)J-K~P0H>f|X?om:>& {thM}OP·2BhyËle!,M)^PsDdrUh 0EEL?21AI,#F $n$D `il5j<*IFBt 5sUyX`EEU=IB^?jN2"DRKCAEV,3YmA3BExJY D jT%EofCȻ+͆uO}'AA./|iZ*>  0ƗseB@]U]l>ٛr飳o%t~WETdi^NsIۼ9#3K)iD;X }3Qg',J mz PqwO y,\_śXB7P  %j!uo' .!F0% YmoJUd۫RUbbjI`EZZ0B4>la[$dl'W{ +Tԩo?}4{h裷P l%`2=~mm3lչOܿ?egNz̙?iݻͷVbFNWc~¯>} 7-zRP&zW_>w#utF>?yTO֙=h&?zꑑC^<xs~LM>|ʫ)Hp{/]ջ- 8}d+F_]0g޿XMշT;OG~tЗtpGOs_GA_HBJ#LJOFľG}(r-ܒ%_>bI҈YijѲLre`W6٢ɳ[ -nEwh J yM*%Wl}XPq i_,tѷ:L| 05 >LЄm/Gy 6 ^GKୃn<$hA`M&Mg|ȇV#(٥!Xަ?3{3+lm5֢wK"+YJgƗ牑xkO/T`@zRTFܓ_9Oۦo;_`!o}h><=v(~BF~_/r rʖ=H XDʜU&,T&V JW? aѡCYʦ,6[8pWɰ5Cv(ٓ~Z2ܽ]Wd[_Y0y`4EB e EP%>(%:3eF1fZz`(Zez|rA @0fc-f9Z`w1Ue\ x9pwy^H⠆?H+ŏϿyP$}@ Ū59rn{b@O f+Lֲ%w ]ʪm˖}E%b肭NİY&TyJX&NY&7SJ&ȞսE_R1iғ۵+ *'"vXZIs5ќJ E4ɒΩ Npgiș޽:CzW=~q&d!|bkJuTӏa>,yt gVH?Ј&yy[V,i|xelyŭ;nh˅tQV=xL8q>6ƭ߳y„'LxeOqNgO:{*L||b4"hN673-P|e^fEy'@z|YѬWs7Zh{Љs{{ùsΞ ]wFems {@ 'RA'7 Ӈ)eVWUfnN_̊_1 22sO6XAR4Spb.3S$PԶ<{6&hrG_T<M]@4g+ASp^\ggmiO>',|ؗ_>zA,6`aG7Ʀc~ÿ\.jC?-pïj;!;`@cw!K>|%ϨmrN=6WASKZiKTD+OUbJG`=&qʳZ Ç Cۓ,灍؇&!ks?[wY[d@NZ9df5mK Ui"*W'j/59+@GƳs+@B*0O> ۺ{ؾfP|+Rx;=S{;VMelU'gzJ'_}bz'YE/WʽtXKM;/;ي=-b O"ѢuJ,jyK\ʩ4SƶڲYTz 9rRXur_;ƽ?=a=Wq諹gh^G)#V WGcN79<_[ 2ՓQT!~;ૢ'h'Ns(P:Te:^Ӽsf}_{ S0bQex촙,yE:16ep8 Ka"qݹMe*ɛqk+:+hBNhϦ ҇~e }W*Cq$ `Rx b33&²ei(x;rtvꀯ9IG-gm1iB l+4 b p|_Ja_=IЎIaVgߵG׋ziA5 N[eu;g{ 07z-eBgr'iܗDVEX:Гzva*h%mؗϳF72I( 'ؿ_'4Aa ;p@H GϓU uł|yKѹgVv q^=sH \Qw{HUzzh*z(>"6e>d8S޼ǜ>m|,h4 C0qݻw 9|J/xʰ.';Ni$܅ۍpnP iv"}isOO+sM ac   >SF0 EI8Veڈi=pɃЈ,%rH &4 \x0!u'ƌɋn" @>;d[R% oQ:xc{ Q;ZOvp*n,[қiyDi1vQk9{ZJ5`CݘUg$lbIs)<ڸ(vL̰h'˶>T67 ʲ3=ҐLCsp?SLGP ltmV<65rPG}(^6gMK0h1C8~@=='#,n{@/@[bjDL<6 SŸ5ҕT=Cƾ,rq3>85}Thxr[m֘mCfJ5-VTV*mij)}޷ sӻʖ#Y4`$ )98&=[ 4V /ug29 .Ub!s7q#ԈU_2GOV3 }v]_m!~`>|t7 2 .Zh>+4p@3}GO [|`PaaA-: .ىMtɊ:+YO{}f]6Qkƍ;T˛G`c%@8zh.M?/S!G]}`Z?pars7O`#>;L+e39yO;`]MEyKzfG-q &3ٓb=qXA}=ӑcڠ\<[t%`ucʗNR!C䨟s>+[Zw:d3ə1lU0–m,?R @ ʲF'(&ZIJQ]R nH?0b̉4Oԑ g΀mbpauˌ Ġ ]J+kԻLWQc@ OбHg/}tk"VKhH5փ1|%4EᬑLUQ6iKqZ_N·@P³љޔͅ7Nb#N) H+[SbS6zo:?T.±ʸhV C}g)N gJܖE.Mg%YFh2873x[ѨJ7ֲ Of{֒DhK ǃ PX:g/~b-tI>:{s*fЉi.4-fwo}PW4YcڸT%zd|߁xf9&olaOv49C:p6i$:aW瞆0#FZhnfs҉mgFI PqNC0#T6%|=PwzaD܊QuS.:i#>˚6_Rˬ}N{ Y6` "j5"5pUTl0biڢQZ2=W86UkbRXJT_ .\6?9w {AZc˄7AbnB 032I3h{ RK&~ 3ivK_j"VCh(vQOQ"d8dO֢, "? ljuʻpH8DB]9ꅱ)ͬ)ۡȋ0K͋ik6*FC^y?-k(Uq*iRpkcMǐ` `Ls[b{p3&PEmcpLjj 坶KVگ%; ӦayBlfЉZ;)k MVJ2LZB}j;U)UPSrmPWjUfkA$H֘T0)߳IpF &e)5kʓ1^ H^]Krt,rh`>|ts0kHl쯅 Zh"dV 0v}S50h>|߷+ðsg_s@Qh>5=o9{z35B|BBC^E؝N(ȃt*">ўk@QBff,II1m TD0mN0)Aq HA<0NفE "hVgU@"Oͥ%@˨4 ڤ P^+a PK9I\ ii0`q%sZᑇk3cU*,W º&A C'D&kL^HI`wHO4|V fg (# @kArӹ*EA|E"Ҷͪer U,<"'[P.YumKŰWZhbAz \ZLC:ZM[)vJ$p9,\VX;Eœ-W*p8sP3Yz&{f, VFfiP4EVX)Tyx `2QvZ**X'aUV 6UĊmd+ڄ$o(O ū!o:%I#J>DЀn`*f7S>|pEe6:Ա bXhÙAx*gދ]=O>G"6\ӖUArF, RQmlY$NK%ض23ێ? "dDf>|(^5_ G8ɟ} GÒ}`=$k!B_:9i.i¯}pa:_+;:^:^U 2ڜ9 ~fޘ&~[ֺ\nrEx;e:/[PNڝ$&[V͝r!.mխlekYNm\\6Lk}F:_i4,.l}kuJմ. f,\f,ryMeu`$qߩlb&KX&$Anu |Nؙ .9̻c-cwB6+4Xm\p|t%Z ݬ"'|A3|ѓ@,,v# >|t!ҟPAbK o;'PwXÇ KZCw[<'~V Pyc+abp)΋('+JPBr:݇ $ Nez(_g'AZ ǓY\hGOϵybuKVQꅨ(ܶka!: ^1옟?U+swmp\o\H #bA*t&#]}FVWT8~MIQ[yc@ lkXPX\8N' Z<\K\ƍؙ1 xTeePGiw`3 "W'*lށ)KmuZ;vzIOpuQYmi4~l[gVTK(Ss탰L!*VRGЊ5W" <(J)a8\hQANARH3Ξ~딉uӱi6}v -C LVku)eL`Z4;60mN󓭉 + ]$pR sGUw=c=s[O5E&BVb|t!"Z D>|t_Rz`>zm?݇.Dmof+Mh9؇=F0L>|0pm+tZ FgS ??ݑ]Xw`W~\i>$Mo8^`w`w]%Jge{:l=O{*Lx6FPVک;=`w]Ԏ½*q4i5m ,h٥J!TѬE<2vg-utqh$y8؍㺆{OM[܎̲^UC K&j7>?-хPO8vޭpKy`:>|rP2^:3 />|tKxJs4j{?-{BK)5]܆񡭀&^=bP2{(XfcAhcXhn$7>%Q覦kˌ>"so?Oև ɇu ?G`waI{ˡv8pbi=a ڇ.o܍4`h:^C^Çn{؇B+┗V@qCFN|姤BRuu __ )zLrGXSS/ZE h):EvvgJr˳㞁9%1"fJ~u}O|Dn0 ^P=Pؐ@՚)QVUU.ZOLzSvI"Xkw/(ϮBJJ6~-H18B^b kBp5U^':t!ZڥS֗T/nI+a _^TZ(3)9R*JH*)Ki4c})/0 ?Ggh&VWgeqY`aIwbE5Q?jO`9AӁn \Y̞٣SB/*-]k=F/>=SbT޵6L>/$>}ӊ֋\y{w0;?>|y?Ӈ?_8wŖO/X pRѳ`v®dŇ})?{ݲfyKdLzz9EvS Y>6 ;A+Ӟ07f/ϮZSf\SMYSUW\-KMZU?;e]mC8ê5y)OYTSSS,XPB¤RJ>;/|aѮꒆ&jǍj_oHF{͡Ʊ旔7>eS PTS5=)窪),XI_RUU r_W[(dm~yF@]TUgÇL #7&1L(C_^TYOIa.I -~[))/WWO(n(EƄ,: ]$ iGxxl'9L70g_qu!SfkCIn+BVsc؏kXbSЎ̇ -j)M3]僃]9 nCÕl'yѽӷĺBc'r-q"*VGvJv Ty&w~~yFm\wx{g' @q9!kC1x0`yc'7Be䗼1Ϧ!W$ŵPWӘ~=v{lD԰-I C vƒHe5\甄8`@y(!{I 5>gC^;|O؂/ Eok!-: _q9?YCn,W 4(q⏾pפr{Nb]W+OBa5>|DZWYMamUUF9ۣf\AqkU{pfڸoܸi(,-cu~AC39"@z򞚺x+;8!%%%U%qð~ Ƴ:U=}zmܷ(c},`I3iNj.B"_}\>0᣻CF7ѓ3AL_у bz^SUO{7SgPEDzӜeNI,3) ST9,.NF0R)):K|[/h} ϑ.]v*SRKyVUE38Av(_Wa9_sЅdp/S|q﫪.(f.HlIO2zY?{l #X8`:6 z3CCɝ(Ҩ@DND 3 hUܐ/__x  O$܋8^ד 3h`piX?]ӌ%U34%k>| &}weLȚ2Mhf(Ƶe4m~!3HIF>g.j|Ќ {CqC|NX%jcɔ70S<|R|_z=ʄtj|y!Jom)XvlikqrXh=o>32Ax<>K96u—gh,L ီl VFЯV۹؇W|; >|,ח^>zyǟmmOp]|5 ۗh㞁9%{(}>@#ݺR%Jj q=9j^XUYR/kT _TNؓ.__W0(^dm__2p龯8@..dA KBvp@  Rnl%wCJ~ڦPPK񮠨KRM}Ҝxh!ōKf,:]h*;%?zBǒGg)B;W|SN7:-߃jj~xbUvڻ6, D{U`Dj'@Cm,SjBxᅚ,H*}"QKlЯ9 qU)лֆBў;uvar{Pm@7 WCkC~2'NTϻwE֓7q@e8B`>n(]/>|`n^iXW㔔6n(n)40 ]߇՛$^:6~?ƬJTB?UW CLCwBr%H%^N+0op4Ա׶޻Xdi~DQ$ g ߒy`Xȴ+LbEItNxx"@ `CWlĖv Wדaw8K,"V}9d/}:q漍C [jxds sLK=mX+=`Ń6¡%SCgO*@(`0(ҒQqtRz4\U#A޸?\+?tz ˛#f>۲c-gaA?vEP0iWmÂ28c,C,^-dy2Ð %DyY^bI.K<цx6*vEی %ňiK1++B#O6mZpȑMŪ~عMʺmv̙Ko~cvWh9먈O';GCz6n脣/d#k"q#CD:VӰ޼?BfmAU!CF:X :$b:y3Yrjm #"l܎6eLy[vu1g^Ja[L6C [555K_?iL^{ȋѿV  t6gA: [Iè&5 N&݄_Q Z;h5+E"WVr7ˌo;1dzFpwHkL7+Q> 0KAQ L rPB\G%-r:tcLcPe)'#-@{q!60h j,3Q* -_Z*u-.\jK6ӛB(2{>~cnS&4WÀ Xjb ('O]iRo7sl}DFY?%9/-a=+HI!ʕm:6h{m*D*`V +"$!,2˰+$AaX: 2'i1_,a{$)P CWUCLd; {z6r#KGVQZשݺJTVF0- g>̕2-îl)XTوJNZ tg;d5PVwyPBD> 8B(c Rφ02[%DO= eR6U03eHAV:uCҜFqaUj*/P|n&D]Cw[^:ZYU[m2~s5GeVQZ'8{Bel lM׊0f~]D 7op`ʉK71?;ynXLm8{9lPw;pOFy|~.\6=`p 5AheCѵ%`nQ:,q8^*˖\ P[vT*+԰kUQP̆m,ZQI 713*+(2D-CV( Mo(U`|n).vl=–t 0ա^ɻڍ]PBKPAY%T=VYimj:X\wJK^c+ [$є78 (%k(v3bymزpe~ZY!5zi6 Vb߬!"4(rFpf7D巚QSO%wZFĐƆ&a8Knu -/\x9k9FL[Tk]zisߟoΘ[{[v Ej\(`5r"ĝ$$˲̟\}OnO{s|3Z_Dc}|Yt-k|wn~ PwvʃxwyfߑMڿL:$,3{R'8 -W]7r [o Yu5?T[f$4h5M `K +ǓE>.QZ55;*Eo0-C<dl/ {Uz9l=f1Ƕ `tt0UPY bȦ52OAV 0ȦhCG?Xkm W+J!LE Vb^^u}lhzmȔ\,8k˕"ɲE][ +^!R= ٔzy6 Dd)5,Z?)RI/,&)%ZlOߛN݆ X7 ̀n Jetg+ΞgcEj27ԾB n1 >:MM9q1j%=_p6 .4s/ҼljһwVիO?` YH'qVW|GDClpS9SοM&g_WߊKNݦm@6dWcndZ<3ո"߄U))^oX?aбNCI?b?|>~둿^ʨ{GG4 Kr޻=MܑrQJ /D֒:Nם2G}Ԕ ?15Mlj2b+5+ XA)uCph.c&7)XQ*YRRBc_ΑLGk4-";q*]Uc(K )chAFƢ`/Fb:~GOzW{4=X-Z]uR=AghjAbٟX$.xǡ) `S,j\mDӾW~~ͤ/nz=&;y?.4ɑ~?3B)F%[$G5]kɤz1g?*'qLUD6/C[D§=wbʼnԨU2M̃(\ĄrՀ"qEmqٕSj7r8ݦ\|jM 2U^p5:-[{Ք֢z"ЋҼcJ *tWn}:* ZDc+lmC<;DL%|*}VPVfk|_]au +aUYZƺ:Ć; -7>Z}_>˺*Z4[࣋?ZD}dV%Ӟ8iߔt x;OZCG9g6ӣl ~}>]#ȺO=՛?r/[Pߚ??7?ȇ?9 ?; )C3ŝX1A#Üq$_4||FdYw=*=1d,:^#G$!1c 3  Jۄ_UqukJѮDUzÐ_#MzBΉe)> ,BɠꛚVxۡ]-m\( V'/9%ȳL1{>P΁fS>-5(VRŀlQ)D9*$e+9嬹?m+cW5u0lTSnV {R}LYTjM(c 3Y CabBG8+*'r(I;RɕϢRAkbҙ}txsoض?i{\<ɠ6wTO$àzex aɵ)`i_'e=í[oЧm+e?Ke\yŗdd!*oV5b3\xD*`ܗ\Ǵki\U_XάdECUe^jjך_QjD LMXCu*dَj8r%.[iT>ujyX%gVY2iQPγHrmM76n6OVyaهHY]Yi]O-#w#rFvC/qGq)2wt [b8]ubC/Z^1}CBE 5<{"̋lp.HyTL6,xe'3#K G%3SE_qڋ;Q:3q8]nwԲju5(mZ^emSƧƭ|ey)_imb@us-)MJ 2fxCs6uHty}s9rf+FwdYGA±EX&Uؓ:xtw}6h\UJݫۻM:޽S#F,+ v1 $ a/ߒ>hi_81LKx,ad* Lʧn{luTBV#Va<`#hw,!bkD9ehcV&rQ;Xdx++1)HF <u 6?^;tW>l^4є8} f[nWJ?j\,5#\w|JZ9UT :`*l7ڭ]if1~E#P}w᫠Jfhj\YVdR_( ( o]h#6Gd?D SrwT*w־I/eC2u6$j6;46sgj_,Mk3UCt s͔m~Y4EkveTjXʁPvI#3l^;CвV 4jZ0Va*0.RԻēNkn]V`mĘ7*E\)Z8x !XVZƮf{ҬdM:{&Z rڦ>-]ŕc,ڊXGB þUI:Գѣ#\J[*h[uz%-E`Xx>ٹiJW,\ {-lXUVExS%#FNQ@eNMd((BIjpX 3^/EibQ&bli0H6 uefd/ZV6miv :ZCkgW_>|ÉgN:KwfP' g>|u˞rk޴e3:%=jغÛV:v|t=^]ͅ= *`ls}C޹]͋>|0ë^r9뵵'CrѬ9`nq|Er=[3tnY rBNF)O\gi:.+hˋwDp*^`+tƻ)oײ!&˳We&U/yˆZ16/z\:B._Nw}ޝ<w]e_UrLsm<8Z Vpݎ_ίrR#BVC뫼"s놇cųYMU{zKQhanJl UÇ?~[q-E_ 9AS-bDx/&bY8#WFݻII`pxb\gCrGIO_Hڷ^]7)eųYMUWO(G3&C[ &ܙnIH"Tsrؾlr|3/s-m.bY83"WF򟼾\|4ODSٷs{|{v&M}!pFKM|I mm/DT|t ,lU|r<|oﲢ=qcT9&F ܻ,=|:^s5^ GE*?|I@mg)UBӎOKjH ?I/FjS}Jqw`sO<OpOcY'M #(2VdÇc=l7=lRdjP[o.UBZަ37!bH ?'uӺX6?Z&OוڇpXz !E^aⰪbpQDw8}=-~R z_TXzK·_\ȟ<a՜Pq{{Ew8V4-~R auRÇ!uVÇ>|[/D{~la(|sgэ7>|x"oxrKⷰ>|~[G( ]]`5mcs>|GX|ynt<+ꡛ׹{C1^8}ڠBѮcWo%DV9jH z⦦vqN ꡛ׹{C1^8}5Ç>|J0YחXΡ(1f. sg"v nzP ^4<axk/pןx;cG> Ot8>!Y 3`g3X tÝ{vk7Ɏ/7Nܘ,GkEH*.!+&@0.ȦmYsiS gȑux9ituZ&~oiqlvȸVoӠR琽ja2E#{ e39} Oe:I4+亀5QRV9Η^b(3C^AԺZKkB vgctv:*ij,uY' 4{[҄f-zZyq;oHY3ӥh!5@ ˘ VO(^5SYZ'u{}rr7tl5C9a[RܰGC0?׾e_}es<yeLDATaMvVjsšGqש,wKz~Of³)2Q>&rZzlN+9oȊrPN?ȼrL+_=HȿKLY*BmŌ_VrVMMhU۰@fԶ./ Wf":#RyWIXB%rhK־w'%fIH]{XcJ۬˖>8s-4߂FAV!|Y)Ь1Uxʻ}Zx vUeT]d9\FHǶ/aq8;AdS8w[Ѽ  ~ٯc}# HQvءϜ9'H]@Xbpn\lphpH|o8ս1m;;(psi\ڷ7uVи"+ʹw<[^W􇓙{K `1= ].bq,b\X!cVKcۧ+#Љi].C~uޔ{l~۽z˹J|n( qhnI<~\_=/G W&#}~"nnޘ>>T&ӏ6i4 dudU[2"_n;@vv\߭ 2ݼ.]cq}~T gR,5׽{nSq2XN>~lvKIFEk鈙:iCw6nMd7 DhUZVhC :1A/a9Jt'I vIo;B O{mVy tKasw*Q>Wn}<\³8?H:P $6p?yo{ ;9m7 &Ogp%,g G>>Ӹ9aCwm>6ݼ1K{:|^pדc7MwC-D6>ٰaqAnD]D<<]lN/IH6ݛ}zRTȖѰѯ lփPӷn71S-'$~0蓻`n$򉭣 ޿Q)jBs@If4]o mj!~@q=`]z}C}ع]F;s|L޾%hc}U ºG^}x8P_qg>u+-=akr[7]n=t%;n1Dn,ߢY)Ew.9bCx'sQzXBXߡ*r64ؽpt`XmL[ urn{| ?oҟ-JՃk Q D-@%Vp0&B̭>h Wt1ݱyE25neLCds`g.:탾tȐ>C&Aim5}{D2^3'\oɵCz)&qkgEJZXeIK $ a7A*dYvyG٭ :dIOOԋ#;qjݽ1N d5}}]љZzH]Z{vxs]|gkG?8\̦0L<>︌ k3O~|G KDvH݃^m=K'asw5ĭz },Hw<ŭ] }X1?uSf0pNE(vEuitIli9'dmdI ?V]=Qc!=U3ZYWoϒ >i9^(sWIxeGR/E3m ].:NnBO5~lmz.ײfVQaƏӑJ+Q^4=KF NnT^Rm;+:ExP>zBݗl^F7W n=zt@k~X'ϡlG٫dҥg a|747"@~)s>_Yaqjъt \sZЮ2g@u0CRRќkP+QkT GpirC*>L;>=27q HZ2m~huv ~iF֌̹Dk#K Z2="+!0 #$+a+;U\Ag/݋d9pch}.6ht|:?޾1OV|I?Pw>%uL<8|VB&Dn,r)lU(Igh_m{~(Zòdo%k`]EW5s1K?k?˿&w[7Kݭ\X\i9>^T&n^$F7|} -z 퉅;cp+,;Ѯ6N0euY(StѶ6h5k|{#tR+@}nݰad.7%Tuaڽ˷Zj_%ѹ iS1ZL tSu Xkx&w'}Wx9qAZo5rY )$eex5DWjnesDkژ~l7.^[ǭRW3y`s}aCm6BMU(m?rie j\3=,Ѩi`6ȭ] 3Ǐ nOWGxGmҤ7jV>@Wx:5#*yf):QKv_TΒ*:']W,ނfWR0t1cuA}j)3c>KD78uk0  ѵEC8KYVMO,vu^^_]ߠLj]/tBri|#.,,ag>9DT&Hs4hb*I$Ȓʿΰ.ez[Ӛ:?|cTi0_uƞ˽mAD:3{?]`AA3G(6`hAYTnA4vnAȷC`0x]s. RK 0񦳣~PX_" HoY K#k qЎ#Tὁ!~ 5[nHq=B35 :࠺; N\N?*-a=g了MJQ{k)g! ILY?tkv".gHoCHK̹w)>=yc)$޼ "'DO``օ2N>i#S'S0hfJZqrũR\RVKac] Ov)QvگH4dVyKa[`6Gdز@W~wmt f p@=:aG; y(քꇺ Ƭ,&}uyI I+:`77-`"t nHDzl7 ̔5!R<\!02gIM{K_քatqꄈyELroޫN&7"2!FinȰW:*xъ>t^]O. 9ypSLJus { 3!ÒkOL1sPnX 2R}^TH~ ಸȮ;d i.s:S؞^"x4d\|i8<(OӍiAQ\ r:}ҡA g\8 dPt{^Bzn4s4Uۨ}y_C"GP&; _tBx/r[\j%m{,L|{#KrxGj{|zO-r᫽F݁_yReFG˂)Z3+?F':W NG잝1n/TZ|E00/I&Pa7Ȝ+,~A#Aa7NJ06oL2=1g4vL̨ |N$O#>4![% Im抓j=]̀s2x*Xt;3La"T(ݷGaM4̌6iKJCC_(6AAϢ9VWSe$3  ՂEW a"ׅhN|Eaoշ #TՔr(:+Ꚗj :tw~@eO8hU &Ϫ*lP"E S X$R!0WúC!qاIuA^3#Zf鿤54QxuABmAy7&{,yGH~e'Bն[ϒ%xf#kso )["M^F-+tԕg )V \qo+7(Σ+=% ޵@I:o@كT~sŪ`LYS$fSZa;.5yҞ^Α'Wn|\7zWUaeC%L0 d:"T>LTEϑ᳌P$7 & 'hHW- t Hus(iȫ5HptF咖S\ u-a/s*;:,K|&sT"nԾ(iGÎ"JX[0lXn85*FeKh.B3~YmW9BËU 9*6FrچIׯWdz=iK:wsevTZvb͟wB $Ϊ*lT%J4L60*^``zT(LKGLS4n+ÚbY\SuTT$99OBL*5WJa˜tFsBsP+ , a 5HZ;L?e*E-ldXhZ7iPj$?hKx(-56\[0!WQ!G+YeRUgm8kQםsv[ %*knjaՑ!M=^AV;*AAdU(qujx+v}fWTKj)F0Ơ~Z҄EUO+70t %00LZD> Ö=O;0?t f9I$`4ЭJi˰BH@a̞K?WUaL0\EEa>"pƗ< YXj2i>SКw:@:JR,-abLOi'7"DKS`0^.F%ZP.It#"PIõ7o_nl9CcD6g#G(B1o  (uAm>N=aTgU*#c,糺umا->1H΂YhWVP XM1Ex֤*?+m|dΥ Ӈ)fj#r,ޤ`%Mt6aR c 12xIfs:՘cZkR{n9qaj AL>UCT΢f4r3{'?֟+/tH`vfzBW Yen9WAO*>*lEyfX=գL[2”"5vN ·T[Ij3GMiAO33 6ǠgА4=3M#_PObRZ5LȪ=Ѧ`7 n8}x33 #˚RpmTf%BM?ƉS ^Xc z>Mk>+w J-=E %3`lC;zqlMm=-|ͨp}H,#E;4H:,=8}U^#ڟ+x Ae T] GLAKkUu;`Ay@_"FAy.mr<c@AV)AA\:b  HX?S(aLc -cG.:p9ra3[SOjr0202 mokΐLF$ןY$ehz*gr2_*5[5Y}#UM#»_cPߎE&s)u2_k$O]uWT !a=6H,̗7rd˘Yz,v[H)x1zD".[Ҙj=C:!m {Nh!cg/k,մ35@O*Y-̌9^BRrȭmlj8(mJ$D";BMOcUS+Ӧ SvRmd^+0!'t O%xYn$2?K|vxc Wª*FIJz]~"2v_K{vҶ8΅.l??<|~eM L v(\jbGҷZd䡟{`4d(zMJ{/ o0'XD׌\AYZCO ϐHmӤ Qת%}듚zmʝ28[Ւ sN=3LT|ber7(ƐJ==Q^&mp]qV舅}&e+)yRA+d{0XJcX?5襝mHC&Lۈ(&ĖV.dL щy9JARyzӖW ^t _ |>&UY<$2l Pajm\!HA)o30"TQʓ>)VX3Nw& !NW?g˼ލg|]ѼYQz,Ad/Uc-KR\R=f̽5!1*:(۷mX3?} G}l\PR߮q=*fa]&}75"Tp_#&'w π gac@AV-U0K0 ,5YAπuΟC] **!}0FA%mrC>C# RC$CvvH' UoL.7h˃;(-sPݝiI C.=/^Bj`Q\MlI=ٵóM@GLY5Gj%ElN~L!Oi.S=>&5 XH&Dr>=^qYv:TAN ̳:|YE@t}fc:|ZKj)lrgƼiS_Qey搏 .Usx<RMoj*>"C-;|~/RsHnegKN)qiڻ/H,*(-AzMBY;H@ ha̞K,}2 dbʼ, g|ȳR>P)B4qhYc'/ 04&E ~$ܴp$x05&"!o|-42ͿqT=FE,}Z1;Rr2ɽyOww25b6ГU'+ [[}"H8L_5ogK&#* 9BaLȰd˒ojH@ AEyJMd0U[1H΂ѝ&82Y釳^=酱1-"FCeÃ4ݘ̥ Ӈϩ,qeݾ\BE77%D&XI>G#]%z;@E5Hv@XbC,,Lxcb{OaþSnKHJwij D IoIxW}ed#ho πxW.>,N8Vw:d9c#:^`ü(y02J=٬*q6pAa`l'dzRcev^e'5MrH<eIlR ZO33 6Ǡ8_ l LG 'shdZ ]t-x_RG"遶6_OJ)jrDn!vƒNl <q <4}г5NO*l+hkUZX{ []Zi3?;zqlMm=-|ͨp=8;emI?H˚Ibtg-gAVKh@'H\Ex.hAdܹSyLnAe  ;vXϜ9JF.h.Σ1Ad驸VѢW Y\@] RB׆bE  Շ+fb6`;8z(O0 H@nE}\v̿e?ůDav0#mp z&R!hy<+# SV ,VNKeUF @zk"Q&qڊ~ LdNǠ)»_8rC2G(s 3AJ Kf('N>)y-W[%ܹs;ů+$_o(39a3zvhk̘Xx dFYYފGd8NUU!fM.ANxL6 z]k,{}&ۚIk 5DgRPtz.!>(*}  ^`J 5^5^5^u:4s&]2 ,;O K؍?kkku1c e /}! kW>gmJ^BAe7/翞uA# Hq2GTQrAA yHxľW7 L̑+=ʅWd50[Ws/C3Io5[-Kdhs؈,f5c]K$,{ ܯю5"KaA21p|{MN{=}=˗fP[3?E/.GM.܁i3WHzmEg; !l Vnz#jRӲmzWeStby;:N&tiρʉ&իBYDfeZ5HBt1B+dB$dK=D ˄9BRD ڕ.DZyۨ : W\9T4C/Gsx'!}#'{{)*5G} H4I,T8;ಋI;!/b3NU…Wߌs K]HO0Zm46'4[H>XFA eAr+Ύrg]N㛐<9v?XY\&d~콩8hL"K_>ҧaJ吞BsD,DZw]v1őr7َv:XG?}}>:: g'HS=uJ+f-[%BYETʰ͓nXwJ'$(q:4HlisH %Vdxbua3b#^*ў7tEZ"AԴjfר[+O" HC"b"k ғV ! H͓xW/}dM#xO~>]9ş WS̖ AA`U|Ee?ߜtw߮jAd?:O~6~}y~ ǣj[TyE-Aq1k.B̖4/gtyn~ϫpBK?O;40k ]Q$;>5GA+JRl/׿LӾϽge?4MnXB^!謹D[HBq4Oo/_i<+?]GA89`Quu&'EeW7m=i9ɧߑJ˩:x8K=VG/l&]4!]92,}diq1sM t㿒_y %-pPEVgH&r ݪ!Dv>䛧>MneUkA)a9‡ q:AY}ݯS] ?9g#GGm}0 `Aٮݛsq<Nf{_c  evQQwao/m̮"UZ>Y Ǩ4]e)'=,ΐYQ*A$+CCk6d^ݯLO`5͒j)mL= ><]9_~{{{\H_2}#},'S?f%ԵH6+;+Vn8FqN{^盏yJ~2HHˏA8\&4rdSG鏮CL&e`4W6%* x zpz#jh~4*?f1>W'QK^c߬dXp RTVI.vCM.qV-gQ G@sL~;L*!:/|.rL!|K+~^eV-g0.ߛWKZuA|=2mzKM [CJuk[#/4m< f1B}Z,_oSo1+L% bhz#n-qCAVrBs R}KX>f[G^FAjch;`qIBAdނ~=A yP?nٳS@Bn%4 Hx"VA;u,XCmA܁S!V5OQ>.*J-nqѭp$,&_xI: }/?bT>|,h[%O,7Ԣ 0CL/O~~nv) ֬4ŴVCd %uRwwrTJ9, %:έ[\t`ѭ 0(A{rخIHo?#\?.*&O+pe |v>J:fHx-twQokz75..BsbfطU RHpK-ϡ;A?^ #R0[WbbAKl;m@YrwLtô~>L+Xm?GZK]*ޢʼnw|SלGnlJ$5*[\㘩_ltQrGӷƲepHCkxMhC bp?|_ $v!>&bw!*+BX2B|{'Q[X_nSH?/PmAGwl#XWAA-YvӄENة0 ,g}| pFA%CUQRH5tU%Hi`nZ׷(3#  krv+Tpeue/6r qݓp\x!aD=LR:&/ T$0$ȕ~LSJ87n\6yᅹ7D3un/fohideT(Լ B|c  ;3G?KL-iYyY) gj` g>^3]9ݧ|طĕmT+yޡ/Nr,q|J_J0u[Q ="R+MAHAVׯq|ƗӦ=`Y./a )W!>%O7o>iӦ6wVJ6e*/vgH 2L+5}ttZkr{ ]DqDAc jYj{By IѬ\Sx(%7[&0 44R'Yumy(8A8,/ Aeώ; 3gΔ `A).n.OrP# HT"z9e ?ߜtw߮0 ڈ%ܹ|7Mճ  7zE^5.A">|  Jŵ9Z?sS_̏NK'AY|`e}z_^w}뮻|ƝnM_$Y#b AC?"ȷ}q+ .,^P0%_ObĪ"eby\6Ty]o#=bī?F5׌bعy3]5m?ŵ;:$:W]_.ȉcxQ.a\z*ĻV>TqG#ZVuZ}IGFYi52bcz 2{m&sk_|aͶm?}Ɂ/;b>n{f1Ivc'h1ǍӲmUoA )F P PBw?tZUW׭uIENDB`filius-2.9.4+ds/src/info/img/pencil.png000066400000000000000000000264011506574241500177150ustar00rootroot00000000000000PNG  IHDR22?zTXtRaw profile type exifxڥu;# ބ?F7=DlVUWl5K&Rs[lW? y}hyFHX:{=oӲ~ c?~?}q>߱xN~,?Cx _0Ӈ /y3]^#:jYy}?n>g+%So '|?޵Lj>E_Y933IL#jZ[Tv=]str&C~_x$]:XCO2BԷ;Vdt޴_c>;nx_}9jl}ܸ+A? j~2eH#qߏte=o@xtb0.Br}q@V2RAB&7Ѽ{OÆHW!7-tc~JPO!ŔRN%R!ǜr% { %J.Jƚjjo@3JyfΝww.}FɌ<ʨ>)gyYg}*vRqwuv9O9_LȚ{fL5r '8I &:2^ +g2y 2)g)cd0nq/3QeGy3%~ț/35oemأ T}29Ddv:l .I^Qab+rϞ3蔝1#Z7ZQ?b~2gOFZ 1?  \kylXc f{{RXm<+dMn5Ù\L' ZݚG)9JN_G+8 3w|43%LϴlY!,"KqÂSL~_7sE)՜cтYZElgve@bR{>ivc6O7WjTʡ|^gkk2ZsB`>~pd ^GSm(@h~j0py }Y>ﱯML/Wt#"uAr YHY1ǦEVPhu#ڽ*> &!-{@,=#c<`ZX'r8&5pE c4\96I:0HmSi״vߓr u80i36)>1~á wS:?0k{Zv W3 Ό˱R>1N'"4(eB<+IEOA^,WFDY7j)d !#&8g'.B#2=?(kêpnk +t.{ʨ(;í:=Dh<=Dc'lAaH%Fc/Q%a_&Rwy+E1bȌZtJF"dCІiH\(}5&i@ g^:NEWnaAD(z;T* 9\H?hKgn&0<4&m%l ϵ!2} .ۂy423fчYۙ*g?@sٕC7O&9mc3n?+]onyеtuob,; sPyPs30 &C{ణJzDH A3#r8z50Һ9]{Dm[g~/[n\H^*@QEEE "È+ QXd E#?EKC#}# M#z2Dg+I2"?$Lq4L̢1i",/#v$cY]g!B*FHcY9 H2ǺcߚЉ'' t\ڃuL:4Nx/{TQ4Z8#31% IQ:M<ʦbBq %)jy" +gFw"A6z" :ңbh|æ:(^~4WO 3 "Ĺ=D& n0I!bG:G%Ƅ;> D<7%Aah%=APLƄ10e iJۯtL  0ѱWU(jBtYIa“{l$F 8^,刨Jo@FOw1)AyN!5{jꞶi>HEAMwHȑY:) C\D(u}9V{tPy'Wμ=-ub߼S{J j(@7Pp[Qp>PCqbI7:(朄w\26{P7piCIWR!avaʕIb$#]R)G0gIhqݛ I7ѯJC7RwѐM }CRB.XR@%ب,;{B)첮+LUS;h˱r7toM/Q+<}m3'|]M?6i",jIHhCo;K)bQ)%p5eRNywR@P}ydyhgZ#.v f ɐN`!XP'3eY.?$i= Z)\ޥ-2vOVGb!>. @Gk*#"lKSGZ ^C!QCKkk(pin=Azx[ikI*LU. <qBԤitB%U"[hJ?E*`u&an.FK)l؎\UA:< eL]T. PXi2ьڂ 䊭ɀF MhEDb|p '䣬/6khLYAbI=9+~FﵟVhS`-yH Z A!F[Y@7kۡsj /FWLʟ&42[KVJSU-Jx'e=P<2MgoNԢa:EFP߷.!} KAϐsFw,SJA5+ w#zij]xp3Xԁ$&a>9IEq1iLѐW`hlxR$1ܖ:Mƿit2aw'c̱aF`,Kn=Qb费Syk/Ք^UA m1 e QFB4Vq}Rɿ;h1&xZy6&n]c5ܐv|RRF% Šd31O1eNDY '´H8n=Pi(,յJ #w#Y<ūP4Am1E7^YEfD|"VLB6GWָVE4*IM/zs{7,gx|/b Q(ڝb`- a^]N͒t6D/Ƙl@6X#4({d5L`jfqƶ芤%ŏLύ HbX #*V 45|-PO/N`Jjb7%4zφQhLHQJ4MĺД=P398ATk^RC3p>F  ͿMiSL9U!`Du|4߬f׫T>o fIx4dEI"jMF*!Ie'b]Mh,[=Wfp<,N!:Gk &A>EmjJZsAJv6kw` k I^«1(-Y[ѩI]ojN0#1 !S#4}WDVfKeHGAE4L0H)1"H(౹!XzaRG7laO6x5vtt}V5>|v4#C"[6DC潖+RՑE UYtY`+XaW4*Z~N`d1"KIbAW `NBkZTcA h^#,[6\r% p0#\ U]?kxjo: C@)ByIpl2[My1!Bm2&$-_G򼽝wcYW4?|[AQ8lN|iҰ^+O<:֊VBWJ4>%/bժˁ(I3GKg]]9+PzĔJ 7],E&C4V8H#4,"DK q{*MaFy7mu3JuصZ +nA9i)@!CDE, 7IRԫcus@ܵ ŻIj0QB'OalGT8WZM>\~GfNBP  ,+}tZm^f[{SMW Uv2vP%T8Ĥɻ-)z.&+Evږ=HS7 '8"񤼄ޡ9bUdOɛZ︫2-W-+0 +s3Lp2jǗ}׮D&u߶ [ pyw;8R6.T FXJ4Wp" bZRx_@|Rgb({SVPHAs#H* Sګ9Idʕ7٤=ce;`YRܨ)|GoRQ>ѡakR?QAˋ~#>lB_81&:cLFE1Ծѥf12 R :]ЦDY xJ+N;` [GbWNMJ˦pЛkysCm=! Y6[0Ȏ#,Hވl~48OGVz/nw͜aܼpӴ-YL[gahyX["_dsĜ$|kwC=dB,:kAq 8Meo߁uP%rAZ63[ʲ&: ̦ d̡Uڊrt@>D ނow }hG& &qjsz.7ꣶ]БUഹ4U[GS"}%l zKSLi&p+65ziKbj+ ]W̼H\j31W-p bA{YɈ~,VmvZJpqpd jw#gׅyu-/bǿ]OywKoTaKo}鬏[ `Ӵ+ڗCIQx]`ˡ./$U%G1m#vFV]\ TrklǤx>u %{g^FP~_O֒>|גޝ4֏'ͿIc.ZwX_aP`,oXt0a[QZ:uX3Z7Z7ZQd5?#A~9c~kOr|1¢IRZ9~bDk8;}*RNf wBLN|iCCPICC profilex}=H@_S"A;8d:hATQP Vh MGbYWWAquqRtZxp܏wwVb6e$bQ1^=L}N_.³92Y>xEA bKGD3&M pHYs筯tIME"IDAThioTsfm6xLb6C JDRm>HկhjKj($6xˌg3؞@F9y>G:s]$5D$iGC$dCLܑ"I1 Il$..nqG$PpTV,/I90Fab:Srl6,q>y|+^#?>:W9y=2} @o>#E\dyhszB 72>g^;it_'RXΖG0Nq?Qu5_ 4 n^ĝ/ذ<| nh^/|N0ѣS!{V|DBQb\xmOZr|GŽ0>+:,J_8}f:̵3}ji srPkkQ,ov!{Zˮd T3j&~[ idfI"C0m5Ȳ<;ɏIVis5K &XZ9}mLXbME-qH"5?Ln͛Ipăh_ġb%(*ΠXi5~.ūJgޚs4§P3M}u9v aYh3~" BH̄I 0s/G:AjzhSWaljMv",KDȠeyeEel9DVN sko\ٿlV` p h4ld=g(1Z\{KyKeЈzfG@DGkDMoHbboY_:`qm";3I0(n z]*=c S_h >oO*Ϝ '>!1zְ4- !@  b^@&4k5VXVv]ߤhUc˯k.6@X%!6كALjʠGŷ>~,˲Ϛ:֮oGX&JhdŒbYt&Mi>:Nw'?5;./na:JMla \j/}2vij?`ce*W"d,-Ftr54]iPײG?~Vehʶl r3e Sώj!7ʑw(%..~ Gi.D*l`,_'F̋"Q$-k_t}G'>4MO)ZMqmBxxB"1iw* M…vAX K2VE]6J_윅NܸzKIN00҉*bkNa_ Ķ,H'E+6a*6CDn04wƞ7ƗHHH\xn|8ֵT&,EǴL4E#ϴ7!5 q>N\|z页Ì }9UaUW<?Sf9.A$P6yGt W]I)U=ZV*1wnȔN3wRPT'f@Z_0O0T/p G3^qm}_yC-E. е =|R.gJGZt5V > }.RP3 KvDc0fLMx_>4VLY$J6dt9bbӘjΖ3 XJ2gxE.T]rb.aw0Gښat<(r̟j`xT]'Th5% _m>H6~/Zc+.;rK V]0[A:uuAߎJtIl̎LQu$R(t\xKw5*YqRf,5: PͰZI[ !/x IULzQk~_Z_p%: (1+ /4V糺hZ)ɮ.Vb/U+JQY"Ԝe#YVpH׿\t*Gd<B$RqoJںꪫV-W_}饗TӆpرSN=y?'K 5>C2=ryk_;~M_q2ڙLDˋ+zc}Ν;K.!Y;  z{(BQH"A:с~||/?{/[٫tP6WW?IS7~w  ua0P~||?v/}wU=?s3o=I?@#b=:+׷}J%%]!c'u{7D!Q-D)"_O׾[ُǿgWsCJG^[~/C L/8òQ%q22/maNwƨ7يa& K}I,9%53 H.!j0cS&AyFdHOic+zK9oR:ǿprU8`JeXJx@PdHN񽵶ἥHrX?齷DВ~udJ8Ob`t}LEui%taQ ;cr~T4\P40eVa>Sߝx_xJ+1u":*!aiJ@bz!HcgImV1a+}*#x!]Gi|x.ݐG֋s'9ϝ8zBAԞT<17isBCKu Xj_t5pLzꩮ㢾l{D)Uk *fg ^uOq^NqCKQG0E[:pKyix!`؋ m!2&O9:F;@cңJTn5ٖB/ f2j3^%ocɮCEMTС~WDoMꗊۚ&}@P M֖]QwudJT4 ȣԍ=$94 艑B_$TJLX,RN4rbr XQTǤ[yLW繰BU@^9pw[ZTm~ /4혂2WjjKgA{J奊U=0 R+W1ؒ_P /)T"]hԡ2dxpaAu`k:ob5: _XkM .;|W\AN(W_}:|UW=>yῺoydpqxS=HD{lo\bo4&{;dw;`H]~ůq(VtS<7^t?7?g G}o|ӿy |Ϳ;$.!G7w["{;HS{eZ=٪NCz:EuqjPRЅafEM.H:_u>+vvz0!ĩg6vrY/.]/>eD~}hQ0xO?)H Nm?'W>'bmv fK#W'^Pܠ&?dgn{NjhKW5=IՈJUYgS~3R}Ggx|؏Ϫ|i9PrWz {H\ߧWXqه"tK+wWoއGQst;N5:!Ұ bRO}0uh0QFRM}V!#ijX;=(%Y#] "w*(Ya=z/ɂ}{œ<_,J2ax R&5{ygҧq#Y^\ARڊ=Բ6|c%cnl;Z8m^.;zO}1Ճ9,֠k]JJ)59 :T@x8,7߹OiP U6B=ZfLO6XUDp3[d!a6^ 2a6 ϶6sks I&\ƬL;]JXPo. Zauw*S>!o֤]!-TN0SDRL:eXdM)̗ +ڼ=z%̞`YŤ`R6ZovF̩L"~-:̔kegcV.>`_[]q&NYR5ӹ?9,Ѿ,FI=EQ+u%.VrpXAYL HK:k`6=FͦkనP+:f㍘Ԙ9Ac﷪' 8TO3Փ]]D'xJ4..+`&/Le=J磪 1dL!Ֆ9e^"ﶋ4it h)ʮgz ,$rE^ŀY2cAsVt6Bŷ.I%!A]Q([1rY^Zzsl澞iΝWoՃŎ BQ3pry`eYnk .W}SA!v&2v/<* _BImfم2ͅ` 1 0mu8ʷdui&+DIZs VŒHFRe6--"ǁ]"(Xl=fs*|#ʫt?#֘N>əiBj&WYV}nE̬ljU#|}|:ߦc$j\>FA&b3 :Yuf]7dI|anX4A&RϬbʧG}Ɓ.G*ő*#>L B5bAL,z~&21PC !'U}CP)eg48@j7V5: IAO* Ί&F@ 3&O/qj",Lc2}d`&iyʯ)Ogط`,͈C~!pDs$žNw>z2V!!o;̫U'%ihۻП^C҃ @ Lei񉨚[׉%"$ժY_,LW(@?b@ŗ7 v'[PsJ|J_Z5k^7ً)s19RFVk`+׆p3;~?>;ϩI=tݽ7|9_ڗo;>BC28CV>o}읟]sQhCΧDaQш$)hcS:رwwlq)7A~;_+o_:o>⦻o=||O9&>7zW֫.[y:nyiUsx.s-YMl|e. 2Q92$ktާ ;8Bgd k_}gl{~/ TMUeDhV$D]~NS|[w|͇}_{iJuryZݦ|wg>v}`ܡS<6qO9<%vSTguZrvNa+~cS >N 5"k[3wzկtL}򼟹rqIRbwo:Awrk"E~pu(#|;pDOlVY1W$N2~uCub-uj7p}v =1f--܂jUOlqo 3mXh9X1HR;I|4iOattNW#oB$zf>/ XDa0;snZm^)mf,hHV_ыwJA.tRk::eHِu+z8'6XǪ7+ZmT,ZWlS!\y;6gm^ ЍOXcB]eYQ4cw'WM^=CY_`1Pt"Pf \N{X,{R20̙`EKb^iҚ4UԆy^!S{'SV)=<T3dS{a&_a^3S]1J-dM 䇴Ɍ_A`KGgfjY&hȚyVJ;݁3K̴xԂK¼a3JZC E*:8%N.Zm\wjHr pEV}5)lZ(ߤe1okn%g_ыx43fFլ>x8>L~l߲~KfhZm1 |q^DPt` C|~9u."+_L&)dY4!IVC^U4x-+]Qj&)xភDQ;\j5Z ",Jb}Z7'%^҄8TR ]^ Y Dྴci'(ɳO/[}Ж}MWeoQ@.+(,lkxpvwRfA_VuNVV abu]e܁\Y^KˑFn.ޖ,d`<TXӂv.Hu=ȃ|kfQǢ Xa>Z;meLp%cD AC V*AQk$im 5KTiC(8_Thޡd#23ExEDSҙ xXiˇ\d$['j3zvIƦYEO{U`:`X];8PIDU`=Q H0URf*@1%`,29U )j̸^}H,!>Xb|견S[8jsXDh΃: g;HDͥT>͜Y=AO+u`~,:j75Rlދ% jy,W["TCkZT L7W-XѮCbI4SqM>-rztk.O\AWtA_w2oj)K9+:}"XJ2bD?525Kfi,H r٥p̼uT)I$aZK]  ^tҕ,uΆ+3`|D-ʓ2n!MPHX/<r YmYZ ^lԞ7_,~z: Z3 'x!6c.vKL%9ӠXPh)5bƀ^C?-rwυxm$Y]9[;KwN88h`jWt0AMt@ ʚ$Qa`f0Iyvs"8vWC.M7|}t3v69VŨ;d5NA{5uL'QPQ5)s؞SOHɄb14f|o`̅yގ̶1n7D"u_䅬W#m>bR]%juWLUMCLjtk`ǧէI3-xpOLCL Fyv 3gxfF0`CMLj|.1`Z,CL SbRΛv$UL#5yV7 XWju|yDi▅l}a -`d- 9G1`$&S~&ֻ>dg!g ]=o^v?U26/1oDZս0I6N}7w ͷ3 ~ӌmϷyNeMc^" o}/(` dߎVgv &OyT8s?H筿pѝ>['}~oFTKam_?_>v';׿ݻa2ΧO#-/jVo#"K;EjjQϪ"G_j$5Q6lҗOQ+Zvke%X@쯝"tw^_+A/=aI׹]ZQ+򂟾@4{aR{jN럿>3<|hGD* hvU1V3n`{s,>ˇCw&#ԈmAI0lWkͳsmpֹUg>ꢷ]?m?qtSQI]~0w~3ᘌ _<_T-*/]Eu;~>Hx;Ц{64B=~+/ߺ4)ӌڧqN{9N>{?hIk{L ;7}yp?T]ݛ~t:{w:L}XةpP`}^Ph^jD{=۰d{0 ЧUvBq!}36&D`_ZrD.?٧?G?2灇dȡyqQcqzĿzS$b%q0~2n6cgأ]CǕo*]g?E//+gM|Eg^?z_]_L8w1Bq1bc_-bni+g`%?_>ysssccz.xu!6k'w^o=Ck'zZW >; 0aIۆKh̓iT\o[yC6j:1:}|ȩW3a:#OGw/!C%(9 9ڶ8-iC]~~G:<]Q! W 8E~"өsѫɊ}^O<}((Ta>R9JXİzk_ǿV|҃աPbeH>B[Fpr~/@(mRbEMF,3w-c(r $YZ(iR`8[m4}GFaNX$ٲnG  l-QSJgjxi8tۍ򿨚!ූZ}.su@AExn@C[!B1gapK@D@ՠmvxnD媯Uެ%Ou :[ӧS\,ZVc9Ԃd&2pgqeBNv j%tD!s~(| MkUXroWޢZC﯀sO6[w{'c_Xɘp 9(P"*.yaH6Kh_1+JNԶ:'aH)Dդbirխf%>m.#fHVB&5R­J6pfbIgf88f$'9U %bf5N\\4L49PԦR:3etI~xRe f[4Lh$Ȓ}9s(c$4`m"#r8lqW@쭥ƃ( _5qņϛ7C;01 G5I0,aH4^SϰX͢[qHՄz&ƌ䊾Ԕ꨺Ce@-Ev +ON4!!%0$,RAF00XN+ i@]}:0)_4PoL4u3Y@\84{ f$ ϵ)Z-}2pӒ|b%ts+ )Gw\+xYd(qwH'gEDz4ʓM%tzɧBe|{NDe^x%⁹ɔ{691aIOj-e4X9^BNہ3pKT'hFj_/߳:G&]Bkcv%`߭H$HK蜺4dN̥~15h娤n)5:u+zX'XS +!HC&oUSڼ;zi $5#7^*xBAvu-IWA}ьshFNǒ4|rsU1*-IU TA3ҌY򀘑-<*TR5`JS@ܥԮϢ X'`%N[oLJ9%EY@) Zo hFՐ2KN֌hHB!ųᙚAڜqCjoLr`4#HЌ419&4Ju]ŮM"DZ\"tfm\ P0jDl}Xw(2mgV*KQ60OV?@֥ZAV;쪵ȐI>}oǐ/|} hH ,,U.=q(jTe6Me#P++oZ7Q$uI~0 Thkج›"wєZ^bz柲apW@S2pgN+T.x_|<̷(&rK."ghXz^jk\£^~a+% t1pTX&M/s9BNtٚX_f4Lv6 ^e6zC>]ٜ\W ә8ۼjVٌWVwJlZJ]jx$`4JBm%޴8g+vZ+uYO&aĊk#Z:%$)j*ۿʔ  jI3%35A V 5w"r &]&:WX^ā/6pieH1s]]-W|"fe/2@Pօ oFD,Iװ_XBOHވ(%/0ŗ.V0c:!gڙ^<qi'ep,"]&τ@)}s:G mQTz?534#!L/ պկ~_ X8PJ XD|/CF W]uH{ǎVU.䊚!#/h':tE8[_ղӞ<{1)/xӗ8AoÜ3Ӎu;&Â[FD{ 7,6x~P[G ^:1Yc|ռ?xT0o?8ܳ$d}HuSU@ fQMUnJ+F%wGݿW#'䮭;Olэau 21+GtTxb$"rr\V 2a U">_)vO޵3$wo#wü0뜲O˒UMj'n @4ዯUya;hhgSұe\q/X?vXMdw_ r{F;ӤUwoo>ٽW/Z%U=Bh%/rN;*3:?S/ [+6 =D56ft@sg~AQTb!/[^9$3u23d@pa߿g~~eŽ5UUӷ>.t20NæUԨSd]NWh]P&Kn=;I#V1wTy^7=G{_W_o5hUyXq'^>w-]|7ezԳk}ߡ@M:}H _.2#iYMњάO~7+*Ȩ [ՂK\w4 V]B1'UT7{dwDvU+ZAU-ٛܥHurHQs /xSzVY~(rgT\?QfT<Ї+!/O9V%lt[^sHUpuyuaISN .&zgy[8֬x<#oTL*K *?(̣bwLCr\I&b>xDGdsT򵳆dIkd/|رc\sӞN^yV%( IuRv~taX_-Z O*}X4^%aGNqXUX`觀qrw+*#;aݨ$֝QWʜw2R;UeF(|H˭)y?x}.)w9kTT>xܻ>混 7>-$!?WRKYJj_%C)lΊSnuH]ƪMA"z5+~E:]tL3`vr{Ėes-iEtkWw$~)X%zƈZ=y+i1c|w?{;du<ĘT/dsgVi5DW­9Ҵ*ֱf!͵" TbL /wwʷJy%(ŨM=EV,>jSfʎolc>J^9M&M~m)kC Vy#󧮮ɌI(asb(/IIRblU$_DW5,>c*| {{WD}뭷nnnZ⠀ ۯgʵ"5zy_'K+hMYR➚СC+8|^X%.qV1?/co@顢/=w=aՂԎٖR{ qL~P~SXZ0+-aŴ|XXy$ tMGD+hX$?;#GeP$D 8V/mwjn[1+^sg-YZ,2 mpz=5W*!:%)i??Cq @,&.:7v7|S8-(`IY݉)RP3DKٲZsg/䊗b0zM[\eNXcLb1 z>+3|Њ%g"9$͏;) ȴ=VwU~s/ ;?_Q7wh;ʆ!O5JJ- o YzY2s)Oc6C?*)(ʎdD߸O̿]PX$]E$@)QCh(\^Yos`hb%Fc#.)Nձ9i1^SƜ̯2g? f\a*(`U,F Z!R񳥥be?bg;5[L{g@.аfΖh`xh$+b]G~";N*j-GB#6f$ NiBX}a4[ֵ߂\)s<  7pبgsпcXthGsNO vdƍBKq{!#Ӑ:>O [t~ce@4J)Q}/+MP^zl+<7,EAݗЉ_8LNQ%&2z@ht6P3OQ#nXҟ >SI7n$6WX*)ӛJELus KTX&PVioz6ь@' ,ձW_BAik94KdBA_z&l|@=K]Y ZKnHHA&DVWԧz|c{-ڌD< aN|BtlyCQRh@- VATߪ\a-ۙM,Ӻ.h/8a2.!3 TASVw>sF-p=o BS2٬ηjZ>ao++%Hk+!eV9Z*mD_p? `)jj5 Em5/O%͐BY@E_;P1[úS8Qźɸ!{J6VgX(=Keeҋ:uE6ݛ.T=F h L <&@J{~ۇp`ͲTV!-ǂZek9hkt BT0+sZ9BNA )=fZWeͥmt[8f>T-[;K ["Jm!wo,nFґpS+,]9N XDwXVv0!#`*ck :oi~U(.G7RXuTT[[սEvA$`+ 뢠iૣj=FPEͷ-#-KPѲ*Q5"ʊF]YL 5UJvt"<%;ΩXaX0S@ĩtNrȅ*. uQ[U(00c2lOP5ijʽۡZE#6CB[vr4R~WԢx,α I=ރƞD;=XJ4$(W#^ZkvJc'fفDz3+Jq(РnuF./ XXᄡ宭2A–.sI[r@t0#9iܭk:Ћja=V f5h%RZD>,˵ ""pc,m!TܸZ6ʺ}zT^@uHM[V>[v`E҄Y)xG}#oE(ߘ)Ӥ*S5 GO|@2GeIÂjh8IJwprG%FU$, MwVd! D&`+.HDWX00 vJ*aHDw&7E% Kk p{W Pٌ|y")Y<+CQ AX$ґ+cf$AB1 a64؂oX,)V݆2J"'8&s9=3~g(U?ŕ NN!ɡbL`bf`\acˍqK#(4 iidaVMATb!В/X?) Z؋g 59ZJR4@tY j,Va;0c@2j0 ٪$0.߁CILE*msդbZl}J}Y>>c@t۰v)SH3] k ~f0Cgk^I^xۄt@!^]wB ͬKq!S)K%\L\aM d2x?$֢uQd>7JX&P7ۅ WiվVbZKEdDqdzVԺvVYQlRp:1td7 T:*FdWHl+bVInsmQ$0bs|X갂e,4BveEĶ$C|+`sIB\/u!#!B˜@9d$%5qtW" Z%.!fKzYhD1r"uW]1VdS }.Y/T_~fckiLe,ă:߾232S:_l~pjHs 'lpSZp3_0/93OЋD35V\2pšjK73y]vjh 5n%'>?=oHYXR B$T+ބ]u9Ri]j`aa<*% {v`dRTTkj ꫬxMIK,xjLf%41-Pm)HҕezǝٺqFF :Cnġ&q"6ǴYQ+;_˛X@֢u=͙PGAf sېX;ojeBleBkX,l , V (MPU c4LSZʉ##ɷ1{Ċ# WKrЭ&xRq,iU;f[(.CdRa&ﮛ֔fy٣mi/ Z8˜/XUMAu%UY;ch/RtM@Kʃ,jCNΥYoQodr @-4Ȩs "sx& afz<.NHDW]BSfV!% N*А2*+fqLy&ΊꈅD #ST:b5K+eTn lu0XXagD763M<3dHP1(`xK ]چ =LA~qtu6ϴU{NdӠwT5D(x15.P-P%@k],;qz* S}zHzv%AQ%f"e?ZXu#}8b .񽲈M:!R u5MLTY@X mV%c)(O1!"@ʥr-mvjB 1O,G5e4Wm'oWb`aHEL *!zMKhZ$ǴT7[R3RfhcN 2Zi}2!fIl9EEsM~+Škt n3dw6#M# j6Iw -7讬t*Tq8ߕ9sNg0oQ#v N4%3~1o  H 8$ (h` 7w1jЕj>`Q*<Z+n[U7bю #!q*rXKmmM`<ڞiaymc!f?B [roi:J,TJf[K\MevR9빩]zRf= 1[ɕpiu", diM*ZOʕrǦ霖3XsШ^N9Kb࡝i{aqtnOUgQ~@DU/EGsJ́ ZJ,͊lv*EP@t.VF!kUqS*āZfn@ 0 fvb@䒱QMS_o.Sm԰eMHDW02hd` "ʿH+TVYz9~UP SFLY6.4`6iH0naf~P~k~uqU$KY+V1)]17%V}h8c4UC[ZLohBOD.3hvȵ0<0%fUr4oI6TH J4#lTG j_(-Ith% -j, @2^0bR,J2eى$TVu{PS嬥a-$`3$6کY`mX"pUVd1gH٦M\B#}wslHLͭ) jfwF/ "B@F1a&z0^XCq3v́@tchX9-+%1XRJ+h%^ˢF &-;*(Cp~˸a*A%10`B,8ټ F$1 [ 0朊fk2Қ㿡?Í0>p6# Z^Ͱ%$QSTX2,$6՜Yچ@dr)ċ@dL4kt&"(Zh"J"#=l-_h",=艅@RGV1j6W4в3x@$>tWk*P5È\٘6 r΀v`Iu(qҮ͐li1bV)S  AeSފ QzqmUFkt!YI'6Jt@ Bp\0 iZluƳrƣwBDLzL7 iGYh%OM?_&e@acu𬾎:Zv6voorJ&J@4q h ɹV_e:^ݎ)&'+ ` ybXZ}em݊RՀǜID?TV 3 .[ HDW$)O,OIfBKzo*VA Mh Z8|Pr# _ og.CB^u0(< +I:-iK|pH+EߨUSOn f`:3_ )XJ$d$YbŃr3AmV#^ ͓->IF .,23!P0 zﲰ/d6(p t)cA3i'ɟxVQ5Xn!ʁ4 9@3l'XΛ"4HB߮DQ4d't)K4/%`};,b6k^{d̙ ~# VnfhFj;b9V%[uH#qL!k Gbԙ$URb#lĝ`>DF  >{ "Ќ4!JDF5f> ׉ЏkD/89z~fջ{)KYЌ4!m,3DyYZ3R8e*L>~HSRg3Lfl2==or%<*g"oyJ%{33GbncJ&2 Z!d+jG ydB)]f.hFѐf4#X)8s$S %`L)PY2*CNS@? H!r[8X4NR/x^{`4#mH~䛑dH_=$ɿ9#m2\WSi4,O'>ӜopfoEݛ+Š%pґf wYM%Rcp!g&#Hr46iFO*Iu9,qu5,Z| 9p$NoQ Κ*ڠ2lH3?ș ~R?x!dT<`,{؆fif#' EDՋaKnN7+mQ HsR29CjEo&kFJ f%HΚG esWվ^ڔD3 Ӫ<6ZuˡYPmW9̲*5oj*a~f0OǖJ,ؕɪE804uWNT>I{ ,tWPeeQwZB/y BM.M6Zf?+(5p}k1 --ݕ]˫.FЅ;<|3WroҽǐnFPq&.3B?3'lU`U LMM'AuDŽ0 ߉ڻ$:Mj$+E`L-]jy,W6qRj6M(3 g"S3LTcn&2aL5-3ųR]ND Zv{ D +PJIa&Ѩ/zY[ͧ?n$K$QHvÆz-[*+yɦmz"5R$bHJXz^f$7Vΐz-mV0A֊Z+ rʭ90>䭜c,b'L3B 3^qԮX9(,Zw ]O(9 \/S@ŜGe/ 2a>gڭdݡỮ?A.ٿ dwkur7Dnp&?Zl{E zab Z}_~97hUYo1/J=9 fWAWzfovJX%a!-_~y#߂{&krjuͯ_7gtm kS)9ec~Hш5ۯ7$c\~"z྿l<>͎&doU|VR%'w}Ư{F3??~~lgјG|ײت1l<*wur^󧄎G{;.{0,O=>y풽=V+*=گlWb;['}սON!9FΪg7Ȇp7{w ɯڿ{/#*9RYH{K.r_@L7^A FqF_X%V3̸z˖UKvl)Ǥ俴bsL[%< F8ubtΡo[rcq=pxC}z:b>Zd}g 8&:ԬwXI[F_Ck_ g;7/|+)^Rov-O]fﻤ[tO~n:bNe6;{Eчc|V wMwMݽJf忕[2) wdK-\͟GMBU[ŠH4Vn~.xơJ%\߻B_<՟&Z*1wKw1Yk( =( Vg?\9oxɃ~ڽ8?UBXGU[z:߼?} WZ9U[uȱWOi,faw[k3w7;{@tX*.{t%GowlUnIktɸSy+Vx?FFiRn?o}8q%r3g Z}ُqoVLTw'Oeyۋ5۽ N}sQO&. cf|@vH^k Z$Ր؋>|3G|ߣٻX:ۢwNE/5wŪzC`8">}K_k3a=E*}46LWs?0 ˿19Y+.*V+֬dqtyɞԘB+qivdWB"&[r=+GVYXwL&PW«/ JS"-7xT[6޻-{8gLF{w;GIkQVYCzy0$b7/=q>/x \p7vt7|7|O}k]_Xu/ȞSG䇟/2۹.@(y3}>v}m~GWt5d݊W<!ƐTMpz5\rMVeVWx+8rHŌ777ͧ~6!!# vB>l=ػ׫7{]_2ESzmCd:9n9TX :A4 n*Q%6`b1!ؑQ(}Oߠohv6 <֚ =܍5':xv*&9/ш{d_6w`v1gނ_Ȑk "~;*xT JeqUbC.NG8^۸-"2oy[v.}ooj3uj xPR gm~ CIV*x0O׆VD<0S[Z1s[wY@6˯<&#'dSy'Du2F9VaRZU.4  )V_Q s2pu:6 hr]rD_cֹbdҽJȬ-6L+fGձؗBhy}2s!eoKy.83o:߭VvugtQEV2-z7G2p)Wl3'==3d%LMKr]*fGz-6 3 )vVB8Ulɵ5##l0L' B /LwF4߂N~lӸWloOD/k^)|<wIx OŚXŕJƅRE3_1𸒂߹\4t:Bx`񈤒'Um9g\gͣswn6̥|<ޣe)PU2uA>뛵-p#g?wM_wu@4}坪vO4)5.SKv0|~#{r֊uJ/zӧ}"1pv.d AC]E[(ro i-w 0@ 3Ul.Su"01>읝*1 {y^1iLcO2:|oщws*3GV4ɾTQ3v}38SF_C$l֠׿;λ袋:TO~Gu]W`}+CC#N˿wVŪQǟiLcӘ@>Ʌxo %?(Έw}/ybkVYd_V"t*t}8cnWܸNXG/˂VD=s .ޝW?G( r2:GWuaZ6X2EyN{NaӘ4}"X?:uԂXUxnQ<i4lJUV 0=L:X}`88rG(mOy a0ӕd|2287zG3k->;0iLc1|sa4t D,"= ̈۔SY$`@ &qX޼B ;R56vB}Ƿ }y* *@ yx+3Ͳ3TZTJebzZ KؐiAȲ.#h)ʃhIc<o#PeޔvŊ~5!g?,L-l`ykH90eVA{tN>Srsr3Lna, nGń =Z1*3H9qv@  0訃", pJ\ůG"`AF將`4ldNS_]m:8ւH̩C%8glHꒉ+'+jZ`Ngl<锈07w5ue 8\|Eg`#l!yH;ӱ Ij:V'nrr:0lAO Qcy-(,dT %f.1\(bTN |ّLN& ?W0iSpM"uLl5D6ӗ闁vV!~hZ T2˅@E4 ˆ?0OpNByr[N'mջ8;g~3t@ y ;:(jzo 6}+fQwjW):YPd-1]ڛ = n_sԾ^h-XnBc79R/#!!b?`NTš#lI X^DN#xaHA"ˬ y!hMwhe'1xqzr ۻ\reV։ϘUͪ^pu,*('w@}p̷8KY=8wN_;5lĩ,Lssl?W[珪ToU8}o>G 4t@ yIO&VwvbwK[`Ec4_AbpU-NCAod$r+oM5gflb4_ɼu(t`k9J3Xy¨SxG'] gAi6.ԳIQLS :1F8%( Lÿ"8B0`&  iJsd$~$¨޺+kpG B2#v,xonmUΪ+OF_~fBxlU3cWV!Ng6{s!={ojRw)Gb:aҁU$'re!d; J$2*Rk^Ћ X1 9%u.NeJa2/c E@]?ƀ@ d CvL-IE*$5LN"3=s'ܻ\,'$NJfHi'i>YX ~ Xhw*X!T̩}l"{]. &|MA|/ カ w)ET;z%?Dui'nrK;%.3VصgސbO]ӭռحKX}8*FW*qb^@n?X R9[q 8a_%8e)Zv/>w) NT羍┏ )s[:ɤs)1Mb%sЭ9C ށ;W]fw#l5ncXbn opcPݲ";pMFN,~&tɕoƒ:a!ZT Vjc9<&-Kȟfy73=GfoH!K6ZK: I,!f(f vrVeXֽշSvݻ 5EaO> )_7%W>K 'dXNyi,VCjlCޡCSzOъQ=dtFvN*Ĥ'K -7lܻ|TGv:o]gC$Q8 3Ҥ́:XI17C,-^4k}a͠n33ZjG/h@> {eoht|8| >'+,lb踘m {cqt̨$$~zUMn*ٸu9 Hǽ",6Kud,  z1`7Alb2.3GM&$_<A'`o,lӇ?*#)}OH#7k$xEAOYdf.HĆ@:'P{zc pr*jCfe\|%jbQA;2ژȼKd1*gx~u0Ӧ3vlY|w %Q]:tX8yGuؾTXYpa ϲ;,חT(K4Rc/XhVpfI5`Ga`犜 '.S*R.@4SxY`6{6RwZ,-4SP%n}',{}UI]tl5km-E'xR;1-vF<,vgkmOV,m0I7T&UK 1?`S)u#6B|4ȭ+NXq'3@ b`9=Di 4ul6uk_wc2x0:z,83:劂ҭ TFd nb߫RV&a1o,Gn5W5v_oq]d'SndcNcuqyfb 7*QF7$HoeҳIg)s :\z+JKqQDl*Ha[*`;cK;a9|q טL:p^+f%9V=i}=q뤦|+z;YTdnw5r0GC&FJ,)2#:ZEL͞u{ 9BhGz,вޥ)_Ub~ebmCeRcJ)5<sKVxpNX80g u%7Ł6V0P,4xmE;6ސQ,>gaH~HRƟs3m.@L  iW:[]m̂-H,Y|2aHWscUbdÐsE={a T A fPO%| :ͧ=@oeacnMEkR.NĽ@#fV/olQN_Qc9%Zn7@ Z"`i䢩j73%o (Z}Eܨ]e?Ɛ|*lfaH=/^' X.Ѐ $uDZNOVIPޙU%FaH9Vz\wf=@ sTws(@ T%`U0P@4zrsf RBά"dJ2!޶[sL9$ZqA4MĢ',Ζ[_IJNXR̽,cf4O)+%^[z;PkS4S9Ønf(`~pFϝt˙M /&Cq9ͺQA=QzrL1jߝC`Ј*rci+qV1+*gG6SR9¡eӈɯ p93XF,, L{GLpbm )~\2a#" Z*h)k\Tܭ)`UTˆ1Z)B5B KLkH{sABbd1 1&-]9h(&qcу,tr.ħ ȕCMt?  @v#]ef]eB* X:R+.Yg/3CZP~osC -',AN<1L5!4c2ў`X&d CB ̡ IeJ9榪 ұ 0ǫZ~  %3MZ@ DkP%f9X1w笀TAKvZC%N3t5͌Mb؎㲭s=$4Ctqhbo;'C@, L\d߰qrΒ2DgUSԕSMIFBÇt&$d6ਛ#g``2lAIb;XfW)bt!1? B)ٲ>B5i!{ Aq.\|8cی )&:b`qZgcⲬpU )(v%佖l\1X_lb ؏3'p0n3SdސX@ &Zp Ъ$וz*"ؙLPҨTU?lnL5S*kQQy]򃆎g" 0 G@IUa!9 Jd82J**8TMPŌuE}iݲ2L 9i5ly崹,`2o b o/j-[J6*# 98QhKN"ObN4 ^%r^USqi3:%s|w0`SDL?95[HL? DG1B*C ^{&g93klSRc--o)B6U)0ͤˈUIÞYʽK\*<+Ĕ5Xŀ}dŬL"9X qF?'wK\NZbܥqHpFHSĞ–An; >HUgӯMNPsẺ?[| Fq[Ybv2+Z޸PաƉ'38)K9Sl'K'R/ ;3}Em.loL1 KP;=>bZkh 㖱蜵/43s:=!Ũzs"+}C< :_&^;Wo$V{';lDԁߦͿ"8*HqC &KG(d׀Gu<*mLW2(e)6~_O3j0!i҇~p3߰DG 8Ec`fkiuΫm'$qOS,&yuPxo>sxt9`f> #gW,oc劾 J"QB{j^I^@`qJgאjyYGAzݝS2X`w)Ws9m{%*zɬ޶eȌ;M} LG:j;V Z@Kb|/^}A߮2E1k.? kg+ˇ2SQ~BQ5MLqZv zIt@ V PìP;SP%ڧUeIgVYC.G3 *%Sh~q2-SeN- d9 +x%5W[]K>*_jýG3%j1nнKo,#Rᘝ&܊6.a0l`Z&8-k,XW} #TnSƙÐLJ ZLLl+ʘk{#6z8CJ>87'tHOp ++鸱4=V/$,k{^&{NɞՁe~(𡚥)~F5u\Г _{ fOa/V]K<ξKLpHq³NI7ˈ`8TK,acbG{fɞՃ7XJRp>V.[2̤KUnW*\C.m֌7+"=2tmz;Uä٬$`;aɜr8tzH9UL4̹u~|Td04N_{#zB( ='={,Ou<BaHARi-`JsQ͒ Uq_eLL-F^fAWz />m<Q3[]N63ˑw(䦱.{MYXnUЖ ne\4 ,Tȶ!稩M=c9a!1[XIJf>Y&*,DZ“[ktYEfq nMJF  lY6l%/J<5 ZQYJC9^ lL, xat⍧ۜ{9XpdXwSA',J9mE2eˠKY*2Q@Ô_KUFi:a LNLJ2XE+5 j5Gb &ZHw@ a%J(΂OSrGjd%. fiGP1K9X;sQSˉNXAvj|뮱L] O$C-8=gbņ(x~G:a)Uc3Mv:SM cC[(RPOkH΋4q,R`aHP܌e8"Jcaqp\F뤻Ua a>!V jvl]aÞא唜Mw*̼-R83Kb NXJB",z$h"GeִL =ôLj۶1"=V[&4x`UMI)\nYmjjfE@Xs5L {838B]c,޻)XJ/h@,7`f;4W*Lv:ai0@;v S9XD k̗5S]_wcFvۊ9%@d[MNJuK7WA[ֈ˳iܑu !plSL+AG8\nv4;+1*dK{1װDZ<*sw*GuҠV Ȭ n" (e0qﲾ1OQ : ( .r*ɱE~Vdž':jؤo_  ~nm2v-ZrdJ&&QK-h:NRrpd.W w9dē 9rCH |Ń/ CPT%I/R ^QZl~4? ?,BRVS{D1 nPJL80[ngj}RcJBhXa+?'X ujUUੜ`̶DT ɗ-E:a1Ɖ偕!J!^;F  74 @SV[`eQGܠdSʩQ) C T@ ǑJ>i0@ - 7(fB}l7(eX!AVOjbǽ+,++u"xɌkv Lǻ[2[Z8)aYÒSc mh hdcY|',5&5$ǯ*3Wta3PxߎXQ̙MX&Ya㵷re7i*i;9LK\<,9@,|44lǨ4_ G 2,? | XtL,cUܖ3}Ft<_bNݞƎbsG2$@ $Vme]uԹW皀tDªw6E8־WP)SאH,dU_OBqXԍ/K)&9/5OHlغ}&wYM&v:0aHDˇ&8wKHn֬@Ӗܬ?3ߐ ;Φ5$KK <\9r{fNNƇ!smہyL)?A :Y;F[Ԃ,802(i^ :X);t-ܩ,و᠔\).cJ&bd&rdK4C͐#bEЂE[ 2(+Ga%scTmKŗՂxaHaxۀ@ F KA8L2h0UGkOr%+[K˃2 [] `1 q'J131313,.I(Rz9(V *M1Gq[pPF \qOy!] 16e읟V2Tqy_氁A6Lr ЪM);_2{,@ x0ԲG*tdYۗ+fbםT1^LQMZu_o1hL\G 9 S Ŗ>s%2gȲm7l?sQ\O+Ãl!3ї_&$t+:|io=6ju?6KbpX)Mg4_c ؤIQcCZV䶏0$ڜ q\U&8DbQ}\jmT=9U!@&Fƾ` .VܥEb!3=9}ٕrlɛ&y? ΥIwZ4$]rOBdL=bh6njUAD;aV U+,<qR؂(q~>;i3]z6.жXt^[^"H"SI 5 %x= F| _:<ؑbvfaλ,(.])`%W)K9~O[4v0(ѐ|!m{ηKN_MDwp&HZhB *:e9e@q팀N 8+2pەd{oUr@tpR[4:iҔ3zLv 6F Z`oa|#Fzq @ sA%IaH)|kb3m|ڌݥDtG1Z[`!du‚qW9U.֠8!MC/=]KV"_|N_iv[fnb ` \Zz/$H~'?{udL=bfnW*h oDߥ̱±<7$0 _wa RY7K#|w:b0f8g_VLhUK@&6h¿l3d2e)]pRi+.%:Ifii3g!mf'؝o{HYJ &bvh4'kQ늍2×c<m&il\ND[|Yu XLmT!L] hi-8Hp)EFtVp*$Ԛ@TJ282`@, s7fA6Z[Pp dcRMνg,a56ջK&4cRj3>CNG+B-EC4-BvbG ,!MC/=]KV"._|N_iv[`:8ϐ|bߟ3Q=Erz'StnzFrMa.ukXY^s뚜 :&tywX&:Fh啇 ][7ίߐ p@ Iw m*ֻ?E&濬 YS'kD~=26( Yl3d2e)]pRi+.%:8ɚt'.vC29$؝ϿKg>H/l"C{t֮E!Ue?$(mr,1 W_@rܥ>CiZu3]7vԭ6O)@$;{ƺK\Q΀ѶXt^[amq6 órAg!~!sJqZjח({JکW0:1 Ap )'ݡ:o:ݠ.-KWxg,3f~eRλߥV*]&< >+Jtbcm'.vC2$7$؝ϿKg)1*#Z–S]]q62<*u١ue݆Q:a CWff'($yh39M`f4tGZkf~V[ 3aꉿ)yW5ey&q?a4՝ĴG 6 r!|VQe$Nq%l昅A#v5 mm0@ fQY2$Ab'Rq "^gA];S` m{[dpfksAO7ν@ z×w=nj#ݠT Pl\lNںe=`Kgw # lgi%2cu /)N KXfvi,bI y:nPMD~E8%aґ9g>b/ 퀞" "a71 SOLٸBJwr0F5AG7(kUI-Z9n'!7#FV3Iܐ[uP#x7FJ!~w䄣@  A+G񮐼kqwLs@T}as/?g 8?Z ?6U3[ O=09sbbc,(;'8e5vbKn$7 sQ@ U٤V;3d؊b?gO SOӷpx. @ 3GP:[@z =bmzW€s\ut9b_^̺76rƼDqn04˗PyLܤ>G,:6|}]:8a5$L"e,MpT7T/bm_g;;p8P\INGN9|l3qC8bN{ 6:CkN71+8r!U tq9̱vrR.W-ۻ/ w\H)?!;&Q=V |8[/Nk]9Ʀ4qV{lD[=)v2AB7q~)XPlr`JFVK0%.s,/79}6Bݗf'c.xh27 6%!j294{b%!igm:抍6vE1qʃ2v_{l6ɩ϶C;̦8Vt._9b| =c xz+dN_Tx/S/fۡC۬ɍDZۀ3_*zжzNL8Fi-^ΟRW嗙gf/U ,չmVJY>xMhj1]Xs0ֳeMBx+/Lu&:UG8&jr)72`@3FQQU˾VbfZ' FbՀNSV`u9c@@ كRW&@/Bѵy w*#0[4eDh d57썸̕@3Ҡ*{w_{e F <@})tJ ԕ (_.D8a@ fet"1a.+ m}9),JnqU}@  JlLsGj=2WY|uHsȀ1{L  sDqS!/r[5j0B@  F)'HrXN}Q,#=e`Cau:؊3`@,:|0fOTh;Oh}5rE@P"l_|ܗ̒w^ r-@ lU-5:eOTecԨ;#3|ѶM0们~aǀ@ eus@pgv(v ء`*FXiJkr1F4rexY b31"!b:j^J|d>I:3 {F@ 5Uvsq7qZb_/z U _udZ5 &Y":a!E FΡI T0G h%b,FF_Ɉ# F@ bS8Ü gP83Ê{9a!1 8V_ZAH= r 3MO6nVZ-Q+^0@ *_G4Յ]ؖ-7h6׏/ɑ:?N! 0cDdZ-FlC+bjcoA9! 9B-sr[UcuF bcx&UD]ӶGd9$r,MXRKSƔ'LU}h=C '^;Vݽ^s@ Ȁ#р.-oyK̥^zUW巉 @ ,<Ϗzж5d@dɓl 0@ Y@@ pԩ4TGK!F K/~_@uvQȀh_jxx嗿o~}W>ȀH#F  *!uB@ D;\yǎs2#1ܺE8[_JNq٪F@~!;M_{S 9}9~޹΍$@ BJ[r\IENDB`filius-2.9.4+ds/src/info/img/pingtrace.png000066400000000000000000001765621506574241500204350ustar00rootroot00000000000000PNG  IHDRtKC/p pHYs+$IDATx `Gu/~f~jUPc%,U6q pB5LLuJZBCLHZ H ߒKC"nH գF2ZzDv3sfﱫ˖Y}|9s̜9p J[(DC@##^_Jxr33p9QXdɢ@ @Yt… #I) rJ)A<l_B( bY -X17LgHVc,J;؃w%ɶ=_~%Ke_-~᫣ew}VeߝI™$OBa,r打' X\B"I~O:`枪,L8+|bBqlI !+P™8$Ifq,OSr):<@'$ Soξx4>d<|bAѓ~=u~Exwc'EW mgw/BOY=N)ga;+COc%\Q*+- rÓF+enwF999K-k/=Qqn dŦ?J7px9x݊M f $+|+|\=STui6ޜ쥙{ eiO/).b-sdK~mCE@ШxnٕO6ΖξEN;wW-ejۊ{i*Rk[KX$EjbO|_A*Z).B!TV^F j+J)-~Yܲk՘CuM!9tE̝wAvr']-C{FXpkJuX0K3rEPE>+*%\kOO%}5.?D""o513RXPW@xztBrV&֥(OJOs fl4!1{4Ō3ԼjPdzgqX4kLI?0~fc{ɑJݥ1W:_UyVZpfQ1W(Rg5S֌s?KP\s9zvoikf[µ%I.9HxSQvf(/)h0ҹ;/pHP ]yFzc]1܃[G 5y#ILOi*yѷeǗ53a_+` ;GDÊ.Cp/>I+QMD[KlaF׮->h-!'FQ]}}l-5qH1|hjuĶ9#RĪ}r*¢ǿye.SGBH\zS4>zm\12c({Xs*24YV5PW<,-\Kd_M+v첬x'O:8]E3P%֓IB}V$ sEK$Ahq KrT@(/Mt8B_z\* +}iު@d%&Pb1ED՟I, I7>o+0of@)f .|4̸Fb0*Ka\z|e}yG/׃P%zGI{an [Uluw#ajV̵YWڅ\qGzbQ+J+)40@ThJh.9B^4SLU OҪ 7kp,+#::b"k䒍k}Jg,aҪ ^ k#'7̺D bA[4:2/pJեQEw=pdşcǔo㊼^g~g{7Tg40'Uxs'zC(U٭14Dcj7\˗~u4{%#~3z*dyh9E䳩x-CuM@}rG'pcsDARٚ2o ]CS=٣l_=ڇd 'kХQWبj+V)[1i0OVX^yݥcR2$ka6y4Vu)`=yҚ4g d2LcGj8]!p ~F 4wieƍnA^ o)ӽPlbNAzm]-J$tϢҔ dzneovczg.KL^D 5KW 5.T/ ֊/'#T1 pČ뎡or/TK`K SX٪VlsJ N:Eԭ3+\PҰK{L e4u]K-Α5-ja`"U3L ;#O27W$@s|b.vV}h(.-'r-Kw7S uWa,,BƢjM/=D5˰9J4օӥ>J0Vό +It,b듕=C8epK~.^dÜ;>#:yzR;"<:듉@pZx)CA%':.e烐uaui^`ԧ=/lnP$x:{djdA9s-jgJ`*W泙hY(Zrڃ 63-B<#?ҁKLm( K* 46bٸ_IOs!/N%XSFNՃ00eB_j!e!4&Yꉱo*'Lښ&kSiAX g]8ì \8oF7yzՓPVÎC0Ha> VTH}wTQ}XdE 3p[:= !n5 Qo>(wܜ+s]K*P =M,IǹAYTP%5^h"aRpR)ԞDebӡP*OվLZ{U!$S B-~ xr$uc BV!rD. BRĢӔLm ߽?\Eyإy$[.\Z.Rw 6~yW?&ީ[lzԙqKx2} pwkڤY,Ѫ\S #vS ? I s翀li_l< ׮aEDN:-?NNh?_|Z/?6qpzU}kE>!|K~XX,qH z+xUh>7>no|0o|0.zgewEf`c!9$ґtǏa엿\\yՏ 0|44l?φA~V]~ߏ~mvzVxO<7U[>lNe6eQq Glzu|f='q\<õ}PB3nLajڥ:] ftiTxvd5Pt>( <ԽO]NӒ! JcROccC+i?Q&0#R4W~p!C8>*Xq8C[0e>Wޟ7HkLSCRpGcJ9[H׳TuO R*7!U݊B6: L,c*oМmüvʴ5=ZfC)Ousy~#>c55lߺY}jԏ86 O-!M &?(0wֵf1jej=e]8v.]쿤&$|6Dw=_ؿCc/ N0ɼ#em?D.ِ#3@ u.M .3 9h=,@4: HI4s+h3-4xuzc'>V!:VXL^՜~ fёM~ Ɗ n)>*a /+nm4''ܯ4 cKUӳ !l4|Rkǝ7gUV3D X׬τsy\aqh&*.XeESDuOk8g\[78΄Ќ34\}4hߦ,6SiM긁k\5ٷ:!p1Za$w˪H*!\$4;1ãlpqDg?.ߍv hf;G JjlZIX5kU"PyV7 ZC~ 2:J8o[n`/`tWy5NM5̑y{. [up-Fw󩢷 ".+h^l40*;kzBCj':Jy1̕Q%պ} [NNe%:!_aԌ֯U>%0I5RkyUB|]VK8~SX*â\ҝyB䜛JV#7n~rme{ҝ:։듻׮K*Y ѮJsa!:Q5w=MjݜiI)kJ\k͆|C_^@]kPe"1PAPy74ER9D) |6Q''6`VDmZk6@m( {Q]R)+.6*!?o6$ fqPF¶26bC ?L;!1,)ҵ@S,Z%ֹΆŬ@R gqAJǏ5]_הޫ»n^rU?י}Ƕpߞr 0C2[&h#7=:l K$xRZFѢ0݋iV8 `h #49ag` "N)R <,F4%vyha"m|J;)T-A,SA˴ZeXKmAsaB5r4'ܰi}UImʔ11Ym=׺>Wj43k3!zL4om49JoN g4(-͓ g-dՕ*gΜ LӖU0kh,hTX-'j.js,A(YCf_i)ZNj"X'oG~bPr0K>xɛNR88qSgO_t o7-]DZ%K l[ >1,s,epҷuhrW}>N]"Ukƃ>u0eC Ɔ_Jƅ]gi8A[Yw4+* T~һnMپ)$f<DqQY0,-#w|$Em-ձ/Um dx̅&]]-Lr㋿DY/ |oYޗ?t?~h P>Z=QMïyX:Qj(NIFI4kҮ - {Fpr `=e\d&f5cP0|(]/X!41Z01e1_vlhpE#kJf% CCYk`<gH]rб1$A/6 \qo_-YK~}n ꎣ7ΰ9ɍ~OF>+Y[&8'hq%>" ԚSbmSap4)F4!FhQ`6z$,2M8gqK$;˕>|: vxSu1YqeKׄAp욲s%gXLNg4+%Li 8&/`ǁ7p"dU`ODyC>B"ZF$'v+nwGJC蜈oɹO(AS d#5;m+'TS1-F΂-;j R񨬺+䠆k)vR0b0vuP^bqjc̓'9!|Jƈ$jK(Tv<ĝ@Z_xa p;'Q >3ޒߤl)~Ba0xl#w?ʎӺ_U|%O(\ W^Σ_ۏ/Ga;?77> = ʲ;S/{G~v2Jc绯q( ſ}W+ XcxA8^q5ݷVϾ+e[0 ,O&^Гp +௡WO>[żp!l0xQSdOCKxt1Ly߃Ō#,3x?;g Qޠt4pn , DmlqGx\Ra:dE'p&ę[w3߹4up2^SvwRP:--dx W߼D6-Ru \y yK)ңxv8YFX^s5(`RWw#x ^OgXN2nX$?pqe`%vV_ fiuXo /ι3 *o*BǙn-5M |#9晀l瞱 o?Iu[!YuIX#wEa/1n vWTWsU(Ky ^U4?5Sw%yz,rEt~]7t>`qמqٔ5 ,4$26ϳj4 U7 @!IRɼDҎCu-=:ŕ sf _TXpG(-`\Cȃ]=mSՆ|z+Zڪ&iOz'bڎB-w-jmjh(PoW oII,4!W)D1poK$v  h>l"[qDyU77|Dp_?`D:㴈n##[nnpgzn]dAE'.KqZEc2̑u'uBu[ꆊ'2sC)2mٕ67+#Cun#R:$[!Dhs_Bد= *+{`X2{s( EbQ^4X*њ1yB@|M DE-״9~ n+ܬߛ8)(mU XzMH_PF>vvu>a=p ~a!`+E {IJ7-eW4,dZuh5a!0ٯ RtST\VaStDi.ieBrsXncG徂AR4Da ͘J@?~ene9\Ŝp)=fQPB_Рh|3į{j¬+;%|?Ҳ>-FJڦ'X*XNђd.Ph%B 5|0ve)}>$j(/ar7:2zmZAǁ`L`ydp[oFTt7w.t!^I-Մ֥n=p~'5{#4)ihSeF['>[1szsraC-RAY[PIlBGWJ<8pau,N#iA f)v+ފXĢ`~_OZEG.ڤhWdb/ra1ӃQaoiU؂׿RzKXm3kpf&'6Q >֫ yNxSxOiߴ_`0lK(XQˑخy˓U1{mUqon:lSݬuY7GA¶j`|OkUw?{"Pr,SrD aPhM`_wURm9nܴ;FH}Qm#qPk4us[aҤX KO(IzW@GVH}tݔGRRnE?1a7 LѢGp5[G~W|r k]Ӻ榙JWq DVt [j~Bz o]Aah;M:ITJV>R4Ry*9p& הꋫ+xxfp!tG&W+SP'& O jJ0ƹ|W-77N2wDI.ó;!2X'pSҐ`';(d`6;萇u POo`faZ?: 9z`9@And=awֵѶ+5?@kxe/RpqQpn`b;"FX[^БG|Uj9XeG_>q4h wώD-cGXs1ڒ#bç8m"#jذk 7~oz2vRiR_3 +WEWuh~QFע?/&.}ҿHKtbKT%%,>hV>R}@Es+[:pA9$$&=ʶwa2iZYp}9};#mqc-ۗQtCmKESUWBׯEL6. 0'ߙcܝvHѓR| - _@>͚v(ԛg#,%]:Pbbsa[Y*\p'ms_+mN/\ewC(+˲0)\ͭ /CbjjgL0lFj8`+E?XU쫿PޣμaIrxp̽T|EO^Gn4*;%oV".zAU:v6Ub|羭=c]{c]y|%šӇŽD.UWs^BL<|8A);bFg|YyZ96߽RU|hG Y39.*&?pm6X)..fLp1,*,i8ϯ;=wP̋eVҪTK6 g|sS}~B) V>h:[xO6T1ӡ߹{d::ۚ"sL}>n `;a9ע1Rb kai`ƴ=1(R|g_hs8 D!]aN`)T3bl!JXw|>WsJS|翲x`,oTHY$/9YYH7dCpB =_C?~6R7[oih_-x⼄<n)<u>s>=N9͒c-~/;Sw$)?3~qu#)q,9Q`O2:}) &bF\)ObuM&%dR?v`Y`… 0+A]Az";jw6i֘ŋ+SU3o Nu}SR#|%tq Ld$B!DMɫdËxXPՍD kFEeΑxDlX'Tut ~j7jNw!sP~40q;!;`! 4$ <㸁+e! qe:ʇFN;lo|>uk8! J8#Y$s__~3|}O/_9)r̚(cĄ*J@h ;o q2`E(IݩT=Q$FqKIOĖ33EiBdʀ=H0hr]h3[94L0Äs$Ӓ?dUPF@GUv ,B*hK$E8?P/$X Pt8˨$vߐ"zȑ)J *6IzDN$ƎgB֩xv׷kn++8'AgDy$Zã`;L7-goTy%73 c}w=2C@u`U·޺9> z<#W« ȡySEϢ1w,<+z_] aDM&Ky$-ޠ3O𴣦^ 9X}D9BH.fBJ)hT=4,IOyIR=(G/lvŕը%8$KVD9Ґlo[>$HRǴBp TB4Ք,3t"]@%3|躺z h pN&)FϔG/^V5˻u/yk.#/0>zo'N}Ǒ}8{y]{#?қ[~7yXo^;EKn︵7> Wm_}%~h57~Вr\xp&$ @qTIT߂.mh%a7+Hw+$ZeN-W~8aR#MP3 cҰ˘sM K(:֎Fz0dlrܓDY?83@AqBNS"5B%VNM.4A\IR7^Ql< a{N^~n3K%߹E%C$I0: mt7X>..$+fߍ&KaoY$c=FqcJK5Y$^J_y]v>Ŏz-h皋n_qђ޿ϲWqEKXMᯯZ_}XEpƸƓfl:EҚDLRWMp^:8e{ܑu#h!fհf'e3%qpNei>\ar4E|X72KtT34 0n+&2ȓ3CuO;Xʴz˥hA"JƥCȇ,2]>v"!BJ\P]J5;jzL_U3k{øq.ECx>o߾7Ta(/~9Օ:-aS =*m;[oXc◈ׯVéq`PF8 wq `ɟ:Cze824\q؉SMGGONs!dRNw@iY6+Ou3IO Qص1M5<˲-'Icȳ:2riƤ*pMKGPk/)JE]tj·MvW` 3A1|"$> &P-txx'N%zƞE8$qpMpH, aCK5.27Hλo܈=]I{UUoJ x ֈPqLEe @]#5jzфg>^Jcl5w5I傱4T$?*GM5;ML՜jXs`UV+3'C\Pz L7sGG0.kņ$62|XҰ1?(]2;)&cF 10&Q\XH6(xMtV:y&'ظ\ej*]ڣpS:۞>!pbԇ(`t8O/#vF,=H ɤf~m> g!s|*v`a1{._Uw@rGu#7\-7FW|cf{/{"!|mIofGlqf(77dy*\5E+G'eaaqRZ}g/p'n8p ؏>^=Ǐ Pއs)bDm;=ܻTʏ}WWl?>s}} 뤧\=~a+N6*gV-uN}G^ǎ~* [W3=oj8GX۱gVWOizYuo_q}GY_XXeκȱ{Uw̲E>ǜ7VfX :_Bvt6i# ̹4ΥǮv<=1mv/kWsÁ;,V$/>wdD+N<\}r븸3o7]_ߋbw}]6FpuñKg8SE ]CO?'+6Uds7lb5}^n@{S ӭPYI,Hg;)_0|ęyYrp\&H>ǁE3ZD'Qt=Ĩ[h9y9p&0YTUɁ taYXA<m^Ty|Xp -Mܩ#<}Xyoڟ]t0_"f2WBV4ug؁ula09Ὕ$M̱wϬSd{>_ESEKJq9oSw+cv)-Lem1 y0~{HsUPWuVޘ6KRgՈ5UwK~!rw^nNfoguyJ<zeh,uä{gYʨB& anX.xTAc^n[VTS]S,/r#hI >'Ϩޙq7}XVv*OE'b=Ѷ*_ћ'kH[pE#xq %m^ nͩ7Hv~e!E#[;j[YZk"B!mlkE -`Dz4Cܘs-7/Y x#Y7= յD=YZ\)WBUXtx -s,~Rk Z\`-Vz|J6Yx',G8$Ϡ#NE ^7o\b@6 's]tl!-U4 WYfo44Şoaa;}icN BmsGtvs6!C_x,rt^I3_=+xY=_B.Uh<=&EbT>>LMibh2)30RXed7NfϤ(NƠfm ݟh5"/a\vf]| g-{V3hu-#pj"-)|vڏc=D&`sA9b)M]!XmWDj3-k荷5ݸŨ:NłssEő񷻽(xs7TBxdNayOúM{WW y)^qbb.- ʮSOvAg(! r*n:jc~%2VB: =\\_Y,7|<a{H^;oMփܧK1Ui,Z RŠ}a3Z{c5QULG q(HISP Z6̱gT6f]ݵbA{G3];pu,}md 0|_Rr0^ժhܴ7 TokI@i(Pܓ CkIiSeZʶ" J/ بV[byuCЧ5VeZ]\g K(Sg:>ͨ:bsKڇH8aD0<-KQ#3DH]0("mToOWdfI3Eݵ 9y'iJ݌vRS;BpұvbnXN{$6/s*tRm9׋,,.4c`}>!pi*3?̰-1V\ }=y|| PF^C@-zZv "GYIwdm"M;sX!@BdY@ PgHĉC)X<&S1U(,"oP\Շ1w T4_z+ L[\Ed`\ @, gQ4CA.+c)ty'̅DsLc*( TP\_8Ha4U!"ٻZn) <]п5 LRD4wM# S)UhڟW9 {jjhbڣ>@ U[c`9d]kR3~ ; dz6]a AWsPiM$xs J aTIY_xK?)vtw+@Z%[+<_D0b9w/s̡K D6bXPNkRuJa}vJ Na(?\^KŘ3ίܖmή#ﯤEGK_'X _|h]4C<븞Bx5AB8sڴ ]Rl=evYQizga)R콝f%v)e7?3-aeĄ 3ķ%W`nG;Dqu'/U'SB> )X1bz7LJvh&QX"#:Ia=90w{CvԶUa+ďl6m?ih%*^՜`e]jKt5og0G6HnIի{+;dB ^*@-5T'~4)C|:YjQ2ǂPw8eFBn|[+kSyWBIP(́Rk=c&umz{΂a*}+^7j"U PS1**<\ky)A;R󤮷HyWʁn"A=Z&'I׋z`W-,"UhBHHo&McڮB[XEK"< Lʰ?ְ5n g$`O  ԰d` [ְ;^T"4ihLʰ$awk]v9z0]H ]p[p8gbŁ=Шd s5Twawk=ktM& +S)&Ó55.` sX!h3+;Evvְ{zP ְ{|΁~ ytF6\ۤO!W\'i 5n (k@f~JsyLڰ$8Bkv3aE*Lʰ?ְ5.a OkbvO [hfvb.4:s`mW-,"ܫRe#od Dˀaw)+B^{3ɕ5n [K3Y2awְ5n O;ǡ9xI2;:Lְ~Mwk؝'cRŁI@fW-," xRk8svFqc,,,%FFF<^%hFu,,ʒFf|t9ΕE |-0JLX{cY/< f/Iebp{Vb^-b C[XCKҊGFFRżWȑ{Nrcaa?*Yߴ48k7>a)a2:LǓQt '{B'! O̩MJ{:]wn!ܵ5͢Ic Hu/E, Bp8%B<Pfl{IHM|-rv0o.̅Sxq!bяN&K8İ^og?wTë柄5ITl<@`#̔;+ BqR7o FyrvD%z5ifoʀۥI'ݷ?++g8όv2o 9UWn^eMSTX[Z&f~ MnWFf! 9IP#e/.$r(J5%$~I"{! 1-++6tQ 22%Cr '""ù`-fPX)%R#"dDh՝q]4=Feil+~r7eP5*ꜨQ)ATG/Q/ŐI"RX0SR8-Ol5.18S1bu*508kNU& pF.zgvVK~.iH'\S+ /ѣig'dɈSP AEq!>SN 0D[6H)DT.sdؼNإÂeq]fgsB^IFYSRk2BUpRP/=ח6IU7gRcMzP ' t0F=a8J|d#&8S?Ϡ9eACCd1& (⡺8VɢQd!임|<4,hN8~rYhy~#zO> ><|2{F00N9O_(ȩC‹D|haH/Vt]F!. i|J :bu9&9 tQd!?2!PbWjK¨Zl7e)8Q8ݒ*?F@b 22?Q|*+;*0nbavsyьLyRD58dA(lp{ ~P1R0ⴓZ*OKwrD e}e&5 '{Br!'(.K%]h2o쁽y7>9p=e^؛_98>ߝzeK9񱫢Yz.}e>|j4ק/P" bfʏ'q+ARsf* "g2MAi 'c!)qǪ5\=+"9d԰N%i6gsdQZW}P8]KxX%/!A,8ʬHYKK IR#.CN*Hʮ&w9k}gaT8D0v@bۇ WygN):|x%]u8lKCt䷿]^0Ecǟx񡫣9,{,OEHFN@ Ԁkj,O>u:d'm f.US1N9&ȇTUR]D"-6IfKB/($]7#M3Ik&,!;)CIj =v\%'jY)Pөp EPH'"oD)M(.h (HY-KurV=;m0̹AFgg|#tYYg _x |3[׉8E縙oNv6oM …r޾[ DKC(/}3ܯ<O_Ub{/Cr˯jkrwcCwWx路ev ٷ83KDS͒jA.IV˙Ե;Ajbi9mE܃ل'ȉ$Ҹ=zM~.WNu*׉-br)3)Z3#*JO(YZWCT^=`GcdqVeeG"\e@qfm}g/C$:_ Bfm뻑7{O\pEŋr5;~Tײ˸$[ecݺ%/v\:t3o{8ݯH(0-Kn}㯶^<΃?zYs<^=pwcYDuqVĵ:h_%<}E'0#u?dwJ|䍾Gx6{}GWS>mH°@NqZ.hBY.ZTPdNi<&Q "I6aTMJτM&)gz(` K8L8xZrڭB0؀T|I2ĿpH!#!GSI [H!Op9QEN]F89o%VfEӛv>} 15rI:2Xv$H@]>Ui.(ՋYTe6)w?Sqa e|Rק:4il0 ɩ5礹d֯Ac+NCTf NqU)RֹugR%"f>J=a'gjLQ) ΂dFUB;HCrLir$e &Vİ-Bj,FՓÁQ :|8*?. ѡx3T zrA :q+~ Ѕ"qAXҲlRDIFvى\VU3#T4f6 +<{KI=Q5';rSWvFmlʼԫ(M`dCb*: Je=W&l J. $trL9LEq"Nٺ;+j]Elny؜R1U]&ᑠVCDhC#e +urӻg2ـ^ K&~bBP)eD&Z:6t^\Ö$%1ibępw>GS4 A_Ѱy?͋è6 @Iz?]zMߩ;UDũ]dXcJ4.5G.jpy5\8LFq`tY,Κh&N4gtcjI[XeD[)rd0FZe$6rW}׍G/~b?~.1}ўmG(t@%Fuv_"+>W p+QKo_+F~~Gj|)O3?-,>0`=?{{BW}Y7Q.Ί:jol7_e}SNb#*{7Ϳ^ βo/FE eρw%j%7ҟi7|+a}uR偍ɪ{Nx궫w\Qnܯu@g:I[XYR3a;>p)Ǚp1sNO ABo.>_+gO ٲ;>]̣2)%ma1ǡ/7;ٰ $G4+ԛx: Y?  *,:qBn`@-c;YHm7n<26 ^#b GL,a˒!_٫spGg= WjW,,m$Dhużk8pݺuyu6>ǟ6p9x=w=+69>bq;y"UEz =̝~4+M'GkvŗfcCco~z~vnݦXo΀*P~Cͭ[Ww@'`f~n@0IŋQ|<< *Od!\_=c;}ߺtfN3M, ޮLTɊMd&77vNПY}S}}ul_q] . y5}hq% ^pwkDMN [|i%|ų\IAB>\z#yodSϦR3 ~e=qf\}Qϛ*-6A* Y۪hq~OX ^: hp?\^;$ŅL ע"ݓ>7Ng3l%CL?L֭8E[mڔ{"SY,sܦg(# Dh9sv,,,2F&<}4Fu,,,SzTPK-DhYY^RV40L",;JċIO$PX<|ɦːVUZU&uj={ G iIL[Yc#F[E8%G*h"GzHcMPyU7VlE."{Sfqu n YpDhqgX.Lp7RH0Y\?PˆDC"#Cuֲm1RJ 4lP@SMђA;^[LXQ[v%]zCKPUrs[G.ߙ˒ s\&4싳`(^Xj]Ys,5~dƢž^m~d[epGU8vA.OU.h={Ӟ1[GcVkx&^TRHlcߺ+O?)/k*kw4v`}%&=F㲞)Y<8g =Ipv}qoՍS-nny.Vicxo?ԔYqdʉ"Xo=,Z{a-%K!/{5_+H@!1R^]9X7]:ŸΊMP/>a$э;V Ң##[c!һg^_i ,-c=co/>~BbuM$^X'Nl0;ie񰎍De|$!X]ǝܮSRb9VOc5,"V8-A6,fq#ۚ:,e!B˥+!BNՔf.nV[cA'H왋ܪYzm%\#EG*>Y;-EZ"~t [ad%;Gk&galswU76RJŰ,DÃ޺2ʹ(Ɨ1(v(:% 6`D۹.\;}s|_{|2Zel#9524 ŵ4ʂaGth*i D$6wJjܨ0vX*s %ȠhpV4ܲj"e -CWQG#_a6!7K>EhCLD=>+*oNa>İD>'L,vGǪz|UM 5zbYq1kS1#Z|` N(X4bFA[30w"WӌSk$L7n3rHi&Y : M3#B[XXIf\xX!da1o06hja1:R3IV(d|!ĵIiu-,6Y}` y= Kv!b@(qX>Eh&TӉjJ4=1:l̷Xên撻~kXgMLSLV4Eۜ&h9>u&qS;QSj`)XboFUEcukl׶#F5K.c̱HM,'=̱p!Xf#sս,9yܦO̓JB:Bn< $W>՗&V @@=ܚSKXy_Q:yKuC47M`7 nVo t i`>ѝqAOT:81?ĭbf_ZX<+YN󜱩o,-0ߝ $+SԘcYmR.8x܃Fe׎{ρQRc_S 8F#5>:Gw3!~谷urkUgbM.rm%? G"T!ʏ! $U43ѦS`` ( % F*t ƥ{ x6!ьGw_wUojNK'}ɉò=Pkp5=ڈ$3f1Uݨ))= ?ۊ f곁&N;ДR֤Φft䚶5 p.)>(2g:T5U4é|f1"<׃՗?\Ju*X3.ؼ=&JD𼺷yp" 2m(5eE@F=2[t4Ҳ@=,q>>5L ulsE/n3!W^./z [44})ƥ/PgTw{7fQm/TFøs*^>ޣx_)Cm8[.u2yEk=PFLDh'Qcj?3qxhfg2<6(6~6M7}^5ɻd]e XcDZ\&h(D;w sR   \ky`Y>e-WVՉg9zHsQQOT4 uHH,ѢP:7!sUw?t{ZGg>TLFݍ;l8)~X<&ړa*fJ&<`p5"$}^Q6bGQ@f`JD39̠OnԵĨN/M0Vrf#!Jv=c68{@5]?no)g9Ͽ7=ZT#N_LF޺QOњ EjB[X'pKPFz6(B[XE(]h*iV0N-,, s`maa1/@߀m${b.J̟(>[ka1@*|Rwa=M ְ4 Sa29 8+V5ۢ---ϫ'RRCrE ,'b!l 8*=z/&L?kʑ{!>m}xavGCBZڻ6m_'gɰ mZ=oCTy6T`ý&i! &O/5,w⩷0ZS,7]<9=<5; hXh[{^~!F~ N?Ym*@Y:s Eh MTazݑF&!9,vG`%L&̠OYm{MkxP*f0+96|+d2|4,k1/kbTOcyxXWW(TIe=sM4o b`RW~yD`QSbApF0iBIJÓ*,,"/a y9 v X]h \Bb>Bh|MRS!8 k}z@]X Z\PhabU3IKa _"tgz]hk}F Oզq6@Ni+3Mg؝Mp$M11T U-we).Xh)j“cgzB5.I:c9$4!/&3~,cv6TiT͔ް;R in3jK@ksTh>5># ̗-v/Vϖawbn(j]A~^1=?DZPKXBTfw24@_O^!MR[V~+"uMm|TtM'i):!kbY5QgGSqPQV5n2z<pF 5)k⩷aW)@ZBͲ=vlv>OswVawSǚaDk"NO6w&@̈́Sn#_k}چaxu,gra ; E!i_U5 L:fA u,AQԯW86B_>)w4q׸n;$?X>к=da|ṕ-, ́R#u(Ox!0c,i"4!,ꤘ[]h  mܬL5ka1ಉeaa1@,O#a Ł~ }J&Cs`@Xְ5~N Zb ֿ5kvϙa4z<>ݭawְ;b 8X~ݭawk݄5nϞaw ,GKְ;XnX h]*rX]X=`a1p0r` y [Xc &s`S3aivM'FixmZ2ݭawk݃f}"ܓ|>/oeXqa*ni [` 8ˆ5'#B/22R0NkvWώawaڅ\xg$Q awka E ς5n {` ìv';feIk}h9灭.\[:a ,[X?p fDh}8b^I+B[X3n+yօn#}4&k]Ij [3۰{90yo~mڣ-5쮃5n [Am$p:awk݄5쎘UXܷc(;27>Y5n i9Rs'$ ;ARvkvP2,,"aTЋΖ-,%aw yaT,,,><X]h z2caa1ǡm ka1~]SBmvQ/4XS㿔U)M=x6 ]`u@X` z=P9d~-.'{RMM;Aa 5n g`*4?BA0쯩@dh=sXְ5~{L9(mͤa{&l #awk}J%29>y*525n 39̠`m$9k=x ;ՅPԠրm$ WXXXqn [XKX:C]B}5n [0 qBzE5y5}=5n [ư;?Ä9VS;˖eo [&aw,vW;`H<)Xְ kt-<]$L,DkawݰݽAfͰ;ώ9Xsְ{:, [X/;,,"`b9,, ^8<-,! i-kXӄ5n1}pi`q*5n [5E,F,{W=awkawgӰ gݭawkvth90TgOvְ4픒qқ5n {` Y0u)}`9k}x0,kbnхFlFl^Xhaa1סO#InLŶ7޶e"3hvM:xdde"E\F߹D$sWk7#3 u#ok >'W s#-B*ŜBżV8dp` q *5aa1 9s >Y PP [XCHka1`R4|XXXG`t.(>AyrG3Âf$N*GQL ҹxD$zUwUbt_U<遇v 2!=M^V6}ڪ>MSrTd!'og $=GhUCGpB4N48t.TB2s)4{RM 0A}%/?( g3ScvCP_}{LjbRSpzizٻiOllcPARj$T51@1 dv>c0 =T<$# k L#RbL."7vS6~_~ݏ3I_|E]}>sZ4+72o* lUSiI!7\dE>$HCj- iMɰjGƩ9>')u"N?5TAS&8lzcF(&e&kF,*1Di%vRgzXҼ1դcjaY+uG'ʛ{%-ZtL\˛9Fejf~L,ȇ% ĵ8j-k%ʨ(UFcE%M:Dd$++AӲ4"DhnVK7k5ood$'K^/yӧ_yWFH2$C"%K4>U)R<R0cÑPEg";$108\U)5wH9d"lU!k:Q9'QlX $͛1jDGk`کxAUO1vz,+x f,1Q%OgO>7ÇcϟRWM;. dlpr3O%?x+JY/cEzqdQ|=% CC=U"o ~}bNN~b"$/&?ɕHzx,8d#~/mX3r2@[BNّsVagaV-25YHL ڿí]k'2I,ʉJ I>3ZRI^^T%^瑊害򣸰í #LwM7)ВB[Pr]ԩ"<{W>5xٹjVyqy9.Py'f] VNF2;O*"ƪ!$YY""`0@Y32gpCD\ATsLu 53EBH|"P~Ϝ>uSccβs΅9Ȁg'__ʩo5٪./123A 6i2ϜxiwS/߿:Z 7 ! 2orGI/W̎>\s,ӭq$sa VYDXN|;Eu ΟO8ɆҼ̅E7v$3ǓՑ-\8ZhH⍓`V %Ȼg7E{o=}?Gr˾\ _K>GKmbn<:O\ zsる;όؾ_2_|:ݲe%({z?}ԫt"؟o?Wv5jQQ]|^R`O{U> -_I2(I гapQ2AA3E*+f]ttViyE$N *9y -I^9}ꄙ{ne R+>!:`ߝj0w\A֨%W}L^h0Xg(!ՁX4X!e/UFT1ubtĦ˨$gM%y5M7۝ ߳Y${12WnC*jUl]Y{%_gۇ?;|'}"3*#;~9=,9$4I`6^VKç.zi/8~2+Dl,= {`x忛ݟD~ ~n27>k>cȇc_=͙;?- n _߇?3},??n5yo'|߭fCw_To>]{WD)B%QR+uhd3_6qݙ .]ARWB2w꤉RI5M!x*1O5=֢u䮴LQ7sxIb%)N]J^Rr:F{gTҢz2K0ȣy$)fy辤Xw%GH|q&:"-^+l@do_zC^I^DOY橏@#׾|Pɕ| f.Gz*3s>wdǷn?C7cMC|J}7xǭǾ. ~oub}prF*+?yM/v-d|Y`W}[WCT܌dTAa\a1%F:Bn^kp\51BbJf$+M<_g)`ɊjwS;|_أ)Ȋ~Q0;/EQ%Y7iGti˿[`% ~7*m]t=ظB=~x>?z57vmWk0#L iZD%Zݣ@kn|"Q7sOD[uwEdoz-6^-nKxKܪcYLB/$eq*v` !>dɼ3j~k2]WɈn DM|]G3v%SGR" ,cW`ֳx\CEZosJ&ԙdx+%]%gCRJ?/@1J)*^vS+b@dF}"y% V(l?W'fƾnf.V7! *Hk΅3HB9&pV?5[X-LθP\T75U9XE@r}IY@:JЙV#K_~(g!~/k08/^"IE+_ίbͰmpCN ;C+ĢuM~S,ټ u%TCy16DfjwfɇPNࢋ$RvQjMv++SؙjtuK:+(Tig ܑN1-Q37ZIqVH(K֩$@'K ^3%ySM:`:cvYVWlw2Ęf0 Ijq\*Ɖq&@fYf2K8%Ӕ=@:z )h4 ԭ,Mk;U¿0CM k-cr H+hʺ7dG!lX7T=qf&pN ȩ|8C6Z\p,S2ZaJI-]0R7I$ߥ6 U]c^ 6VЬtx{H˓@=4E5qbp8ޙIWZvOW &Yv61Pi qU2gb 4v( *Qpw8s#=pQĉ$uO/vȝ;W7ʘ qO8z <N:tTӛb1uh?b$: $uX$IP6bALa"$xeȵQ%0Ju-f,2h8$\CO &PIFeaaaaaaź*jLJ~wgP_γ3 ~O<OGZ;39qOT\b#SYFg1;&f&wzҗ_6l,;>Py0&*_>Ek{7)g?koL$8!e={oW+>zǓwhޛD`xҌoh  0._ g7˹`FA,dy2j{* KVc΀qK*Gf~s,à qr*X 21zF=pLB^f˖Mg %7}>G_ǓsiŵɟaCOAq kxڴA2?&+|n?{.j?[$vmc8;8a 5oz'MmqAboXXXXXGt׻ې4ԋ~^aaaa9~76ح^&ۿ?\-,,,,,,R㋷mIe8R_6\ hYӏd1/29E`0Y<[X}0J`K՗at'D]x's6#6E&gl&g 5?-,,,,,&L-h1=7^\4#[J-r5JL+nժk!9\Lù kp2b;p 6yS'>LZLh36wQfK\㙇'FgNyf;M?fVZಊϊk909!a6Λn0G7M2E C ʃH I?6'n'&&X&621.w8fen4%9R%qȧt4˕}At霠#V.o'W(aFf!K?x (^;Ag<'06Def1xcaaaq^#b6@:{A&g`z}̓k pF. Ɛ g)z|dt+ u$UWp ::E{aſDzInZU,ay(7 [+13ˎK[gڽuh]3=3H!mlH++/Z%;b^ L=U2D4{+8ǐ&0mmC42*5-9‡(.)}#90聨 $2YV~{wӻ-;ah]33&wdg/o_AjCuC~$\{Ynfl!U$bbpwF|ǁG=u3T s efpjpM, dzrnC+#={/8+XT.jյvƄA)s Uzr4kPОFk [vtէ䣁O\̴ȼ@ Zc +3e˺9~-ecK$F!'N*LM3tr*68OKJN}<>. nfY.K|$ˑ"g# \vTAކhLp/ [>dn0^k1C,ERwJXRs\ݒKlJ)tf&]^Z3HJꟓrփՒ/n"/0N6gji""6.h&੥L 3w0#J(ŷ9YA&( _`rg'4ED\' κ6ѯbF^PVmױ]j>wb(=y0W9T-$/Unm!ҳ-]"oz?&wA,#N-M(H玴-XM{y#tXON7hd$0re_ 9h&ΆA3"JCzmB#8& "eR,M1 wy_ L`ҽ8<5x"aHm}J"px"+N~ρqG'9};_LԜqq5 hfӣf SKfQ4ǃ9s}xP#%F*8<0ZEN8F4HVFKp6Ҟy ŃD-eb"l S#0TopT:G&ő gq5r.el@HDcGg@C yy׏"0U CQX:ᾬC{Fmvn DcdK0Rr[X[9ԫN?w6;}h2 J1A$BNMĆ!4]%Rxbm.T2 ,Ѥ2ܕzН"v}̼5MfzcTyyH [d@rX1UkjB ˥~fb*`̕~]JX`^j$YXXXXX0dyf^`aaaaa.cP{ѻfĂ)AiYM`sm`J6-h d Zj:¡gɯLCiY WE,,,,,,gPԀ&DYІ8o:k}Tay˜&aF<Ц'iVZg1*g@C<a7{W.z7 =XXX(=`J[ \`c|l"]0%,[>fa#^r耯@Z+Fi}7ZpWdLLzic[8o[I*iǵH Ȉc~M-݃T7ؿ%F#AImJ$ /drΝxD nuL$,^10Ɵs fv7|G_QM(ݲyy T~~H[0TSj\t?˙52ҳ7򍣽٫sF<"Law#Gs֏5ޖD g] p U1g3qS4$08(ƒӯ#qk1s!/0%p\)}uSEѫ@p(p0H9qZ]kAS',XSkԢ>fR6oX?(y$F!'NڨwFT[b19OKJN<}I%= ٕM\EPFS0!cp>l(=~S@b {)﷐>K܌3n)7' -B9*{;1.nq(kQbW?cCE]Ug' ;.)rszq1 ptb-!Uk )K|6*.}%ȱB`pԪ=qr6^y 䚭nR^Zk'RK65y `!)vh"BTHDwAaҲ^PRxd b^M7ZJ43҈^t_jcۓu&νN6=jI [EtX@lDDlUZx4f b,]p/ǤE}MMQɎlbXTL|epmLF,ϘcԼ|h>Xk7JhsQYŝ+WH|e{[u}7">oq-XG@_%+rKC'TDmܘ﯋6.?Ч2۴8[ ]MD[˯kRq|{/߰hnWB'st?gm] MMDs]» = 0[WHu/C.6hG4vEhSKUEH$=ஸk>Zt,^ld& C3Iˏ3|ԧ u&yZ!O-iʯ)@)2,^=gn0?;y\d3E ʃQR﫩=|Yǃ9s}|lV_Lj-U{YcW 'TP;hBoM:.EԥSIuf_\0{5{u``#rGgOda_f ,8~JI COo1LB(RZ@^ƀH BG*yA q3G`Yco޼nand3פFRuz2ak "#ق@5+~'64bw1p˓sZXXXXXY_]vv`/cA11$uBN-,,,,,fIJCĜ:KxY3b|W3a!6Sc y[bVW Q9n{Н>=iD+c0o8kf\."kХ5cm$E!q#}B{;e {&l(eƊWMMHG$4N{zΜ~5ҡ!:]"0zpLtmC覩ZK: ad#g lX-e'MK4W=9!es==t$ت|Mm#מ(\ʮ/. LqF_3ɡ8qkZe@O| 3}y)I}o'r':MKPVڧ Ȉ]u3"-'n,.3SXSoh$o[Inc@:+)P]V}XNw-8v^ .q!<(n~ H®V<:8%D0-/,Se ئn8~;7[ђX[GJF, d#!`+OFwlɉl+-j-99X׬朗:17le KGEv_Q([B0-fbV71UU*!Tq*}M;x6욊lt;!c> 'oCkJʻ&yˎī}Syrm4e/=_ g-iP^T #w (/I]/ԧIZ#li Hg)Ab= ߁Za7b1##Ti2pas7#4bHiji Zýlaaaa'=3~oL!ziWwG+1؂>:avi2%1XXXXXXd1(x[XXXXX%ht `k "#ڂWR%,Nj[XXXXX0$~#tqcHx-,v1'lthHϪq w~Y @v"axdhV͂[l;Sƿeʨ2%͛KsvneAd?W9`AˀvX/^,ŅV]L], 6+Տ-])#zj׻5߅DhŒD F #sB#{[Lma"B:y PI/͉rDM!nnY}>n2ʢ4vhAk%Or}aerD]ƃ dLdtb f#NlV>%>sv^4m)$gc+-$ gC竎Mlw/-Qf=?1Tyj-&?lP=6oq\! vM7tȸEuFi) 3^n j5j/tex~}' *=0aW YztuDzj}fn)2 {a;=!};?1ߞԎPPK:4ozh]Lw^=g\/!UN,ᚽwkȵ< WF=Ͼ+4^ѯhK>TIsJu¶>!:x*J]87>^`oY]Ze콓ά uw]nOy\S-vA$SĖ8WvQ=8cx힔,|if_Yq V2#2qrwKv%W9)64FمT, .%T}i-N`q^&pnCٔ hG҇TU wnA NcBqwA5q)دSڿGzAR 7BnygTIyl)6\ߴW0cOr- dɧt̥6KbdMѐyDs_F19q_Qdr#:SVY GH؊b#Loe/m't[Yԗ݌ rn3Jj;򍊺Uf!nNƾ D\'fAQAxhi?<*|-&r7PH{]@ȿ'Ë-1JMݳcH0V2 0uq#h c>70Om Z-q^W$Fˬ ]`i ]WvPpϯORNMj#J-t!j +Hd!>«4 0pA \+X@PD '1w_ c b :<0,K\}u0!`9`,[ B\AU87ZƳA1. =;n2ݤ c퉘,OAUx0VeI=W$%END)@9r4{Z\ ,GVer_i"S:j''H}@nim7˺қPrepK=$믪1ƌHa$}PB8TA뎄`k ;8 CgPDbQqZHF07){f &<{j<+X8(%q:hk+ jxD'27CØ&%ڛ f+:K(xUf~̽IeØm6Ƞʯ7*D j#0dzZĩ\k {.jBb1?A(/o/YjK^amOE/2Tc`}P{cY[:$2kY Y72B .# D#~sqg6lnopu,K*Nթ̹!Q\.N!̇XjrbBe݄5Ҫ8]*_#1:VI9&ReO0/ [fQ) 堅Ǹ{HcCrwٲP*`s+z6Ky6;\aj/ߴ`ґ!nzgbq3C,>I@1kWJᠩ[N'LpTN Ux/;es,s$U-^D6L`\L0JitnyV*qrקl7b0\8'+Y)>Kmx1AL >|a'?q,=|zj'pSԥ1#.ֳ61-P r@wZTD2NLbA!1ȝ1wK5{`bz!9}WsR(;Bӹ1)1ohu:ģ0nW c;EB%Tޥw`Eq'S۽G*'YD6lwO Zݢj4*+://tK*0ӭr܉B3Gx4妌ڝ%ՀA)3w"X,H{1 ,pwF}HS{ M}22HírcF^DQaXbiՠ2 8EH%RqA2K׿![Xa4?7ą="dwǿA*? OD0 4   0{g~` @$pAϐ:[X0qEngH`#~\bhm(ogySexF qOxVe$s$$*!0;iψ6pzH%P͞b0 A>ELCDc{,f3pArwkiߧ B2Yavr#.%\T.;)Td ,8E' : jDOZL4",]i3:hF!, mktt'^z A`"AӃǜN+yƩ.9K(miy;=BS^KD}V>'f1ÕT^fWǥWvk\ /aڇ1t>ڨ6׬on՘c,\rWbKd1n &,%Bڧٹ-29f6vbqwr+>]L], 6+Տ-])#zj׻5߅DhŒD F #sB#ze3*Ug;axH#B:y PI/͉rDM!nY}>n2KoVq*l<,g7˛!|Y&'oNe<@D6xM|@m0 0V S9; /\AEԘC 񊼰xaɰĄzwct8K|KF돫x UZˠ:6#D)TϬ͢[cq]S 2Qb>sZJBWyxZMsZ# s_gcw)l©Cn90aW YztuDzjzfn)2 {a ;=!};f*sIuD렓yMy.7{nϸ^B穝XD5{ Wok-0y&%dD+ˍ{N?}Whԣ_і|()&j,*/m}BȎ>E*}"Y<,ftiN;D1ϕgNe*Zn[ ""Ĺ:Xwld4KkT8?NJR&xUf 5NnlhWr ^b{mLc4]JŢ"_Bؗq" ``G}6ĺO.8/My)}H\KV*pi:/l,{/T/]B<>?:?{$p$Šx#䖷qFKgɖbM{uonZ 3>` ˿.pg8 8.K|OG\j$I[ KXWK4etṜ>8sܠR)Fu<@jFvq~_Nҳ/3f>g^lvx uBK❌}A6N&[ͪ-0`r {Ŋ, H%:<*~G`,_9X|a@@!?rt " /޷j*5uNB! )[,ōU0s<`VH?hw&XUg½4&-^fUK뗭O½:h S~~}t*ujzU`QhQƦQߵo%\A"31*f "C91jz=/>䳡 7:uQ%+  ! Tx0[ʱ!_(G1"!@ ӷ\LrW߂v0~0"hjf}b C򜑯C qVܸΫ_k2yHĘ`%q{4!|=T):Fت,)gWȢ;i 2d%:cti s1s_[]S__2,= lבý2%ڞ^d&T> ę?mztқIdֲfo ]=/d\pGF'㪥ljꢹYZUSsC|$\1!CZ'#g0S.ń<, j8Uq4 MFbt: rTM󇥓a|;v_x yS2A q)>ǀH9: r8c`[fkHiY^jAs6;|`j/ߴ`?ŶX}ڒNdF˽_`f;a}%чr?h}>tuJI14u D .]_t 7EN Uxo/;ūA' v,(tm|?cプ8:S7W%>/7oe&"Fn|P;[ n+ ۍf.dJ, 6^oga&>{ϸp J /w;u)mb̈KklML;3TAAn3bz@1ySӱnP`L0rgRx6XyʤITJgMOq7O+?S$TJ]zVD_'{2xvR>1#rlkpp\}=rMI.}*"e *sXb:<Ǚ !9JW1#Yq^ IZl/4]]܅<ݯS05ޭ.M'Bf͔'R0Zi W6S\ZPU-NN^(j:T{EKsbQunxۇGloVoϩLCh+/!ZZS#ĩ},oEg9Q`0"n'5]C *_S1ر0cѴ yQrs=ggEAxj1B1yaBV +

ډue6۽K!~= HbPr8̈%NʳdK77-{B0_{8UoQ]F%K>姣we.Y$-h%,Ϋ%2\N q:J%Kי=BV٭ez+i;Jrfd}a=dK(ߨ[Uk^d @q2jVm ی43;B>*<2 ~G`,_zA|a@@!?rt vc1JMݳcH0V2 0uq#h c>70Om Z-qٽp2Il.E2BXZl|BAcf|TSӫRGrh"j +HdjmI}'2ŦkfƱVهXr}(cHWدtAS\OTeEpSdP\W.Z_mty@o7ɂHWݶg36>xX3uqU3rCVj"(mԿƧMBӹZɪ]b ݈Yp Tj8gIP.rpux; 3/S|ƅ`^Pr =N~ϧ۩KicFD]_g 'mbZdݙr re"vBc;c%jlW`쿣I< 3F~u 0U@Rq;(ʏ Ru{ށ#++ɞLmpn5즷6zvve-|;BL5 p݌F=$ "٫ab,0 L1Y BFg|F7|s^H4$ؘ2 YjhhSeOIV. [x"\ЂXFOUAVYͼyy[Z&Win+ VzjtR;ﱚܔ[R06(#eNDi=&n٨ZbjoiCFbuWn1bވ h j;LT UGTVˈ+`!s« *)tdY43E2|&O8 7qa(0Y/oP?3_| ƒe[Ж8U01 f1 XeK/~c)%,$?}B^Fh߬' _o<|acD1=bN[nN;N q} x؋7?? bC0![ }D,u ! aCQd'ǏQԟ0! aCP(3`'OGxgwG-^ :z#^;`C$mWtYeҘq^0ux sX0@YTD K0AhLIKmY_qV :u=n:u @7?.8z:n{S3'b-c0ot+?ſX?/jovs8u>>~t:W=52\+6B ~|ӎ{88ۄ|~Wz!Zx~T6{OQ=e)9S$O1>0 zHpFDzcp[;~sKIZ y|hNn#7D*CQ7qZԟ t,cFOI#'b0 dzf|Yeœ=U`mWV-\?~dZپ`7a0QPK刹4+lZ ܀/X8ga,3sTnL/2;'fO_~Wo= 'vMjäISѴbDžzgai$jlm-+Aѱ,<,|/Gvg9x\:M:8]pš5=g_Am:JJ[jbdcYIENDB`filius-2.9.4+ds/src/info/img/router.png000066400000000000000000000141741506574241500177670ustar00rootroot00000000000000PNG  IHDRM:.CIDATxݜpUպO C x EB!1 +iPQĂ"{ػ5v3O{{뷒/n(w-gf{e.\Ϟ=]=zaÆyrrroכի󚠠S6',ٷo}׆v'''g<#˞={L Y`5C=6<7n ^zI^yO矷z-[~m{^h+++k hpu =T]%oao!(Xg}V|Ԍtۀ4SFy睭@z-v 7g-=҂¬^?Se˖ӧG}7.rΝͨ?s[+BV\)R]]-Wm۶<`a} kY_}in#h}4E0 7z |ͲyfilljkkmJYj\|ŘL{1-331<<󧎎7?~mE1'xB+Uq*Ss|_SS#k׮.8Q~gRZ͛ ԿO>LLpC-io>1 <9rdݻ^ۨ'|҂,.1f*k֬ - 5M P[WZZjK]]s9r饗Z6 fn;)f7?>Z;Rֻ_~C]%ݿCͯ0PGl~$}ݶ1ZL!RTTd%%%pwS97f x )>s5ft7\4ߚ<O'38O=B]@k:˚!Rsb%9!*@Ϋ*HWXa.;*裏dy6Aﲲ=ZaG;08s;ҡϩŏPY-4-BCAc0??nUI@ `:t:Lyyy6X`p36j~XV}!g\###3LѳSq7 4o F!*0+\w y{q{[.|Q1NA8ۿJr#z8yϢEnv s˗/e8ﳳ;a)z=Q+:wM7YЙ:ڠk~gտ40M%ƇQ s3ض膹PN#(UP@)\fԿ::k;$Νy晶nYPd ߅v9/1vnhc-<ы%\bS([ bEl9*q$yVx";[H;vj1cFU>'~uk͉3HLI:\l,]Ԛ TzН ޞ={;=׷L<9wL0n믿ޚ$^xȐt S;P  -fvWwرΘI*i2L $ *^pR(00]QwNQpV__/W]ul#b$&r}Ƴ y3jԨL c߶ ɋxb1- [=DZ@s>],Yb1nÆ L+1]M@CFy?(Y'jW Pffa6&r P~"l9ۢc{JSZX8E, C}NX` ,h4v![S<X&O=V *A=Tt0ԿiP6b(otEXQڿH]裏e.n!uȖHTEsε @5QS .4*tP cO0+K^L:L%[Ly+aRjj-My zP~Zpr98lݺն $Eh7j#Po1k̈41jf HP#qsdo͛g+AiB * e_xg=8. (#`2`~' s=NЃ 8 kE`mJwP*k [ʘ @ Cm~86MY X<1KaRwqsc0aDM pT*Au脘bqJcn:[o* 0MFQ0͘{ّO?*WU!;Ɣ/i@r0&*(EMSNQ(?XS$x0Pf , FLͨ Shlh E̞RBtִa"" im޺J>q8H i3a<H峥2] j;'Gǃg] usCQ(QOq D:K"HJJΟI`o 4&3]7|C+T1[,( [Lxl9\Y]ʂ\)$i/ 9$%d[I@ u^( gs(܋d?1j#ve ƌc->9v!C4iu,Ȓ`(5@]F6`7J^aYt2f&͕ؔ;wΕ9,I M%91V }%&&Z9ٳ9F@0u!j4k1?r2|6בb8@$2eM-pQ8GBi"(ח_~)[gϞv(y 2Ban ں5k\# RTU#iYy7/M2=+1(/4~UXr|$Ζؘ%66ւUcsH,F'Ì♡Kp.NKgo.)nwY7fY"'a?x`{3nDnCKv]m^U'u5Zih4i4?[eqfO˔dd+gHB|$%G9wOLW\Xҷ+t뿺>`<=5zh P%\Cy!;|,O>rЫqbYUQ)s*X@5JKYiUt- ,@)Qx!"0 x%&e"fVVVȑ#]p݄R664x)"aaaV]"߿00\ot4Tkpcm-*V/ OIDF|k8}Qoln6#T\F ;L<2p@Ex.}$2?,D+e$3{,Z`FK .2 MEGʧ餣,ӱV4!5Up/{ +ߠ7ϜjFW&LezYlvlxL9T4pˀ$`H-?^&CO;F<ɸN S'JG6o=vHl⿈cQZ'󍠇SHg%1yꮦ+m;;e4[ H7OHHh2} ͷBB=qNLYݺqa>Օɤ4O RY5eLd)۶R"$!)N,Uh2aPD-#FҎSSW;IsG+w;~V\ÆRYb2|#߱+0*@e zJ4Uta$fl?aR)QYya321]Fcq55o՗evŹ:>{4Ĩ0lnjߐښ[ VYRL6裏r-?~(jA!k\+ $Kð?8&&jq9gI AIENDB`filius-2.9.4+ds/src/info/img/router2LANs.png000066400000000000000000001745371506574241500206010ustar00rootroot00000000000000PNG  IHDRzh\VsBIT|d IDATxyՙyZnhfAQ(JB81s9; b„̘yɏw0Cq- bHqAQi޷Y?n纞SST=HeY@ Fr+ @ @ '@0BBO `r۾})_|eus-[n~Y_7XlhGvG##ڑ]Ȯv|u[WWs=i3{lNj @  8j+0Դxϻٶm{aԩ\ph @  睏ޑ#G,5~nz yظq#~!t:'8k8,zuuuԷSXǶ{ŗpKGQWWǸq?~<@ 5`SWWGKK [laµj8^WWDh@T0 1oSr aj9BCC7ߟ @0bUrʕPo>8#&G"H>4?(>ں"e ƔinnFudY>@ :;; oqa Q%5^WUjkk8ԥ# r r !XS[S0?{e'=>ޞv|ڲlhGvG##ڑ]Z'Yil"NGuV>#Dֶc(*~"mư L2L~n`v.7\ A"  1a*++3,@ 5]]]򗿤%/ ˲d4Lkih&\ΐF% z!L|?h`sĞ ԑ 4#Y -s&$ 4Mc̙L6-M`X?mdXlJ¥\ihS\\LAAA7B<^+0Z<<;Mw|i>3qsyBde]Ɣ)S@ gerMg<6r_g.//\m}UUIA)))tsFmݺ۷c9t(ݙA轳GӶeAO۝H3/,d\-.RΝK0p@ ~kqmWk.HV?g;CWyHۯurs7)bD"$I̝;kL7_ DV .c,^w;N"ܱ)X9ȹ9~sXG 6V>7d_S7HM6vZ^y#egrya۱c6mO8l= ,{}{zH9I11On5Sh"@ {kr^g}w}Itx$Yqĝe[YՏ4f~AF KO;iZxNes)* 3# @>@ kr^UV  KHlwŝAm˞{=t:AcnGyz}lKXWEeP3w R@   <&+y ^mGsxn`Ka}2حb1EIJۜ9sFBg% Xy'H sH$k$!Aف#^ _K|}|rN9ק'=L %%%tMB @V >ʕ+Wfywy?VMK[F]i0Sb2, , I>YER>d YQ>5$E_wܣ¢Gf_4{]jjjbTUUiZ?B@ 7xfv#]0 ɟI!)$͇U}^!ꌿ->vPTR/Ж!Iۨa ߈z:G>ϲ;t-pHeٖ?gwĜvs_v"Iuv}y ]nXM ]ISïG8|p?۷o'N '45!)2` Icd}S{ .ktQXT1ƨ1¸Fy1{*QvK*)ґVjjjxwQULT̈Or,ˢKb].XVOV<`X0V" c&"e0f"j/ÚݹNj|;K ɟ!yȁ^ dR=giVV>wfɪ :b7z IuVT[I߶)$=Na))$=t kXzz|2.cni7nUVe<-#ZcY8ǒ%[Imv$՛Kޣ;c|X}xϱTSe~{W 8ڂROTRtmܣұa*喽'pD@  µjBѣb17)X0H=K!),^Sz2Jn"GչVVA "ImwNKQQ$t7 y@ yתcĦWٺu+XnXw9)U˜0fI cƻ1cx7V+JF=͵TmȮvdW}D;>nG?תx3օ {)μTgs+@8θLD1Q{L6 $-٩Uys_n= BXm׎^6~#6c=o3nlK²$$ ;ǟ.3ZzGr7P6Ȧُ9L4L,9,pEI i;-3ʲ1JJJBV -#Rb1=Js`)$ð&ew(IQ<ξ^@#i:X4 M0X-, [Y'$,,;%af$ƘHN@  @k;}Sx,6.TX[Y ?2v<5Z,1tGԀ}71]%۹d$]!ֽv 1zD =7P}b1u{;IƒU/kE :{Va`#@3,[Y v9`< gY՜ IȆAiG ق@ g2#`IXǹVq ǵlQg\8S 3 b2QȦcqW\*Y]%Gx})z.czmcG >ۤ$) ڦd:bʹGge\&iq޲icztl'c$,[X*`!!Hx.@ Z5Ho>FWIvCL;arLR?$n;Be6=QȹYܥY KAM`o1\f @  µjqULӤKRIc!R0)oOvx"-Q2t,Sw̽{){'+iG@w?}~[$r$}J"oYmȮvdW}D;>j]vjDV" 0VNf&"vD,sRŰ=f8f*'ssRqH%鄝"N{^3`XahZER>ѵ*^%҂a|lݺWk"_׌j1w3cWn9aʞ/'l[s i>|HrK?Joj}+}`z;7!믿o=@ ƍyzGtۦ>UD:}8>o2du/=&M Kf>d5mSۏ UtJ$H:e1eMgǎlλmfbSr*$ ;#Y+hJgLDŽdח:a`c `YJ1Ttn[#MS @V =ZA$!yOe\#eYL4S2oǼۼfv\2u$OY}p SQz#~$7$ }K6{Še Y:@ԉ@ $NpI{iLmΜf>\\0$Ɂg(X$"Q[aGН벗F=췔+'V !c$qA~PQQE())4M$I4MƏτ ;ݻWq=VA_ibl$#r?K_^ñD[#.5N[=<`p(?N@  ׵{?$Ϋ5kd<*NZ%95̸/q0$ӎt{Pd'务X 4rxn(NfIJcɲ- I(O)u:/[nZ|MfΜ_ԩS=gYTWWs އ獏1 S5Y$+;rovlWGrTqMˮ`I"٢7 kYHII˶@ ת3laDQzzz$`0ȡCxikk 0 vϤI馛2eJ%r***g[vSj(-{dyyt,Iv}2gk{s(޶)>?r/TBee}@  4µ*{U8b1>,>(藺4M#LիYz5. ˲JKK7o_QQ{rC "s_DւZٍu9J ~}3$gK 't{l>,#ڑ]ȮvdW}===477F,ӟG}oVs׿_b 'G# H|$RI_Nh\{;4Iڵ~_w 4UvMbmmmirss4wy_~ àtN$ PUI$|>())odΜ9X-Z$Iaj4CZہQǎq#}˞$#)[E{^ylgT٘$zS8[5??3 @pZL&V~EZw6MYih*Io%Vj8JN2fI̕sQ%Džǔ.ΝwߝPV\2ӕ8˲D"ttt(,,$a׿rQ4M#1MX,iY)** Fx7 TUU!93zt)W]^ɕi@.ՇكIt~ P|Hj'gv{Uoٷm}L:sfl0 n, ϑ#Gկ~mQX6v;}(---" 1e$fN kRihѱմ>y\QQ1W@Q1ÊP`yʥm@5S̙3'f?;ʢgYxnE!//Yٹs'gkkkt:iRRR$Itww@,#''b,"D;G4~?yyy\wu̟?@ Y\ _*VՙBo{&a',,S퍚펧yV?d-ZM7ݔoB cYDp8 d2o[}A44HN,BurrrPU)Sp 70k֬~l+mP;oDՇ[|VX1F虦Igg'dBrrrhmmW_?rrr(,L&)**B$Z[[ W麎,*t6" (((`Μ9\wux0 ^kgx+l"-$dyǤ,ib ?I@Ӽ?zϰʶ-Z5| Dhkk<4o6o( =zD"A2dܸqN줮d2IAA%%%x~X χ'bPT*E$!LbYEEE\wu\wu5p`wԏCA[8}[x@^mo4no!'|9@Kh@ @nn.pM6Q[[K]]yyy4ʹz10 xX,ʸ+Y\h1빃# 9\QfȐ qҭߵ'>y+c:u*w3zDdY& iuuulڴh4JCCѣG|hFww7eee$ ;FUU@V:;;IR0zh4G b1:::HR^'$ ܹs=Zv:z~V*eHz|NcdvҲ(l[rCUU?я2U@pJkuʈuN4@G*bٳdzuww܌aR]]MQQƑ#G,QFy=i]]]^FggNbt"rss=`8 _ 7رcYbwG^Mr w>!v=_~LpEa#pޮUCG/lEaϞ=g?cΝ9rPX 49e3e.bdYfݻMTk IDAT<.B&Ǒ3,V4MP(D @$(((D݈cRQQeY+j*֮]uL`0Ȝ+.d5S2a.?>|?/A)$+Eip$"hp)˶vdW}D;>gZDEeF$I~!%%%|3aƌ?p8Lss3yyyTTTG$B`Mɤ'𪪪(--%JQ__ϳ>y@$/eY*,'~ C[ɗE Z}ЂѺΣɎڕ zлY6}WgrY8^UU$رEQ9hBp"2---tvv*Gd2ѣG BHnn.$Lee%tvvLOO,3zh t:;;e`0H 'pw0M4 ׿ɓ=1P^^WBKYwS5,@ ,K͈ h@ XhW\qE ^>F:2M07֬Y3_@ EV<תAzeF Ø:wB= /?EQ<_JII D0펎B( cƌ.D"A&N,b1jkk}ssss(..{~ t$y믿//R߿6ۣ3L%`X Ina>y|F`d͹,w^~ߒNiiictuu /^YYa`Ycƌ!BKK x FmrM0ċmjjBe 0 ieYӃi1m4:N/nvS[zRXg{gw o{7J2Io}9sd9wM虦ɱc<EɄ|Νl޼:,ҥtuuF|TVVRTT,tuuڊeY3fF~~'Y;>|X,F(bԨQlkaѨ àx}:# Q[[˻K<gL2jo&mBJJJ蠫йjBbԨQ|eĉb1;gPPP#''P(D2$կNan:VXmۼ:iƍwחyVb俹3LSi2]}Rٙ\;ftFqoMBUUOLB!/;Aͥ6 `ԨQTVVhnnFu|>xT*$IQUUEaa!HF0,SQQ| BAGGai k<=w.A @ LkZ5BOu@EN?W $Iϙ4xBP(DSS2qD|>pb&~ߋ.jjjѣGL&illwu]|:;;=_| 0 K./}K~4M#HP__i$I:{̙ 79s@@ ZuV zd2ISS---;݌xk\Ʋ,JKKݍ,{AtttxF"??[un`0Hee%tf1M2+K/flǍpJRqʶmx뭷ؽ{'BcѢE' @pתӻV~Be cΐmN2^N>u'{uuu 477JZEWWdYpm7c̘1.EEE6.ɑ#G_W$pWS^^eY@^^{PU2n{O[nQy/|ٳg%pd*\@iii&OŠW9͘=f4sq|{|Zw;t:M2Dux'φ hkkK̙OSNH&^VѣG{3ߏeYTTTBA#FQ۽zE1i$={6wqcǎ0~63wiI3Xt)>,˗/g֭[Yl/f^3>{YG##ڑ]Ȯ<]t:磽d2$IR~o޼<a6RSSƍ= 1k顭P(E=a7ngNUUꬪ*@) MMMl޼?񏴶y}^@ pٹs'/";wv 'zzzkV 72&˸xO$xWx׽[7ɣ(4Bn͛m|o >w_>WJWWX yC: `ߏ躎(*0 jjjxxws;`̙@ ~Ѩgp} [oe„ µ]z.TN0HD"( /Ϟ=k9rěekW_}5_W :^\g---$I/6??ksbYxH$ |>@Ff{#[nfj3f_2 , sV[F;[oeB~7Mp85qE؁=+>w]i+,,d…|sX{urSb1u&Lmhh,o窒1 24`K/3gbY'DCG^^~ACDV=wsx7Sk;p$Iϟ9.]˩"N&"%J1uT|ɤgjvrss3. /aoYq/\0qa֯_Ν;4]s[oe֬Y{ԩU#z.nx R)nʋ/޽{(++cѢEvm#<-3;w^B!.\ȢE>{ תCV =#pݍɤn |~x ֽNkXp!999DQ/q{{ׁsss3f @ cՙm6~{~||_O 9;v`… 뮻(..TUß!Mr6e$ FMNNE{{;pD"E/KS:nx衇XheHGG1ǎf+Fb„ ˦͛ǚ5kxᇙ={6<#~<g%ʲ>Uю쪏h߷~X:˹{yYt)|~?ѣbь5ʳ(,,ꫯ??{'q|Osg?7=)wiص7B***wW_ 5|D^6^\H$a(X/z,x]IOVɤ3d„ ٳ׳gvm~'8  {ضm/o[ne%Z5VBBMu{w-u$vEӴSv0ihh֛ 2c 3A{}v8]w5$0@p۷uy.8cǎ;`…CZV /K<D"A,̵}q3__>xx<ή]H^g9s&'NP+m۶~z;@/^<cxGضmaP^^]wży2cYX p-|axn0DUUdY__qw6=%I"JDHӞo5*cM61셞i^(x2[:2`ȲyZ(]v K.3giZZ6t-[xhjj`,^ &dv@p~F~ǓO>I*[o[n%kU2b^_t]'yDsts O^^СC~&L>O41 gy~h4(|s=O ͛7~z:::r-qY?XVe#REu8hԋq-}{3P(E.Xӧg'ca àロoY̖`8os=gfgp6@njøq7neQQQAyy%0~x.ӊl K#0c 0k׮eɒ%^`5[@e[}D;>Ujǖ-[Xf K,waڴi]˗s!@}`{}gnjCaa!~Y1 knn6 5h7|_~.N)DU0uD"e-[n::::׾=#TO֭[yᇽe{gf>UCy/gG<駟橧~.LWM <ܹKz{tk н曹 K ΀ 6~z(eee,]NgeYTD"AOOT90 zzz袋]#1 w<#qYbft +ijjb޽n{G Z50';vkײsNE[n8vի Ôl2"t < =x &rJ3]-@ (x5kְeo29#^e ˲>֎ ֯_ϴi8|0{/< a vCC#scϞ={lٲ@ +Xt "/z&eV^E `<ٳYl< ׯ0 f͚ϛZA `ػw/<MMM"PC 7tttvZmۆ(u]|yϐ .w5 9u?Y=Íh4ʪUAQnox`DraVXcB,_9sdZ|]vyZf3}Eۙdeg{H(bժU,^EQxGXbxUюO߲V.]͛ B|oʺi;Ν([Ie$kֵ*x&f$d˪}>S-}ϤlLr.d*$S<<m AsA/_Nkk+'Ofʕ;6:gNt&.Mu@Vՙd'2tI˥lz&|[bɒ%|>y/_N8tٳ￟Vf͚ի;kՙ#}Ϻ`o/9[vի)((tnʃ>H*bt D ɮ תs0Ng<Rxtxﳽ@g045k֭d2]-@ SO=ŪUHRvm2$+P& @l`H-z<}ϟ?II=ڳOf~>}ǃ>Huuu&<Za KXx1p |Yx1uuu'UUU1i$TN]]G!NV"N`VWZŖ-[(..|P" 5B -fɒ%q3^B6;v_TTČ3gt/w9twB OEE> f ـrʕ@p.1qD׿ɓ'S]]}ׇa~_pwr~K?`/ &2p2dYSѯD;>'+{衇?y[4fSYte֭?̚*dӦM,[~Eso8СCxN;v,էMp1;TWW;' 76mĊ+8zh*(x4bfϞԩS3] `^{턲1cPYYyN=dC񉯵,LrVjAD0bUPk.mۆih3uT.4Mt !ÖH$իկ~ss~~>&M{;yΙk2NHH DRQ*-`uZc]?VwiUv.][oizwQE"D`4r &d&3d23ǹd!&8<̹|^}yZ'X,Fkk+Ȳ:vq}صg/sSU^Çiii1$|bYNv^ MMMWw>GAQQѐ---x<EA#33!= dYf, F|HB ɄT_M\C5!."+W+Q䆉\sytvvRXXHNNΐɈI%|waٲe|w5$DQ4ʋCWeZZZf(`٘0a^xCMM ---Ơ!L8cL|_,XA2?j(m`W DHs"e݉Ec<.wYP_~|@gg9=i;uS)$-^y|Ie`X=z4cH줵H$blX$" n\.gddPZZJVV$!"$(?iHL!T_MdŸ'<ۦȪEOQdxLmr 1,V+K .ׇᠬlȥurW0a„s}()!_m۶c{ v =&9rH.H$Bcc#]]]LfbS=R}5dC__mmmı ͈E#uٓcdY%{C˄(Ͻ 3)c룩 LUUOÊ\.:::ظq#;wF ,Y^{ PcĈC~zO(GX,'/Y蠣Y wQ~~-**bK'R8HT_MV( wf @DJYF4 1úa6懓(pغu+W\qI0k +awwwR[[@<߰|r~ f5jrǃ1$7pX,JJJfzn&}_iiiaZ`锔RHT_Mf444p/ fOˆǵϘ;j$0 r%XF2:?We1:ooĉ꫱aE>( Dsw79( ~;;w0$==}H|Fb|$IbĈfjoo?d!==(baoH?FjC'z\D4NẍSwqF_ }Q\ݽ ?=icmˑشi߹wKZZ9>1^JD(O~~Ӄ>Aﹹ ضd"==9UU\s5*+W*'lVB ɎbXe (O-&x4aUjɓ j{*S("x1^OđeGyYv-W]us!77\_6*bϤ 92wyf#{F9|0[ne߾}jzG2%`ٌ7<ȣѨQ(Ʌ#'AEEŐeYzln;K;ʋ_> /D:@2ݫJn~]piKIlkii`_wbz݉`s"IHRuҲY+v'=SwѮ}23YL^fU_| TWWCO*͒W޽~j 6"z!Q|=܃gϞ=Ʋl.2^K<7om6)**ͤnų_9? 7o>tv2XK_8_dC IT_MdƖ-[xyo* '#qEu*q:k.7j]%OwmEErİ]!qqf7f=~x̙sΓFŋ/>GpOpL0ddC0[L=!>ڽl6QY6HxfbQ0Ek9-ZMTev}<㔔0}tz]K Px<(8NAu{' =`QMKKcĈCŽx^|>aa2bHsHEmF@ E >33TƼt6mbg yوiYv-u:! BLsiZhS325DOa6m~[y$K[>x@4eϞ=رP(Dzz:yyy7;1d蠥5؞;ﺓ 'rS&cqwqff0"4g;x/yꩧя~dtd*u\\%mGQDxoJoJOJO<@ Q(}mx_-'T-p񸚘aԤU2U [5@f)2m6)Z[:ՁbIcO|΢z_'}'r}b\)rD䪥I)Қ6Q1FV|cdY7]^aԨQȲL,#DX gffI<BfP(رc3gdF`G] Fv~#hooO>#GOO\y啧㲥YG2UYT_?9  ^l}+JjfhR-|%OlZQn2p- YeEs!guyp6m .^խFcQ.?i6I e)E tYWK4sk̸yr16 Lȶ1!#Fuu5L2%Enhjj1fV+& QbbrOOjP H0 Ax_O>˅;䦛n&X )$R}5dÎ"UW h[b?M(\\;EU,2Lcif+J̪h5"bdhLЉauy RK:"3r} v I 5\ĉ㗿\D0s^x;\83jRSS$I\s5̟?#F+B gW- nכ)o>ulBE!=I=4'HI'tfɂ`LLVUmoc*SfSX%r&F* l"[8S"鉞nd!XU^G M=U'ĚOI/!5Z#{ft/Ev̀"|TWWS\\Lkk1px3mކl&;;# l4^o9瓭|zzz(..K/=.*u_ B444־t:9s&Ǐ/Ӊr.T-pE VDz'J(ӓ-0ZSO0[Px1f "dU]fJ)ZDa-D# zBqX@<1k,n|G'.TͮX,Fss3deeDq^H.Y9|7fn:%FUlvp嗰m.[صkdff[ol\R88W}5tZP(믿~pt:$|N'瓓 ''!UHa-za9}DOϤ,zdB f:S{hId A-Om Z0 I/"HSA%fPQk)-$9K _An7mmmsdY`!z_d B8N,!W}FƼnIHL<oQ[e} FS>v4yy{ρE,X`")p>ࢋ.bټgI^y̘1Qq& xFAʆvu8NlJKK999|sHoo/ mJI -Ѥ0dquzlVc^#(bVU2lUcM MO2DMc:H&$AUC!Τw(Gq5 㡶 u]pJ(`DE0isE%vZ†p" zvcȟ)Z9A9'>;5~*to03q]wy"~?466nP(d" 6t.RcP8x}(xq\~` I(,,L8qD":Ax{8z(XT0090"0%O'zhp5+sxQ5E{33}uv5'|Boo/L03:NMƤI3D(rX[bj].]zT=u{+\  9QȨ1k>-mZchB0b,y5iI&5>O0<5/()S263" ogy][TWWts})O,rhhh0\}}ANii)bƌc̟hc=ƣ>G}Dyy9 <;j=݊p"}^b1+\g;kJ]T\XضmH=~3,sN>cvIss3k׮eڵ8&O̜9sNvBR"==â:O?4}gAGQ5VOtf3[0YYo=LD)XlUO;Aْ`K~z>A4>EѸ%z=_t8! *8 &AQSuCTkd Nu u bW!IGu3gg>gHB577Jt3f RXXxVI}}=K,a޽Ȳ3ji&& Պj% ~9fL2ybG??1cIeÆ ;\.cyNNfbΜ9) =/K\.V^͖-[eΝwm6_A\.FX=zz|O ؽ{7⫦/1"Ȫ͈KpjĬ?΂`ҖOm,:dPȡN 5mvl{تj1hJ,䇇[o;c)i])ērݻw/b޽Ff`pP^^Nyy9cǎ5ܫISV^^Κ5kSO=e<:&ɘϧ~MDKK 7o& !"> HFFICNNwqwqͼTWWrxgyg͌3p:zRHᬡ{-[1LȲLfffR/^޽{Yf լZwys2k֬aKUUUɲL}}=^<. ˅( RYYرc)--2i۶Tee+rEL6MvMFn ;m#% Փ+IP;8*D/%xl$}Q__O}}Y;v ?~`8CQAϽ͛7[\s O>$F:-Q\\}}Gmm-ׯjjjXj3fছn3N7~?6l`\uU\s5{IW+**Xt)۷og\.-[ƫĉO:א$ L2Mw޽{ٻw/z"IaݿUUUt޽j9SiJ<} b * i" HAwQ[GAВ@t ?, oQA83mJZ<@4ϻ 7A'z n[$YA@4DݺGbF%:cfؿxEChu= LeNySmmmr#I$I ƏOYY`0Ȋ+xW2uicgffLQFW_!"eee?+,i&6o kii)7pfJYR8e-[;PSSc<ʘ2e _=eee}U?UVr^ӧOFTAjkk(++3m D4VKw/t_'|>GI1cx),,rj*nV_e ZVcG>ڜ_w$1m4֬YüyX,lڴ;3~mWvbʔ)u],\5kob ϟϤIlކ6nʕ+?>g6ƥ?pZ:V]h{e̙ $"p1́hD;N>e XFR[@2fϠex\WL\YʰFo{bUjLtA\G~m{&WIiѫgBz;@$UtD@Ԙ:Qs&712V'XlQToaSpL[ݢͬZ瞣LGw^ ꨭ=p0qoL盥dDXl6lD||㔖hutM='Z|M6md 6]vsaڴiDmm-o[l1GtML6mϟm۶K/~mmmX-[e})ic@WkZ\.|ZĺPD|ŌQٷo[lgG>J<ݣ5]*EA WEd ٨,5NK.,l[_ܟ!_3^=),!gQWWw[caa!UUU+++FD4777|3w$al[oe?l =z4|[?7dڵƛ.{7ʭpDظq#/{[ΝKeei_2U=~Owqy睩 rعs'w瑩(4i %yȲL  xGZ~'xvvhBdиSb4};–Z֬4=vd2'Dpd {؊Z*)ޯkۻ9RQ8'^hWH#5Z'u.NI$xH$w^/ZbU Sc5KMM _|L ?~vE</{?g0t|Aru=sR2~?/֭2oʲ?s=G$!??|ɓ'Ѝ~) ~v'o'e#2 w^QTTĜ9s= ٬el?Є A\sɂ&T-j[f4 o<: .bA)xp -:m:Cɫ)Nu&HVC[1 kY+[[cQJʔh^L1woiIkjrTmS*7 Zn*$IJsϓm[n=nJ/_>(Kc=նSݮ; /p]w!I6l;`ٲe@JHd;S=˹;Yv-P)SӟŋJ<GdaOwY ICBȲW_}ŦM:vi]D /]Ӣ5 IwZ%j$J$&eWW$3뮻[o\_aP(Ē%K`|)̚5k -̙͛3S<,ˬ]^x /LzQ+R85[UUC=(B(I@EoNMM ===Ȳ㡥h4J<'==\Az{{IKK#''p@Sf3V ZOf@A p_ϧL^CW .2~A$3{X sagzs@Lz{"I/fԩ 444|rjjj59hxլ\d2e ~{* KֆࡇZo*8>p8LVViiinvp:Kcc#`Q dgg#nł0],CEI@ $I8N'SLktdO׳CNOq✄eϰXu>IujDX>ٰ}_a:K EL~~>K,1pp}vV^mN:秬$󕕕})ͷ`0ҥK mƽޛ' x&##h4ʮ]O$p666G8fF<χ"t:Joo/J0DeH@ @8FQkk k;@%|YAvEsP1ɨe ͥ+$uE՝kdJfp;pvyxgeO 顳p8 ȁ(--%==P/t<'++J.d<鴸0i MTy@_?*ZqCx Hj%5GD+Fe̙g[v)//wcƌ9ׇu^dee~ZTTT GfXV?,i}S`X ]?g>:::bdggc8|0/"L&"~ݎ餫 Q 455QRRBQQH x&1cƐMvv6`χ$Idgg" $Fg|>JJJHKKS54Wvv߻jr8\ӆςo5tdP-[=O())9+*:ΪE/zS)sN-ZD(bĉ,^d ⊣رoC_+8mP \7"2V,zzz|F ҧ[J0VʤI)..`CY9ŅgEdd peΊ,vF|Imfك ^;vOO~#=ZEE3q{ g 2lVubG0 ѢJ4F 3Gky4j~)ci'۶qF]tE,]t;v`ٲe,[lq7qg?g…r?>+V`ƍC:ob8^e…,YԩSy?Q}\ꫧwqk?'?mɬZ ^/?7nD"-[Ɔ {NyɌP(%cX0Lo-[$ dpDp̘1U,|>&#FPXXH8AZZ#5kooQ1bvh4JKK >Ql6|>! !ǹ LyyayGGG՟\Z'BIZUU=EY1˽g'1FGQ1>ۃt: X,ddd~LrTA:sk9ߊZDMA2%ODHbjhRu# IDATEK3%>UK찐i{OSOy`ƍl'd'vGe˖n:Å[SSVo1ܬé8Sx7R^^n.XG}xR`0'.1{U &O̫w$I2^ggh.a:t萑|!Icǎ%++.8x v+QСC IVh4&##ѣGvzb1b!F Br֯_u]ǤIZ(BQQ|uhOAE ;L#@Zdgg%XYY9# knzj^~ql6u8~SOS67#OMp1}8/jVُ_+VSq_ 9V\?) Yf1yd}Q#a[na޼y"zYl[.0&z)Dž w^n.]Jeeق(tuu g%zz`ܹl߾ 6ŀֆbaԨQdggAmvbZ;v,@>H444xp8`6q:0`0h,tuuqڵkQFy4`Oq}wC\& 87<@lhh06E^/qƟgĈ~?`k* 3XHDNUQ2s5W~ e;6l`y杖6őpq& yXv-+WW^a,\pL,[ MNN .֚xW>Y|9>([la,YdX[=- 艢h=L<+ |,ˆ0墣xN%KXr%/"-b…L>L@w{J4(K)I)(L4+_~ph4jdJD0$ڊlg$}zD1b999X,l6QR!V+..NZZZb䐞N,ۍg4551uT&Mȑ#73ucA97Do*}}}4Xj|gd8 x<|><..bl6gL0 . L&(BNN غ"yGx}@R x|eʎ&T4UŔ&UE0[5I&bJYJN@%y{P SmӵdYfڴi̟?Prp2L $Zz=6$J9 0T[\b n6b=K,1XOG۹:y$ h"v5mc=WljlXx*r"ww}7H%KO}!24o+Qr ;q8_aa!eeev\.{AE dϞ(P6ۇf„ L6 Cؽ{7Pɓ'3n8JKKLFF- Enn.^."//Rn_~=DQ1n8Z[[8nS!Э?H3g,@oΣ>른EbSO`0HYY-\V g +WDe͛]wuNkkQl[ʗ~n۶~.L&A&Ű픔 25 B{{;X B(2dx<JJJ"ڊGEZF^kk?Á,x^dYl6$_=w=jU7Hfl6с$Iѵu9x j$EufNrmp >ehьm(Aqm~;RC=DCCK,G*H}Xf TUU[oq%.]RN~}yۿ[2)//#GPUUEss3)Z( tuuiRKR=]xn7k\.dY&;;tM7~, V@ k$ r4'Ќ3(//jrCejMt:.]y=CZZl6JJJX~=7pFѨi%˅բsfŠ=1y &#,e=Jyy9;v}om6|>-b֭Zlق"''۷PG裏lʋqݚJyz{^uOGG[>9rW^yCgg'lNt:NSn[[bh"5̫n4bZ)**" DSSH_ϒ%K4 ) "2$+j_L]?PDߏ` ++ IWWWnݮ5@,((̙34@TUlɄ餥E5@T=yyy} |uuu9bZS-t7g~"_RsN^~eٽ{"v)++a ra{1|>t&,A/VX֭[SJB!\z͆n֪O/D0$  en6n>>|W^yEZ].fIKK/q8̝;٬ۑeFVVSXX޼yxv-d2hOY,,,Ԋ;A[uqp8Wb޼y],p\a蠱ϧ9l6t:ZZNEQx<@aa!3gnMբu]QfBǎd2БeI~\ۍ`%bPrZ)Rj|_~z /~ m`p:||o2sL!a<AV^͏~l"?O… 9rgϞM0^'==]D|>F&T$I׿5麟SsΨB/ {o$Ih ŬZJ 3l6 ~_P]Vm9}4&2225kyyyx^Ο?O0,SfYs[@5͔hG$~۫ˣ{Hl2o̘1C~233Q]]͉'hmmsp8ߧ[+Քy|Zŋ|駸n)//ŢUN6 ݎlh4bZIKK ?O>$jv'm8T?mǮ]xG)))uSy$bLmpB<[lW^IJ[A"2?[l<2D%=15 jw^~LW:@^^hy=ZٻՊ,˄B!p3 Uum2hhhA BZbtj k 0deeiP'0)?cЇ0eÆ |>6l]wݕh '^HvcGz6mڵkm P |^<\.]]]EMRyx|?ZMҖBS5ZHgg'62N',^J8֪}ӱlŒp^ox>^Jpl޼YkEu֔^A0RPP;Ç~HGGK.M:!1 z=FQsAmAR.\H G3^``0`4-7#Q$%%%Z zfV+z4zfD"B!1cV n6ϟh1͚XUj_ᦻM$ =GWW P?RlڪAWTT-.7bZIOO:qFNV$ZaaI/>}:ddd`Z v+ .Nә1c$ȠJ{{{Ɍ,lݺӧOh"lْR Âe…RUUE(bѢE)ux^lBuu5v,Z(f R2rss9r'OD9RR[⭢뮻YzVXwmŬ(..fZDbVjooNGnn.UV/ GdaLV ݊1Ѩ9Qa+S*----lڴFrrrعs(N\>,w`)W۞|>s_=$IgF otџ6yw@[[)/ ̞=Zūv1X,Ҵdg>zjlYi-{q6|>.q+hYYYZ59tNŋ `p8-=y<>31̙3GkHCCFͦ]IUqV^MIIGT0̝;ybIx0LܹS,%ǏuV<ϔ{.M6r(++c۶m',$駟fϞ=@-xGgg'@N )^?Qo)y^~9|yyy_kF!$ a20L8r0hbr6@Luuulذ˦MXn]M555l޼yҋ6n܈Mٻw/&۷\7եivWJ\ND|Xf 6 ׫}ַj iiiҭFUQbޮ:>Vkkk~޽{*ת*~-Zz~}"-O}iiiL6 ǩSbf͚EVV |x<mbJKK1Ծ7Zk3gAEEַXlàU t>"/Jǣ]>^5ky2dK6{c|k_pw^mۆ, u<PS5GOnn.eeeK!=z*pڴjX(Jsi[8֎/,,dڵ|sn)V;^tEQ4鴕0,2FAA455(RXXH$ZWmٸGQ0Z)u0`03eWW(//'HA٫=͛ill<شiZɔx:(++KQVFi3:Na2@S---;^KɓISSSqWW\0~)tvvIqq1ӦMvH8L***[(..&hLVe, ӦM4@[ yY|yM0N~ B,YdDzI.jٱcǔ*$ 7oZ*f)zFff&6M|jO~.5 B=¸Pz^:;;9t.KpBI?"" NdYhhhK~~>GM IDAT[|8qԪ">Okp`0CN|R7WdbheZj7pI>3-W̜93sbC! N>N<ɦMرc\kV׳k.͛7kɲLss3ztdff9" /hI۶mK/k.Vo|x^i\nkUW"?CWرc(..֖-DWss3a˖-c͚5q8ImZ+V.__5l6dffCAGGޮ- $cc,c {AjHdgqw|rdYzTlmmTnoS j uV(xG{W^՜N{cZ1͘fCg0$ 8wvP .gib3;;y2jbyصk0L8UUUlڴ Yٺu++WZZZ8|CͱchiiVżdb֬Y>d2qix N'{ IZaٽ{{S z|>twwݭ!^uI/~y4χرcB!-o…̜93ALx|"d+H(jUn'jУ`, q)jjj}Ll6***1cƠ~g,Xs^ c۶m,Z;vܧ( 1xGill;dڵ6G07ogΜv UCҢ; ߾- rZZZlZ@ @ww7v}ȞAYt)o&555ahP^lttt=]SSSùs0͔|rN[ݻ۷Q /Ξ=KmmmoIt|Ipu\aa! .6ܢ" $5'OdƍȲ̶mۨLI fUuy"JKKPPP@nn.X,\Uj6Yz5~;'NHSΟ?Ϝ9s(,,`"Xz5Ga߾}l߾]v%,[>F?@ɽ"ES!`<V0!tuuq!8Qf\QS T1+ꨫ7= 661v'h^NN555ș3g=-C7u{\0:f͚EQQQǶs7o%$Ζ-[ؼy3>,_}K  ELJeI^%ǒ͞ю5667J&[H6{zΝ^?eK.qQ>>~S=dm3np<|G>|~{,sx N8ѧD\yLآEh˕=jjdSb({K%󸒱᜛Uѡ.n:6mڔhsͱchjj38w/^W>e73;w6x!mƃ`0Ȇ {k#7P`465~=VT Jmm-CkM4$ RSSGahhhyhDh YzVUp d2a6m3sn~5k$$3*jr@%ʩpJ;~cƞ={W*D`1}փ]0c t:? e#lfܹ,ZA睞NYYyyy~DAV+7n`ZoKaLCqLwq;L9IHnn.GϸY29y5STT`4bOFF +WRYYÇٽ{7?pM!#j2?`Ⴭ u݉S#{xL3cf̝;NǓR׃(@//(..F(I̘14yw}ϕG6n܈je޽M==l$E{rƪyýPTSsJJJسg.ƅ9~xޞV>>MJs$woڴi̘1cm&++ ѨA~<'HZ~_SOpBv)>+& 6@+&~H|n7\s5[ff&X1[l,{&Yf`v{dӉNcZYz5odɡ_~9ƈQu;Tt4a1K5=JUU%%%^:%%%,^x,X,,X%:NE=`QXX`… 466vZy |KK NJcjjMy|>_-zC1m̓~Wp!(SYYɊ+p*yxgfܹTTT/&CrcZyg[A"ֺlذ'OqFRE0fUTTcQ^^NMM 'Nq@,rhjj"[#d@ii)999Ѱbihhtt:9p@;P(?w]rssyW@_lذAc~={l2oߞhsWګ2ggy'Oodu*<2ޜ:u|+<8N*++l4{Y`H4޽BjNF^Ozz:FmnMMMݻ˗s .\bᦛn}Ho}[XV>&ܞ+K6{D{P$o޼YT ,˜:uNdYsȑC=~7t+V*r0~C؛`0qN۴iӘ?>  f'<=ӟ+jAE@v&v;:`08"իWSVV6fө=S F0\DvvY~==\d… 8q߯5F/H#~l뮣Os= ӎ4t__hk S =܃㩧$& @x&oe+W 'HJ HOO[" LFF˗/窫DڴiwX,=ﱚƫH`CQWWcb֬Y|_fܹ~/ 466&*#K6{<ѣGilld…=RmK6{<>h4h"nfmH$ŋܹsxTt:/b00 455Q[[Ԧђ__r7X)ss:cfϕٳ^YygjxʤG-U)G0>࣏>ڧȲLmm-*kz/P9,/'26<6ͤp8ġZqsW'F^ Z1PHA$DQx<2Pr8|0v+WNc2뮻(((м{ӧO&˖-4|>!!>}^=Ar!^,Yztvvjm#9p,s7E)KVV_җ[zk,Y/}Kkڅ |re5*rss[zis=z8pd !P(D[[^Oa8c췿-_&V`B/;;[{=Xޒ%K[1͉@0)8~86mt3ψI~֭[mT$>ONkzB,_ Nq`0PVV5\b`0u:L&ͻۛgٴ:6}t믿^ ёG}Dmm-ӦMc޼y6I ^eccu]GKK˨rrrO~­oXN'gfܹqz#b|cL ro$ e9v`eݻWd Q}?N" ٓ!ƈp8LKK IC(p `())aѢEA7shʁOg!.^j' qiӧiE+$villL%`"PHx@0n^{nϛv~?m"& ́dOyߞuNH(Z} LD蠡AI&Tv<&scfGr#\ߘ Ǐu{D"~0sMP@ Ƃկy\tǏ("( E" ptLC$dK_׾p5Dd~?v2&xɏ1 <<ǓE!R~@ 999,YGK/o|1~EՈN1D]LG/"{Fϼy_.k[ȝWihhh4pB̙''cx<6eTA^H^h,]Kxl~۞`C .ŋQ/Gs,t&Ңyz:4XцSS]୷◿%UUURGSSL5O,G"2/ͱc4q1m*D{sAjl2.|Ᶎd1G=w0 _EBHECć]4'[Nt^{^{%Kp 7`ZV$!z hjjZSFCoѥ NjނqWo:u`ǎtjlù@(L&7|3/B՜lXXV|R$Es"HHr(&BCQ?wfcUf9۷s 7pM7Y{#ګĐefΝ;Gww&+j^WWK1d'1-#^X_3~L%^&Lvъן?~$o>~%y*~ypE կ~EkkkR8vimmu ÄBav3/p /jնѐDbViz/ ƾFQa(PƀBE&T{1IJnIߚeʇn/]DKKK ȲL @Q]Lx44dmFuu5GфͫQfD -p´R$W/*ZI ߆C"PApYRmcf>|Ç3{lnvfϞȷjܘBh4&ڄxd*x 5~_sLN*++ٵk>.|.\2$SNw2AQB*2J$t HԋG8&bbO 1S Z,3gI+|{N^OBQ ~_Y+b G-]It[$s"''JdYf߾}iwB$tH3Mqyx|=4tGtS[3.Y3,Hy{Xӑ͚Β1dM֔M̡C)S^E" B=VƓ>}?б#eF:l@0|=---qӆ"wnG k'@ȇ芮7dIzb}1x ME~e'Yō#z޺.dskz-[L 71<E|ϼd)(Z T\1 1SD QdYfny?rc6' I:=Hzt$t7 FPتXF0h9x9gOŒ kBA:HQC/AVVZ*%CUo:]88v)Hd›'y w,H.{ԱѪ۹s%ֱK6{<˞^9r`:AyP|"ݞ/AnG z HFK3gB:#.\ky![3Gv$%+epmy~o*ګ#Kanw*DxBЀ GO ٴil6~ٶH5!Iz @ L,Z&N4[Qth~$H7f7Iâ@  (;CQ|( JNju-GC4T}՛b΄ b^9!z hh>{Ψy KE Aڑ+5iB/EyGO~GIfjWDD/<@ L9Μ9@@?Ћ+PQ9scbL w/*Lq EDM+ހdz0 rI;%3 +@mAW8wHWWWHj%33tfqPP("/pƒ1G#H.{&ztwws>mMګtwty/E|+k_@'JK$@'JEG+"FPB>HtH4$s:KZt)6dmH&mVR'UbU֭[`tX˲;NzGXGE{@ Lyu(ZAF|K5/O2Ċ0 fX;I-0AoB{$ 1yQd Pd9f%FE b Z[w'k/p}QYYȷrĈm/]w!zbMZ@ Ly԰%OJ ERP I".`DқQ&Y-ULD$9 0DB@$ jC$EdHd9-^| RLxqthH<B_o~G}ߏzɓ@ LU[r4%)+b t1O^p"* jZ%B2" !bFDb."_t)"DdP"M}c0D^8PLt*B,.!Gy/| ]6@rtwwS__O%%܅ "1"Jԓ&ZoxZA1 01S䘠(r'OD u*Pz= %%4adE"Z( QT,!􆤻^{[ok6& @2={s D%"aEX1Dk9u\hUH$*Yx(D"1Q$%*#@(@T)hF2c$e#Fɩ!ge^ ,K Qö˹oJ$ꕓ( `AC()k%UE@A:T&!b.vyEyb;鲗.zuI(Qa7 @6T*#c<;vOL!yL+1K6{<1[]] W ͱW31͚Tܺ0ȱ5k@tpEF7#E4 QB"v rlp(Y(|"GV"cJ$Zk4CUbU~Z[[1 F {,~h;p8Lgg縶GE{@ 'HGyӯ!ۚbmL(aT*_ VE2XbmT,p*XKXImb1_n",HFSǞ SP}ٳg?oћlٲϪ@ F磾￟uwgru|tH;:k:@[:َd]Bg.樐b\eH4WOQbrJ$tVQmpsCO`40Uö r4xرcmmm6G $ ֆ'( fbΜ9Zu>FzW1! gVB$hNWݜn7YviWbHB޸k8^0،Lx<=]UGOc{cO{Zyڪo^yߞQ` \$U( Pm(:ht 4mFiᇂd13of L[5Q XSEG- U۾EZۿ?%T FGGIdY"JMx<~"/tAeiR)r+TUu;Cٶ͚5kX~=wy?7oUAqsigŔƴ~Lg*UL2U31h(vOk5DC .ίŞP5?~!N8A"R`T8<犾:imm] Kl6K24MEa``g}vMSSe( ej*z{{aS|;/۪XjR)*jSsOnvoffcWQՂmccMKϵ,l"9 T %y9zsw^nMظX:ɓ'1MB:( r]™NrL:fddi&vACCU_ T*b1 STxx׉F rA6mw]g6+W[n``$zeGKָisV;W\Us(zۆ| }r+UчSq6mTm-Hy'|m_5UUG?!>,G!Q*\w18 y'/X|jm>ښ#먭\:L$L211i~N:œO>ɁxcFGG A\&JDZmJB @u֭[Ν;ټyks^DGyA914-T[Qi(h(tܞ]4cJŚjMѫ}nmf)Zfϧ???G]KvoB??%1x衇{O&Y?rB-wEu4/=AaޘI4\. 1 Çl6%011ARFt]'255E:?~&~nvlvE_$ _L__/'Gyfߧ5l UNQi؊6-ؽSi wK뾖E}{=ŤX,UU?fڵf|>z{{ٲe }}}B!7^ζm6l@8Ʋ,JEY>=Zm|kF:f̈́y^)Aa9b6TX,㡾\.Ǿ}~ǩS0 χŲ,ra`Yt:nx&!p<炙ۍCz6sǭ]lY]07Qà{Pu/L֫:ELB̾o/}' ,ml&ϓN4p8>|l6ZFFF(XE0 EQ܄\.G ۶I&R)ZY0 ^/p;vp]wp*BT菗P몖=7vBNrrO9Ǣg辡O}ֽX[9ݻyN\=c``={nݺ+ymmmrs:gAai`YxbH}}=@Iz-&&&8qH@ @}}=tL& 9Ũd2$JUUimmEu2hd2I&a߾}~;;v bO4֭m~)'OZ8{F̊::oMp0+^ 7j-k~nrݪix~8]]]ξ-Xu¢w_+ \;R)FFF\ eY+fjOfdd1Yz5MMM|>&&&H&&mykKK.#s}]kqܺmS;c12*jCڷ 9{I˜YƮXox衇0 cB UI?~E[zC-ݏu\;c6YGmg)P(022B:fŊN8SO=śoɱcLjF ,²,BmmmDQ8h!| 駟bY]wW]י¶m#xz{w3>iAzzzhhh@4('OĶmVXAcc#rSNQ(0 D"&lq^/dY8\rLCC`rL<wӎ[ٹs'nC.?UXp%Jgیn:ztcݹvMnK[#,mY0$Fo`0q*o`TU;A7NRa~TU壏>瞣\.311ACCp\.eY u``LĶm֭[m=J>5kָǏgrr :mZ84M^XĶmWEQxxcǎ_ǃeY~nz6nH? 3ؼ*ʭSvZY4?F|syя~tIEuRts' •$SIIDAT'c6/yw4` /VZE0DUU&&&8}4ڊid2D"3::ƫņ ㌏322tvv)ˌ.Zr<0 ݲ,ۥi.E{*dŞ qe|0޳/L,#J( P۶9~[Gj*4Mcjj>0ꢭ ] B;vrLSS@BV"TʝeƍJ%ֆiia---޽n-ž7ϿuÊjrEmYh$Y-sJv^z~bYsms_Ndݿ9AɺA.۶]k|+<۷f9}4@p8P(Jmp8uݿH(Dh4J&kxscz SUߏ(LNNXr%iH$HRd2iii! RTd2R)9B?f̆a>wn`Ǝ%)h`2TXSkPZZZغu+CCCb*(>,xL'\L  311A?JR,rnic}2'rt?`WL.[n]21z"fIl۶ EQ8vUz<Ӯ' ,Nγy3af]]]l߾v(tҍD"x^nrnQUa7zt]'kR,tKtvvb B|d2D" P(*h4o088Ȋ+dɆz7o2yLQ:1w囹JޒƲw={l6˾}b5i[>S!Vjm>şORq-[N ns^ζ[o˿K_`͚~<|rL&!NsiE49z(|ìZo.bccce0MFBxx<(~7MRadd 顣n,"LSOo>ܢ֭|k/ʮj/꽚|]u>CVZu~F*:o{1:zT!HaR='8}4cccչB:]fm?gy饗rݢ^L&f644΋biB!4Mò,:::gppIƚ5k“'Ofuz Lq4MömضM:&`tT*ER}v>;88 ?0ye}W~l۶F7r]x{q*A|>O6%̫Ӆ@ر팑m\.rjjQ2]]]Xr$dKAcc+7Jq|~ײ줹:~?dRD(" PO:&j*EA4nDͲ/222‰'7|37|3dG200EnT__I&A+ESS|IHĵ9:>g۶QUn-[p^z%*ۢ1z8ufO>MRqKB!hnnfx<$gX y)wPUQt]'P*T*n8Tgg' $II$x^m49}4A p3>>fjFww7s===ضv4xbˆ .U B', 0ػw/--- r޹^R駟ԩS_ gY|+W^Ak|>X̵^|>ײ7[lL1LR]={puםs#$t-z$N*twwiXX,i|>VXA]]h4'J144ntuuQ.g||˲hii!:[l[nmWy^~_T4MV\dbDM =Gܹ5kW^y]vq}gzlnjaɕz<ַFAaiQ.vBʝ)܆/8RrL\4Mzzzپ}9Ǿ{ $ t]G4Rez)n\GOO`\.(BH$† zT*I$I D"S󊢰qFmF(rxb:::Ѷ˫̤X,__W$9?+r /fu^inf|d5YGmGQ[9p[뮱RD,s ,b6< O?kY3MJB\T*q1oۼKi-[WW|_' Q, ׻reYT*SSSӢWTxg8~嚯enT*av܉A8NX,F"p˚B3'ok4 Ml_"gX}]—Nd\.G4RD\W,m D:4MZZZ(~n6mڄmn_("qxrp8L8^UT*&&&hhhp]3twwi&V,zȑ#ŋlsmи륻y_Aay$idYף zno;/qDLzzx<}ݻD{D"&E8|Hl6K4% anӧIz{{illdʕtM~* b] wκܸåi^.s٪ǪUhooB_|wr9\WOQ _زeTA²,8Tʵi >EQH$kuωsgܹ;Fv.xO*Ϸ$,'4M2 ~bH*rSgM pKJ =p8̣>>2 r&ϻ/$bѵ$CE.>ʵUe6J,R裏RWW>-ˢP(+7 YzBIznCJ'\sL3Yhg&qw#,A;\?Ğ ):3 -϶\߿}׾5nF2TbH:\.xxښŌzzzꢵF pSϩ7c)+4MnO<7 0wwشiTqN:L0hkkϹƫu|ތ=˲bض횥|>RYLS|Ϟ=yWEAŶmr$X:־ًh/JR)|fGG+VXS,XD"4T |APp8_ ) $I2 \l6Vp}>^ g =g㭚9MyŢ)BooTzTaaRu礕{< p+;:ffUUuMQZZZ?c6o,q ¢FRT--l6 9qEAu7SײsDcHq ,### p8ƍq?9X~)6ɠ:UULp~u,ֲ,[ٻw|A%ϓH$( uX,u,rg ?'ֱy:D\ ٴiW^U^[,{޺up'3A>w8g㘝 ]illOO[ •]SlN†#6c(9tccc0ܰa6m`[qW*܆NGn\P(PTP-s☝/עܹ~*\AA2T*r[ ʼnww8f#300~륷/~466^_Hy`׮]n(Tuuux<Rl֍0 @ ඄qb뮻<~w#먭:jk>ښ\q V\IOOb 9+eʕ޽{V'Cʫ̛'N3ϜS*4M٬0 TU]* 7xAAضMTP(f)JgļgYn3 1#Bo?v"-䜂NcYA**n7M|>U]  iRT( x<7v^tDE<U9@*34=UU_:]wb,WA%9xy٬wJ.zqڵ! ;|z^{Bo[0sV& r@<.YCcǎE\  )21ϗe/{wm۶09&i&ofVWXGQ[u|d5YGmGʫ(hO>I>,CCCEO4">(7tbOWAe+DT"JtN AA=AA%~]AAXj|uC众{+w5Y5,$$c |8t|>~3RbAaVgᚏlcQϺWʒYHy:jk>ښ#먭\h0>/fl>96:76~QKCʫ p\Ȳuָ;.ul>뺔=ǵnAasxqqKI=AAr6ӭlX,zj&_lrIAWչP\W̲/rvg-T_lrJAχ-7Y27i P\orA34r?{l>ӽgRs)k\V c>c6YGmGQ[u|d5)r9_ޥ{<{r/Ƣ71Aa B=;h' z38_B #IAj!YUs^]lGD  ,?Eofȳ5~)brγPs'  IAAX,{׭  RE]XGQ[u|d5YGmGQ[:z  E#[AA%=AA%=AA%4KWIENDB`filius-2.9.4+ds/src/info/img/routerconf.png000066400000000000000000000142431506574241500206320ustar00rootroot00000000000000PNG  IHDRk8 pHYs  ~UIDATx{PSY߽TM? ֿQ0@ >`qilUt{jt[npqfZfl|+R<$ *hYIH_TuVJ9MBMs97_ι p -{dNDR /<=߰<6!n(\~n*OϧKh;hoO-\X$ȕlՓbܩޫ$"5sqfLY#,yJkvCM|ZmR6'Y*n9b werQKڼn Ypo{+hӧOO'=hy]ܾc&0³ҫrcz~!^ggg7?b[:t?=xnΩv}tigg+/N}3hQ/3 _Z_ ~w˒iJE"ð}P{ "U*"R;zPhDR%b.‡s5=X%?Ufݲq*wV*f-ib@=}{u4YZwP~ZƩ1mR\cWηRˊJ-N3%ʌ︊b_݇_Z,_efF`VwU[էҷ)q]suq,n3_)ԇ_3 (ۗhhjpkf22v(?a W>o2M +efL]Di,\59"ט$ gXCCy391OBx{fM N?TobG+X pPEṻ]s suĤUš<}MA,Zt ,yQ{[kDD^)[?!ZNf0)2?T`QfL>.6$S:7Y"/N+SgOٞ8~I}WRS#KBw)hCzc?utukDW T~uaPMwOTWSs\Gr'_HDd A?`ދ`M~ ~e2d e2d e~Ƀ9+lu|Ng{1FG?p e2dTu5Ugbd̳y.X8###˗/5&x*#`)@ Y,@ Y,@ Y,poptW]-mΌyFE4/rNnT͉cf)vWjA_[cvݪ5N" m|,O Y4ss'(tH0|05`@jV\$_<5@ja7}r)oP[}=Q mf-) 2>|.k(2n^W}KpU(݃DA"" `[xeBZB˜֏ 򌩼 ~w˒iJE[*9yXkЈ(Jg]QPP%(9:B#.(Y۴:#2AͼZrtc^VYʹݜ\(}`Pq# Z,D+ Tڞi}(7>",=N og]#]LR.n"PBR.|.\sVEkޡV[_S)z °2! \Z[ŗAPa sE5=]99Ӯ!㙎&1dc  ϒo±1LJ'Pa/FTnRbh[z^&I73;aqdF <`~W]'Dkb{ ~M?~PUDDǚmm%y(i}1L0 ᛾g8N#ueו*}LxÝ8W鑜GiYg% s\c伷]چ Y,@ Y,@ `Z.\###,~9^rs2`Z/F Y,@ Y,@ Y,@ Y,@+n[މd,jٻp%XkL>PQpH0K;qbyg4My'ݱ ІԄiceD?|^4c%(Tav}y2bKH&/Uk/UkO Tx"Kpɹ[ߣPFP~m>97VV*ny=[@ń+3^!(d*N,+EBR.|'؎DQdYUҪ>9#3U}8B GNJ-H3}riHyOy<+u.H{:7vG.y)EqhEQDT啒:Y X"bLx`?⊇7o^6e$_*\J)T\1s^WjQ^֞ʠڼlb wejmNTT\5;6~K bg>PqG%ıGgm.T-9ijcg!׷UTWJ7/Uk{jO,s^V_X9W:L`Xݹ<%׸K0^=Z(yuyEiTkFŽ"zl[}*Tނ&hDA2% LӔF7W~xne~u>E 8*[V8ThDR%b&9@fYi}d?͙T ,,EDJ_GzUY,ӿ9q͞_cɢXc4́n_Y 8a4hbqYe%?YcVGLgK?z%wlHXvfGkY=h$R(k-rx t{Nguߣ&ao)3-U$ν׏fyڜq"Xq·q#>f>VND*Qaq{2+C""bؖ׺7UѶOb4{wghv2׆q1*ۗ[{c4{oF;}6:n jȾaW㤪G|Bc}cy Ycٹĉף}GDQlD&7U},sv5z63f뗿44PZຓ'07\nb}eDDycwg麩S;6Mq$cdg1Έ'8ˇ_lc7?W]ƶM/tF=Mml`$"3֖ IYXJ[Zn˟PkӍ U۟qNkPiqh>u8jgG&Q,6]| |s܍+b4JL&pkem[ʯ}fxcE&X)ϗ|u{yˬ%"վ߻8˔žD/u!WF o[8%e,yykLY>l/r[+Q޾"J֛%-nɮ53uII%(Ưžn:i&"\Īb$Nֹc;G&2kK[͕Ƕ$7 |Ԭ9TʸbMl(Zz^܌n4-{ך(4&@85IK>ƈqg|܆cEFٜYn w>g\?7/isN[ݏOu(Df_jkh'(6DFrъm'R3m R%nu_y8&.p3%HG_Um>tbWHD,K%0a]d"un1 pu["*1m;iqR7K~"M;o}ƆDmos .5Qj./2f3EK[jyU߹3ϕ}sFم -ٟዅ]?oļ <Y>g^"1 i^$ ,Cy$'wJF9_EeY 0/@ Y2gYաw$,,I_Y,XգIENDB`filius-2.9.4+ds/src/info/img/routingtable1.png000066400000000000000000000033401506574241500212200ustar00rootroot00000000000000PNG  IHDR;:xsBIT|dIDATxQ8fk &`k" $}\ p b*+,=|nٲmٲ_?wo?_l "J[ȯ^Ownw:Z[ȭnW?Z7p?C O?:7I:K1)=!i>j+bkS$5b_sIYr[xgzÁFأ=I>FWcאWu)3g2ٛ]9]}r$etrB$5vkSkzwM{$Ǔ5\Jc}Uf_ N>1Tw>[\rsBIyF#Igܺ>4l^FoI;q"Dݾϡjږ|H9/EU/+-gro!u١us=T-9c>WaoJY2N|,$5?eyHm{h۟|z;"Iڎ"Zj=Дm'ҡ9Ϛm!fJnoKY۟ꆝ> 2$5rz%SBH-K71m!儦_KZ#̹ZW|)Ks\ˉm=Fj{zE,~$1M[?i{$B)M|(l?1\P8\o5! U܎IzK~zx~7 (;;NK˶r˟_Gu|///ywBOV)ragj2:_*2:_*[ WWWW73WKi!]+JtXe$SDz}n'~d3?\_,M܆9&&C;׼n/Ωg8ybOighBgAILGdO{\{*[wZ2\:Вᐒ'*אa_)'F=4lthFHϾ2ik/Qڝa.ҡC6||y*n|s4z#W7Gt0'2mW7Ky6*yuвrm/G9b?z#o/G,9A>r.^?߿~*nȭRV7:_*=_PignCz[ȯ^GTN\9a8":|mqQN\5/wkW<K;zT:6EoJb/Wbʴ?V븅8<<߻cf]?lsYvC/6c?B#o91tȧU~Q}J'^KՍ+h||q~j6ag*s~?qIENDB`filius-2.9.4+ds/src/info/img/routingtable2.png000066400000000000000000000033261506574241500212250ustar00rootroot00000000000000PNG  IHDR:SsBIT|dIDATxq(q|RUs 줂y_4L:H nÝ0LfWFBhw:_&c_[[nWwc̿1|}}mVEl"_غrݿ/(r^ET 3I:Ku8R z(C)x+[5I[}sl9ը}|qWz㑫uI1r_q׎kֿ|mh_h fOkbqHĶIn<4)nWRxהGr>Y}k}s9Nm?K}ܗޡ YOz<)۸!uG.9Nt$ gHizn|KgdL)#'吜g#NkZ}.]WS[ #示f)w%OwZ~BcX;_iî5(^hJ',nS")'o_)mGnc3Uh!%bnJH/Ru4X=NI~ִzs[KJ?I޹TM;2L?APJ##7-gv<|:ͽ{rS[-i=hg Vsk?Oq-=_JGy:N2_-O/BuK&ϔ:>iy$bMRx(崟/PhKJve뙿!ǧ!:1I|^oeЫ^b{cys<Cb˟='ŋ7Ƙ';UF|˴3 >>|h|h}fnJk4֯J{{eX(g'w:_&pr?Ci|j_zٱs,f1MJlk7^W#!QΗtLwιWmR*mnPVuۃu7\%R.|Yo5rikNo'Ђ|1rˡ4L=K*O<;":!|ߔ&-aO1harƣT_Yn>_{{JĹDݗl?)/w} g}9 Ĭ{'G}?_9^5Uk uFLFC0[s8՗Y^u #I -Ӓxic݌>%u/.dϥlwl4 5I>,cR=h.s\rsBIA2|v|sgdGJl]}Rb6c7'G<;]lҼڒ/)%˥*Z}[\R7WmK:j6|曪`*4m~,sǷnW1mG궇fJ|X+ڙIO6y禦4P=NI~[.eksr?I)M;/}L?APL=#5-%o9i b;zcʿJ*3zJ]5nϥ#?WK|\O66RogL]O=Ms\2QJi?1\Phsr}vU*El$%z=vVzo坧O`%V/bۗ<^__HRV73 @a  @a 6~;'v/di"_;ݗ[WgS+=Oiqfg~Yzg6ϡh/51]+ߔz]'zq{4^L;J)BgAI,z/go uۂZuLls1veKʟ9|Yn)h-K M[{hvڒ贬x+F=R/McUZ]5=":|W9{:qK/ʗvz/{+]WL{}ܲZ]'yyɯxŤi+zw֓<<}L߻cf+?n\[Q|mB)|1AkWkh}J'^KՍ+|(|q~lo@-1IENDB`filius-2.9.4+ds/src/info/img/servDNSrouter2LANs.png000066400000000000000000002260031506574241500220300ustar00rootroot00000000000000PNG  IHDR|ѯCsBIT|d IDATxy\}9SM]=VwK-4!3Jf f2n|ܻkV⬕<'uŠ_;&/#11##KBjX]|9gw$4u?kժOUSէw~_>\1=myyyyyy.D"H$,FH$D"H$NjD"H$YD"H$YD"H$YD"H$YaE+rkzSVkQ[Q[Q[Q[Q[q|+SzI$D"Hf=~ H$D"HQ+H$D"HQ+H$D"HQ+H$D"3D" \ץT*a&hP(4]H$q H$3rLP@Q àUXD29?`֭u'?-\ZqVI*"seYdYJh myyy2O1100OS àK.Kt$Im:86omgŒV.!X`px/Hmd2* .ibYpXZ$J_Wg۷sNwOgQw3 HVxo( Q*Ų,D"c(_+\ui@00 C%3NԖe:mkylױP้k[(M"R)ʕ+YlLDrbYTJDg>|dP*H$ѠEeYJ%ZZZN#H{x9D__ } NATֱQɓ bKlw/|8٤R`&]vvH˲T*Py'}||`VH$/(9^Ψ^CCCȵ5DQ"Q5Ě^Ѧt_Fh{FHhj%HQyyLD8 6l8d:ٖ:Vj?8OTzE\ץ\m*Wl=j#Ŀovw %(&ֈkBkDymyck@4ξ_ܻnGrdYyϋ31mgTW_gWٻӅx&: a {LřL\SeQƹ 9 Krm3t:M2=#Zh@rFG}w}~|BU 뚾-'ƴcv2cZ~kWmU:+ ۀ3Т3EQkނJQ+|p=Pr3݁Sɮ][Pl׵PtWQ:D0D϶%"$9%HuО* 9ah=a iرkիW7%\LdttTLM78ެlDRkLiYug6xJhL*5*U^o~#]oa6lUŜ16LJHTpŊU5P5UMG¸bQl_aiz'egRĭLp*XVk+ o> ֭[oVra6RuV H1+(.w=H@ŷ@o̞w2jX&]eSdS2&쁁33F߿=vR(ꞸUu\EG1"_ȺSl$-櫓'h}WQ0UJޮF*;(gח8۝vժU\wu#$I&&&Dtsa&id5H$OM4PTMXCC@qLV)ą=0Avvqƈz0X)eȬz'᨞EP44TLhM EpɫZp`6U_؆6 ZeN~2XZJ2nׯkV2kI$mblD2[@ɉIsN\%4N1[LҸ N)[3嬿-SN28q@6_zp=j$^4FQ"~[(a^:p7c[}Q^o"W(n:D|Vkqpa-fK 1j?8N\ .s)e,V ^- z%R'l&ᆴ%ÑYo)IGD=?jcg>>xw믟?m$/~?Tŷ( <Um =xB =Zۦ&3XUȬ'X  6ĵ+`v'8뮻fH>8\a9bݟbYִ\h~ M6٥lT̞{ b~1/0S315SQ0FaX_BSUk3~sNEqՌw(o?AU'Nu5o m!qS1#k=o;p5ŪCxU+vԡoQ"9v!g+i[8A"x=Pr8#D;KI'dQEVZM?y-B(/vgEøz;Y4p'ZuwiSc4+8iTfT*5)ǙLzg%Sǁ@7-ޞ:*jÏ'Uu݀PlHgUuO*pVp*VE(ZOkUV-eA<4֭cÆ |ߜKRuKg;P ^j/ /8itn +A#;5:khkEN-ZOZŋު iW\AѮ㐢V2QUFRTMe@Z L"zߏK ` ;`6pJ`={ BlEh Bzf^]`_BLjmpF R[PP@QDZ/1e_)zTEm="`C`;;{z^^]/}7hBȺIZ:s @00hiiVw0MSOHfiHN{Qo>"{G4b&L KUCNaXv1ž&<Lת:(Q7k|?y]MGST ]iii!NH6L#큒ik鹊[)38E?W1S8n9[VSnrۧZq%RfFK.ca#(Qe#('z"ym-Zq]uZ[[ ߉j`3E#ļ4{`)SL3"H?vAͬoYەN9S+1ٱQJ8 {CuP %jnq(jg1m}J|_<g\UP \yS-x!d(w={շ' _5+`=cY+*sS\n>7MYZ2kd2mT* l>i):dJ/?sL[fR}{r{ Zų*=PAԖc}ǚqp2nK隞6H9ypJn/W) @l)(z$_`Ĵk(_ϫl:8a‰_ϫAm5m\o<ES*HU ZiAPnڶp8L(T*B`Y4,HjihNkQ[,ٷo *:U'< sTSXeX`Y(%wvԶs=8WNOŝ&j\p]?rNdWJPx\% 7񍃄(b1(rr|\~[qj2uD"dppßXlj/@rE$EyHFL\;ի`D2-pla=GPAPb;EE49F VUxӶp\uST\U킩 xuī$I"큒iq O*f:+{i;qh=JKzY Mu+7µ-\,|?m*mzϝ_\,RLk]_ OV0&S`"5CzDU0D'qx)ljpZ*TreY˒%KXl]W_W&xo.19X/Ed=4L6(OC MN ʰm\Fq,\GCq4\W[EG5Me^lٌ}ɩX,3o}/x$HN"(9ֶm,bQYUUŴ뺜uY,]o^݃Y\Bbԝ5mr/rE (ggb7Wu&k"1yH$$</7pg}6Hd%HN cH,2`DPl?Ǭ bEx V\u]ҥKu].zzz!~z0NSՏ̦)=ɪ"E Mێ GqK.A(CkGGG5;v`Ϟ=ƒkwq;IH$'i fm|>OP?L"  IRض[o͛9묳[Yd4[ܹsc˖<n" ڵo|<$I1k&쁿{wWx5vZ@6DQ" (z3PlJVd=_3icx{[ou¿WwumD"ia=B۶IRz؈ &&&(˄B!immofժU+l ( La~^Kb 㭨 V\[EEtEmъUAQt(_O %Ŀ//3K$' W@Xٹs!S|M}|ඩҫ\.3< O?47|3]vV67{ض}?],΋?xVEU@41EB OTUJÀ֐b1/Ovڙ-`ٽ{7ݻt:eYA6=HF"!pXlw3t"큒&#R*rhF}}=qFyM$ a&PWWGkk+X,hiiu]2 l10s5pD \EQV;?>7SUy WGLk T_1꾠 V IDAT {V QP (w۸[gHN Fjo>~dNyu]o[oE?mS.QUGz>ȰFYr%\s ̀JN\ץ\.dp]p8LRg 0 4fR)\Ų,bdz?iLIOqV_|^NY/]g+Qm2Xqw2Ø2̪jaB\n5Ha[ԜuT*ERX,8/2ccc޽FbQUJBss30>>. !kYԄ똦I" ͢ixFVZ5\Ccc870}͌2eJZ?౦bxW'rJ >o[YDrZ_!yyW0M;vvZrJEav)j%5(%~_NdYDdq|M~_P(Ġ~a޼y,ZV"cccd2WRX,& kΜ9a"T-[S*;w.XLDm\ץ1.}a$67%r{h0xvx{HC%A Vʜx_ ao!Hl7I$\zd.rr###b!gpd8j_W"Ɉ(6?QVٴi˖-;IN_{`2$ DX'7 0 "pq(8B!UU3||>ϖ-[x7FtwwO9 ttq]\ lԦzCz_vh 6OZVEiKlҥKYz}S\.f"<#ر~oTU2XNQUl6 tvvRV#H: .foׯc뺌H$HRa,X@[[hT*E:&N$q,BQ<\M6/b&gμy|^4C)_叿Z,A)4iTLUc⌰zj9Z̨, Hh4JZeڵT*^۷B@WWϧ]ٿ?R&D(S(Vض y$\&̙C]]mS,V /3>>Μ9shllVUUv4q퇛97?<7͛G suw/EPYpw)(J(/u.m=3b>Zi8r;@?s?w>򑏰fu]UMlҥc~\.k׮ij-m֟38,}}}J{{;a/kOKK CCCl޼[\+Xp!LQ줡p8(qKYMM}Qn?$IqDt_h 2Pu(8zcp(FtZ[@~$ų2aLǭߪs-J$IuEQx'xW4my< .UU#J:̝;J¾}b1CQFGGW£A44MHRJ4%J,zqa/>cV_ݞi]Q*ֺk(ܲ (WUJN;b~!3Xo%K|=օbwG"x#jO|$Z\.L&QUxj8|sWS(ؿ?XLW(BEF(äi|/^L(bdd~\sΡ:,RIDgt`X `۶|~ډeY|l6˖-[x7us 3{}}=:,.c#;3T4LbUy^Drq$Q<^#q~4w?)j%g"L E-[po>o\.PUq.X,`ϟOXd``a&ɤXLB<Ƕm!CeQV) }}}=bQ6nݻ]q]]jscU12ԣaTO5u!w)AQ_뮛DԎGÊ+[hmme``q٬蠥EPTbiBӌ&&&E|dRLD"~1 C$nkk#Q*7䭷" !*r˹pn 2HY UavܽrUhEmxtt7x;w8ȱp8o~}X36g?'3m3<<0J.Z[[ihh4M 444P*v)y?>-0 &&&T*1oq!4MJB"`xxbHOO*ܿ?L9sL+dEP)hbXv'h+uVJ K/Z):>\'ǐHf@ԶqƣSKPP(dɒ*088H__3iӍS˶mr(udz %msw|s#ܹs%200m<ܹl_ Li;v0>>gUW]E<w}R%\’%KAu,aP__/6455J2$NHӴC<g||s=Ȱ*/w}o(K,QNqwZ8k??yמ* Œ|n-s: }g?K]]dDoGG|{aimmlV-Ӣgux!˶mhoo0-X,~XXFBXx<.i}mݻw|+Ki`wpczQJZ0ef:R[N5::*7͢m}'3^_zL&C.0r9TUEd2I2D4"H#XMMME(]]](8#@e]ƅ^(54MHm,X@祗^7R8W]uvA' }bbGݻ ?0wWNI#o ٳUUgLi |;X|(a;00c=(j41M۶oꫯ>6l੧'idY۶q]H$BOOuuuE)466|ra$Nc6XV4S|++b!\0'eOS,<;L&SO=ů~++}M7IywYp۵k>(ll6˿ۿN̖Vrk㸮KccQJ~F8H&T*066eY$^%Zy*T*}SO=(H:dRm۴0w\TUckcc"n__tp8̲eDq388a"SBZettT5Mn11[T,_\䜗&$ׁy3g"DJ` T`4(`Y###G?//y'I$bժU|೟,T*&&&mqe844Ď;p]NBX$tANO&>loK__۷oǶm.b9bt| _-:ժHU^/ A[KcN~WBADB+d21mD<?L8&)P(u]wqU XnB@" cmmm EJ^ZZZX`\p0*b1g]׉D"*/###<<ӌ޴ 7~K$3ɽދa|?HG0m&.R;11#<">peVrqF֮]ƍEs9??=큒cE-xWz!NS*4M[EQH<~$4M[0hjj窫"N} nS8{B:X,Ȯ'N ϏeYs8bqBDQ0cYX}m 6c;V3 +WœJQD/"뮣>FdDSׯ_O>ƟԧkwG MAv / M *p`n|#M_u!n+bkkMH$,Fz{{1 C\p]WTlmm%a6s4M(^x!+Wu]!$V*^횽Ԕ (J$ JX%Dn #AvΝR)* ^{0jVrJڨV,]??Asra"uuuTdxx'#GvM7x$gSEmpꫯ>_ą}'(j2<w@Y^"9ݻ￟7!3 IDATUVO}?yϥ=䡇⢋.Ϟ鮜pjR?J\NLE1?Up}x'TY;4fT*r0ׇB!EaZZ~z֮]{xn6N9"96 |:#a "z+7x>UZŏ~#v-S%j-ˢn믿^Seƍ|n u&L{n/Bq-8韸KH&;k֬ᡇ:t`VkqvUUihh 2V?O>$ٳgϴϳm۶C=x</l=f[[cÆ |ӟ+_ ̝;<)[l9 BtttMGG"j:66FX+6G'?I>я|3B#SoV[/l!.X&:;;W\eYS@,Bi>NUp񏏾ϿMT&uֱuVL<}=Sz rL&C>X,R( D"2HpUw'j*H=/ٴi?6m+;35kx@"9Vzj|]]4i|r4551w\QZ;'Jm6uX,'> ]2cKqƊ+hjj>)lذ|۷^ }s䟣{KUWw]]uWMSݿ{ns3bSԞ(p8L|+ca>sx3n+G@AdPUESeLgj]PYYIMM$jQI,#ljFfC&ƌ!Fȋbb1}R0Nʸq,7x 6,-[fwmqJ0Zw:R)0,q `ZEQ)oA:}7o( eeer-\|ڳiheDnvc\͆([$PT*ev,//dخH⣏>⥗^/Dd7h \M)`ͼ۲2 V31@UU~$H4J;(O#F4׸A"xڴiL:u'QEQزe ?0---̞=e˖Q[[;̵T̃3"xp8 h"jAy~f1,a}Y}Q$|_eɒ%#n8ro>JS4MtUMEE9yt,_x>6pOW8Q,DQ3& =4f޽߿Imm-\p)(OtuuAp8yWD"L4 Zw/2˗Cp"E"f!]y=9=6;E.w_?޽x<>qFZz  2xܜqĢwl<ooݵ8.O>${5s#9zl8&[1l0#ìYŰkq~@c^^{5z-i4Z9SdPS)=b h/Mͣ&nf̘EGYb@b1q{1sʕ+1cpWbsN6mͫ6 V̑3|;1]hg~{w;vpb f+=p@cc#|s:`djD jZ*2(Gŭ)p,S.&HqA`\r%؉Ӂ%j-N^>ZnV~jd2IWWf,ˤR)8,SmfZ._isϱa0ŬX⬶P4d2I<'L&D"=Q:R`r hne*i1kDeU!=HhNmZ* L $Y6;3H2F̝;k8&XQg}7(((`L>}f1hnn3X[ȕ5k3g SŌcveҥgM4bdڵkۭԷ^O#Gps0Q+ŗzBUN"GeYcMɺИ8Nu޼yC.n-Qk1h455~zv؁$I,Y'mэ裏J`D`b$aۻ J?E,n6fϞ}zO"oڵk s]wY[CoͦMtZçVSi5ifVN[ԌŮ9ɸlssȱⶾkA?WKZ :O<?0XZ֬YCuupWb?L4UFQ3:ktA;|n~L!n:l`>tY_>#M¥cD)-dPT4-NAP)rٌm1#:+toiJ2.I[bjNb>p(Yc5e#>#<ٰa'N,]'xEQFy)v{;uJAp\x^< J-O(XlY7Ak=FOHOvҥKٲe .իWbŊcHk_FZ}8~޽{ DDwnFt!]b{9s9\ՕJw;^^وs攝[ZDcƞ={/OSn:(NJZ az)Eau] 2 z^{̳5Njʲ}*** /䷿-׿u8N,'`Æ (YjmqZ?Ǘo !A@@!vK cPX:+ܚX3!jj29~MN[9I$1%'xSXXW_|r>nZZZAdx7yWO*j~`۩fٲeTTTpBKZV~?ׯ7@$no#ݻw?z>. ZAQDmM_$h`gS2jZ i Mˏ 'nJarv1$AK/ذڙ3g~z\zwp8=ömې$[oonaxy999x㍦,Qkq9pW ˪U7opW,ge֭>3ψD"444jFAA9g Nk qa  %iSk7- ML ZɆfˍ] 9$>8̖r!Pm)`$HEJ˒͖@H%$;dp\FJTUU|GMΜ9WAY}c۾e5<ٰaW?gٲe^Ez#L (,,lg4ᠨ~3"edWHϙxX͛7zO(hGydb[kys*e' $mLZs|Ϟ=   Z, Q}QcAx(]ǃ`~BKt&!Dj\O -'o\^Etz\YάW/3.Y从nd؎HK吞IDO[I9譬˹ ;D{y衇xGQ믿e˖Y,nqʆ:g{eժUSWWǚ5kj yHz`ǃ(k_̃>#wt%n1(L!ƫ1P̮bs蹵RWAr˜zdg)KGm5sIf Kl) r-MZh?hG_9ntrhr:q:gsm|rO=V" w,,,,NݻYr%L>k׎jAN|0h-Z4zV06ț`\Z؝{QI)`K ͗ڵ+@p#i\Ɏ2/dCDrS]BwĈofu8v9sL>x{|r֮]ku[XX({16nxFL,tx>XF Vt]k 3Ťx`v"V0R .^%.h'BS jzQ䴸u"\>ID A=ru\ApH[_dR~Ogeee[+WrN^j=YsDx}<{~te.o i*'ۈ? XՈ~Td,]CMxzd }}*JbVɱeW\J%t_[ݍ$Hd HZ ( w}7onk2eʔYI Vuh̶m6֭[(XUUta( DGX}Zlv$Sih/ 4EFS崣AڮKף$єYM)I49[~iZ9C1ƐSA``,Qk1Xr%3555 y̙~3vw]-q{jgw"q۟mOƄ }d2muΝYd2ɭʒ%KK,,]k0 ALT Dw.;e,ٺu)H6}t4U_E-:jӋ!M!\Ԥ~ @YR 1D(pffϞM8殻qv_w]\>wIxGgޢ''v$XXy`Ն3iɒ%vmC{H26dYFUU&L_oMiJw6;ѓхÃH#UѴM5#ztWMOHZϺUQe8q‮%j-FիWS[[KSSwy'`pP wܹs7Hٳ9x<444pw0ydK^OF&PՖښ IDAT.03f`ٲe,,(tvvN<G4DQ4M?~<~ /W3U~>b ѝA^W# n֡IQ׷hhi"j梠i.r5۷c|"۲aW_s}vPw8_ V[ܧ(^Yf ۷oe#>gyD"Z[[ h޽{~C=G$DQD$TUeر|_aͪXgN4D);/Fpnpx2\)sbnz}`\Cֽz4A=*Cdeevt},K/QMKK ˖-# xbVZ}[,[cʪ?~<6[F!,CHѤ555Z[nu/g֝l9^ϲ4,Ni?}@zQ{l޼A~,dYDZl$ ^|E^{5|>NI:u*^z)h(9r]>_n}ͨyI44-\~PD)n`IQ҅$@i tbHw߳_s>˄̟?[ou@|rb˗/oMfϔ)Sp\}ڗqܿGgm OajQQ^r^O>8UE!Dy|ȲL(EQr1~xj&LM mmm|<[Q ACtQ "(l`!HtM^#y0llN.n.]'i͚5k[X 7_۷3uTˏ}0׿578|pKyGmmq#>!"n+$P_|?ryZ ~p8_XVc`x<ιkUiq~?T ˅(s{ ha*ͬd\MؖBq&ػ P#*JLQ*[{:k_m6} cAD ))sN򕯐3eEj-z)~^/?lcjժcl6ƎKee倎/uoƀa1pnxGXZ(#JngΝ+D"szXfD p8Φ+y`0C<~ K/cԩS-[oEKK ,F8V[84P(pG4onrp:J4n*n=pp8qTUUukN".Uq94SAшޚ -3,MAE/EϩL}N}}="gdK^ҥKniF^^}De9tPw:L4x<TWWs]wq7;dΝ[ʗ444XmF4b]]]HDvv6(cviFg}>G!J*YYY"]]]9rh4㡠MB!@nt:f…,ZeFmm2dv/x30&G2]3.T)=̖mF-R\AsP'k1'FZ}8Zp8Xv-<3,X'x\iӦqy &ʎ;1zm1w\(++c̙}Oc=6d]Y1[}ǢEΊz*Ie#>Vvdi&V^ÇڪD(󑟟Ovv6T?{I$N>L<'HPSSCAATNH$RXX@[[iFq88N0zI&B!… Yp5roAOٷuχ)bEaگ6[+`0іAJ&=r`!1_}\ھ`Z3?w9l̘1TVVkA"`}W4rss0aB,9bvVWWкa"YXm@E^/v6mD8ȑ#K$8v;]]]ijj E{{;$I<R)>L 0.ˌ&ION'+4ŭ(feYOK|"#T˰DwkZI :rɎSB!֮]{sa8~Weinn֘3餮~ LS1cpݝ#ZXNӢpL&ټy3w6]]]#I>Ci%%%fֈv9I^^iFIR瓕eF} Ӊd֬Y\veTTTtDQv7|UPQ1˘`[csl"W)u愞:I (la1ذaկBMMp\7n^w466H$}d~lTTT0f̘+ rA"Ǥi,sXml=60"vx<ݻygHRihUUx<޽{)..FQ4M3{سgXrƏo:"۷vn)^EAQT*->^}UvĉYp!8TUv3o9Ld9X4 BƬd "`KGtM(OR__m۶f>cK$***. hjj2GAJ$I4Jceee1i$ƌc8-,F+V[ɴ(J7!)Bx`0H2ޒm0$zI&q7RVVFvv66 Mxy$ ͆f(fرdee555a(--E$02fI&@kk+L4NZ[[9r(RQQ&Jq:;;EǃnNUUi{}v&M׿ųmnn./ʹml۾?*H BD}V2@AUTTj3@TDŨ_JKK)//Mȑ#|>DP[[;REQhnn 8zJt# Z <+9{c(^ 1:8 (((K/eܸqaUU/˔yf~ٓz4}>c"I|v*++)++fz/HRxp$ $ BPX,dʔ)$IiiirQVV$IvE1׾}/~Aqq1\s s1'q(..ګ/f:+= oM 4y!Z*iA*RWۏ tBL+jz-,X0eC]6s6/'|¯kB1,J(yK&9yNZ},̃2eܔKKK#1~Wpu=B?Oxg|~ϟ'Mr|>1b5 [v%S^^n.sssHq#TvK.1ziz-ON04,EXfΜٳy_$IglGG`MRB9s|l6$IbhlldnfΜ(DQ3F$N'Tʴߏ,ȲL^^|XB444?^zK/Yft:4rus44_/nϦ? sAS4W@`Yxz)~h4 AMMOttt]5>$ISRR2cuuu@4>;;j$ QAW~(@ӃVζ( `vS&IMdRC8-..6fWNAA$Q\\ҡDUUNn"v͛ihh@4Ӣ+͞|DQ$ގi0fB,Ag`FzZ[[sQE8FUU37PUr*fϞm>y߾} T*I'$QZZzJ]t]iYYYLj+^㢸x`ǵ89]3 ( MMM~M1޽{4` ղ23zEqqPVVvJSZ~SݢFm̙ܹsywٴi@d2I"vzD"۷YqTUU3{#JJJPU՜.7''"bB|>UUU hnnQtmnnFerrrL\ncسg\tEh$Idee1k, ٷo+j\}lnvΛQ=g}FCC555;S Z /X[T';"ܴ z~.,,䔺J;;;fEnnnS`|}ix3e2Y˺Xl:4m=p---tvvQV#Z]]5ؙB3#$IfJMmf񢶢(#ncd2f#L ;0KKK =MpwݻUU),,tbTUUH$8tyyyR)dY6m:;;M׊rssQiӦqUWzLkhii!H xd68)}NÅEs>~·b֭ٳǜmfH(i)((r | 7-HСCD^3ĉ4İڻw/@~n7vs71GxƯ*ah]Y1۪gƍlذUVl2Jn.]=ƍygٽ{7MMM8***={6K,[oeժU]7fyxVZŊ+Xd /4O]qje61cPRR G1l%LQ3⩪*>nΝo׋'J1(9ӆ tvvb(,,$H{nvESSuuu̙3Zw& 1k,.s^Ӊq́g555qp x<nj<\0iҤA룏>⥗^/Dd7g@S9m+SS^7.'mF0>3STTtқ1wK?HhWi *50[v1?yyy\{͕w~CGGg"C磹Muuf124 رc9ti2OAAA]`-ϠgN$I xUjxXfin5K.Xwr颅|g?n|ʊ2 Yh!`شEx<|_P=-,voFu9pqSUuuu?ր3C;x_Rh4APeLA8|eLngWio j$-?DQ :+m"Vp8L u(++cĉ;2***Y,˴I(0#VE{1O" Lv[dY67&<袋Ll,V$8@D"(墭 1cꢵ|4MrTUСC477cLAxX`eeeh,ˀ"˲y6ʹ FU6҂mGio IDAT .lQta+(2SR<sŜ,7xų#H4ɰ"{'3dx< CUU~?ݦ:}pIf~&\~xz:|WDIYWkg+/F9s7IuuujaqjT*E0ӧSQQرcŠZf˅$aFmzCDݻ[gJPNKK >(/W\q\p 2o<ϟ֭[MqkRN'h5,v;^La:::BUU=\Rn3f0uT4M3E%;;D"aΪV\\;{.> _̛H{)̲y(bvs$nQ?圜s*`|*Y2/3?k I#I[oގ3?* 3;9s e@+b3mUQV-F-X G,3S)Å/D"ELǑY2w1#F$H)**"LR__ϷmrrrH$m<ft:q\dee ܨI?8r{JL׃93r4\(2J"7&VSD$o6lmbr̶mضmͳaN>ݧXjj*Q<(HjKBIA!(( $#(#DȮHq !}!o~plk'^(H膳gYf }O>d^nMKKp9ikkѣȲ'б_'kx<$&&b0Tx'@ ǼwE, RZŮKx~-%{pFgȐT='|ɓ'EG+cc1f|I>~j٭Fƾ}p:dggG^^^TcVͦ ]]]Upf%A$A@ijֶ7Ue js8}4nuOAAIIIAa饶`}:O==ϧ+WflBCC,Ƿm,6'wNGri/0Zt"2B? F||<fx=q)f6oʛoI{eKzF̭xbq{֛OnnnՋnf2vXrrrhnnڱ^Gg۷qK|6Ƣ-i;z=)))撕Űa$11Qz<:::8<ϟ˅ȯj]TY>رcz=?'NCh^>_|:̬ uGE!pV#H:uGAQmXs돐[}/~%ׇ( NbP\\̔)Sn˅餡N{{;vȕ I6 Fzz:999$$$j%99w|~괫޷Hs27ChVӴ+MU;s Jj$SO=l6?E()ihٳݻ=zfŢ"11ɓ'0~9=WՠIII׿fƌ6\.v;;wv7<UPPСC),,euc Zt xJ{b/ե$ rFm ou@`PN8(t"JOGXv H -"Ct"C Q$=&:=Y,.R i1Lle?B&!G(AJЇԣ|+VtG{n***zW^3dYziJ0`Z~s^`ɒ%رJzz:|ߦ[cZ " l.տ$T||6e.|MV^i_wUc4|WσVSO=iƍ+M7qD^ynٲe v !|,))ey LGUBVV:7},B}}vEl@#;51  ۍkV'Ob)))a!I'ȱDi @/H DJD~ YI0#HVwoBUkX^pA~L.8Dfdf</&vv%ɫmf1bRSSIOO'==_X]]/Yiooz>I]MF#n'ޏ:QuH:z`ÇꫯRRRr]t:?A{f1m4ONjju1D#nG\-++c(DW HMmm0p)jZjߑ#Gj̨,fo 2f>}u:\6<..|o;w?ǃ(,X_6l6f͚ŬYlܸݻw[o[oEQQ())ݾaPO>;wrtȲLBB€ӧb }***p\̜9m{jj*L0A{ln]]vΦM;]Gf61v]L. fD}7ü@KQRIEZ(7\ܪ^dòIX(U8 "ܠ7S$'_Zێ[믿ֲT׼ۭ#G\R`Z5]nnn.1[bND=} aYA#R ҃pVVYA,m$kΨ"r3Fc0(H . W.LU(t+rpP]]ɓ'V$rrrgԨQ撟l&nVX{GGGVD>{UOj&riYYY|嗈Hnn.sXenξ}zNN=ӦMeoo1R{k 2;w?J;2a}Qrss|&۩3ydب߿jP$IQTTC=DAAA=!hETԞ={>Bq IBDvSۉ$SNG rx;8v׿_IVo$I~ٳgNCCVgxnf,mͯ7|gyE{n%%%,]UV1w\^gvCsmыC. }vLmJ㹹L4RV^;âEwCzz:,S[[)--e̙3zol`h'wOݻ̧<44&k 2^6^Hz]8SΨiYY$.dmLyoV~.L (++( XzO>zKrQ[[˱cǨXرc9r$wdzYl@XϮ]hooe("''gj'O= 7uV$VTTә!I>x4z!jjjqz$6777ݮ9"<Ӽ JvG}D}}eOÇٳ|yr.6nȆ 4-j@?cƌ *kg˖-;VV$&O3< ?כ@պ:,XۊvСCTUUQ__  wKQQ JD5Wv;#d9*Q6VP, taRS$xꍈ:S/kCfDVЙze|JR-;5Agz_dŊnl6WlhpY9‘#Gضm>|P(ܹs?~?nC޾{7̜9gy& bp\;lڴυOZt5 LlJ )5"{esE)\$ISrv5!'Æ jr>"ՠ;FbYYlfS\\̼y޾};,c06m>,^T!Fj/|￯ugPZZngر,]tCn5Ga߾}}5j#x &3 IDATEԒ#G㽺x!DQ䈭%1PT>e T܆-V"DX%{ek5+E' Q^GLGG%T-[&Me|pݔi)ΝˬY8cӦMx<$I c6&N'k֬Rˎ;9srApPZZᠤAMv;cϞ=h+vTyG?~MuX!hDԒڏ>7?[ x!B}c, Lf#T} L`0^hpI~~Vfy#DWo gle?X ?tN7****hiijh"Ǝ;aEZZZXf [lk s̹Hdv˖-EФIxg(,, fΜɼy:[Ν;ٳg#{lL:U n/ƍw8pK{ݪmZK/U/}iPDUbAAjjB[EU3B0L@V5.|^/%hj(J(,j۱j*~SXX$IwuFmڴ BAA˗/$Xovfכʂ xO~$ITVV2k,-[鼥w%D~mGxt|ry6l؀a„ ,Z蒄6vtRV+.֭q1 z {rJfϞMnn.Nu1|z),YBUUύ]c˖-MvvرD]i/^g o~Ȗ,?J{W(!pVU0fZߪ]ZΤI4@DN #[M`ت%͗7=lFP|݌5_~yw砄,ˬ^u0uTJKKcׁe V3fu.wsvZM3Sy晘8x x<,Xi:ۆ>2ZVJJJkp qlů]L.ދzrصRq\k]ǵⲙځٳ̝;3f1LќhND0'"%h ]t s}%,WPBEQB!(J0ӕe!d~ePԿ#K(Q`$SP$+@@x6x<5B;o<,X;^'ә7oVúuxٴicez ԩSYj1B{;  I˖-jCmc֬Y,_k2w\x<3ϰr;z?D z΋zzƮe.k%b-[Mގ% ( {/L5;wc6n"z !D ) AHRzK"HE /(r1"mPD!D I! E,C t:Yp!HĢE4i@5… yX|9UUU,]~Ҙ>ݻYr3;a{Z3{=(..tb Yt}z&p+L]g5Ve:;;Ȭ(EQ9r$Fb{v'Q;QBA*H !H搀v1o߈(alo(bUd%٬m[1\cIMMew?˲eطoW 0i$͛;I.\ ={6ǏfΜɓ'˗5s7mhh`Νlܸ֭[Ǻu$}\VT^]ﻝPRݍBeA/ddd0}tRRRB@(bĈ2qbV&lDI􄉦 XBgkAC6Ll/Fw. 1[PB"J(D )))wnfлO^^/kN 3vX6lڵkٹs'dΜ9<ӱ AͪU4YIjj*s̡${JKKihhŋlٲNz6nΝ;+W2yd&,vPuǝ!`ӉE ٴi[nӧOw^ 2e F҈(deeÃ/Fl"dY#Aw^Fr }^GЅ 9C D(꫊%)@Lzp=]1vX-ZtMy)QFGv7$I駟+WeVXƍYp{'hnʊ+p:H̙3={vT`zK,aΜ9԰l2ʮ+; ̟?y}v>֭[ٺu+O4۷au;9~e*eٳgyٳgՊtr^u^{5N8(ڢ( L:W$I{1grrrhiia̞=9shѺϥ.m+hcoߎ>SNG*pq-dY6F#?SNa4ILLs҂'##ÇN *x ^y٣emö]E#)mD0ƃ1hE0! `:7$H}Jd br](+c˖-,YD#ͻ[Ľ0ޏ¥L7krG+$IbΜ9\"ZZZ(//O ̟?e˖i]k֬ZBe^ӦMc̙x<bs(**wys璞gÆ ̟?RvijEۍd"11Ň~ӧill$>>DJJ `Mc eǃh$)) ǣ9%Y͛7OC&$$UӃ8̊" EMj-WVuM0h߽ DOs=*++Yt),3{l~ӟ^:.z-T=ф-[ƦM4BUUUTg`ˢr1 sA,YŋhjjeO>a֭TUUQUUfgeڴiQagjEfC !!!܌h$99fN<ӧE܌^'##$, Qq\9sիW/i&^/B8 d2!Vw @` ,8ЛۋtB${+"р [$iR!'Vds+kv̙7DhU\v־1m4֬YرcJb?8N+3sLV^=(4WD%I ݻٰaC70~x.\Ț5k5k6 ʕ+5l>apP(餣x Bgg'6lѣ0 $%%`bz顧IBByyy҂1tP233>|8ESS6lW_>S&3,}wx@A[ LnqFsdV0"n3 !"IЇ]$+&I,4*++Yb,yݒ^y[c2RSSYt)f}]^xjjj:w^`Νl6***.Y6XsZ355Us@Xb`Gff&swaŌ;V\6{l,Yۗ14nVIKK#_~$IB$l6gϞ>|8{mhh󑘘HJJ }Z5Zl~?466;eyyl6$( P6^/{ZX),)ADN 6h >,A Dcǽv'C;{Z_T̘1cRQQAMM ̟?SnȲ̪UxwebJKK%,sz>3~xf͚źuXd +Wko$1i$&MD}}=۷oJ={6EEEj 7mz9w===aXgժUl۶'Nڊ%  ZJ{{;444ߏdSN =z4st:PFBB&K0Fzzzؼy3 k׮Q(ܓ;/-bŏ47B'tqdnƈA%6 LhtPLŋeɓ'_(zyɕNH NVgz] FmNفdŊ?& tR/^=..PqXKK cr>|C뼝^Xk& FE^^Zܗ[վs0݊`~m[R̙Ceee9 c@m [zA L& ~JlWWϟGe!99Ihmm̙3(BZZ6@ esz=L&F#ZXww7HNN&..N*}xGԲ*zzz:z՟87!3WOmLNsCD"'`L8񦳐wԬgԩ,X ٻo>,Y$33֒^7Z_P%:n\b 7o%K$~?[neڵ633>/khѢE!E[[ `2$Ǐj*Ξ=˹s+Z׋bAEZZZ̤2d$''fj"2~Q!COOOVTo>Nng׮]o>Ჸ.R:x^ zW~EAehjj󑕕EJJ m $$$xp8$$$FKK ^MCCs=zp8x< FZZ x^łhACww7v;vP[[KFF{2pHfa-%mH( "A@_ӧO'!!R n_W4662vX˵ 1uTf۶m2nܸxfRBYY[nE$~_<6_ 9|0gΜ}PRD+DQ}Gcc# 9r͛7##G͑k"@:;;}Grr2 tvvى(X,Aǹs >d-Azz:YYY$&&bZq8twwrp:x}z4f3:N#ݒ$t:ٻw/{jɮZ,GANneYE@dQ Rg;aXxgo4 2*G%//#..nÊ6@E|Aسg555;v &`2:< JjY`|r&L0apE1cW_QPP@ff@uG@Ey'ϧ΁ѣn\*M+Q=`/Fff&?lbњ+x^c4q\tvvlZ[[qh3f 68:;;xY52vIJJ"558 nN?tkW| > 0 50w\&NxA_[ILLbd׮]׳w^ƍ5w-޽2:::(((^###cÊH ȑ#9r'|2EPmAAs>S, 1rImKK `db0m4RRRhhhp0tPV+v<Cfiz[K$""Æ CQMHFFsN'CjY"L4=d,&ؿ?`00tPYJJ =pc LH'}Ւ3qĻޣvÆ Y믿СC: 6b=J]]~)sOTdԮ\+V)))᷿ ijj" P+LNJQQ'O̙3ڵ;viqEK/5,xM]Əϫ3b|>MNNj@mm-Ǐ>V===dddFZZ>YINNFe-++|8y$Nl>|8&IsEHJJjb4fhll䭷7Q5 K/*'%~vR dџ [nes[vw1B/,,rpB6m^mvK/~zdYfܹ,\P+|,1cc#IׯbV t}'N`24.તVupƫjyGbEQAazD  ++Kk҂l6,tuu٧Q$: IDATlfddl&< 2f3. ߏjմmjj9>>#F $@vv6fKr!2-⫯ FS B ޽{&PTT'$n R[[j^ ]o z$vűcx1]^OQQ?8@Z8m۰lQ}-8qgΜA$ gϞȲfw#*5L~<O;3{qVZYYY<#,Zq#IP 䐓Cww֬h4KJJ zV!U3FeN^/ hkkժY=̝;|6oތ,ˌ=zP?EQpg5v;[v gh466 Zty@pMt:FP(匿 N^bV7_֮]˖-[/l 2D{oVV 6LsI@QIKKst:#;;ŢŤZHDvv6$Q[[p IG&%%OSS]]]2tP)))aƌ$''k5Յ,˘1c*B+:7?߉mږ^z%yyyk}w1HNNǏsiȑ#1c@j:'|Bzz={ptN>WMm)V$Źsw%WoflL&JZ/R񬮮淿-eee|g}lz!_Yff}>n͙/6{IIICr >EQ0 I=z4sa{l`0hn 7nÆ ⾋ֱ_v3uTf̘qcY +1k,Y,{8^x.]O6m\?Obb"l߾}㹕cƬV+cժUp8(//gѢEZwhb9s/|$Ds29`Nˆ&i9v s!q\Q2v뽪ޮ$&&P׺u8u>g 3226mZ}lvͦM@${:::|`vss3aÆINNFQ  qtb&==EQ@X0ԶCӑ:(u9˖-cÆ b c3uuu,Xń Xxm_^ `/Ihci&.]Jff&W/,aV^l63o޼ao?  9 bKH`#A0 $? QC#"@@تUqmii``0H  xaǎV͔)S4mww&KHMMΟ?OKKVe0$ ׫}*hii! Kbb"Vx)Sh6fӲΊLZZ]իW#IIMMbl7O>zok1MTASS999,[,Fhc`ȑl۶FV+@tCEFʹi8ugΜ// j{aKHFFD>\gDЇ <_VW(T;Mj!U;pB!t:x<JErsssiTJDQDӡB:u;v׾S%6=twwHKK# rI:::"99lp:v $''3zh~iD-fDRR~nDCC-t14l6cƌ>@ رcowUUUq qt: \EPZZJcca'|W_}Ō3b(dV~HRRR1z|C:h7"/,!Bf t *pd#>9dxZBl#V= EOݹ6>} K6GɓTWW4`MZMŢITBpDu>Ç3i$FAnn.#Fg?ǏCf{ճ]ܺ\.M3b^|EFE к~EѲ6fyP555ײ+K W|7ڲ,sy@U%s׋."w7>Zz}ߴ/H!Zl`a9M{rB…\58`ľܐ8!s_l# ZHi4Ѭ=w-?3tϨst}U-u}ov^x.]zsTTTpYΜ9C"`(̟?o'OryvIWWrK^tmHCqy.fbYY10dGV2P2Ɂ t$qtvSN}vuuuRMkclm[^^,˄az{{ (nc<~t],",+y͚5_5^/dۍ磤'|o~|>0p>M.Ӊn(+Զ/Jx̜va3կ~5k֠*< 'N} p)mbL=_s5c6;w;dn"px<6̡ncU`qMQUM6qa8۴HRET*n-2[TTDyym?cyTU婧Gyٺu+> 6mt,.A{{;=v)+d=Uyu T%c*e2MAWY4z9<(ЕtYUYTB&j*6l0-50Zݸ 7IL+˲Y`6R܎&b +Tp8L: ]vÇٴiꫯL5&OM7͛]?JGpH=uǏ'J*++Y|9/w{/\  _gy抎G}W_}5#JUUYv-<5߱c[l׉im Au5|juME0fjtF̪k%™1]M+r5]'l>[FΟ? 60kzWäԎ v|>>,LFw!QͿ/6.uUUD"DQZ[[ioo7sgkjjX`^wx*ַ|஻,v;vz˪Jww7---$ɋre{{{y9wܰeh"eee葹XLc0 ;oΊ+ַ5[Cooٶ;,.$I,_yq!>soΊ+={a{-i%}@rf$#6@eim|Z(3rv9Lf0')7ErDݻwsI 2r;%6Y)**v:Tt:M<7F`ێaH$ttt駟F$.]JmmeI5?Oŋ7i+)tR{=:d矛Xrm4e׮];vl"G0̙33mr*CCCAv/KJKKۿ1,,cټ[r}YN0>ԩSWYf]w4_DD@E8ED'[&Y-+b|AJgۖ]炰E(2$ә(%Hؽ{7GAe_ S.j7$n7%%%nSmpD"A4% ;t:= 2m|#CCCڵnAttR-[f^v^xE;5"/TUUvٿ?஻BU3ފ]]]^CR)=޽{FőPU^ۑ$ɌVUU?>>(Ss",fEEEtttb9!L3\.wXcǎi˖-O `֭ļ.BL dūڜdDom "֞#\[!+`s:HqUlD5Z$ȉ9ݻq\!n&jslx^~)p[b4Q0Dn4%L:Ds4MC$,Y>:g\+{9:;;yG ŋ9y$@QQx<~P={,vv<T.p|ms$IbEEE3°bijj---}3mLDEn6ٳgرcqf‘#G4HT+$AIɦ@tt!qLeD[k#d dDSf#(Y@I9qn5'YqhhhțXAZu3'vN'6 QM/#rJtM8~8gϞEe{={6A}]~Q]]ͷm"4662w\/}}}ݻVXL$j |۳g288Hqq16mm8ϟ2yV9:thJc}5|G__;w4r}e#{af!W]K=F[opwرcfh>mmm|NOX: ?0?ͻ 4󸒱ŋ+tRy'8|UԩSΠ(bA z<Cz"]BK/ChHƮK2y._6]+8/!:옽gY :n>9/g)LۄFB*vF,YÑN;n^z)ӱN?'\|NimmɓÖr9.ngݶٳgjfa1?<~!7|O QU͛7; I6mbW??F,QA<ȩM/%̗Ͷ5S dx̞S0f7=j3d&A1,BC&7MBO" 258YxBTDYq:x^|x^\.V! "/5)t:͉'Obv?,"jpZw$Fv|>Nsm$===D"|>okqIزe ϟ_*7MEիWr8x ۷oG4/_~:uXo]I70EeL?466{:gΜɓi`x3pq&/o䲱mݱn`E"oNYYK,ǟ>===0k,-qk1&K.e֬Y;X9_W_K__O?eէẅGQљFqYv@%;bCJƫV#93*6%IPl 2ϒ-#ܿdʔ<ѤaEj-&??,~K{{;"X#Gp D 4j;#b={h4Jii)fGpX^,Ƥ>^/YEӜfn&/>cݺu¿'sngvҍ0%m%'2 9 mvu-zW؃eSQ!ŌQZ1@DDHK|_v'y ,Q{DxgI&lڴY(O?Ȣ+Jqi;w/r8w1 щ ݩê200@gg'$ّ'I:::ÔѢEh4ʑ#GHӬ]6S~?~;;wԩS߿k׎[s9m.AS̒Okj5g/DluMAFfuM4kгV3yU(d2b6'b("!pq[ +3RXNJ-tݻw7rZ)~[ IDATm1 m>}> $m6:;;l>qm߳W^y ¦Mկ~5v۶mLWݰ犅.BѓGz"Y'EOSs2>i` 7#6]6.B@<6:ش<^|EnC=O?XP碱d2ə3g&cq5]YJf.\8m6555WT!m1xyw,{H$aӦM8qs|g4aϞ=.:. d6^lk\Y /;xˁ#aĽٌeEّXfˮk$ t =DWe)ܞ~苲)>3imme۶mHc=X@R{"a( ttt V%/ǡ`"|u%fĜ9sPUu\Qr[ wfƍ{JDQQyطoO=7onzΝ +.]!SȥZ697+3nr Tj+iA j hM~eŲc^HEеLl ;1A!M?HTEYYY^-X?!CQZZX0<ȧ~zu{H$-+]JhNmm- ,f9(--|I-L.]ҥK9z(۶m㡇,&?O?͉'x駇 X,ƹsѕ(BPՌDCӳEZ-2,5l ( (P(YDeĭ#!fчZt=u d hAА3|v\\Ǜgr9|0w[QZIɓF(=E9WZ1|)))aٔ 0A>,SVVFmm%f-Feƍ=z_W\.^~ey?s=͛:u 3T/AE=!LȦ `s\p7XxWMEӲ4l `:>lʂ-#l@UiO+ [n[nHFKXRcƏ~#}Q+Jk184Mhylnn&޴{5Bu2S{ɬY ,eժU~?NKK s,&#ᩧlڴ_~L=8d-Z~]D[$N&8*;3^eg(:z&jRHz6ښ edH=#%# L^8"aD"MhбyQ)=z s5O 0w:k,sHl6GqsgN'|>+:kqI$I{[oŦM=%k$IKf?Omm-5)COZ:"* )V EAoqY3ZӖK!N6@0 0#7o[>#Wd5so,w>s&hǟ]jS9ꫯca>+X .Dŋr-˙;w%&b5zeě ҬuQ`<\^A2.d ^#Ui#e)ATCN2%ㅛbv7z"h2u hy\ץrj/7:rJ.=b:>|~F~YQ#kBKK =|E##>}zTuf~%-// ͆$IHD*//?s$4^xرcO?[;UU9q{a>mAseFF%D+R HUWֺKglv͗i5낝~ /[fظ)RٸqcUԎ%rtJ444:4&KJJXh---D1׃kW5zN&"rQSS0_={?|-.<;/~aJ*" L&q\M7IJe˸3رm8r<_` u54\ۮlA.IK^J Ȉ4EJ&`y;0 9FDtdTG+ 5:Z(۷GȽޛX\'466:7rt:Yd];p6`,S[[K}}=$ke)//IBEyOʟٟqsN>3,Yrc~YjNcccd1IJ8&( ##sa]{{=||FSD+Ifcl1+|Ŝ6Wb@Zz /6}vA*`\(g>-S;YrǮc?odri(TGGtvvwm؎:xֲd\.5˟HmEE˖-`yC{mm~xܼ3l2vooSQXLSxmU-h" /kF @$DQD$4M}7yyVUbCpxټ[l@&@ đ\# Xxeܽttf Y}5  4]ȷˤTXM{_>| }qaXvmcq``~w}[<*ŋ`~Ofxrh"f͚e6PK$ Ǚ3gse˖-|_'? X h"||~<Ӏ{m۶]}l1P^o 'N`׮]|[ͩSLak1#?},FJ!!8|2gݝ-&s"\f YqB&fSL Jȸ$@־K3 /#}AtY YL?x ?SO=exY\sBy-ZwΝKKK ǎX MOO\UxʲLSS2y~Ӊ$ItttPZZJii)~!d~t:=OJ~_$O?8Fg|}{j*n)+" P]]͒%KEIֲl2JKKǍ"K륤f[OO.k֬رctuut:Yn{8tnqDQweȱxȤ L(Աkx>book={Mp8n)d>S^~e^~e9bFmr֮]~W6ɺfAp3 . B9 S\QZeDn1 #bz/uVqկ{:Ej!˳>˪UhmmuxQRUUa``T*5-))1z6 ÁEedY&|Iϼy8q;v$I|eƍ,Z.(** h裏rx7%PU~4ÇyFJ0ߏ,DQH$v^/eee<Zȭ B!ζjLhȐ "$g" ڊ2AA463 hMX6⾁x3H?ŵ8N֗Ŕ088?0VR۶mȑ#Y9sH$z2vE!L0rnٳgS]]=xr8r((\.>AJz-֬YCUU1 P__OMM {a$,O<ǎ X.avbbo6ܹsl6NN<a4ͼF !"%%%D""ǎc޽\.p8*[Y9Ub/vDّ)fz ٳ`EM?3eA3LDQDv%ԧO0|V^so`za|O:!NL&|Gþxsmoo/(q~(D"a;u:f.f#NNĒ$@cc#6mxmn=_7|+VPYYIOO̙3y~԰|rl6i`aq)"{E4֭[X@u8`])))trmh q8455Q^^" 288餲Ӊif=A$ahhGNihh}"255ewG#qlPT Ard=|aX͌fo39bYe{wQuzy%jg,P^z ]yg m k`ppe˖'F(N9v隣vI$fRQqݔQRRB$t]7EnMM MMMfXYev;. ͆>5lΜ9üy={6/(++c~v;̝;<:ӑz-:;;yGniA(~O0̙39sYD"v$,( n}axqq1^d2I8&ݻFr)+-⁵ܳO'q[v B6ȳDlVx bVlftwEϐ46l,DKP~ӟ~NŔbF"֭[G8ѣâ۾};h P^^$Hu:|>ʨ 3lфtbۇױD(԰uV)))A$Ν;ܹsM=oX\Ǩg} Ǟ={LG]n~;( H$fw$-3,H47:\>Q\\ 7`V,[XL6>,wP(D86-DQg?#NKII ^A3:+"d TU5sԃ^8TVVOfhhEYYP(Dww5M:DQl6^ /~c+8-_P_ٴAD2K>gUVӹ~0W~?gV9./(RVVF,3mjnv***8~Y;5=z PUռfT_J)QͶ7xc[lA࣏>bhh XLx^3ָ?筷"JwΟ?I&SVV3Eo"twwD"A$:fϞfc``n8555TVVL+DAp80 (l߾'NP[[kt `:V XJ˳. LC]AWU4IJ1%jgH[[_׸[= DQxfeE@{w^ no iB4|Ogơ:}}}fjDXx1> eee `tvvǩۍ(Ab$"ߴ uP(2{lnYv2 YNL]^^6*ρTUU( QVV[:v]z[c$d,+I*NPLPlJؼyjl Bh矛_w^~_L&G/uٳg_REa)[p!_[yG}7|U3ai\.7sSs"7h۷w}܏x^A œN'bOOtRf͚&jn:::vΝ# զmWWPI|\.@CCCnN'g޼yRN:;[?egd1519I%j-K++l߾}ǬY vW2Vhl6n&.˗i,_??e…"ȹgdYV> IDATĸsRҗw1_Hw5}ƮvtMʎ;jj<GUU s~m{(F{dS[ooyx<[rr wvv"ɓ'9t477s7S__O ی:N'RZZJQQ``0q\,^E^|>ЀiB!9C~miA͛üȃCd0,,{;vO[,TrȑaQV~_ս N=G*X$`;ͨ_$$RxXr%˖-ә~͛7vZ$SFah>X-DggL02˖-[hoo7o;"@M())A04$II4Z9{,}}}f9smmmDQdYC$l!>00`Z l QɺuX~=8{,gϞk_555!{c3B޽IXv\yֆi{{ٳ#GNr7rC,x/HRfJp [κkQw=.V4*H&iu#8PRR2`tVm/| \={eIR$I\.EEEDQN>(\.x<ۋ2mmmf>nCCńaΟ?oF\8ƶϟGQ|>nj4\.l6,sI;íJYYODťI$DQ "`x<~ᇨʽ{Uرcر4McŊ̙3?dYf沈V7ŭqAuh4zE{g9Bcc#uuutvvraVX1ņ]S&Bn`t^7NeeeqN#iڰ!ns:)nE1ϩiR)ӭEQ477_~)I={6vP(DKK˰(xhCww7d`i ,#NI&Bs,_x"'BUUUna1TVVtR$IT*$IQQQA2DUUs휾;Ƣ.FApIw̓>h0 pQNݜYDdE42Y`e0R1F4Įw.@o]wEII mmmtww*x^ C"trH&!2%%%R)a̝;Eww7ʲeL* xLP(dnٸq#W6]fpaߵʩ-ƺ-4֗a/t z),X[X\#v2(rC]]]G^Ŷ{+Vp 'xill__.OR 7.WԎ"gct fTt"v,Ȝ[;w.=-h={; 2}}}f7RԈ@ $I8NX?p\דN顧MӨ#2+WdŊ$Ifx GUU6l@<_\ L[<`REms83>%!NرX MQQ٢{4aΛoIOOTt:M:FUUyXnE޽wyA;+iie|9Nx3Vh)0n=<x(//MrذaO>$ΝwB:yc$L;ٹs'pkfo$Ie_{ iR{졡JKKbT##Ku^u^{53bj]JӤR)N:~|IlbZiʕ+˿K~"$nb3BN4 EQd``6f͚fc׮]>|Gbٸ馛p8:u`0ŋcΜ9}ve~>ᠸ׳fˇE|weFnBX,F?MAk4\u͆륬Lߪ0VXҥKzEQ(**?X4^wXNp!c)ĺ-da1 w```uWܹsxv׎~N8ASS01i|ָnT$DQBc{ݾ};;v  5 qk۹yᇇ]ˍ`pp|Ν;ǹs稬;Nfa1i(B04 .G|\RMtk^{4M# ͨp8A?ӌF!lm6s=]5`>6 eAb^Av;(Byy9_򗩮F4baq\a56Ĭ%j1Ư9붐ՠiy+J#׿vhm*"  $ $I`=z;wfj|w|eKu)۹s)n QkkUU5;XnW60,:t׈d2I0DQ _6m! 4.AdI<E<x8CCC$IKGpIhΝ;yVUUqFnaM"uݎnf$B(@'k#6\虌m4l4;:\.v}5,pQ6whw4q{w~zn7Hl000`z<jjjfS{c#0t@44@`u _X:šuܹ~ڈuD"А)hG2>"۩*n0oP(D"03(bժ:v[f wٰasyO,Rd֬YI B @`XBΤarzԎF"  D7:NUq'uT"F!X2ٳᠾYf軴ӻ-Ov[h:1򶐅E +,NJ"h---%Q!SE k-VdYF2 ]chk4KٳnxbyK^BK5󷰰Iizzz4lH4MFYYeeeqI$b1bdxs FJ͛R6&s׿kK7}8*dM[F$OZtP>B1&2^hjjBujkk6œN'w;v,ry[h,km(EF?r9\V+^^n4ṟ3\\vW:fQ qy :hMt9XיFUUE!H`q\RA`dn YXLq#ai婐1nq[.mkYm!Bג! 8-KTsx<dAF2Z 9(N.J"Y ˱2c +hyWsr-Y}GE~?XX_H{\SWM[+tbBl86,twbŊ7Y/&㘘4""+ 7>s\K,;$"F. ];=IZsjDzkxxo6n݊Ç#!}B1m8^%#jB pV6m*,DDDTڵ & Gkk+ΝٳgZL/_jqZΩ=\'ٳgU\'""PLsPcf3g3DpAX""rlܸqzDT>.U",ݍ;wO.y".WG!V<_vhH\K, Q%)E0ċ/oFL8ɓ'Da4aX`zԄyeظq#6oތH$w(DD49`bb"jA9B!+Z &,DDDZ;t0~a8Im6 sVXÇȑ#G'|p8\Z& ~>⊊(ʰm۶))eUVnܟ|>^|E[8z( he!J֞l۶d"jD+Cx؏O? hoodž bҥ aZU?!DѲSl'N)Kzi"b7|sֲpvRR+e\OD"˅.~&"|w;pR3P(TyDm{"ڵkq7?"xG,:`pJRzDZ|\2Z-H_{_ת]|pZDBD0͡^BDDχm۶aǎ-[RU /c``7o.XjjjtEK. 4g.p}}}hnn>|^{-:::*vQo[[ WdDƤV={k֬_\)e`0X /DKK *j YѩnDĤV%,_+WDcccEl2ݻߠYԭСCp:hnn$IXti^xdRK*ROO}Yܹmkkd~n9Dz?Q;m~O9۞O$?'Nhr.{)Kzi"R+~7LB.]3ԩSYKFq]waժU_#""*] |:_~Yy&6hnn.<{>t71gVqAԄ^z*d4=.BDDTǖ-[p饗VgΜݻ1::ZcU ˗/y7j z@DD4sL<%MNkaViZaRE{{;z)8b`YhzW[f }ي/g&4-1cʁe!""R7wyذa?Udȱ1+8~8Ziȑ#袋qF}hmm###xw1::i:,4d?؏Zi-zlj'ߟy-ZuG-܂s~nh4_B$"~pZM|嗘7o^;`||\ꫯEk> $"Cww7f3n|}}}/F) 1%"HvBGG~~޾}~^zkmx |>̟?K.͛vmx_zP{2`@KK ֯_&կ Wݻ166ؿ2B$2ZZZi&_^׼6a?Ċ+z$IxJhEzJxΔZCFIeYVURFjo6X"xHm<'* Zɲ+Pcccp:0LFkO24zЀ?6 d2;6Ne .į벖(# sX""JF"v;"a4SIj6-12&B:ƺupWjiI/ 9)ߏD"&a޼y3gNO=B. w}7:ƪrBZx}zfdflF8F$A,h`$Ie0Յ\+R B2fC ȹ$$IE]-[LIhkP&Z 4/Q51M^RJB& ~$HX,0͈bFʷgI*6jj00k,w}Xpa;""… ۋSNVkj%n`0Rb0xjF) ZL&aXс{キaY*ɓرcBФ)k\Y:iYD"X,x<]zI&hmmEWWf͚UB8R{BDDTiso~l߾}9&`ll Ff$`07nB$8N<k8RN:Fe9Uex\UYܑd2 ʲ4sIY+~`05:yF$g믿<۽ .fհZߍF#<,  2 f3N'VkFJ2{ea˖-{P&ZC`xx{A{{!.&,DDDz|/p Jjn7.r]K?RaRBDD3gرc駟p1?~DDDU)?.1e.쏨& ""W#F8s%FG{{{U'j9 m}U4P*òXr3_x1/^3GQJHR^oΙmjF8r)u?VCMhb~q="bX^z)~ ŚH+ĊKz ߜls[l/v?m#""ms(Ԗ\S53Xi,"R\SͲ~J~1"\Ya ->\\j-f<9^Zh{er~-e?Lhh:H9PҲõjh:bDDDDT8jש-Mxa?Ċ+Cxa?N-9N? """ǤjZ""""yLj1%"""%Jh-Cxa?Ċ+Cx.EDDDDu1%"""ǤjZ""""yLjqID+Cxa?Ċ+#9~ŊIENDB`filius-2.9.4+ds/src/info/img/server.png000066400000000000000000000056051506574241500177540ustar00rootroot00000000000000PNG  IHDR@^ׅb pHYs  ~ 7IDATx\ke;$*[E"iAKi>"hZR>*'{Rm/>(h4ɺ1Iwdvvvv.ߌn7}vffwwnSҶmL&I$I `0L& (~`uu4>|.Rz%A4hV' x_CCOOOW޽{EEQA4r.$v;`qq0A\J Go. 9󏃜)q X,&T] `Yyw.0`aFD׈, C_R1M6RݲB`fm*m4MӴR*$k1rݻw;k ƸP(T :y(޽:-˪LOOwu]?S]1 ;wtaQgB߹s{Tֿt0 q\@}}}?@B1nv?mK&&BMZ9 Cu3HA `0 C484 )jkDМ0BygtuM6V~@U4EQZ_-۶MӴ,}(R-q@@Vlte* AhԸ1ƺ;Nj}2 rq\ )F;OPiرcXlX!x^);Ƹ=uJ>}Un(yA<z. cy7@D>pp>o] Lӗ/_? iJ%\z۷oٲRW8??0 M 9r+(oTR8uTرcG}KPw,ˊ@~KQ˲.VXgnwo;sss htmm4Mali< zH'ᅎ9SO;wN6333{,4 !5Y0h&JzceY^^^_w޽o߾@ p?\[[#bddѣeO~g0 )rm\FqY5M7^ClM(S7xc˖-p߼yٳe8pĉ7nܸrJ*2 lϟN[n}e1ʱcd/ZO/ie~x@C=v?cޙH$fgg2eY Q觟~F)T 0t^:|po=g}S0|~s"}}}}nWT%/S|J:NoCCC_}U&iaHY]m۶'|24=axAp {K7nݺ%֭[B{ =Gq:)ki0&B'IO>iwv۷ɓ5ZQEQt Ϝ9k׮vhL&]fh8~뭷?D"1::NM СCCCC?SkA V(PѣofS$?~aaM=gD" }`0X(,O(Bڵ@i` JDKmU91FܳgϞ={$IEqvvҦ_|$Im@0nLM&ae7 S˗xi.'_ /Y)px֭BK0^۷uyhjٳgB;w,VJm@QExFC?Qi3>>~ڵՔ *uq •+Wʘ`=c0 tBf({4l>x_xMsK.͵C{49g .3b4WVV@m۶ݻwG"kİSSSŹĨBRJU!+˲.X,F1ƽHdǎEytͤ!3B$mxm{mmM(2KKK->-mc3tC}zqEQLfSm[QgYhV!ɤR)QaY4]Fj irzs,ˀr$ɲ짟~Zo8 m vOOȲ94`uNEA'@ 0(\YzTZxhN&5iSN!˲VVVe9aY["hN|>r$<ϲ,GUU:4)[@\ 9mΝKR\.$-˂m`8 .,RU%@ma322Xak\Z(l6K47a (jj(͵m8AVWWGGG8pc|EQ$I( Xz5B0H;Ry<11_ n4hN!> h$IݻUy8]Uj\xh~J7z/jq6.9ڶ]e^\\lWtJRSSSTv |X2\XXX[[ @Eo›nnkvL$.}T`^iX0)Cgg8A?: 9f!2,@ Ag; %JvC&y $$ͥﱵ&I[8 }b9^'Ꟛ/RcjF*De?|˸k ئo-9dZq hT mmmۀA|Vkvkԡ ^ ڹ)Z_HH$+ӺwENZ> ]?3GL^V#ȋ tY!eeS'sswD,UdOKy$~]DLɀՃt#ͬ6^?77mp\AV+@\)m=CF)MG.p9} ܷZ9NH%8*ݺ凢0 ١t=M܆!3Ug3 DCO.QDC>u/l+E\x>Xx){@j[8lMzLrF|?^"YY{(L11BD_ъУKkH*ڣF56[3`1Jja$gZ0LJ;B+ܓ Ӏt{P6a)w+[d-cTуz>G+A`ǽr 'o~m#"J{|.ð3+&)EjEL\GqDkF Y"F􌝚U,IENDB`filius-2.9.4+ds/src/info/img/software.png000066400000000000000000000106451506574241500203000ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs筯tEXtCreation Time04/23/06ktEXtSoftwareAdobe FireworksONIDAThݚٓ}?Jh4@BB"[edbKU\_TRʿ vʄc زb`[BH1hEb$4sgνsvU:u>۷o7? K855y Fbۊ~J>/IGɁ!.9QRX8o`iFXv=icoɲq ߈ݻf{>. !r p68|rL^@;f}o8JWȑi}NgU9k V? ŃsI#vhK*+׻罝spSƘyf=S,J8 :!fj-w=iGMˮd6>JXq(m9`rrhm֮?SXaHt88n/nØfZ{o'F 8U.<焄^)停k#O;?:ŊKǾ64FlM退61<$Ꮶ72NG i,_9Op1Te`[ \}w} 6ԙcl| ;ߍ: 4tVŞ?eh͵HR#6H4C7 N8ʕ);0 5JȺ5#\58Foaɝ@8w1h0 _ d?㡇-UT Ω05^ʗ$p'FnF%pz1ڠ@"VҸd+r{ C_F ^O/ ~}'-k%q2~Vgp55.L2X^RghDd2 JCS-sLzkVl⎛>9n"ǏᮻJ?ڵs6 C1 ѺX%.%5SZNr/~ݭX,ːRʫ;=$I̖ā0uG "d"] Jg'|ì:<1FE Y090;;Ŗ{'_,@ !1-0ruwrw3]kP(?Yh5Hӌ(JfכwGNrIs/#_˽"RѦá$|J"==%V, "ItR*8|xÇ1tv_bYے(K␀}8\c8v z1l9R uCJH__j@㺊V^M*@(X,0/lmr(%PR %q6H^c JؖC ZkzΛ&q b@Zժm&$1Y븮KRaϞ45;k-w >?BY8UoEr;!I4ijHS@byL ǵд) ۶i4uH)yWضIJ-%-Trj-[͹I\fD1ۆvjeD݆-hl\,y墄"mX֚84 2?8qrCbeM~[ސ@dDm#Me[fBKӎ Q$IR4"uC|f\JGRqlylE."ZNHt:s,&i.DDQ>ؐY(]_T*Q*Hqͷ`Yv & C}'(%,憯T~qwQ"FOO˲v+&KAy^XKo_޾"bH8v@NJ!<%RgϞ}ypUͲ/~/3cb[6sSfOa#r>WEST*!B ,PZ7FZ-: 8ÓO~$86di>ɽŀ\HjP,W%cy۲`9ϕah6Ab v:b8Vx5LiKf1n;]I4s|5 &Ga=C:8s]q' A! K^s===] | ^,$"XRhf~N{{ϗz=>aX$CzZ΢ov!kmß[b=}Z!ڜM =ojh5W_MOwvz8(hɲ}#/G2??,f&MSn8LOO1I000@j5(bjjBK/Ľ}UثQ\\.wϳ,#c癞frr4Mj$S=!}c =[n] K"J( ˜kk{۶u?lQ*F9j4q(KT$6R.MbFaIENDB`filius-2.9.4+ds/src/info/img/softwareassist.png000066400000000000000000001113301506574241500215200ustar00rootroot00000000000000PNG  IHDRXK)sBIT|d IDATx{|U:m)-jR쇪\ĭ]צO\Xr_E݅ҭ [Xi E]J?6ʷ_VBLMJ RDh9?fmf>4ing99sy{ΈW_x~/ B!BF;?)N; 'OƉ'7^{*GP7E\}Օ>eݷX \@RxB}-"!O$.TX}K%I狌|GuSǵr@IX=j6Ky nhn#L8ۃMAu?1pr_C'K/X踃͒ >wb|'z|c;NW^ .r~/SVaP v[{rؖ,WLeW9_ "-dN,)lTľIVVُ#*t1o 2'xꂋI(/n9;VgSE&T\.AH}u,!҇@8?=qI-p\(<5{p*!c@xǜ#;X!QMmIrR$}8Mqf_åxIE鳃m2ÄS:EJKK3^1*cRT\y~Ρ*T_{!<<=',鏄9,Dd%2 NRS#;B4@GVΜw╽?ǟG0Ix( ¿A?;;IQ?-GȢ`l&L{ >O]};ş<P_7n[ueu6ۤ hp8UDj',4Jm\*Y\rdK!6}i.Z[ Ʈ֝*TIu,R ؄TA>rpqk>6ģI8+: :esAV!!ɆP+=ƛƣOp( QE"-lJH)CFϟ=>EScF"۲g<y6F!ڪ -vrB":1.m:a)t:0- 1' EVY|Hb 𔈳LI{r`2@?,`X-JDG&\qTeB,5<''tPe '^sӁb9=iT>2FE6li*۵T#}a4JWJ>$0̏8|+ҮX+:M8*xMD+_jڵNBD^%ubx% \NT:"-l!2jy~dOOPno^wy'Pg| _7܀Y@ĸ6gv+Q!m Nb+}e6& 3qM&qx |҉qll tRV/fwhoz/ 9.@Bx W8'<ڛ Z[R-)߁Bs/  sǔE1 zCOp'8Z 0l yB :E+4-u%Ƭ*V(`VLH&#@]#3b鶬[R'E_@2VB軳nd۳}%˚Xm p`~Xkַm}WؿuEOO#o@}ó"Y©{u:DM\uNE=øj"KcF2A[F/;P.;g^^pxv`f˖ۑ2;DZݩH '?DDVfvq'^ڥz%m#/zb%nʣbȝ+ۅ%v{zscm Sa\V>c"Zqz|lx\V>G{tY'ܠtTS\tOo M+O>W~?Ɲr N9mN8cƌ'KϞϪK'9:. Qjk@E<#=QUlZ ʁ_c=!$|/֦bT=LZ89DHyӁCBsꏁ;P>BJ3c:v g$a<@ f9uR<qFDj67z%gwZ+6Zhd5h4Bڒ@0v7U}-t0/A0X,(a4n5G$LڈW۩ҊhדX.7?A#z$hl7_/K5A-~MoK蔱]=bIsVн=bti=#⼛NǎvHI 8n&9qG y4i# gŒy+Ü>6je-_3mOut@Ihm\9CWΊf`?Uҏ`Ε5zj$V05v胱rju1{R7UI^"܂wfQqC\Q3Q:Μ$Ͼ4`Т!rp<79aD)u/Šs gv,/1ZZ&&d]Qtw 5v*y[%aҕ3Jx^3J OX }A#GF\}sEǬBg9~+]yD68IZ@khB |K(0}[c L[on?[; #{@|Ťv<.o[X^}d`gû_}OصӁg~>$^4?۰¯LJ~J~MoWu&~~q>q˧Du0 IJQ/A`]fKOE/:.~k;v8p:~q4N B[I_?>&*ǂLAQ.hOetܶ/ϊEC?r4HG{%Ӱd 8z˝d /5=c|^~e"K뉘距/K1{u~$.ryֹ8uTLx"&M:v^56fއpwVBN<0WYi}|K>-sѸI i ؽ4> nm/DUͱɅ{tn|#=UjK'%b?&.a*Iw na_j[YI)9otN ( ,{1I4G"qձN0. BonPtImDtz~}Y.X~%\ -neеu0-Nm'-Y1VcDc1OjKm J-`)O)0} j\՚׻VU wuWDK]ս.#UB1cB%zY#ʱgTܾaΟ: gTpBy'HCD %a<!<צ -73ρY,߂'cai} xїWuO- (ӟ](;KY 1fc< e ^ďG٦AS/ŋhS*^Dpݩ1}^M駝v"*6X-ksOaEWᬳe K\?mfLi_bɨ<}2*O_ g?;%xH3s/kgf"os: g$|ɸ!dh,;+;/r2,B̯<~ 34 ?|)oᜍ|c96ݮњ{ZhW~em#i7(:(:a_ w\c@h#Z_UHȪFwh~Xl&nʪ=p*zj<Ǚ={]w[p:&gfӏDu'soiCJ<ԉOs7#7PwV`B?~0 g-Kk/:^l9j}wē˫謝fK.%~uݎ{vQ3Ȣyh'>7i?༩|9|q̫ƲbӓΚ-xb4UwH$1@O`;2PLZBw^~|NN9q{4XVBhEpY$=HvvEWPpS'9`84o7Ŭ2Ea P^)2 DqRR8pYLeb+[mRW ᜚p 4ͳKPi A xsz`;n9E!*(xC-;L]8^UiEO^Xi}K챥 ìdo5Nsy'C鄉o'!g8iy pwk?=;ׂ xC Jaq𼜏 f{;wMo=~24t, o7`FY-oOu/|S%3/.޷99|g8[xlװ?_/xCWQ.~G ;[TS&`Aj 9n<3L\xtxPwIwc׬t6}w%3Wv>lv88WqϾ`v,슌%(7,dQ s(8%'Qz$4 I(tJ&oI(=i20i2J'O<%'4|G'q'w?EK'O ;i2A>N p\'b';qJip\u`UyGkкy೟K=w7/[oa[o^<,%rXue1v80:l=5=!<̜ǔ3>.Gc_¿ާpWO~qf]|oBv.76M1JoٵC{(?Q7iWgOg,/a޽PŖߏy*Ű߿|3gW7yyA_|c 0nҤmZK@ ũzul (-2v;HWg>z HeQu=@ﰛSB׼y9~tpG *(.U%c|ҐHIJ$F]/] 9W}C}$ $QA WPAo0ijVB6p:Y(p8v*k>FU ]|$/gr?gI@g;]uە. lW|K~ͷPta`!|҉O)Y]Yix1JR?Yy>ވZqdD'N̙g$^8m;g_'_=o֯)9IEŦ/KE3qsqEӫP=x좫qT@E>SϾ~b|毡,b O^Z;L;_(>;rvCkKA<ࣸ`ߡc?!njwl-X"\l,{l⑽fp/r֕\u-~c?ӢLx_mF N'HgV{uЦ@ IDATΑ=B>ks,BIR]>LBn2ƺ zN;8C~l7Ծ|}ʞ 3E'!̊y,-L@h!+fe-p>G" DV?VBjA*eO>,*0cX ݶݧ3 7]8͘:Łun_cYi¸OFI7qN1&c(8 'L c&MFɁExD3&!ܘТ\:irIA'a`A9J'N¸OƉޅ)gk̞ep,IZ'NG#ݝV'cg3f-]'%3̿ǎLQ) O L0uXs1Tk}NY6%, ~bfB oCӲ&0 ]\xŘ<.A¼p %t4w#8މTa}m̃xry un\ێ>> 9/7O')ɇkY羂k3C|;͋ѫߋ/Ic/˩݀?s3}H IJ @VP {VTlk|> KB3wL?T 5Cpm9їA/6z`pL )M$o|!e>~&Cx]mmFh)-0qmͣn>z:҄^W7]eMYVW]ceVi ȸB 8pqڱFJi®N֣'v !ÜjTҜ|;Hi ~ :YWQy NBC}':A?|B"LD蕒n "58n" vK;`BZ6 LbjE [8{m/_@:!»=E^}*H~i2bq4")P qSYvԙ&w8blL(ᨊ万8 VlkJ̡p,oWV-YX;!t΅ooGqV+6\@3$ lgmq0Vg'x % /d,Vs>u~F۳(DÚ 8V"Et?'=(D~i/K"`,hV&b5aI w%t%5gW&jˬUڴ%E< 0B.[e~'QrOD[T=in)᤺VZZ9ŵ:gKK}I6g*҂Ua"}/f^lzy7~ K y:Kj& i5zc,%-bֲL?O 8!auS;Šk~8e?j6 [ qChj L:m%$+Oap} NddDt*tg)õ" X)"KF ZcI7^Q|$;$V',oe[2LNzRLjcjh bڪBtة;iH&K`Jh~覧E`UJDk*=4:KXtmLW&yUi Pvz=7C^DDՕ ( S COE,k Z.֖@:ĵn`uA7&; $ܿzm.>&c%2hX>y(wu|o׉/AJ^R }¡1T'ge@ +R{@{ IdDt?8 qt3(˘Lͭ~.. >W)Ss؊aR@@F+ =*1Ѐc6"n.]ɟMHw\d*_?1hʀx`ՠVuώ-{s+̺+M+IDG먮R }2:zکb/$ZH^.E2{e.JvBP~` Fp`\ƒ~̂E%:xn} & VŐl NTz /ҲB9SrmKrqFG+s/ EZ,7 '%9v%%$/H 7r c@ .NӂӣJDh-l%)?9IfywyX+pHK kQn2EծMmŷKœZ\3Q,!LJ %9qyTij "Z&#i(hF%eU*2}_uw6X$ZOdYoe(0v}P OkcV4Đpn?j i), QD$Ȓh\C"YDAtdDL _HIfߕV!'rt_l Ui'8W0]--F19/i/% }3p-iO^q VJ97B]Et1$w i%sg@'V>9N9@OD+V KHb@Zwdh*C3&r8nLrŰg0˳&爧a>)zWq{M7|jmhpNrIU|v=kJ-2U.FxEevjH Af$tK{6QUD񓓃Ҳ$9:dk9t<-R'bȄ,qBZ&Iڟ$.ey%sͫٲS¼LP+\5B7:OS3뮆_]v"fXP3(25lo$g -G9ܱ26@fi:a|skZJ,8ӌpоT/ )m(x \OxN}gܗ,TeP@vDo:xh~}I!BȐayU ?ъ4?ՎmB!2Of7!B!$K} !B$"W B!dt B!"$Cn5 gٳ1F/8~4^1 f݊>v,I,i[rqqŬYUP"RCwv&,pcgw"g3|ժ4 ;ܛ~Iےhuf_z7Mh"R;n&HXBf':3{`Ϟ|kY[LYCo;/C2OB1DG-̿c")TV d +pW;~q>Iq졯q@e }BP@BF$!9-*bŅV镁;PsEԏaXQ"ph 7l+9n] [Wlci|&! Q( !#y *756!NJMZq#*}z> w߽uDݗU8n"&4!ddR<*3G*qiwR*K ^Ҷ~'/?wśyYd,/?<ϼU9!d!R?Wrv1MmB!rۿሳi΢B!bȾ!B!l8&) !B Hƥ|SB!B7^sYl ߥl]6JE!BcŸ{q)$rib,B!2x$[(X@Ç/!B!À;{o6o8.cG!BHP@B!RЄB!4!B!E,6B!2ɢK D!BȨDcM*^lA5M!BF+=l}Mpp&B!$ l( !BhE}ED< B!2:.7L!B}0EwB!B\lB!Rfh:=B!bȢ=>B!R( E4!B!4!B!EU@M!BF+pit3!B!5 t B!$Qs)ꨲvB!d1rrj8Q˴jB}m-jt5գM]ϖTFZn@-_.dW7׾tڥB!oT:@ˏǕq`ǜ^=EVtRH r9F"Rct vA!2I΋T9qCXӵ8yNiT hض;qi2,M]lb.j@f jRN9T6꛺ eZvl5g;,+8f9DZf=[Yk}}`tgC{PWFkY>r:$BQD%dBz[=}ޯA:r +6d.݁1{Z;`nM H(GǾ ܈J ۰eAXe>}}8mLEYem ۊj s9b.:f`Is3ZZ6c!t3 Ȭ^eXij}~Zl^TP}^7-)[ܶ ]5vĖe ڶ@70=wzYU}@tiۍUUU TZtKpe>ܦ~je)OL9T!ͬe{@/¦u`M ,h~-5L ާsuH!щ&9 ]9FNxexh|K/o|znNAKىEX2+qS̚),-Cjy[lA(j9N/XSX823!BK ڸp|EܲhA!r\1l"4@?tOwD!BHv[;@OzEJM%B!&B)倮L!BȰ#AWy#8K@B!2|8g_-Pv&B!Ɓ:T"T״BB!QO3YCTЄB!dt#.4D>B!d#ؔ*0vl !BM$b&Bi^" A!BF-B(ό𷵯TmτB!dV! ݠ !B(%X.9T:B!BwFtCEi&B!eFK@JzAB!q0ZloB!b}lB!2QI6?L!BUޥIh&B!p} "%} )h&B!DuC<3wk.¶I! ,I3 IDATC%N>}Ed(VI! S$۹9JޡÅ;{[~u>o*j,LRv'VU Xkwb%6﫪^O +MUU}Fu[yQy$ǟj *7lBF"BG;)ŢljJqR\0cGmυ]h}˱uNd2lDf 4u5!V!xظykddGGm򐡏j>]MX:ʺعs+*}dVoDb%2_mۗU8B9=qCXӵٳgٳo7~oo6l^a/ڌEneUU"7KyJ,o܄Ef;i A硲n+6-,u^C&o6\LZwuc.lT t 卛]MX_Tik^9؝]+lK_YW+밮k} ;f.rBc,emUXt|uhYU)}R4j#memBpǸmH@qO[Ρsk^+QQ䘨EFd2KwakW[QW飣av 7Cï]͋tfl²݁U;Lˁ]v5ᶆTmEsH2}ia;nk,[*unK%HC|M [UHT?w|vA ȴHW)*ysVf ! +\EoE58V!B`oVt A9ix^ v0(>HW*G),rln R@nBT:(_-k#t%PX:ȷ"uLaGB*l\JGeݠ\!m>A|w2Ht mBH@?#hms0z8٠x;ɷB{^',[-ֶ}~뵬AZ?7!NR/ rh*]fGY#;ƺX)$+@ . Zzm9&U> UoJ' lBF 𬰇R]C8n2qXr4>ڌw7>ڌ?݃'iB7귩]Y"$`W|]U: H%({֨oyJT vB_qv춙\m3J9=h/[B.t[P.LmBȈGY GěGz^Qs2`e+4u5#NcC{vә}Yũ/zG& }a[pݾnSv`Nj>Eg/mln RX1Ŧ6K!d^2t¡:/9x:L6(MX6@gWǂ5ː W^^ҲZk^Yoiֲ4+F%"ܲ˶,Zru)Y4BOX&XŚ|@jV}nlq㾑-P6K!dȣ,&R?W:S _IXx1>|K]<|]r~7k^|?6,!G?/^ooWzWqZ; TLsΝL8),o9ްm,!&#qBddVp d6K!d蠄q‹TlB!^㞑shB!BFBThez&B! -Bi:BB!K`_1!7Z !B(F46ЅC !B/R˥UXB!BF=Ҹ7+l,СV B![Ǘ,gB!B(" !B(G@K9B!Q~0Mf*hB!2ꑈ{7 !B-i:vB!d#,[X-"BRw`!ɍ\6E >`DZr.!CÇSNÇ(R4)I@[2vBޙ! kpX؅ܩRIB!Q"ZJ?IB!2 ,By`~ZώgB!B&B8pxnBjaLCՄZ7Ke7Ԣvڇh!BHvD~^0E**ݻZёJ!ъ]݃]B!2_!3JRvaWkR5KP@öf_h߰HӨoꂔ]h7k)۱Xct4jk%sjl@] NX:]xwjlh/rZjnZOa:<B! lĴ c?C]hfL5) sW[QW飣av,,M(ގKd2X@ҍNu[\ d;i.ޗdGf -bs mݩ}nfl׹/]MyB!dX7tjEҨͭA väj\c -ہ]#F:eF 5s0: kqHJߎ'DW_TjU ZZ6cQș(_-k#4ִx '@Y1]=P^asՓZL&UUC-B!$X0an}VHUʦcLTA'Q65)`߁nt̨ƕ5);UP :ei,X W _4E"lZ5 hב|`v15vN\'B!ϖ@6@GUPBl y^Ʊr*BKlG6lCK m?KP.~%7nr NkyC4һ*Te[Zж{%gOCY7Dh2E!B5%2v5ݻZy]f2O7b9ix-tPQ Z:=\=Z=aǓmty*4&M/\{^'weq]8-"$Bp&pHvoY jע25ͭA1mq ]@4kDeHXu|nYBe? HiVε ִnk^㲅G˚X` NkA] }'B!6,Gѩ+38[NOϯBE8l/^Çb(jzr4nZf3J㾇k )n^Z!͏~#,^XYWW7˘vY:ju4:f藺B!d`qζ*[dž$sB!$y~F7BB ø㾇B9Vbo6?qBč\SN9~ЄBȈ!٢@\ !RB xaRMЄB!d7('K!BQ{#7!B!SpP?B!Kuc&B!d4B!BF"<:mB!(hxqDD(\̎B!V|?PbϥKhf!tB!BFđ?HhKhyMv&b2y"Ƈ3>2dڟ`?RJcXZ@K `ǹ !}!ׄlMǒ29|0N9>|xBF:҃}",c'_>RJR^ЄB dRuKT!BȨG^E", !BM$hB!D%в o7"N }46Ӈ MA{M݀v)v>HٕX֡FPB!C,;ǫl߀5-ڵؼ}KDF;im^*!mT勰ePҽKuNvx.Q^LB ; ظ:+$jG:rg[sSVt:z4uIg{ z/׾\Tu;^m}ڭv:YFqk+OLt沨~-8٭Iَ Mvl\ڀN^j}n'AyvT[߄.]5"P_keH<'7)C6峏{Aي1~ѥԅUB&Z@޸nB'8xˮ&m݉L&>:ir[C*}+A/ϾKd2X@ҍر47k_r񚱵^޿$ <~-5o¢w]`z޸myzvtcV6.G_ZMqUM twp}`R5sQظ5d2عV[m#,iFK*T !qta氬3pv-LșnGZ+};"s{Wbv\w1ƅxݻЊ-+4~PGB":>|m_ )dp XصtkZ<p gժlƢrs_Vt e9ix^ ڬq1U3g˔rˆ^^asMTB&* mF˪IَLKmsh5b/5@.tI@]]-2m!ev5s DVKt=e0l3m]R≪Uhټ(qr\XqW ձ])媻W(GPefҁpQH|B!Cq(Ц#kl оpe̊,Hc] l&'aӺXkN~hʱub˲b@7P޷cUbZ> BTaeX:[<_卛I^p@]T˦cFGmn@ WVc Tj 'J.}T"Y*-T/[ܶ ] [ts! N-UB@Vᵴ`uNYd7:xp ʮ /҂+QU}ߕ@V!#[yY"V62< N,Ͽm;p}O'aծ##|"Y2Z_vKLIIwf%tHfHd$ەZH E|%9AAމjjyw$~jD~o?tw@jjԠ)Ϙ꣖i55uR#5jњZ$5sMJZu̲n#;-YٓtFInl+qAt|м3FHjӉIX=ٻ'_]8\M{tіE7INԬ{9Vr/nހWS5MI.tWX:J7V:5xs2ް%j<'ts3t5˗\˗r].e0 iQ[2Uqse7B7G=lwH`Hi^ÍU{S33Լ7?f ާ=;5$=VO:-7NI;{+xlanꨵ*nUJg]ayYVtk2Je[c:=Ү;t#rZ߽G1$5=Û%{urjh75ȶk;t[wtخTr)44hzX}ء;E{!:ߨL/X#;̻״W<3?3J~S'^ކOzzzT|H^s:;;5?? ֆO aKoC"zU=r8{wٙ+K{7zzo$q>u}1=n>V:d٧pF5??/uϲ,ɘ{W%^$`{Sڼy3`yqtBnVho"$"XX[*o`@[g @D`c J w >E ,(>@ev۠ o thxh"DD`pl*5S4իW>e@,Kś-ٹ ֢;ʤ2eʛ@Ph nshE*<|t vDBZs|RTpH=\'߅>ݠ649 &p )D@(hK" Wy߂hľڒE x=el4"S8 x ?|6p8M@M"hJЄ+B%E*XA;r]S@g 'U$>&7 $ XC!rS8cЖ;Iςh*oI~FU4@*~#?3,Mq!@ $ Z&tle)vʤXýM̛|?Z KX^ZXzSXEXWx CT(ɉ_D PȄm&Fedd AP@M+` p[]Uݴ@|]8LA7@{х(`o6y @{ۢi y>rdɵ@{,62 о.d[m }  m`+| WM@Qȁ =#Amŏ\qh߈蕉_ Z  r}7%H`u"el~[g;]ce:Pw{ڻ4k!Nz50kdΩ ])JܘgscHCuعv_,'7o>tfLsXI|Vae7t=?X.-/QJ.32Pmec\/WJ%tRejx{+G{x>Mr̍k:̘Y];|RCC=e+v=0{6|?o#e,ޖE#*oöe.Vn eeq3}ɗ1]R*37q˽7|-en]*KQv$;:(qgarO|Y_;P<3ng[/L kQ jud_ֶʧڷoTjzwXT5LQgʭ5u FJլ֔4{Vhl8ȧp,_DPc% IDAT)RWהNuVa' ܫqtJ]}J:Wgn܌&%I˧3֢k:+xFnم;q@c0˪ n۲Oibj+0/ȳz-?JK݃M>[tye<ځFM\Cy~R ;8-ULeYR]Tܖii8՞[<#}ۥt"eMzW"hr͛뛗fO,`1q n.wa N2Lx%EP>5PQ5N<vZh~hݗ6&B Z%BY(s^viiSRi7rwdžOgRm-5ma#I$4r]f%Ni!;T}(=M5R鴺.mOwJ;%:X[TW)pU|_MܮJVj )=rGǚ#j;tv g$ɶuz3/{"1wə9Em^ iNR^{M-5քqtzt~Nlm -&TSE0s#R(5ԬT>vmw9b{nf2Y jOGsReM:3ٶ) D{a%9yyKE'^)* }} A?z^:5$@X1 +>/OGwe6H\J"\RTl-mIYnN+]|î'ݪiiSc%Xzsyv+R nvW*xrӥNȷ 4N+kq.ۍt>tg$RJ 9!}䆆zԞv.?F<;Z^ʣ6:öed>E3HaJgvD"dkXm+^'l|k+.YG4RC9:XeEڨtD.@cZe*ݟ.A!ۍc\Vm.IzZɶ+jWy2y0}bҒ[4uHZRz '>e~ӿН o%?~Bɇ5SKj~عvR߅ݹ;б6-tkpy h}n;^#y} 挐=mY{=+wvXIlPww<h~ce[|7J>8yXd` -gY:64PŜv*j7ծvRXUn gD9Px#!4P7&b b ʑA @ Зs{h -@UoZd,˗Z$ Z$,5@K#V; X玿* ˎrBYJ8ҫ͛7k~~~SэcoTV +TWy@ @ @ (E*c̘εwk`֬vR3va0n?1/Gnj?78$3vNT*i?7RnG-lv_JRKrsO|9s5wgDJ5hxtN`3kij-ٙN;'Ӄ7lYr=Ч˰\n$)qTŘ1߱CNXxZ7ZK0fV'UU ã͞Iuwh\츰E9͟횱sjЬ1ٖmgvYjؼiOM9s:Osu)N: Zry =0[Nlp8_v %@mRryMqiN˪]g_ˣ\nXլ}]쿖?Glm'{~9DbH'wwag3s40НVi<= c,?o˳UJoRF564WWxjOtZ7N+MsQK[R3sɇ5T6TE;Oj˙kR'߉N7W(s3ƘgzܙĘ1?)q#V:}jtv@'{vbj S>~72J)6*Kl5fnT)6j;tbj.y.baKH5uj֨sx 6%[rߧ>|NunAwL<-<|sRVp^qCԻ|+tz0;q6VgF]谔 xZSJx+aJ{w8+9>YI5-m)vGȘY91cI'L_{RO% 3gXh\ٕN7j;.jXsyZV ]TGmԩd5-jKzש!3ty"QPJoۨ{l*m4|f냻[ڻ9Ilڣd~Y N8cء)c,V}#8&&+u9-mRSԄ5ujѬh$ݠ־z%{GthZ8w*?F)zs3ԚjYkPGEf4מAe޷C v.R%kWϐsf=xKl\jӍ :㤺C.mU^J%=ʹ|x_&6w&A%(lS_miRm-բ:s8[9_ g%) %pDV^iq7`M) ],ʲj&]ОnXϠy}LWMdJ C'mO0cYڽ7(Yqzn1fVZ&rr5s]'M۩f ) -ǹ2}z%r&;j*,T+ZC87z`@SR'5x jh7g5ȶ|;5cRKLGUkY*kn:ڧەf%*s/-ԕt|LGڵs̶uzpwt:n߹ә]s3eCX>ay>}wXzUĶ*/&Zd5`@Rn VcC:}]T@叹\ni^muƴMIm^!6 ʔ| ūv& X?"o" Gn"J(z]k`M -@ @˲c 4P򽅐p"b BX]9$h%9@ @ @lWo?hh h @h o"M $ &b b Jx7Ph h h h h w@1@1@8A444444444444444444444444444444CBjX#hb btJ@D@ P@ӍM  4444444C>)@I@1@1h#C7>@('6N qgh :HM@ do4DEO0@v6<%l>"hcْj@lFby|N6| ,㉠' ͛uȡN 6 _ho*%9-jfL?`dhvk6h`u_}{ 3PE*2< Q"m (aD D&BOl>W$EcG &9tvE*@_/-8=NA ћw&B{[jpߨ3@:T#yԽ)|D ܡ}{V; @yJ0 P,լ86xmW)83~NgXψ)A0A.ӅDPg` FE>Bk uOmmJ?'u֏hBhD۔r4up"Pg`N|7ZxΌ|7t/Lv` @{g B^$kJ5֙枿P=DRj' p" weW4j+3uG:=Rח׳|myƌLzoi,`9L}_NwzYnnP}m]l7j3vWu9m|>i۟_T{(kB].4(m^ed_.??peztvu7=>e#c$=3x9u|ǒz42&H 4y#c_ ՘o3uiݚzV{*2$m@}u &FE:W˸dgόRQ?]iדa"m]oiJ'J\?#J?>CzO-r.>S/Iyc~ ~@<>Ό|]͞=cl'ma|W&F:Z_Z =~4-06j3Lݡ@Kz\GtoDOlwn2K]dZ`ӹV]8z폮=v3:q)M3z1m]]gkZPa= ۬Oǘsx`A_5l}p_cvz%=a۲S}V[֤z֘)}hTʙ~4cka{:s}5|S7nֻDŽl{3^QvȨzs?wcaӽsf>^|ίiץoMs3ߕ94ã:}>ءy5W{/gMՙ ߕnmQJoKv'dhdY0;mna=YUO}31]:ՓRKrԵQY#iҗEZ:=M.Z=]uR#IJ'}ijkQO:!CMLM j\j{s2vZR]3uWzP_lBvm] L_l=rtLoomnDB۝29??uy!c'H\׭ۥj}Rqۓjy-#oYۻSu!W}_4SCI6<{`ۚ^wu͗kkEatlyYrR09CQ;l[ >ldg1dfпMD]Lڙ{<ҽ[ׅxy-4<5=I:rm{Nkԇ&h3FJ$տ=LNMkaKD^mΪ3R kZȖwo_|hdYx,.d *ˑrcMx=y4溺{ֈeoC/X<Y-]'c^jt[R;c +ctsojBKm'F>70-d٩]|]0X>?~C~_S i~uvvj~~~jLNƆӇw:t?kzSESgJsT3JOHauXi:;;uWu!~^gϞo?BhTKo]׿ګam=n2Ձ:tV3.FFpX&wQ3 j֓T%_{ N8pPg`Uިup"*z)Pg` FEhM@o Pg`!A '`MAxku֗j'XK4J@[tJЖх8Ж(tbsq7W.j*@uvv.w:5!0.=??/[>͛7C+Յc~~~ <!@$Z6In%YF2`юX/|7CPYPS8,YLshߞN֑Wa)E5;6}2(kP=L#4PM  TO %22&@I>}X5]8B Ve G@h'@iKJ@&B`6%AYA ,X)5!%}5P7vaPz ,B^$`@ a!hW4^6y{n늤ON>@롬1fZo-w2}=[oɘiz^o?U]{N[Cʉy~m:#ܞWzZWxg",k?1ywҶk0|Y==ٺmI2sPւթQp3[%33cҟiiumԸ$56hLH~IeFb+qX "];[6*i[ 5tDBSg6]OJևuKfثȶw)K2[ VJs=[e-̣{R6RAmןĔ22DB5k!cSJu=qM5҉׏jg[hۚBfJokѣ{RTćn)QF S^}IOضy{ߏHF=wkLwtwky(zj2FF/i%ׄu=GlijdPzP_nBO*[OצÇ=\wu]->p;ό]4.gz(ka۵-dTF ׶&51]遯"ʈ;Oh![{6l5eM[Z@Nltxw,YưbXeuu99ҳGܼ#q߮~tSD [ؒݟsvZ%6lKn}7y.CY W MNhzFTZP4iiRRC3FsQedSeZDM\p>4CVA㮃sc>ZwtkjF [%NKZ.dfu7-d٩r*EY%'5v{R;a[ʨYM;jɹUF~-hcJO Q ,jc\OY;I$P[{kS2~]i;rX|nm|-dn`8\|\~eW]וP[jĭr[2-$r?),(@Ó?=׵蛿U:I9zrHy0Zȸ62[;n;aswXe-EvQӓRm͖~AAMn;߾BƒlJI=!>/БkĘu+a%?)D{2dݯz܉N#&|-=ӹ{u䍫~a5;gw%%9Ïh`1BY@+!JCZr`P`{b'&^*BPBY%Ĕ X!57, B V e /E@<5X)5EDW\5X)5<Xd ՃoJ@uI|KD Є@i7]GWY3o3$IVQ̀ɢG_]n__ ~1%![1CR'&KwGفZ + Z+ZǪ-W*ʓRg2ΜϮWs*,HeN$x' ہ VǵrCktGZ4?m1~u\ae-y 9sDY23d9N6JͰz˩@7ǽ YaWfk :r mOSzk嘼 Je_*ܔAj6`Ks{ZՉ[O~^ѴV=EH%Fͽl,]bE~2D+8 [<7d@9Z+gR7xge;/|յe,.RzD.5,rni^ ^q0o@ӸݬmٖuUд+ xRq8-U+ʼxq+I?_tq}b[~Y~9 XzcqKN,ZA_-o':.Sep`-4z֥l,oXF`"Zd,nfWx:[V8V`0E_«LEU(E%h5-Gy)ulo]|[ZOlS]]Yf~~^?5;;?K`imٲE˿}cqbM1cIENDB`filius-2.9.4+ds/src/info/img/staticrouting1.png000066400000000000000000001537411506574241500214330ustar00rootroot00000000000000PNG  IHDRo<VLsBIT|d IDATxyu{g羓E $!]\EXw~qUVeec9~ⲋD G BB1=3wUJ\Lz>OUt_oAUU˺uFLoj1cj<5^`,N?v_d߾}RɄ8"oMMMr7wC/W.Lb455QSSCmm-cGlbbbbbbb2~)>8!+́!yw׳|?T*Ecc#7n$)uSmTySkX,\commmuf%tE܍7xZm25EQF>$@ `Q|9'Yf _̑dUMoϛh!ZM6m6mޛKJV.Dti^yyIڇ̙3Yz53gΜ+cr2455/~3$??;o+;|;qhii!Lhd{E]Dqqm$1x>n9!ޞ}Y}]~eZl2 ʤNL4m7#G h[Z_5mm*'3뒔;EE(ˀ)V0Ϳp83g~B>|1?SGqg'tLM2`;x {%L늋YhшS\\f;M711{+'XAER&E"prڈJf[:e涩F XqxڦE䴟v+tjXr%˗/+er|4E9,tvvf^l۶X,61jkkY`6mh@ @QQ(011- +? `!D+ElC2,},wLrD\v4NU'Q CyE> kL7ٰa?ׯQ絍E<溜nᄟdjMcc#,tFپ};ݣ ll63f̠>'662sέ^޽z)_En Ŋ `jhEXA"HjbD !mBVzUjc)SSıRI1C4l2.r\.d_4MMM<WY*~3gNȔ9NBcc̞=x<ѣGTWWsw","2,r+T}`Xbr1gjkk|를{2bbb29}–%FG2?è0j9FQ}x5ڇԢbhEXyXLTȚ&e6MKJJ6]ĉ6mh;&IgKN Hr,\/~|%}ÿ뿲aÆaʨ .HpaqUUwB=r4mmmF oqLww7 سgƺgfH4~$QWWcAbbbrrVcX&6 %'ZLM+^@ : A8뱿Q䬪DNT-{J9S k9n,\[|W^5\3W%CX>}:vNioo=gbzO`)(y K^ٿ?`pPJR9r?m'fc֬YL>}z<M6aYG? ˤ;-ň h͉8ц*"MBmi jCHՎjkOrh%t5%Z*i?EItbqHg#[MǏc~a"qSUi˛qiii1 fmZlB9z(Fk$UUID#JqA= ͛ٿI#b^1Sws' vECC-2*Nٹs'@ 411|j҂1T5*hRAhQ6oz&5~(f"q8~i5 &3ZUrd|,uݻwLl#Ʋ9700u(,,d޼yâtvvI]]ݘ.MLL&gyK 씩 8ՒIJeE52p UL"ȶ]&Z${&jdF cv%h653OM[)U{¿ۿ? Ƚ)P^^>a!FOOWQWW7t,˴ ;Fee%ݻwٯ{,c߾}tuu#eG<===TVV2w\$IqCȬY&\cbbr9kBFhV4$ FP%ѬDcx%я@I @5я@kĠ$Qں j*Z:nÍ`s#؜6=\`w!JN|-}zLH$#<‡?a^}UÊb8󩪪:a#2mmmݻ^K>kn===ܹ6f+,^c*6Ɠmpp7ottth1H7HSS_ؿ?x|D{=qFx)zȶ6􆧋b&L&477ח$ _޽;ǧ= qaBШ8=5/Fywinnfƌo[[mmmL63g$&&&SVn }x j.Dqe 4fT͎,EIbGPM)1Ƞ*E3a7TF,ZBaaهpD">{9 ())$ k^79^dY.dY6Ү[^^ʕ+ygO9MF& }v:;;m=FbE'#Zw"顧ZfϞ=hllEw&&&gREQQQӃJADPPT-%-!@JC: i5UΈ4EF37UD" 5Um9M@Uբy]KZ1@ OXyGcZfee儅~Cӭ7ƺl6& tttX\.aU^+WRUUeV"$[lamiiɱ8E^k"O$jjj>}a8N3}jb29+8ĕFW)iTU؅XQmQɑI}ʠ(SeC*(JF 0S%T&T2* R,WѬ̔|;vFO/"hNv E˙5k$x}n3mob29+ś2mK*ZLNf*UN*2`ԣf ZL_"#2WL,aEӴi͂P5(J#qVX,&STTDQQф588Hww0!3BO*ϗҏmZq|iK)sϛ}Ì :3vb9NetdE߉'??iӦ ̝ߓ11193V![n`@N LA,u1W-TXk+UN!Ii*Z>z#z5YUJXq#ͱ9kw.Hp8Dn.TFY} ]<f͚PA,tvv@___qN'#^˹py]!+2w xMxva1u,bXF8Y=߉yȀLŧQ`7Z^!ՑqhN]J5VGf}@-D$G`u H6N2wP̙3׾6ٗ߿XO 8e`0H0DQchoI(//p:ai9Ӊ$IH4sS(!?7U!DUihh`֭F 95GhǩF$4'RХfmbbr9ҦX.>ϳo>JUU }}}x<cFsF(D9S(l6c~H#ϟ_=:Dqq7STjuҫȲW_u]gVN<̙Ν;m~2sL:4̘Lh3t臨g:Vt 0-__wykbbrfMe"hmRUӧ3sL.[ëotP 7 ѥg(3n s`1 T ܢEF(*#ilc f鉣QUχ A5?ɮ墤dBf  B(Vt:=bJ G'H~~> Ȃ z{/F;w.7t'͛ws411xKAx饗Xv-====z-[0|.rfΜi8UU%KصkS Y&)yY2,TE4-g=U Ұ!YhBQDO*P? )ӉM SUUEqq1H(H7т#ub1t$I F׶d/^k_êUp\oאϡC&o}_qq1UUU9FOE|>9_$b{9yٸq#\r gy'xG5V3ϔ ep8l r:477͛7cx< l~sANOr՗ow~o@^pz^-؜A*Nř IDAT3}Jm͇hͤY3OЋ,#9!+ UTVVNk>mSe~ ja9󨪪B ~f1dΜ9FٝdWq|AbӧO5kZ%k(gfΜ9w2Չlj1cj\9I QUxÁ(PPP@AAo&D뮻T*Ń>WUy嗉bx$ [oմ 11Lx{JR)n7$sN^}UeP(DWWQ%> ql6^B>lٲ z&sy0o "فLFDwDkČWEt [`fڵՆGuj>O( /ⰈbvS[[n7tz*A\yyyZ"n``P(4v6!tVTT_ܹOd…̛Sb47kĂ`-[Ԅ꯸&/gr|Font:ͻktP$2]]]ttt ^pǎNR#?m6. כѶcH?頻bݺu:t/~$Io|:DQD$ zcəeRěD"p8n0k֬ѣ455zq8R):;;6NPHb1(VAQQYt鰔hs>i`G0̃*&bbHEfI蜤VjH`QָYs޴tɤQ2n>O?4x^zi̮EEE#2`b1p\.#<(ovbp8KXx!y1PU_>]AV2,/ٹs'(2sL.r#u*)((f{qu#һjL&5ޓTWWcO$v;^Պje``7x믿Q>1f̘җ(<|SbΜ9QRR!?? M10119Q*X~DQbXغu+;w4m===JPMaa! O[[hEAAD"&̲?VXʕ+ H\2nJ"i!Fi@I bj7A18X3䎥TxNV^5\3 @6V;tHn@#L]vnluoN4fQZZjB".;R6^BQa) / P\gÆ{kE?#؍(46e۶ ёsğ 69rAAAH|9UC=Ç(Jxi… ˖-cŊ|>Ce{"m_d[XA(ѷLT]AQs($7A."&Gw@FzR8 {*2RtGEý},tSDسgD-[H$r[C˹{^~I9&"4HE۴iӸTU?C;t χ>t!m;tuup8 O؎Aos4NTYIOeYh/w3faa\a9b|Iӿ T(j@ p:Nv;#Zxg?O#|%s!6aobb28#-C~~>^T*;ömیP(Dss3xD"AMM R)BMMM$ |>{N4fa@e<dH$B"@UUYb+V0vCyüra(e|ᘹQqR蕩ҧDk+lF8CпGZ8aiR]cE< O~?Dصk(##@ou͖#܆: ym]<+r Gz^C Pāy:t'> nIgDH_LFopLR9}IGzhrvGQ?(t q72c hkk&~3-Lh4jyyyNtp4a /ś(lܸd2>1$-B ѣTUUx())1gMLL>x[n\rɈ;Mt֭[bx$&֬Ymmm|>477cِ$~㴶F݄B!ɤzss3}}}l6~?h4j|:Uv;˗/!oh Dm۶qWh {:^Msoh4yiM> , wvq=#dY&JJK.a޼O0/߾u7oGI~t;o}C'u~WmmmFgu%{o]y\s5l60Dq!:Ͼ. nt:OS>OS^^֭[I$\uUH^^uuuƗ8WrTySk<%N BR)6d2+ݻ~:;;eMuu5HOOj4OZ<G$p8vBv Ph4J*"??mDov΅^W\AEEEN=|}Ҭ [nMɈ9B7X-˗/sܩ87H&KAwnєlcӱ^"hA<+Wr46̖-[F~orN53Ncc#pt: /0888fU\.7x#sAeˉĝJ1t:ZH4HKHCC[no6_$+**N2L{<`8טL+"{?1ʹiOox˅bۘFh4JSS#zv<,388hGkppUU x^(pFnʡCpcZ ߹.bz -uƋjb5[@u v.rN@bٸhllj˾ {fāXv-v"N}̟?P^1b\xlݺuTǵe~ÇS[[pt:hH^#UNE[ymE-++bPQQ sMLLNSy[)fj"X~Pf->jkkqX,BXVJJJ(--%H܌rnwvvRYY# N'T6Br8we``A#2{l>OS__oDŦ-{U,*NhM,#2""jϿ"QӜ4'|YbtttsF"Ę7t:m5TTTpu7Ҽ<7] ˜׶|r_ٳgO2 YٷoF|͛7iG-.2.bNFqR GTUEE\.WNm2ț~Ν;p&&&Soz}j5 ˶mxW<ݶ#J}loo/HI JJ$R)Ô7//-UH$FX,nd2IKK 8 EX,FOOqWUFQQWfɒ%9ߤAڝ2ȜePzzzzl6ܸsRPP6mmF\.555ȲL__a)**vN0)mۆj԰z?o:KwD0 l"IT9&'ޥ˗OU2>Oxؽ{7WvM_訩].d0{lIGG/6mCQ3gΜI"&'b!P]]ҥK9z(===Ǎ% K$HMEQϤo艦MB[ww ;Xj455`8zG"Z[[bPPP`̇ DQ4XOO1C7rMfA:U>}SCH\.X.m۱lB^8ť!w^HՉbP zߧR=˙9sD.87ofcƜEF-c 7UU/mpp?ϼ;R).b~qUWn&gEEE8Nen0NÇ_`<@oo/{pGɞۦA2x+,,dٲeכ&& FߓlGgh 碋.[r $ xhjjgϞ=R]]ٳ|9r;vF)//b,otj8r,Y<9t`*멩pU~ǃnG$N'n~invC>}:_O{Km!R-# 擵mgmr ӟ馛3Rt%IpI&lܸ_򗴷S]]կ:zmQ^^Β%KzՊ+; 7T*ƍy7sҎ6տ8 l6\uU#v8^lj1cj\9at1Ȯo^17_7JާhEQx<p8hooG$GX***ϧ$`hiioEӴѨ&NiX}^7l⋹ )++˹744ҟ Y:ٌ~?)ǝ6brp뭷rwL陜&R{aF,lٲ^x1tzkZ|viӦQRR2fq;ޜٳgdɒckbbrvrR⭵Fn8rhBn˖-Y&#%O.+Y󩨨0݂QJyQr~?GX,b|FZ~n7tvv""ۋEUU|pve]W_m)GrQ>OSVVvj^5 yf}Y^~eBq>ٸI^wu<.֯_O__Q000{80,r׶|o4륢˜1o.|11qR>oXh2>ӉUUUXRzzzbJ4jRPP@<vRJ&t:z$IhƭW}^\]]NP(D{{;X yyy8N0d25>t:R[[ {yUUN>'zMN\uU|K_bI(5uD",Z39wqL>t:MGG1mΜ9TVVD24gM߸)}^elԾjGEEn6Oxsvڵkŋ3119w8[:nTUe;( l޼5kg7H$ 9A:fQYY㡣P(@ `ڴiF;n7zvttBII Dv#N KP(dXe j*C^bpyyy\|'bywywy7B;(¼y?ϗ$oASSS͛ywIRgY2;"r< p+i}w?.Y袋̴yK$ttte*FgvP(D,jۼy3/RN:j bȲLAAX,۱Z|>$tIZƼp8ѣGn3k,~N:UE+65EmDٳ 'L w桇BQ$ɘ ǎ;hii1Rv폫\~T<x++++Iر{wf͚ŽKii>&&&gSaAwFI7)64 ?_H$B}}=^{-s]ąa KNww7X, `0H04*ˣYXSSat:$JIgg'PTTjtR/^lb _.L"2555\X,c=+,̚5/}KA6nСCDQnO߭Ixs\ve\x#V|>?f:drUH6]p8 []6)ȑ#<+Fuݻw믿SftRp 7PYYikK$6?طow醴*f̘Aaa!---ۄa*++),,nGQ iӦǜ9s曹袋EQp:^PRR\@CCÄT6cϹ|ׯo楗^fq뭷c1o<>կ~+VR,\s4;/pYp!_sбVWW㏳xb0v;wL" L,NZ|#xuuuHD" UU$蓪7.**"Jt:Yx1GUUC\z<^1MzO4119lݺ}{a<_׹KX,ڵkٰaD"A45cpUWyW׿ΪU&|,)o:z!ѹvcۍuֱ~zÊCBM38_u]7bӦMF$NEcncpp<aߏ墮jjjXhNӈYV^R_p82}d2??,\zz#v*6mbƍ8p 9ύ~x[l'G;|I~in6ړ>ഉ7Тpz Uou>^{5# L&IRF.$IsWp饗t:sҰSqP(jh4 hbgXi'(--EQc.ftb۱Ziü%{¿>mժU/hoo!䲣qr-NO?4O>$_Sz|ᴊ7d2I("D㈢5{n6l@cc1GDK덦C[ldIaCe;,tuuH$<.2/_b1"Ѳp`p8X}LLLk׮塇"Q]]wywZ;wBss3s}c=,f ገ7X,ftNvHgN\II ԧX`TH$B"T*ff|t .n: .`\}~Dɾ6'{MDC38Fz~]&Jww7wwছns)INUSs+WLSv\.TVVRRRBII $. FIR?=)'2˗/ooz\}y̟?H$Bgg'`&$.Gng/X۲dqGz>dsiGydصkH96]8vc7XFtNf޽{?ݻ)((Gofƍ:vM7b @Fݺu뤎tmj1cj\9Ie7 H,FR Gģp('I"Tʘk?gyy9œyyNj`Q6z401Hk'|Dx0m;X;^djѷkr޻GGu}ft_~N:6:+gW9IU7W'кu,7];NL]:&؀!0 K4g?HHgY3{wfk;\6l HȆ (--͵Y7m6\.6m$ @0| P$ *++)**B$" XRUNd2xd`0H `ҥpĔޒ^}"170﹧C\|m2r6u6M^}U֯_O"[oeӦMp#;$csm@ 8K*ett:)--%  HR Dadd*a֍$ UU t:=fzt]pI$InzYd Ź|rXم%3|ez~gTTϞ+Ι>lO/p#\mScyشi%!\662eܹPRR@ `57KDQKb1bx*[:td2fꫯo,Ide|B%4 cEx̖g6] D'x_~EQxGYve-YnUUUaÆQA~Wp\\.խ!FFVRC$ʲfա,>[âEXt)6-3RÒO}i67||Λw>1LFyq\<X"f lذGy;wpB/,@0 ^ȲlMRD"]35ziiiAuu5re2 HHl;|f%\7Iss37rWȹg=Stz֭[?͛;<KNJL56={(,,yQ__ܹs;w.x3 I;3իWnCjdcO=pf?HDLxBdЉl,#23|k29'Ɖ7믿ڵkinnM6Ym&od=/gYv-`dsX#_\*yDuXĨ:o###\}Vܜ$o~nظq{ǛD%SL*3glzW_} 6oLQQѴft:HdŸ_룡$,\'Z<3?twws|I6lp'vJ^f$fߢ" /\|>}Qhll䩧jVseEQxx<8p_|1&  ś@ Ɂ`V[.]e2+cchc=x-20v;O<vm۶cǎv3o\*J˃'|;vPQQSO=EMMMM$yyx<'?<X6[laǎ\.|I!f￟KY~}x ;vWyq\۷7x#fytJ&Ǟ\?v9>|{[z=ڤˆ{Nr̬yˊ9cs'sl!&n_җpw},Yp8,O<`V۹a4p;s B O4eݺuanVz\tav_ر;w$fJL%Jɝ?mt8W{ts92oy==7ZExqE(cS/|M6*{1b7{<˞ 67SNtm<;xټy3lڴIdh4ڵkikkgڵ6I ,٦l &\\۷-[Ffnr#+Ғc˓ ^*d}C%h6qF4Mcڵ,_<& 2,_+Wi4-& Tql/doArE4֯_cʕw}6I0|+\.8;3ksˎ.N&E(MdBq`r~sjjj7(6I0֬YO?=*yA >@ ڬ "Daz!~?<kˆY+r>cfG~#_(.͛7SUUk3ॗ^T`Psm@ 4M6i<nZm۶+/})& ;wbٰlJii) .dѢEl\8)"M \PyYd wuW0P'v]rM{{;//c>s6Q*D :۶mcƍ԰yf\.WM2ƍYlYMxg{uAZC5tx 2ƴS>X CCCtT@ U([l*B](իټy3[nMb1HcGsِu{FephzZ#%ɛ姿~n\}+ Յfcҥ\ydB Y_suױjժ\#Y^x={m- ]ݠMCҳ=o4 =x{]KY^`\`pC5o6wMMM8Ynݺuvv@pYwC"'"& . v>,A.$|&h/@R홛TXemWHخڐ6$F_`uq;FIKJqQ{=bsΝ"T fiUV )WFQmF8ε9˜6e%ș SU*2F݊3 .Av܅HB:#|7ӛٱcw/I$yeS@0+}vE _B䈆-[ƾ}x7rm2NIO~:MĸNfm3IZ]KhL,\ ܞJ0L6U$믿ΫM7ի)++;y&f-[ik֬1r>9fYCzzz86\\QleyK[1mhݬmцf 6JMR%SI"Zt-bo={gVXq"Nyű|G#칔ž={e~8XٓX|9y/B=F}>%$*˦NoRis Ylܬ%B$W!Ë4EF8Ufwq0R݃E2{yiΛ@ q֭[Ν;xGsm ؼy3?&9㥗^⣏>?_"Ͳ it=)3ˣi \'36R6c ӵDf[jx… Yz5 .\x3Jss3k׮^hxx饗6Ip~zFFF\5$ d I,5#TKe[XMBMOMO%A7ˆ$28;}oiQcz*#R *K ԺRձj*n)"buV4MpXsuqv]wݕk~!EH Ȋ8EER貆ds AVrB҈uˈ343.Aqwsn*ě @k=I6,#*˪!d]RlNPuCe`ӳGU=@2=mᆚtj)@O! %xoŇAQ냖[b~;n{|x3 /@"{*+WSOLKKB\0ZZZJxIGMd$dl6dXBUTUHA!eIv'z*i 63mMKY.pvc,%N"nBo%)?Ǝ;Xj('ě@ 0;wu( +Wd۶m;B .ǏGu dnyS&++!d5#l I*j+#NdzJ Dpjxt-'J)@R);z*s4b^{5Q*dV1RM[ozX^8۱|'qw_;wϱ4o\&ЈG z4@:  ۢ ht4d>'cv9Hv7Í<2ʏ\7Q*d1R4 sV {r1o\,(--h4ʞ={f|7{.yK!xbQ#qYH=h=>L:>L:6lKG A'™!#K'"d\FĥHiݙ}1&J8>X,vƘ$E^/kP4/ؼy(*8k^y6mĪUx'rm68h#Id$, ېTd&'ٍ{aH(bs NP}YDRH.> IDATj!gBdz.>*k"bM8u`ido B!JJJ())aϞ=|>,Y"Xl; J$=5N iz:IF웤uYIYIRHfsҙit]בHi*q:zF98<]H\2!˖A|>tz\:8z(pb@pHss3 v qH-)t]˔ QWiiC%ӐH3B :H!t@tqtn:EO#Ae)IB4i?p)&\vaH$gD"ÇfTVVZcjƊ+3g7x]p|ry뭷x(6J0=mxϴ8RFnܛ^4膓_$#2,aN{ӌMpIXTl3~ʚ 1Dv+op;~8&HoRYY_Oqq$K,R[57t?9pr3GRWFe>t2s!i)Z I${]7|odtn[ct2K,*eGBeR$X72D4鱶e 7Fgۙ?>^{-v}ԁTTT/`Yd tttp.K0#i $%KI$E,`vBHgFMq&)3tI2˦)k'+VL6MASH!$I1&ߤ2Oqxf( lٲeع\$ bR)t]'N#2ǎwc %]0_e&H YtBPl,:2mH6މdG ˶6|fC*+DIY}/Wʵ^{\ŕ.\Bb1:::?cL$9td>$Igl&~N<ɒ%K(//uI  PRR¼yp835%ҥK)-- OiiyecK;wG'-a~hmt7op]vk."Kkk+T*/_͞e0elٓj,zQrFD"TUtRQQwM74anY Q乞L#%T(-IN@V2[l jf5'vȫ,\o|6B y}Ѩ8EΉ'&<|bfz?FCC VRxh"JKKu^oPQQ1iQ&?uh40ze}222i C2$NSPP@YY$1<~p)N8AQQnH$Bgg'pY㔔 I>ݎ[*BeQUd2U^PTTĊ+(**iqݜ?F)Y޷Li˧¿,goLX xV<Ȩ$144Dkk+CCC.,Q]` .\kp6TelgXPJJJz$I>ߏ(hgg'Xx<μy(--b:::Y SNb``A$np8h!H NII vv3|NxV>Hk d>3qX@̈83,-"ɪQ/*1l76~{o}kL7 $ {=ZZZJRtuu5*(vl6͛g}NxgѢEӲGDِJ"Lt:$ o-o0VݶzJJJPuJ gzl6@Ӊ`hhAqq1### DH&PPP`y̥|Án;f Z8 !Tl5j95 YJd$oscenfzI_;Q*d1g׮]'ǎUü{8qbT鏳)qMUnlof\SWzG-!ojzȗƃ $I\.pA}nžE"4nIhiiɓ;vAkrUWȒ%KuχfǃD$^/EEEV}@ P]] o~z6mġC:0byWx++OpH!dGq*-F;3 C< g:*ޱMoII ,':VUUʨ~}+¼ym Άh4'NcQUIغu+ճ,1|7SBUU̙CUUxN<n^ $~jkkZp 2srL&ahhYq\8N#fgE?#-/)N:Ş2zjA]~Ik5Hkb"5M w棏>:c{H,?#Ko}'6cf9tܹs'`Wx)+"5t= Z6~/S5M ---B!K2b*w!&oc?UU 4Vԧ>%[!،Ft:m+&o}}v:::uJ2 av; 2@χ2w\^PXXh 13)L~Dx7i;,k"ě@#@|gĉ.Nv{>bSTT4唕a,H$;9s26`Oĝ)hE$BNt:餶$IJKKihhv hkkCuEve N+ V$p8qzzz(Baa!.*ub.vq:455|QI6Ǐgx7 ؔyMu֭wL L\ZTT墧gT،:EQQãjx=f2&p`,Xn|懬{Pe6l@CCȲo͊+2‘H$ؽ{7{!z͸Ç[yg{Mu6* 466Ny餼|T+.hX7S'Mm555vmݍr*th4J2DUUl6tEaΜ9q%P__ς ltww * )))'n .znT1</fʕx<N~?2ZeYp-B<\6WPSSC ߞ $ID"N8a]*++( EEEVlP|I^|E֭[(|{[nᓟ$Ǐ?6m˦QFIzN0vyu޽TU㱚קR)\.uuu000QYYImm-v~Rvh4J"@޻uuu % "28+歷z>ǃi~4Mf*;O}SȲ̭:k*Ma._/p8x̛7KMģ*(b}PdLt*Eyn7ny=-[FUUtwwpB/^3<ç?ijjj줣J,^@z{{}xᠦ l6M\cuH$Ȉ`WOǿ zmuuuZۭx^bpX,r|JII D>%>K^s5RTT, X޳R`JJJpp7cٰlVo־>kѢEVdeS G˦MMMKE/J裏c/RM8u5gTeTev;nm/~T*ſ˿ձk.N8ٟY٦_hhh@Ql6eee>(cc ,f'SXXhq|~Jex z-BYŋq޽{ٶm`at]ÁcxxY)--~~U[Bk\6谊\.jkkI&O:ª=wM7lٲQ?zđH$4yYȨ 9"$I:tW^y&n&$I"Z3$ Et=#,\pga"O餰Á, sUWQWWݻ)//gΜ9o|r,Xk;w.K,Nqq1բAp8Nznn7@ <|c?js?޾f\/2g}e˖x,lx<$TʊQ^FN{{;?dT*Eoo/o6fYY$)))V/$I"N-;bNZUUfNzzȲL,.P(D__-7* IDATt:Yd gihhr:V3 +JS!ě@# -c"`ǎ|Gr-\qb1dz%v;Tx<~V_~n PUU,S.+q!_h.wVJn-œ9s8p$Q[[ܹsٻw/_=Zu&(]p26l(%Ph4j-Gg_$ӟzaA2n߿wyI;wU49[ĵ322vg>$IVSEQ#JqQ Ĝ9sXD"Acc#eee̙35kְl2U,L&4͊m+//g: &d2I<72EܩSx7IR|򓟤bn?YlW\ASS?я[B6jjkkEݲpfIF];rMM .L6JPUՊE***m۶q-/Keʰe{>ϊ7o"N$(f׮]GˌOę?+**(--E>VvEQb>t:MCCEEEx<nnv^e_AAV]))):8kh>ȷ0dG~sÌyҗg?YkxW###_%'Od…466vϨ?5MQhjjbѢEQRRb:4k\-^3j[em.!9)? o~g>}())_oyy9W]uy^]LL_*̛7oY'i\}ո\s6mөVQQ5\Cmm-v!P.GQl5\ݻ?c}I_x?.ƱeYGyy9Vf nSa H$¶mXnʨcŊ|;jmGŰɲ́8t'OF͛G4ȑ#~JKK)**bٲee#HX̖^TUU=U$,9s)R6l?x;3{n#ȴ&|eEQ{YGpE!J??}}+"OYlEbs i ?sz!(w22v[__===\j0| TU%D:zj*??t:M$b\.UUIRgL!ě@c R)fdddeTvyYx1Y3SgZyQ}H+E}[cd;wwy g3k,Z11c[LvOTN&N/eB/^'|FZZZG3ui}}}V**ͳ^ɮs5ȻЂ;wg?K2.Aee%/F[+;6wߵD.3?[B>Os7:hP(Iv;N0&MhiiaxxMhnn[ >gYqU+ssٿjǖG8(  9eI| ɮh>]~b.[qh,x<<ϨcmgyelD\yf^|ELaa!`xˍ7M7Dkk+?;w* &Opa<DuFFF¾@x<.zoEYGmF___룢zquH`zLzSȩ?{oT7ݛM[R(- E6-"^TA/we.1(ʕA@QGf BP辦I=9Ιmr4iy>}@yNsy|>~+qXwR kecR*ďc&Gpj,77qYfH$]Ghh۷{nܹsiy_tJB!}Q:U܂{3l3&H Jɓ'c̙(..FFF,YiӦ elPŔӘFqFyyf:T*EZZS/** Nwߎ31Ĵ9h4BP@.Ν;qt:0|$$$u%7!HQ*L[LCҬ&nc /#F`ĉ~ʹ" .eHLn<;j*( L0eee>WhZrt:h4h4&6~_BSel?Z-.]ĵ#++ky(oB?ߠ+JÇQUU m2 )x$#.""ReP7[BB~a$''!`/cpl*lix<26>|jt V._RT*̜9+W< Z͔0ur6_r颺.]Bss33AK7!( G&=Æ /DWWl9oCxZ!6J+b -,, &MbZXƆVb4?MŋX~= -Zŋo1jsFtL3"""P]]{1ׇ~ءv!hZ8pdꀥcܹ?~<Ο?< ϟ?k׮h42"Ak~# ߫0,44&SmxGtCXn}.dVJ^``,r=|ݽ{ 1crss\Vw DT*߿`J&`…:u͹7oDee%:::\.˗Q__XD".m5vs~fΞsWyKNNO<8C<+WJ¼ylٲ@NZ |zFb?#TǂMzLRaϞ=Xx1vVl޼۶msPN>#Gbɒ%:u*B!f3BCC1yd<#g]Tj7BBB0c u74ls9r$x-c|^&&&ơM{?}NNcJJJBV:V2** )))!C ɐ<[\WXBǴܹq磤nϣpY:uM2LP(hlldZYUAw-oxꩧ(o elЀ+V@&a̙XjcDy#ŋm6pBL0M/^ĉ'Ht{V `MyKMM3< {% |EA(b޼y?>r9={UUU1^X8kL |vZPillDEET*$ ֬Y &j l֯_Jǣi@B78vv܉6ԩSbŊ>utt05Κ 3ɓ(++Cyy9rrr-Ҁ XCѷk;v /Fee%b1-[;v0[ /;#orށט/.cr0l0l۶ IIIqJJJpe`={`Æ ( ˗/wP܂Iց~cQ^^$$塴~%ZFR/_P(ĪU0c }0P(xb(uO@!'ODyy9d2ꫯb$0 26Ea۶m8p`Xd -: ֬YW7ndu?~<ҥKhsw~y1;yMVތF#s H$BHHO+shZ믿jݺu$`@#|7غu+Z-&ORR mۆ}A*b۶mxg c{OcO7PJ=*fzF!!!䏑jjjPVVx<'6 b(..)cC'0XqƄ |^fƌH$(--ŹsrJlذQQQ>[cqI8pb^+neZܝyຜuB;::\azVZVG x˱cǰrJ >>۶müyF ɓ_6l37n`ʕ عs'mj2ֆV@bbdd"A]}a͚5XhQP()D!"P(DYYrrrֆuAZ>A2+V-Hf6!fFԆP7b߾}̍{̙:͟EQSS4bر0aBP;}^*DTm} ĩT*X(++_plݺqW ĭ[T٦2l}oplܸ񨬬DEE׼ݹsFxxxȂ} 6y,Mբ&'i:wu:V D C* PYY{ y%1o_џٸ˝;wb  Z ^lP˗Ν;Xl͛hVmj2ԄlZZl8?BQJKKmƍAZrV:B`^q{:l;'l<3yrk.رQQQرcǀ̴ajkM7;by> ?;* z>ТyfQS!WX+V6Z-Zɓ'CPl$0TTT@bDq R|iZܿHQT*t:)2ظq#k▝h~`)gCȻz*ohqŋ8y$1@CpO7шzU6w4MT: )//7|plذX1Xx$b˟#F`׮].x"8X 6 **w@ <k֬P(ľ}p֭@;v(1r8b„ =z4BCCQ[[K"77AknnfZܠ… tד ư;w֭[X|9b1o4r'&3>XD"_LtilEpŘ5klmSNENNK feeaڴi˅\.qI߿wD ٧gfJ>sDqp(gF6@P(~ѣG̙3p8rr Eaa!ƍ礕,Flnns5 w B,ŋA 3gN!+MV;DAXX8L&S@aϞ=3f 6n6l0L<+w6r8?9G DŋvvAP"T;R~m  (HuBl^Μ9ٳg_RQ0i$z,Ξ=kT`ĉ=&$d2p:::mX,رc6,_cƌ!J@<cdqT*L<=^ ĬY>(R=ihh@CCL&ӱzjV >%%%t7| 6 //}.,T`Ϟ=شi4 yFp7obܹXRRRPXXJq?@8uZcC!++2#C :qw^o.@ 0dffEy?H7,odd$x {4dBWW_-g޾XzFTbcG,22CeUd2 MMM+>"٬{ NNNƨQH\@kێ;$ƍXb'x&_}._z =?!۷oǖ-[ R[ AVV"##Yֆzz=+|>RɬR(>Jd2lg233Q\\#G`޽Xfӟ& SjRJ%n݊~ǏIѣCQq-^[OvCBB#FRqU466;&%%o~?XC\ } 6 <_5jjj``O>d^%,w߅^IXO`>&0g$''~{9fX  1dMMM0 ̜xHIIamBmmCVtDD . >`&@ 8'ODqq_o'ODii)R)vI< x233QVVFܟ[naXz5GZZFJqST?Z~p8$&&"??g`0ݻVe Dnn. @`[[[?u`Μ9Dq@x󖑑M6a˖-? NCSSVa2  !DV*Jeee8tG+dbb"`@KK d2[qmUL(zEQhmmEkk+(b6HHH@||ySRRPTT{&@ CNN&LsXlnkkɓ'0sLK<>IX ;#?GŔ^jq]֢r& MHHEFF"''cƌa)9(//pZjooG{{;Sz'M("--;V&qk"%%"!4""EEEHKK#,\ΝñcǰpBwׯ_ǛoX֜hfΙ3ضmחfܹs.]ƒ@Qt:h42c(lhDWWqyCLLL'̟?/_FJ c5ggg'ZZZ`4yOŷx<$&&1TThiiaڭuHH$ ,BS>Fw!;;8v͛\B7|| /bFNCuu5d2d2>35 -^}Wf{XZZO?4b7{DUUlقO?7oބF``7O_wCkk$Nc80|pVZ 1bV`@]]Ժ)=݌Y|bܼy b0O l[V)))2e !܌K"77vmWB[p:OWUtWopttth4EahZhnnv8?{2&H\6qmFx4Cp 99UTWWC.+m #oP:{QQQ$%/ u#qwlƌG[[Ν;g3~i9k׮EWW͗!C`-|>ӧcذa ŲeːXG;?W>m:yd,_Nϗr _JR!7oz-͸hD^^_ )K,B۷c111U(1כf渫@ @JJ kw\.Gkk+㎥e?1=ttt22&$ġNoG}DJcڵ yyy@mm-~<L&޽{̱t[XpOA?RO_Dܻw/\.? ;;ifHH2331i$ < j5%! lܳ!!!(*z R]r&M TWGn0= .8TVVaaa;w.,|ghjjBUU~򓟠0zhdff2rm;T*q1޽)NfmIw<쳬acyknnۙ-W6;EW^Y?+l63V4}qrg~4 :::jmsmc6hkksH ( |>ߦ.m5+((CqaX0|x6SZP( >? 7Uʕ+( 3gĜ9s4@ N,XǏ;Ľ|ddd 55ռr?t]/25}{9r՘={6 HJJBhh(4lj BQcQX,fX,DEEᠩ RnaZ Ebb"2fd2tvv26>'%%O>$Byز2hdd$nR#EG{9%4 FE9Ē9s 6n܈JT 2#K y_haظq#֮]  ɓ'㩧D"qk^.]Gd2x7^xO?[ϼ08{,Ǝ . ^6t:)-!!JT2 )ֈb(Jƺfmq¬YrPu1tpܽw ~i.Ν$h4:u %K@ Zlڴ ~qłh >6kL&D1bt:߿NtY(qA-o|wr &N'xorrrpqł&d2dgg#-- P*겱8&HXZ2qҊ@ JVuZmĉ(,o=MŰQxhdeAv Ξ=\.Gqq@6m|ӄ`ذab5oKK ޽˔f >!C@*2ЀzشiV^M >}ӦMsz7fiii;v,rrr~(iL{?,o&+RP8E(2Ɩ]\ BtuutڌI$&࿪ Ç9~:Fms>ٳ6a63111ͅP(dZvhMd2A&90LPT66VPP&3aן޽\K7K(g9s& cƌɓQ\\TO>=l}\.HĴ3L.]״ٝ$DSTtb@&AdxRɬR(>j5sX,0L"넅{ غukRUU*"#GĹs J(n-jD`n P*HJJH$bhkKT FRf}-,,Ę1c3gNAbb2@&S2q |%jHR3f?/@ ܹs(--͛7 JYyT*hhhRIIIHMMePW^+,, #F@rr2yd᷿-oߎj_L6_\N>D>gϞ|QQAf8V`llf Uqu&/BZj TZ^% B!;v4_F\ro_|/.@׿5׿UZjllD{{ӎBDFF"33;(4551鵄B!RSSjR!qqqx뭷p;vWHKK v܉Nl9fQF(rĬ.BP()K+oF?m9]:u*&L(36$ ((̬!:qqS[ܽ{\.>,V\)=@ R> 6f:""C a(EلXǷD"dffnގ0kъbbb"233I yBzپ'ٳawWP8rd2Y@[o>k!33aaa3f BCCkM[݀fu \BY[-t=PG}潈?‘_!""S&@5"<,!b1~ 6LÁH$1l0V[GG~G4773Jڋ1d=Յ7oƦWtTT{,l@\4*ڵkj߿Jҧ٦7\nS߽MSZիW LC[ގC#a}sCحl7lUdD%qϟA)gXo3PT8֣.\FAtt4֮]ٳg[ϟ9񐔔8VT*455ڎ~|'$$ 99+`0rƂ' J+ں&|`6~[[vv6֭[W\A}}=lÇō_°i&lܸOFvv6Z[[lŽx,n 7U4y<|A!GΈ\|ϟGkk+x<,Y_H _Q*(++áC~񏋋Cbb"kE2̭vǶx󦤤xM`566n?n87 UUUFcc # aaa/|رc$ b1m\q8ªR\*23b(Ə/@[k2e y1. @?)t'SSSY+RhoogJo B*lZHIIH$rH@QQHr`:aĉxg=2 GEEᡇbܶ`05GGG\N%K`ԩ(++Ý;w J)c7NZqQu<}cL& _W1c?M  ]|QEž;;;_(<^c[ZZw,YaaaN{EO0CCn]az>|sA|ղ[PIDAT|<9F:tn5f"#G󵃉lܹ~)}PX R N`un)naaaN{SNAՂbժUxXgThZO||wutQL&DDD`ƌx' ;tuuСC裏@z*d˗/ǚ5k=0Yx1f3SȖ"&&ĩQPk1DJFhlb@(ڔxxG q_=Jc/RIEQcq]g?JV{X+o& qx !??999. C+o5͛+a6Nfaٲeq?Ǘ_~^7ٌt179s`ӦMHKK v sߒdL(1rh4ttt@ڜ犘űݱ0Pq\`8~X,><r(͈OתpP3g28 }7.HtuuTާyjjj@Q $''f}C}A+mx1i$ҷK&MI&a˖-QYY/555.]4cƌ{L6-; [[QQ]fU*C+} Ebb"+lL&c$[a2>RRRO"4-[.CLLwHpy $F6|sKh49-BzzDz|WnS V B0&޲bXl,,QQQFRRcʥ۷`Ns '3 EAA{9)õkp5@gg'\ٌQF+V/ ٌgbرrhkkNs:WY@+Rw,]5JY嘡Rc{)$p9\̝;Ih4LXDD,Yӧ[X+oEARA,CCT1XOʛ+lP(t/ O~ѣG{z0X`f3:Hll6CT:yIcH$SZ-d2;V @R(Vyĉ(,o=ME4 mJ8$|r1|AWBL&Z-D""##sH$bvr Gŋ@ ?h4hiiAnn. Vbf2 al6Y&3a۶qH$X,xQ7-9s& cƌoVNF <<:* "( u]AOS@ J5\K.!%% χ``Qܬo2 & 111*rf  a}V5xbxxPLP1nݮAKK .\\d,]'NWuRt(@ ZJrp^z k׮uPC]#!.yq((Ʉzܸq2@nbOWW!]zT* En%P__Pg.66f]p0(FDGG{]@ dtuuرcN ?񈋋s(n8O NBpH hmmEggC鏐L6 cՉ[+R`Xwϟ*CBr8'vW^ôiӰ|r'xU*$""JaaaLiWW\.2 00 ̿E(p\V@͟?b @<;wRMMMޢFĪu,hooRDRRD"MEZ]jjfMkeΝ;0ϳB3 8sN:@$&& +sd2%(w]ZT*o1c| +-++ Ō/><<h4P( ̇T(ڔ8ƿ1͈C=9sk@ gϞPT?JDb544 "" ,3CSFxx8_V˜K/BZj TZta`8 q"22o^|žtmzU|W6ޔJ%BBB  L2|>Soݦ|>rrr<@ .CTLw(oM Ĭaլu\ެ]<:u*&L(3ؽg?~o@0dxTݩéS{.\.}Y\RހpKK 3 L҂X,fRM&F#t:L&M[b@$'K/fK@ /~ TWWCL\}`jII ֭[Ju5N?n݊K.(_쵾އu5fm+cήUOcdu5F+NO9_l7ПR>K`GXX6mڄɓ'";;Yqo;[ֽ<ۏBBL<111xч1z(WpQ?~* ?(n英`卦 ;wFqJ06R!lcހnmʔ)Xd -Л'+9zzzXY5mswYz};v0qLmSgeDh^Fd21I Rsa\a,?m |N&mʔ)xw0bĈ_!?v]9!!a0BCC"kU8d*0 [XlYSܬ?~|{+3쮔9gv=o{}yB'K,; PTh-"y<|>|[l+j. /b_ x'mUoS8}4N8dZ  o7 w:ujoko5X2gsr=wޟ'6Y{Ս>|dggcΝO(b(Jt:F+ntɬ7ł0=8uZ-\.VZ^{ }|}ON6 Wc=7|>zj ZqP v**gKnOcܑÙ@ /=K"22a⻭Q~tuuѣ/R0e\p+W$ '7kb1|IL>?wA&/22s[!o8{;X``x8@ K_FEE<K { Ξ=\.)Sj~QbO=}]?Ǵi#""0i$wp[ b!7)rك׸le/w`J Cxx8֬Yoo6RRR`4P(P__&9;::? ** k׮OA R! >/"~ߠ -?aÆ!555=VXᴵU0I-wd?ۜox5?~=s6u4k`weM^wL)#(z*/^SNa߾}xvk y9sG-l }x]*ė (Jƺ G?V!BD z믿Fee% Ը,;daܸqiӦZtBT@ :\v ׮]?tvvʕ+05jJJJbŊ@I"F Џ[@ @ A7@ ~D t,!.y>K#!.y>H@ B?M @ 6uV Uܝ~6BO @5Yޜߢ]U 9H_I@ ۖ76'7sR@ =ɞ>|vgO]g]0zzݦ|P!{s)+z,!.y>K#0Z }6/l`؇ǥB@ lS@ BA7@ `]?K/EZG3oC7!3n|{ѣGV=ZyhѣQBIENDB`filius-2.9.4+ds/src/info/img/staticrouting2.png000066400000000000000000001576001506574241500214320ustar00rootroot00000000000000PNG  IHDRi+sBIT|d IDATxw|TU?wz&*! B H M`W4qş_(vQ,tYQDEHK6G83Arޯ׼{;3}S"?~[o5QFL&bKz+_kСn߾Q"H$D"HJ#GtUH0}LD*Ѳe˨CN:uٳg=F"H$DrgF+󢢢|l6Sjj*-]+Luw*>!0СC4rH&dX{gہI$D"ܑ={k]jմCRff&WL!,BYx9-^iN~)%%%}ruwn%4M={P&M<ܹs^$D"Hn:(G=hud/waʲ &PBBBݻ;H$D"i8qDJ-y"w5?? 5kPժU ,.mD"H$ޗ_NuD-oIlRRR/$D"H?0jժU@剨h}- "":uu7|IH$Dr?oڴHkAQ*I$WCxG|I%R"H$ɝרQC߱cyc0P^ `D"H$eʁi۶mV;xn|K]tq?`D"H$eӧj֬o<奍4M ?}%D"~%553(Eʰ>Dzr:u*VX? w,H$DR k}Qn= LnkY"(!!ѣG=K$D" |㉉zyCɟG4u&M۷]K$D"\7DNOOSz.zYsii&g}K$D"\G^;r"u]J5SE ?RbbG{H$D"SU߰N2R,v%*dcV}<]В%K^wEKv/1 ~7@N:E>UU)77*UQ=߿D"H$kk׮v3ސ f3^aE5lalPUGDAQ|PaٲeZPUo`bX#G&N81H$D"&~sQ8|d2h5Eé|T\92dy<Z~=)B'O5jPrh޼yDtɢ׺SJJ уZhA۷;͐QbK}>\8yd`$D"HJڵk4kLcni&@-"""CDD[l-[ҪUhժUnʷݻ7effRǎ)**+. 7$tQLJ?sƍ75?D"H$ҦMC˗/Sjn|rR A'OzjԨA5k$Nf͢7Zbkd6)54~xr8ti:r~))Tbl" H$Dr9xݻ @a !5QQQX!!!EQ0c ,[ v’%K*BBBxnyI$D"\ QtXXt]I*7"b`݈の8tCQ>}jf>GۍPTUqaxzh"8|0rssѠA2W%X,1c4hHubD"H$WbŊ:wW(,׬7|C&RL&8*_<[ԧOl4g@_5M2>jFF&„'Od_Pnn_" s%Hv<]IuPBAPD"H$W#55իҪȔ"/W[{bE&4ׁ1bqItcshh荩$H$Dg܈.˯Nq?>]hg}WTݰ,t8vZKD"H$BDb@ez3n(Zl@0J!sm "ss}hq(*횦_|s=ېH$D")-[qݲ[ipr91ؽ\z~ʡ: /Ëghu8|eoH$D"(K6]CŸG["==xGv?]V5j@BBfϞ ٌ:ǚ5kPJP؅`6AD56]ױb 4opq(ƾ\lD"H$7#VgXxp8xKAfͻf3y^8N]a0qD6nȏwa׮]ѣ|> Pn]0 nQ|饗РA'P)''f̘A<~:u窪իaÆL&4M㱐J, Kfq~I9f5j@q |>h<:unݺa޽իrssѩS'l6L<yyy>|8ڷo=z9+0x^*ZVl6f!..ϟl6}-H$DrӢ8NOAAbK#Pu}`ʛaX,<.&tBB2S6v/iG|. N\UUdscY7"""߆D"H$Iqif3TU-.Z+}>w=3cgcc3 $s5VE~Eq,c6ځsr~I2bsL&(L|@uXV~ *(<֓;o޼:%D"HʊUַXИePݩ}5رc)%%ׯOf"M0 2e %%%Qrr2 <[٘'N;RN1X-[P (%%~a""1bnݚ:vH<n|i}T~}[.?32? 6OwiZ l~ӛw!H$DrY?3|kAһK:t^:uDyyyAtaڶmիW\.AڵKSSS魷ޢΝ;)Z.\x0 2dM>ڵkֿ7oG:u!Gm۶7Q nwUgqM(::߄D"H$01c&;PUܴW.YbŋQ~}l6Ѹqcjŋ!!! "ԪU 0p "޷Z^zY&E~v#//ԩSX"waxa`Xп|3m?c :pAԬYgϞv =aշH$D" &h׮ݗnZy W Àa<Q-hڴ)6mڄ|\.[NBhh(*W 0~l۶  a dBhh(4MCHH_bŊ1b7oQFQFܹ35jdĠ{~s=y$V<˕+l8̡C0h |g(_<ڦR*c2\֬Y^zJe`D"H$2g(KdL&vHÔIdi?z>}QF~w܉Gygtafnse (,}vL4 iii8v  6ؿ?|>>cnQ5LZͅfCExx3g駟F֭ѼyseY5,Lb{*%D"H@nz%Kx咺Y1Sm6L&tfǏ+WdBڵ( 6oތbҥh֬BCC+t. .Wah @u]hԨ*T͆Cy.]~d_*U ]AD@LL W À8zZZuܙ'ԥKu233zt9ΦD:qxӏ?HDI37n{|JLL,""4hiӆ'۰Ěw} à]vQaxڵ)??4MƍӮ]HuJOOI:n6lHG>}XrQ$L#Fl2e`$D"HJk܆a_jժEu%J 6^x{9Sծ]/^LG}DHק+BDDhǎBqqqJ 6I&YZnMkצ à#FP˖-m۶Կ>?MDD.SݺuiҤI\YtRUƎK*|>Ƞx&UUypLkCުUf@"H$dddTVZU~]Xt]'[1 h`T8ED 'Zʎi_ 7`>~sz饗v=zrD"H$gO8F R`bܢRmŖ S2)ItNۇ뺟<@%TTZܜ4M'OUP!3''\D"H$k&//b?apPU"]iXAע+\‰Sa빹~۪_ T>srr͈0EQbZrj3f]g*UE!EQ߭o+W(ї%]MSnvfkqqqR^I<3fݫW@E(*X\C] ۇz/kudhc-űMTűM8$!qLXTrS6Ά խ[ ٯ\;>v|Q9v$DD_bŜn%ۍpOi\UU&;Sq/9rFvvŋ+f  5$Y {)u^6 Np8x o 0`ymHVG<]; wۈeBbMJ674|f!??6l?(JYz%])1{ -õ4\&ɯ,b j*{3&z/;ưbtBufvPa?b&Hv;XHP TL& Àb2mϺΰC44L&?+a6cd=z/K۶mIDQbH%@DD"2lQ^*~5ua#"]$QI@&M ~\աPOYd=/W)`LHP+>f?x,f)dK/pIb׋qVF=S,E-Ž1ϟӧ/2H)",s}^΢|-6 Q3h s9r E801kD":4`@ 3ɐˍx e'ʧ+͑ζ]ZEQ΁Dr%A 25kfܸq.xko`C4\3(jOOO6;&bcxmܹO&$$y&rx?uQXz^~f OѢ۝#Z5T\[,f*Vy[DEsssblA_̻l cOI6vHHn7}g_L~z<W4}>|>t5KUHEQܫWk/tp#}fy.pΞ=}衇N=zR׮] ڤKjf8N|WX, &M`~ GQ“ LMӰtRܹYX̎*[3%job_~ ܒ HnoL`#[BB̙ʷcr\Lдl\)dpB۷Ql6_̲xQV16l؀p,X֭lp8EffI)Q\JVZ^8tR%LKniH999XϞ=?}ꩧ>ڄKQydjBuL0[nիq]w8|0KJ5P4ΕfDȑ#q"El~c2WtWfH$7b\h5pPv IDATE/-[૯Bj0tP;vˈx VK]ס*ƍtf޺yEQ %ۍ(\"JQVJ% 6%΢HJJuʕmGu?󝌦i~ ;̽V'Nx=>4imծ0 1,Gѽ{w5 a9sPbÆ /t:q躎{V1p@L6 bƌ^:W~AUUjbԨQę3gxsQMB Hn_D/d2pd2!11[=܃1cl6c0͘7obccf͚aӦMXjE={>,ѨQ#=zCԩSo&Pre 0yyyذaL&&LӉ'N{Z8<"""$KVHnהƍ[ &u+̬$Ph޽}sJ0Xz*͆x<h+W0 $''c۶mx' @HH틂?룢( ʕ+1zhl߾/"ΝvŒ3߰i&DFF< ^"af G数zW_}ǃڵkc׮]4h +WF޽v#"8,Y|{9lܸGƇ~`ݺu;w.l6V+~[UTq[,V=DTŘN$\s?Cs1jԨԭF`dŞ={2۷o;vQFi,{%8|>< fٳ 11| f3#..>(.\={%$1KV!!!Pk׮^ R*"2{$"Fl۶m!CWTIE wٳeee=ODQ}"*Zd&OWVŵpSSSg4k,o%RXPXNq "vuO@LL :uXl9/EO?|tǑ#Gжm[Y[dLf%`ߙ&ްk ,fl||<ڶm3gbaԩL&̙ ̝;5jkڵ8z(VX >۷|8t:wŋ#??{jɔ3 ֭[G̙Sː $[(K Z, -\ Es%I1͜9j֬I!!!Զm[ھ};AO>$wMÆ 4uFx "":u*ըQBCCo߾C۷o'L_~%izi۶mϟO|  ;'ף;Yw}Eniܸq/\\zޫ'f1hҥԡCJHHPNٳ}HDgy_ +TJx<). à,իW利;w6 w!R $;D5}>^|C܎)n+GDӴ"ckƍIR")!@(e SD# ul_i5Q&_v~ʦUH 믿]wE7siѢErv?|].p=t9 Y,G8$XSlgJ*O^vTk6xHʵgzw~>}zСC|饗R妪ɱ;;Ci@]1~!^{5+WίWTTΜ9 6,pv#$$Uc]5L f^exBS 1c`ozvy CaJkaR}`׋"-XV+\.+x.[+V͛٘e!EI}'+]?|o;udySYkiĐ/5EBXs uܹsoh;8nܸ-Z/ʤ>'&M4{57Bll_ 0.麎9s/uq * <HбSPP u]炘ŒL&XVp8r 1[,p8lX,> EAABBB*l6rssyV67@J$%H*v_第,xUg5snܹs|df1>tvt@UUH2ECl8 6_`u]|ŝ@zqN-,֞X7JfPn ,/l?&ߊz9uq111Æ ;u1?v\/{/g? j "G43|qn1Qp7N뀹,C [")!e|(ʏkQs.΍dE@9iZp)8niqϜ9tĈ?VXј4i +. sFDTj1GUUQ0O:t0֭{aŊO YYYonUUaٸ(dZnm۶뷸RJO?5 7Bd!aXȀ]dl\\|^g1H, @J$%Hq~9Q(DSf2I$n[ L4ؤNQ *ꫛ+UdL8/b=&K_#f˖-Ժuk#555cݺu}KhhFF3xgx"##fŔ01[R+WIiʵ-%-lƎY^-Z5kjÆ 3g/_N O<}nnn`R%771cfUR%g'ϟ]o3IOO꫿jhmذ]I>Ν;>cW1tY{i\!!!E Bw:@HJ(Iʞ su[Z dӦM~v'NHժU,Y`ΩS*'C r g޽;u]Ͻ$+@S\miSPPNHH8ުU'N|eÆ pB̙3֮]{_g͚5j1nIRIJUU#""n;,˗I% ƃ#22W .Vzu}ԩ\ x"ٳ7n 0`˹sn$3g΄Ó^""4m4>f' 7N裏fFEE2L裏~|ReܹsgӁΊ8pX%Y $_ly+IR")!&b#oewcZ(P~(D:tf͚ў={51 /@jKaHOOo9dȐ+V[nyܹO>v{>hhfGCyHhhl6{DfD}:oc~%UX,b!f#bbbN=S~e}̨(n]eQBR,2R")!QQQZ"){\.hq{L91!!oI}'%gRq:D)))_~_$S*ObN;Ç_DDaM)ׯC-95xYO.c8N%Ơv dF@IqjDT0u־ƍK4 B@(>=z?tJ8(vҥ}w0>>p߾}WϜ9T]]EΠk\Y-zry߸l.=q?q;/^ 9]evL"(Jkl6>o"q,Rl.#5g=Ź ʣW^y֭Kuԡ\9y$uڕuƏiiiԸqcJNN'|n7?ڵkGڵrd={ڴiCs(wK[M$"sΝO:sȦ%%]2:u*Tںu%̑#Gz7ޭ[o,XЇzHbVx:uSRjj*W/_N;v4ZlI{M6Q m߾=-_.)kSNߦvc{<JHHݻwѰa(==6nHM4=4g?Ç)))(??:t@[n>}:uڕ[: DRBJ[4wNSN劗afjٲ%NԤI:x Q&MvԮ];Zd OUU魷ޢN:q}W^۵kGouڕgˊrWUU֭M6tB>@k Gz `HBMbbbѣG$[ѣGO;~x1cL[bEի7n\d2oٌe˖cǎZ/魇F-t:izM6͛yw||Vd*< ł%Km۶4 VNz^vw{N>'':D]Ѻuk 0jł<.=(oCn0zhv4n]vEZШQ#gϞuV^'ODժUT( TU†̓K4@J$8DhfZj999pXr%rrr:ʗ/۷[nŃ>]aXp8PPPeAFFbcc# 55?HIIm۶X,0LPa ,,(̈fJ&;ဢ(X,}+BD6w\S \`l") ֑Pl6d2^08q͂ ]n@TT)kرӎ=OO[fMի5jTוOBMӸX,PHMME~УGIII,nٰ}vp8`lL&nQر}֮]>6mڄ 6صk`\ٵp8|L0 0WDr4ѢGDСz=z_~hڴ) Àf!==]crPUUDEEBQ麎5k?ڵkCZZ~7ܹ{pD#Y,""XV^JNK7i$s\NL&|>h# %-Hy ,O5s̰_|q ue߹6EQ k_.]t1zaۋu0F#F@Q1qqq4 2+V@ڵ:f33!oJ*hܸ1N'> ֯_ 녦iuaaa.JgNRyHnmT! IDATUi dɄ~ ={ӧd{8իW>s""9s&EGGSݺunݺ@oYVڳg5jԈ),,ԩCӦMhт6lH=Jߩe˖Ծ}{0`rё#GAGV֭K'O&Byv?uRll,9NJNN_Ϗi~rZza̙31{m|{gΜ9_OٳM6M/W\FW~e˖i=Т{iӦO|]ٓ|ܹȫok֬{֖0 :q+WB;̲E>++*VuRڵ)--3Kӧ,; +)S}V*S[Պ}tÇdddTˋ?dee;vJnnn%yZj+V̬^zF3˗/~Μ9wСaøFXN:$dgg&<.\-[b̙XnLDdee!''˗/ɧgϞEƍE yf( Ytc*jժcǎFUTJµ+2++<3$&(…LxI/>s8\d0qD(*OQeBmbJIy-zIӦM[K/Ub<$80efQ޽O0aTJJ`f-\[QTXF6S֘;Lv1ץd#K,WX&7[JllXTnb4d>sd\Ҷ>ydǫdeeŜ_Gf3233Qn]|Gumڴ?Պ{b ߏkעW^18 iZbP+L(K&|gqDNB|ɭp:Zl$AE;wnf͚mpK[K[L0R~L0E?|29j*L19')·eZ.-zr9ZI̅aTTyh6n>@lp\p:%lʾ׫ʕ+#77{bɓ' ݎD8N@\\Ο?3ͨ\2v(}ٳ 0]ygrrD(@&+? uC-Bmp$|OJgMnݺimymJ4 -- YYYhݺ5nw}Xnrrr0k,ǘ1cp逾EӴ1pu|OwqLr*.V|>tż~2fEu\ ~w(;Zi-))h✰~ پmm=C#ƈ YCZ!ۖF+kBmg5A k,N4؞Iք_ ^qqqQ9Q@ݺu'NDAr$rmhҤIjM{詧_~+ {^QkI[ ؗF後M=?þ"""|( esUKe +Bdd]w0 r$N[VLɂ$`5c=sXր{3 ɶr*FаQD9;[h f͚5Moej_ ˴,< x @d{pyiG\S|r_cݻ׈ tٿp\@ =H•1ىEsAfbl6#==!Xu0QN8k&zM8HLL?,| "̚5 ݺuC֭/N@}ff&Zng}}_ yRvPCkzU틅״ YrLMK>?inNσq$& %{m, ÿHή^)wW_}pKQMDDDfZd2l6lca"666 SYY àJJJx*..p`y<ر#Q=8#gXz5mVd/ݻ7}v,*..&Eݺu$Ǝ+9dd+k:t(>޽sf3[dggkAǎ)f fggoqqqޱ .D>} ݎ{GDDv~B r@L&DGiqDDDl6u=a45B6ˑ *vdL&(38RT-eDGGcĈXr%oܩF4a…Xv-;ĠQFhժΝ " 7܀cڵ4 &L޽&q4 mۆe}e˖#5..6M@2WavQn]9r$`=z4<< :3f<ʪ1xIOez^o@@SXxqֽ+䲲eOvfEek^|2l#p88y$l6:wtn}V8 ~DC&@ =i4>8bs#L>}:K.ž}УG\wuXv-@'5 ={wߏVZ0 !s*1Xn s"66VhF9TDnձ؟C eL~jEjDuWɎ'.^z1bƍ'b8ʩ*N=zW^:t(|Aڵ Æ i/ѫW/ J rȠ\޽[4V!3)MD{u21ltlPľs(Di:uJ]~/U] àGR۶mQ޽{)99ZlI%%%zjJKKӧOє)Sh޼yԾ}{QGN8A믿z)??B8qҞ={~DF۶m{R˖-pQzz:m޼YqFڻw/%%%ѩStRΝiӦM:[N٬,_hȐ!{sTD狀ש`2Vp *ᅡT7gʰ&Wu^%iI%?ݻRFF/d5k֌iժUԿӨQ(###|N'uԉˀ6l6Ӊ'ĉԨQ#餡C /@aЮ]o`8luJJ,h^ TT r6<+EQ7odµ^7trss~zݻ7`ҤI8y$z!`0  a࣏>±cpm!++ :tɓaЦMԫW< ѢE +Rʙ*Cc璟wTTH_ꫯO Nu+V лwo+zZ0a:wof9Y,DEEAucǎСtX5 !99YȎ:5FqS4/"QRR=D"hƌԾ}{JJJ~B8Of͢mRRR;6XKpq馛wծ8JyCe^Aap9$:zހ2]ք7r?=( !?KD4t !fΜI:u zg ׉z:uD-[{.xɓ)33222 ڥȘQ02l èqϧvډjZhA%%%4qD7nXe "ڳgo>l| uA/2%&&Rrr2ݻBeM^W…T3+p =mVt999{JOO1rrrh"#GPzz:Q=hŊt:)77LB={Td%PGs eues=LuX`"K].;./iZ^mN y%@^:j*j׮׏?駟{nJJJ"q8lѭ[7Zb5rJ E~zA}hd <փz>>/@,..l|:8pRRRYfTVVFyyy,&ӦMٳgSZZ8Ç)%%lBD_EEEӧi߾}Դi #'p/a{b:uիW%D;v YW_}~!&&& r ͛'oL&̝;~;֭ ׋bѢE"Ua󑓓R5nWpbOI9Oj,K=n[9,n&AN@e3p:"ÓDe6+8ŕ>#zZv|rf~(**laXlnvԩSqB9ApB\e2еkW^Zx'$$( ݕ2S\)bip8(--)O6m?8r|>taÆ!'']w~gϞ_'NQ#2pZ4DGG8]y1S6&LPܮ"۱f̘1ټhٲhM7jYfX~}@{"BݺurT[p\" )""" w܉ɰ`)笕m!Dzw$ r>k`+\weXNnyϭV+Eٺu+[oJKK#MnhDDGDDvONhÆ n:" tzXbٓ~HjYZnc~L4_G~{<O 3BzQvzTZZJɴuV;[q,Pbb"9u:vH6l O?RRRrQQQ]{yf_Ghժp=Ѷj 4lP:{bʎwbvݿe`ǿ1GT<O9B}WNFy~?'nz-@T~}JJJmR֭iҤIDDJJJZ[ ""}f-d<Ɗb[S_TjժM<̐^{RSS)99&NHf"oPz(++233C4a„jPT뺐@G߿G999!ƌC۷Lz7EI&QFFiӆ|0_DD={R^5\f mۖڷoOwy'9uF={Kv`AAݛz))SYȂježk?Ph.{bcc%%%V:~~jxYZӴOWN$T9yYhPNWy e5^f uʆ4h/.. U!o#Ç;L{! *‡dKz衁{Mꪫ 8pu{u:u^]$\/%`'v’v!#_ؖov +y{urfLz_|͛.1p@|g~ɒ%:u* vt _| 1l0lذѣFnMWp ߿?k|CoÇ#-- _|V\ q-[#M6x >K/ґ#GӟDo&/+~r8j*޽;Wo9֭Ν; K7t}w^v:rJׯULnvW_9vAtĉ A/6**47ʉFQQ1{6Y9u3zˆ=؋ P^jVUŋ뺆 k{w6|jH(EjrDī#G8p8azv{@_vEt ~LP#܅[nϘ jРA@4<^R8N,[ ǎCttp~ڶm~w}"dBtt4t]_ШQ#9x'nYYYhӦ ЧOQ4l6躎RٶxGw^,]<Ν ֭,x]b-]PEÍ a3wNgq%Y 0xDyAޯFYYY\K픔+Vkĺ +2,X)g[>HG0Pxf3fC=nݺ!;;@y= Jexqƽƿ)RQpc〬<ٚҙ>r8U 4"v!,xq ?oJxP(O:u^X#w=rY)g2Pag^8A[if'|R6 . rQ:3cC rR3g>6vX5Pȓf3ك>}0 /w܁'ObذaXp!nݺUjfhH{ 6رc4 ˗/ǵ^UVś6mBZZV+6n(Ԭ͛E IJ21 ,@DD>#1_Ʊ#!GoEGGSJJ effRff& rׯSBBEGGSvB1-^uF4tP2 FI]v뮻t(??222())bcc)==]S4r8DDSO /@^>,h|>g(;бcرcǚґ_yD]׫OQe 4;C4h} nW˸/ҙ0>K-))qաCk~ٺukt\\\P>toa)5$I 'g &˲r!22BH'96#o#"x<w]ǡ+#;|| ՄO4SN-2eu^pH1fCӴ8%@*@\\v^Td¶[Ӵ u֭$<#o6jh?o' ˘ؓq11a*Ծ\7{<a upP9# I.l, ʂ" |.s:.]O6mzN<6f#~7Ld6Jyf7T,aXjM2LG~{G-Z$ LK[8ؤK^ V38*z^qu^f뗕mv9E"omrދ!φ&?裛'Ml{E[+XuQ6 Rn]wjgl_O߇(tC9;s]dᎨ| >7B ~|L]' e(_Y<rPW\yu{>2¾a2X,䖃*]+ؼ(sarvz pw< E,1iЗGDDc"^ 6""" <*9= 4 N3m1)P|>.Ӆl6l3Եk׼nmɹUPPdeHT*^|,8;k1j@0^£ #S]SYX6Ay{p U8/߫P1tk::u1nܸ 4XL>rZ tM9+_C /o.KǙL&ȁӁ@rYe|z(#'lJ駟ZlmƌO<[?* y( 7r0@gB ggnl6%~8p B vrtfY^Dvvd |ל3ⰦI_g&''/=ztxŹyw?qv+_,4JրynӧhӦͶ_ *GAjj*ڷoCr9`̙3ѱcG$''cرB0 Cv퐜g}6N 7|3nQBQ{@QQ?vr?={6ڵk֭[_ XIYйsg$&&bҤI 0عbʔ)ر#Zn~XGvPM\ ,=eʔ>L`x#E/ӧ7|{lj2*j0`w}Yp! 6L߿?}Dt6T(==~ɡד19BT\\LDD=z+V܇\2e ٳC+Fll'9>իMs1Vus*x㍸xe˖ѣG'$7ƾ͚5C~~pAnݺp\U(Ct]G:uz5K1͈ݻ,lp%Bۍ>-)) &66& [lA^^ "fعf͚n<̑_4婉QN'N:֭?8qs{>%@*3g rJ|ƫs~8lD2r̳}Yɝ7)"B[ތQ8, Dg*a-" ^W V\q]waܹ`T ̋iӦ~.~Q.OM@VxzgϞ1cjӾi[p]vc9rM4LzժU>|8>ct&BHѤIc?C7tӉB^Q3x\2>J^=_}~yfϞ}aSMEU6D3gիH*?p@2e mڴ à}QZZnrݔEׯ'"<""ڵk}:uD6luQ`N+VPϞ=̳at:މ/_rF*ngX'NThU/XM/޽{~hƍDtɓ:uؑ6l@~~gѧ墢"ki͢"":~8jՊlBDXhBADڤINLL,^hQ@e5׳s寮}yB|p8Cn^pGNH"#GR۶m)-- FD/LɔB/p8kFiiiBǏ'""TZZJaО={(##(66hĉOW>bwؑPTȔ&22RVgłݻ)==(22ڴiC&MG>}:eddPVh^{RSS)99&NHf"oPz(++233C4a„j`.7뚒O ' ~<Oe,PTIֹ&\aMJJ*xg'0R @ZpHP^ Ϭd  ^>͡:|%W"*v9pk=v뭷722p" .D}x*Y>Ohds\r zzzzт |%$u<Pg??۲~]zupZDH d5^Ǎꊋ#ܹ1ԒD5j8JԩS2`O͛wgBQ-[,4hGM 3WR-@ A`߽^o.}8rj_tizJ,YҷO>߆>(5 Ծ} Dd? #/KKm70 wyՄ(pB}T=DS=Qĉǟ|ᇏ2` AB-U dݺuKHKII٩ir(e˖׮]+: kP~F=XX}X]2'# ߕ&@2Ǐy饗4k֬^<0=|6mZzuܫW?sp?SeB\\\@OT[eDiLLL L8qѣ3BG}Y֠Aj_¾ ௏HfM&ԩ?ST\\,{ w! U۽%KPVVd2JJJ7o޼A~ˌo(>IEQuxFǛ6iD9(xnݚnݺnV[+w]suSRRRzYf%?O>De*Ůyސn۷oקOK.\tip?#eNFB.a˄҈!u]ʙFqECD.]On -&&][§\n2**J xwyW+6l؏?f0JK s!Z ݥs{kn۶m>SS/_KD*Q mUu/"*_7''G9(X^{wqg~rKhr\Ox4yj%ܩS3g<~DžD;4tc>i{ƍw7nܨbUW]Ud6E@~BդI֦M{Q4+ݻw7oڴi]{|| ]eZbQӦME8֭[ƍ׮]̚5`sBǎ5J9(8 o.BP(Ǐ7UiW;31 BP\$wqg|pC lr;, BPZ,YiW ?1cL w9 B2(.{6m9!!ac]BP(j=>ؑ#G*"N IDATge 3337.^ˢP( eǛhB9(.[MO]BP(.+n>SLسgO&M8qpEP(ˊe˖ҥrQ\v3f<r( BqADZzz={, Ee1o޼A\s:QP(*bcGi'OMHH?vwY Blٷo_ӄL,1b=jBP( EU3`L^:;--mP( E5`FQ!"K7͟?pEP(+"Ғ3/2dȇ.BP(W'NO(1E-RRR * BQ?~i͕3q뭷~9}.BP(W$*3 k~ "SˢP( ɒ%K[9(j'OMMMݿnݺ.BP(W,D%$$:xrQxyO>pCP(+ɓ'}G3Ff͚.))){=( BqsʙFQc!"KVV>pEP( 3&3a„.BP( %KU4޽{pEP( io߮i5|5} BP(B0~GV4G}tWVVZQP(rQ$7ou֬pEP( o0hР>#L;<^w9 BPZ9(N^^^פ_(2eQ( BqHKIIٵgL gb>hѢ>.B9(Chi :3f<(LNT۶m۷pEP( o߾IIIʙFQرeV=zqˢP( "ׯrQT; ԩSw9 BP,Xзo߾ʙFQmw$"-eQ( B;`gFQo޼m۶wY BQK8rHx`b1Х4M#""gL(22jL&Y>b9oL&l띫 u9sL3fivؑ:Daʕ+)##\.W]=:#+$]p2 Cvm>\qY~|>M?_C.CVڿ7oر×;uFQ{YnݳW]u=zʅ6mТE*z/0B#2}^ov* O{Go :U!Ku- wQ(7nc=Fn;@@mwn[t^74ne0FyPZ 4EQnn-"",uFQ{2dȯs_!CJ2Ի,jwK(MfDU\M:ՁRNWBPaÆ.FTW^w~B:py00K>bbV'""`X:, & ad2n*Q9΅iz0L4MlufY'+CD|D]@Vf3v;~?#Fj+huԁa_gy6 ]PPz}pj].sl6L&~vhx<0 V6 C4 t] }YUbXiHA EMA>t]jY@`Zzz~EQ@DFF WZ0xi, V+^o*4-@5L6l>AM=X,\.̛7~> G '|rvl8z([nD.&[& 3gD׮]{aoC?q?LeМ /Vbl6[_SP(~n2c|TPP@iiiHbf͚yvSu.;,^qO}?P|߹s'eddPII y^ͥe˖琟OIIITZZJ^zA+W xN(;;\3u̷~K=zz)H&66{?_NVaTXXH-Z*..핿_m~;tґ-T ՓKSL^zsO-Z6mS~~>$KݻwիW\7؎S.WeGm*HEA/U /g5i<1bƎ+gt:z1m4t7p{#<X`rrrЭ[7dee!99~uAIIIfO>3g4MéSPTTƍ ۢŋcjŭފŋ8q/1`DGG},XÆeܱcڴiSN%ptC:c)/e뮻'OWi׫駟cKE4x<4j#GO?]Ah&M^zW^xG0dٳC|Wp#-- m۶ 8?pTO?ENN4M%q~[n7|#ڲdjjbРA?~3yW裏raΝHIIKfRL UwvDGG㩧O?(7ĦMxb^K.Ell,Zjf͚7t]-܂+WbŊa6{nd2 (0p]w!>>vz Mp4iD χ[aa!5+O:4i1HDDDǞ={vZ/;#u '!|p v9߳ |Ov岜 EHȦyZbRYK\W].uaݺumv;֬YիWcѢEXl|>222ТE ̞=a[oŪUl2ԯ_ӦMݻ P.fԯ__Rの8ua`6|ӧ'ODBBpi֬N< 3ͨQuVO_ۈ鄤PDRؠPn7NzCYXr%v܉^z!''֭֭[E,{#3^{-nFW_ ,4?{8x .]?Ph,X4 "a0)4 $y}L&t].0k, 4]vEns`;P٦Sv-^e c=g&"%I+q=mXO>bRÓ0*E{,M<GP>a,++ʕ+uV7pv؁m۶l6 Mau< p7n#)))cfr v¤FTT_u.AMĄR֠;;=z%@*&nDfx<X, M49sŠAtR\k׮ſ/~x^!~7Xj&N B-[sNl64h~;/_.t۷@{n4jH܃jEÆ qpy1Ʉ]vaÆBHDYY={a!bc}>Z܆ b|mm5k˯Pԫ_M4u7oNlZ.KiVUhHǃRfQF={6bbb`2`0d,\-š5k0i$PɄ ,믿\ @x'~1єK-Z]ѣP"##cǎ XQXX8@4ߕUS(ZCe;v:tA֭HmVPP@IIIԬY3*))<ƧMFs̡tqޓ'OR˖-iǎ"Ѿ}*3gu֍ʍ.2e mٲ~Wj׮9r:tӦMD^-['IO믿6l@~6m$s믿ҝwIsέu]({9y磨(_C\\($T]x4QF%֯_oy6l( K0*dg:D:tN'͛W_M>[jݺHy>JKKȑ#H[nmrE\nᆀl޼O"hxvv6"ݻi픖FO&Gݻwד딗GD}{n;iת,'$]9(Ke Dg3p&cǎQFFF@STT9 /P׮]{t]wQii) *ѣٓnV2 g֐y'}Fw}(:u͜9ZlI4~ciӦQRReffs={II =~i"":|0%%%QaaaTiO\^<9SRRvn߾=*E3qYvROˉH Le3=voߞ7L&a4i$ܹ3p 4h ***֭[S=h̘1ԴiSիuޝn喀.H=|0eddPV(&&ZnM&L ZZZJ׿e˖J&Lp I3gΤJMM&N˟; @r9;`?Ls|*oAMs̞=>g^(ṲEk{=8sy?]ϙI>;Jگ1LG#<_=9O*9אۭۧ|Cϣ\>^nkBݗ\mS_U(jJTrau #{p˃kr7n|K.yq9T,\~|Bp! 4SS/~g|,Gg$e N\}Maٲe=6nx3<3E8jG֞P :::`V~8#I2bĈx?jzb93X+]Ӽqs^Res,¦,8~(ڮmm=̗_~aM<󉴠D$C SZ麎 NPTJT#/(‹yP1"Hy߾}?~$Ip8XnF(..Fxx8JBBB& K,|xꩧb L6 |~7[\f ֮]f͚ IApp>O={aaan|w6m>,ò,j  Y5)))9r" 4vb})k۶mώ?SO=q9s ٞ;YyÇnGLL ϟ_m۶ŏ?g0zh=zTNVTT}f܉hzp8;vYc";;[?wH;44TfvYZZtɓ{O&>>믿~СCTExaq\|򔔔}> @6l2$$$θ"G,rPdZvӧߧOU׮ٓ(U /`AD<**n7LZ|R^lc&"}>}pQ\.zUuDDeqIb ݽѣG}_թSAAA& Ĉ#""Jמa1L6WDDD-jҥK)((>4)**GDUM&M8W^Ok׮%4uT)>>éBBBG2Z|9+B+̙C?3˗W;+VidXO?˗O뫝sne|^/41cTTT<...6 -[F/$""UUiܹfΜIDDo6%''S&M誫]v;{4~xlzSNIӧO'"SRf(<E 9QL(& IDAT $IBxx8+W4N3G÷o~ܹ>EgŃ.?w?TRRE6v]g-In傪 ջʈÃQ^^˨`(/@n\[,d2K ӗ[,`n2P+ EDD )++<ӆD$uc6l؂89XH.B$O?iu i(^/ѴiSҧ5-?P0r6nAAAz D#u>"#i<`6gϾ'((#c1[mHGUUO-QHM9緱Zvx}=99rsss[kcׁd"g׮]/3 -CO-P$IzPd ocҘ_Le]s:~u̘1o$$$쫇b1lҥz|/2 (5VJ:1722+Vv8QOg1_s߾}ՙ&**JeY)2I& (UV{lrKcq c1I)S[oW1d2E%ƪeVwfʱ&O=? }i1cbZ:bh@6d"dXÏdRkzmvѮEǏvc ү_LӤI}8CjfV憆V&"ӕW^v;1 Wa3(~?33s񚏌e|k$bbb:tƏ>h0O}>gi$I,|@eF$5A$N6N4b "q?qi-, XH$=IDmڴyw!IcC،5"#G|7?R[z{욦ؖP2tEv`; kdbbbҥ(i4Mӳp"'2dXZH4}`ii)l6pBQ#VUVUO) e͛Çcʕ?~.$nCUUȲ\/dQQQd+`1v8d{ߞ9sf/An=sBUUL&#..NEARR *, rrr䚎mٌp 2+W[qm2`e-YfL&OVc1NLڵs۷4.4<]OD'p#MhJnn7eee/BDD6l "?Mvv6mڴ6oެsԣG$"!C̙3T+@Uչ111za3cSO=C= tCԲeKz饗ь3(%%M:UTZZJ^^{5JMM%A&MQ^^NDU?cN4l0}Xv c105oժ!:M1MHY}:ÈDg> D.6b[Ǩi5y<@2X#gffO-j4VgpɤL&5ZcZ!˲>z] I@bo5bpEQfChhh1΄H &K/5bcL&Elfilz$IZz%I/meM`6aXjm"ɸ&e]enΏcHg* $\.=zzPdV+=ZbFcRl AAA2@bH=P6X[$5c c X#&I}9sF\NE$~l6> TeH_ R)M3m\Gd3}d}%໴O]c1'$$XLtSIJ6mҤ 0@2%%%پ}iЯ H(*ix$c!1okՊiv^"M&>W-plҧ&22R0vl`XE4cNvbMeeڋAAAZ@cU$cL7a„ٳf-$I FQ<ul6s6cA1ׯ߲ݻw9=PQע8T l}j_[>ƿŶ7W3" c:I1c̝>}Hz$u E_J덁Q٬l6뽯ŏV#+I4[tY@I(c1b<"i~FUU9'-1_to "XHƘ?;wˢEǚQchUUU7mڴA1b\.v ݎYfcǎHIIĉ؇iX`233zQ4 #GDΝѶm[yp0͐$Iߏb#c1Ƙ~`>} IgҥK&CDD?H.77ҨTU=z/B.4M#UUرcDDԳgOZjJ,˴b 9r~k.\-2:HXHX5}Yv:'TC5̋RVVYbsC،S" ""BPP081Q&"h Y v$ [,6Pbp1c SlziDhkQc6ADzk,m$ VUA",PEm d҃^n1) = @큪!D"1+ c$zJ A6{QeD)^q EQL 2bGq_XK0Xc$c:"Kgޭ |E&lvbcXee} 1cL,gބ1X"4XnwO1ƪ1vJbHX4fY33XC35D]%IuIi &Śb [3UD$[-n Ljd2U.;;{k65&d|+_sa?f˽c5j쐐>lχ~bsDTv(g+MUjmذP9((Pǎ77Ok$4i'^^5V憄p+Ccg?n馟*O)--?=9}:LMj :>׮]۷oQZZڡ~JNNNr#V_R}Ν(s5k$c#"?0hĈcbb#*++?\.Ue'>t|;*}{ۨJ[nGyDkӦMW\W_}Mܱ'^ 1X͎=9cƌiǺw̝;uFׯrLLL?ݹsg@?DDD/#De1c)7fu_\3˒q>vqUU_| z}+~}{Gp c*))iϋsw}Ν;_a$hܹZrrrY>}~޸qU~nمRl+KF5[0>`MմϊS[j3: $c(^O\\kĉTXXHG CDt/y@?]hv藍H1C'c?@3f%k:?c )5ƀT̤T5jAIII@?FRO{O}'Lʢ_~Y~ڴiJ[QF,Iz׉@n͚5NmڴEzٳ'=*++M2)%%F]jFԧO4hPkh^  c?Z]*NS?:ЌC,I& Ν;"Я;7@3a>}?O{ŋS^E.]жmh͚5zA/&Teׯ͘1H,_4Mdڱcs=SniΜ9Dt2qFJMMի}cܷo_>}:ۗE6]K8d5Hܓ4k֬oNݰa9666 $A4U]?EPkX\nȼ^/]3, Ei?W_}9o޼y ϙ"dQU#ٌ> zjշs:EQ0h |ƺuxIII(,,eZk׮(//jj(. a֬Y8p L&dYdiHLLf駟(++xdYƢEpW`6aX*l6TU3c]a+))ir׮e9}Ŋx@PP1EQ2oE7Y|edd`3gμ2##cokY1{B|) ,z{?++ | I/_: 88,faΝX~=x пx 44T?bAnn..R~(**Bff&^z%tIII CNзo_=0P$aǎXz5fϞ I>6 6M'ZPԭ]s7|bĈ矑ݻwCe<, ~W :}EI|IDZZPUAAAz "=PcE-^`k.]`Н1CaW,~px̊ ##6m >|[n%n$2tKmj߾}~ ڷ$Ix<5~!C裏$IxXv-F/mڴ3" >Ll0p@5 O>>~{.\xƍIL#e|Ŀ{RZZeeeQpp0K/Dz!r8p8裏>"UU魷ޢ&MP֭)##.2z爈m۶233ED:uiӦQտo);;.:v=䓔M=z[oU_rl6Sii)͘14iBڵ6mPv襗^"UUbбcǑHԦM:ujq6cAS`駟bŊkstt>&.VQU,#880o@E$x^XV!<R!"h7aqqtbn=nq_B-1,6vcZ/'MT->,pr.Lr?ׯ_)::xO^QPnnngϞܸXә(Jdtt􁔔K.ęLKKs&''6H#cp(T D'>c (-_HXb j'n7C˟1Eu\Vkm1 qy6lkĈ߭\z%A1$XަTjj/y?|fj=|˵wv}6$Ǐ>tP\AAAE& y(66٪U+g6mÙP;&&<ЏTj+Y.2xDۍ`b^/L&fP1h4s^Xl6W+1k/5& c :8gΜv5T[ØRb>WcPcfUAAϞ=={;ۚ?~UUlrHAA󐟟ؾ}=[l4Iw!>]xqc? uqGuyo_z5//$$;={$'4k֬(99ٙLLLtjժ %%%iӦNq@$}3K.jC׍e F0/44tHee%0x- j?vX`Vap1=||.q_.@\/.4.Y=̲I&=`2,'Z%mUZZp8rq899qqqG\Ăj xD4yC>$cO۷o-++&iɲm|TUg"(~>_%:c}UT]cjS񜈪WUUzWո &xW&|駃oߞuر@.v-Z(5$I"Ld2I(::g1@t7|p)R-)8:JUUr\.c@xQUXQQAÇ֭[Sjj*uԉ-ZTصԊIyd]3cꖮ]*"cx\ 6<ճMڊT \L"~Ѹqv㡝;wRLL ODDzpZjn?tgE1cg].]'@SZ(rdYfEs]uUt㉈hݺuԯ_??~< 2**--~zjժy^*((9F}d@8vmذc 1Xckpӿjt;ʈvlfU[d?~'_7ojņ 0m4,Z͛7w}aݸ+vɄK/T>8c5Vnxx~;vDrr2>TVVB48N <yyyPU[n뭗Vg"XCy„ cFȳ;<ۭ[jfQ.--Fff&222pwbȐ!Xnz1X>o߾vځ=+GDzŴf6_l6l(ao4 eY֯ tVA aЇf3dYF޽ݟ|́>/c=$$gDʾՔf,]MPg"YQQV_ υX }ƍjVV֖@7cƊ3g_+iZ,_mKUA ̧Nd%Id3^W7dY֗̐ "d׆u"V+]t19NG1cl{_ lĚ_Z\/}x~m8@7c1j6eU j|2;שJxfuYY}H𩪪$Qee%9rZlI[#G&&&RII18x<ԡC"&TUzUUjRII Stt~ǏO?)ոd =~o0cU{5pnDhW- D@M`v!2bbbp뭷'o Mro!I7oEQg=gg}^{ ^EEEƶmtb^%ÇѬY3q D1c_XXl5_FR컲R:vH,SǎiDDԲeKK^4Mq<@YYYxeTU,Z|9i9@ǎx'`ZѼysx<CUUvZ+p1c SHH Xm3f}C ٳgSJJ ?O=y^=cc_M:t3/2%''SJJ ݻǩw_RdM\DHc5L>F>č *iF_Gbcc֮]Kz5s=Gϧ.LWAA%%%Ν;k<_Md2Qyy9%\ⳍq鞽{ > *V1 ^H 㩊UTUݶX,gX["qc1d$Iz>nCQQf3u;ݻwG=cp zٳ'TUٳQRRq+č7EQ 2EчD{F YADP\\GXd.%I(00ͨݕc1vx*i*(ZgcY^^0}"h#nAD۸/qYӴjEpZSŸ8Ir?x-L&o ].YD<҉v]ŹM}el6Ͽ $c1&әLD[KA|BUU}3хx\r@tbe}0xBBBJ`1+@Arrւ >-|gC6M_NdV]#\D ˥gżDql1O f}W,P.K'26 >FŢg7\Nqz)C<3gN7u1c|DDDT"~Խ{w0`O"G}:t@]vӦMtJJJQFUCh4p@ɽ^/^ڵkG:taÆGyvJzaÆ4*B?zj$CzI-  x/222 c1!ONLDp:ܹ3, BBBp5૯BΝl2}<..0L~-Z޽{,c2zju0x70`TTT*+//GLLϰ|FFVZ͆8TVVE7,l6c9r$`ΝHOODZc|/҉twcY֭C΢:LĜB1O1**JW(*;u~PPPłf͚l6c׮]شinV}I`6aXϡ4Lp:ѣqW 7ÁdĠwz,ˈBTTTU۱m6 8>(8UV믿hЫ/w)o^zm3cqyTU #<>kGm:u-܂޽{oD ׋M6n9sInOQؼy3^|E_yyy;tRbΝ())_|SC' }lقo5kP)o N:W^䈢 e\7TZ1cȳԩS>Sx<#ʙg-%IPPfTVV"88O>$֯_e˖b &&$a֭>|8tE_;@Q}p<@799YYYhҤ W_}5>sfAAAJmڴtZ`iF*5mڔRSS)==ڵkGӧO'Y mٲhт]v[|9uԉڷoO~;\.zᇩK.Էo_;PEEFٔA4yd}cǎC=DӦM#MG4ڿ~ڨѣGf~0c=e˖"{w֍dmŸdi{qf2sn:n8ƌq ˚}ߜ/I&=c1vƌ^0ȌkV-A1Po[(nZ/*~#km eqqqgL_csvؖ-[yƀ3}_MƌۤIkc1vަL#F(#:(vHc(J/n׮B3c7"ife|pEYDvz!rczVVsѢE3c]۷wNLL,?|CvzQx#zy?C;G5/Я5c1X2eʓ7pCq}SiVFUUZlEEEa~c1j I_҉'3r92V{^r:剉nݺ5=Я1c1X+..<駟ƹ<}vK JJRRR>4Я-c1X9kjD'qyvz+/>ה1cmݺc-YGܾqڸܑq̊ ߿{@1cfÆ ]㏭ZJ!ƢcKB m\.[ҵ^{3H c1V~Nş|vzXG@Piir0a 1cLm׮ݡiӦUc޽{+SRRr_1c\aa}uܸqťDT54BEEUe!EbŊqqq .%Яc1XADcܹݻwW 06ogw֭1c5Hm)))G=l ^/)Bў={:voѣ~]c1}%~ٵkWziM:uw-?Z0ce{u饗M2pYY`j;KJKpYUn筪*}EYYYyC_XXxi_cҦcƌye˖Gg͚U**CE9b)4/.kQ7o>6`]]v^1c좰o߾6C,11x̙v/zk\ETUՃCm1ܴikvp~É1c좳uaÆ-l޼y?^XTTeTNeTUgų(JM0ʲN}Q~h׮{."ye19r3<35..h[ @doU5QWET.X`CZZZ~dc1ѣG#L2)44d"Dz,Yy5 U}%4f4_PP@>heddl :xk֬1c_^ߧOm/,kh/r-/113<3ic1v~N<ȋNbbb\cǎݻy]DT*\s "K;vԩ@;,,|/** g1c3܆$I$.OTTԱQFaf1)?˃4eSIENDB`filius-2.9.4+ds/src/info/img/switch.png000066400000000000000000000060771506574241500177530ustar00rootroot00000000000000PNG  IHDRF47NN IDATxsUMDAE.{E k(%$2& D#NJҙX3@|TjƱΜNM7nDTB}}wy<ڵkԭ[VZY^z' *#E?-VNP9֠AgAI>}甆GU:SO=%n4nXz)'NqIn{, L>GN {P RmpUy撞^c ~LJܰaV,UH(11Qhڴ;f |MΉ&-7iD ..[vm#*u> mZ=M6wG"@G-ZUww(Xs@ѲeKns O 2l\M-Aam .UXX EEEr`̝7Jv~TM`n͛gxW;eʔJ( 7ʱ[ҳ$HDGCU%[l"`ͪUС"{,[X#䭵SkrH?leee8̮[n]mWCdsW-@iذCC=t$YL"`}k 1 -(#۶e0N¡Csr2y7*<lRL:3 PPg0G&J~g壁js`(\͓3'{o+2%z $_uiqH#Sܡ( Im:PNGS&J[VQnp7wY$U&K'Ma0xE؛B XYd]ت~F\~Dn:Wvއ .@_N z3*co!o{SUM%tmlذA6oޤhU[Px^:ZC(r*\T$zpZlE-X(\.PnFCUoSٵjJʯ?'̓nC~Uo4hzTnx]?;5Tk/9OQYJ}44$n)'5߽{K~.iO(jo޽[8q"$Pɲsr?v>aTWW׃DM6iwysYYY>>>4M'L& H$-ŽoߡC|Ν;O?/ߣEH\fɓmx{-#{O\Oro2zo^*VvtS;vŋϟ?ҫ^{?Ǹ71` nefB{ogggÜKRRRpc]YY$ b[x{׷wރQa *y- mJqq1jgAE;667~GأG(JTm۶m֬FyfNr/VVv믿m6@T*[xܼ֭['$$DVΕ]0{SBЅ? +??6…P_|ŷU("k׮L]hOȁ۟:uj֬YhۓO>qF7 vww'_l2'_mٿׯǽuNՆ+??W|󍹷2d޽{ -M6֭Mѿ=v^:vfy߼yYۯt64xUZFo/4vj[Ƿj~{]UKu4Ԭȳ!U}0]{orJ?of;iىJcr7m+@C+7ޘx}: CBB;99PRӧOwq BΆ; A;'P*6w탂B=-]I'8z{=+W+5}ev۩w8o7~aoOVr^M74'o߮fI- >tNJԜt^k,%7iԬYWi׾RewF mswKQzRw >cǎ;wB>^w?6fq2[wޅӿiӦ6mꫯ_{Ὓ6m:mڴ3fßN!/a{M4x#^F11cloWZ{o-JZhӭ;nf|F/.nba/FaX矈@{9s/MOO'&:d,Β{df˖-M-;\STTaɽCBB ~ٳ;wӧOF_fG1. _wQFׯ_qL)((%mh-[ځ <3&.4i> OU;e.]^#**ިsAQ'fϞMgWO@ڵksӗ@ޕRAcǎݶm1pEsa|m̙7FhֽɨDQ {֤Wzl3gį ^6l=o^ jW-Q7?~?HMmya޵F-@Oo,;w.|{ҏORb1t;{)nQݢY7ik<؀DzF{oGv;US8՛t/Ilrر>þߤˬqBBK|||RRR~bcc!.^ϧZGQ6yZqo(7n@?H?Hƍ\gSj NsÆ ܠ<<sss ăS7a„{L,O:䏟d@T y88tPyyUd8qg3BS~lZ\ݎhH=hNtlt P*mP626b})!(*=P:v _j8nݺ r`ȄC8i8R{n\0M2$$$22ÇKKKU[0\.%&s?Ms76qѰݺu1$3F!QFiSeQ@cݻgb :sz!MMyL{zlALc2 <" qd5Z^0ӛk[e*CII ZGm*L{cǎ{G@w BJvj=@Q|W!|vZPUEo,Iؿ4e;5eMܤ/tzѢEp`$r"$0|C@\h>@;by ǏO>iӦd+Z+ed]jj*"-9x,0oo.LMT7A[?Pre2^h6hXn3=K/Z it5T3eܠD^%^޽ыU|t4cOHc K"C Ö \Z7{o4"'kw-7W ЌHS.Քd[37 5.$߂7T,mB98{n>[3ʯ0]SxZeTL{z-WCz2Z˛DJ;{^P>(<0T@H$*((puu---mѢEqqqyyJ9s&O~洓 -uƗ-J Gd TDAS.h9~F7ndYf%''?vaÆQ0bf~#=R={6~2!!!mڴIHHGAw}{ޕa®\>>>,Xޮ];xyyyH-׾&0rȹC.\pwwGWuք pE"?~ZZZvv\.;w. /ڿect{̛7?.h"P}Μ9PsdL G\=iLEdnC:wͮ+77>oʃ m֬PO?֭[++ʜDN0>hٲ;U l䆍ʯ9hu $5:)vdWri*fkg9&^.++31=Z|X\ lr4{zy0d~? o8V;ϧ믿~x$6D{`:ĩHy89'OF`;x;wN:/STCO}h@V9Xgj1G$=7-+٩>5yY( 6lH^%_oܸ=VL/{&y˛4i_Co\t_ԺLnkfee=m%ʦ-1c/ϑ孠iehݺuei@ -T*~*_eaaz!r|ufr zxxsύ2@yH%%%ŕ*alFAɦ' i@ϝ;']|lٲe߾}˖-&6lݻ{yyD`+~sŊoRE[l>/_X̆-G233CBB!www! K_z:[͛gΜ0Z~;m۶;g5={+VK;|6dRRRȴY-ڷtIg ȼj* xy_ kj*9Ϛ5 d-iS`iSCa /Z[`PC~Io߸q޽{nݪؿ1c:={Z7 @mB[uРAp!{/t8-/+ɚ-]=p@m>l /4,Xmhu7rf*(я>G') .{Y⟑~zZ#GzktXk0=Od[=iՋ=Ȫ޵kWISY'  ^mBAs)(4hos23颪_wAALLw6<seEU#z>)ß}Ytp=Q!_ɾUym-OVl95Ǝ& (9W M~f3[^golKGw[A`{7S-?tSZ^)gKs檋J.ܜh4pZ Ӹ3.wc(gkJE4{3Ȭ Bk2T{T/ȩ;3./h4Laën> ]9qJ̸y1g3n$ 5jMj\c0;ڔ Oٜt^Q<ܾ!"nZ3+/cg*װ<P ZNg 2R'LS ȷQ%Wg <}Y~[1odޠh+0 VJ97Y^BNnEm 磟_|/11qƍ ]HY978x"D6ſ-OHH8r@__ߠI&ZO7ЌRnuGSgk \ߕ>ܹ3`@8u-EEEkڴ+w;\5K,A3a9**޽{sy771))cT][?uiyVV ,Zߢo6$5}-Z@Nܵi}ZN;\ámX(͛7{yy曠:z;"-[[;w.0QTh8(p4o߾=ۺu+ʕ+!^{M*}7Q҆-\CP鯽+oσyׯ_/((+۷Xu#0YTKzz>jiBFV6N0bIveD`D]IFx:dJY5d[sՍ7/հ(+-J_UUy}zrdP;w^=O$HBRG -f2R=)Kؒͳ~qKWEGߒC:czȑ#tI"Hh"wr%unc)!b D._d%e ""EnhԩSN*4CSv Yi{9)P;{.'xDwyz'LPG!,VA7-0On5AW~)|pkMi\ 9t?FVBQʊ ED'%% >圫ܼ H]b5jon1=5J>Z(he9>5J9ӊrngo+;&&rJu=s۵kӦwb[?:x27b~ȉ8q6QͲjJUr%BL3"wU U!{ ϟ?,!`//xs=sθŋ]v@/ {jP+1yt ΀bnnn|򉿿?;w\(s0j&[н{wt &r)(&MA}3kemvp̢=A6A/mڴ!X'P!… Ϟ=@6mZvv6=d.^pȑ#aut1Qƒókݺuƍn9OE/o ~ VE״qƭXp ]jC1<_᫃pc??l0mGd5>22[=I䡗]ҏ?cݺu5:6%0&ȫ\Z w4:w\}Yf donMF_0U*Z t\w +*P_ k@y2dłKo&=+Ƞ}E+WlXɠ'lӵڻw mՋR*ňx_޽{eWjAa!)^/u=y(à rܩSW]VoUG@JJZ6GFBž׵o" ~ٲeۇvw_ˊ+޽իW.Xk@KKKu`Y9+%%%TUKC f8yv]o,vIII_|ѭ@Z 퐶ݥ|ݺu ..`1~bd j{P͛6m4$O?d:Kx.*o>4 ]2[ZՅ)jΜ9d Ν; ۦލwMc`H@'&.т_42̙|e 5j?V9=گy}emVuVeiJKK钔:uܹsdg<,!?ŋ6:')jG <֭?.`iBB|Zh /5rAN (3v!Z3 t%pẻ~Չ/<?8;wm=}յvޒAVx쭷ނy`̘1[uU`CNyZ;C",<$B̤xΝ;]'x-7ع_uqZ*2׮]pS= =Ltرc6lh:);v x≀K`2 oܸqWXѭ[79;f}yUSa8>Ӛțrɧ~:88 3Y=ydvm {EBhd}̄CŠ##4hП QQ_|\;P5"ҥKSbd;wʎ8g(/ ;+ùxUΰ5ePЖ-[ n9nRtѢEgg# sϽj^EX] BI*]"@KHrww?X}Yw5}vn+O5)y?NdMA3GݷF"G@k.j1zhMWZM^юDzeJJJ@hf==e{,FDyE[OXR,dV͛6mjB#G GXY3̚5۷dDA//lte\r%Բ7RJ${nS*^;G=׻\G#S 8: Y.ŋD(U!i.?5㿍u40QEbā-hK4ECZ}e[YQK*e벹M/J/i?~VP:QEU:6menIMb>5m5ӌHi)'FB{pk rh 1%[Vuf/bRSSei:~6gnI^HZF;Q* R3H$ua2VVqoBE* B2ܯFAeD䈯6RE3bډ_YJ;{ 1 :{R:BH{r+*Va{ҥT@8[0%"?ydq]MY!J"^t5^܊̮^"nQfo_up_!xoݏvq7h  '̒ӧ.d_V)iWeYN8+ŨYԔFM!T+QSN;{ LW_Q@"Gx IK$%Kddd$%%9;;nzpJ,^qt~wn1ފX%4YqNj:Z1,FAZ//rI&qqqo&U*߇KR_T?δbpYu;hspȪ/-[࣏>rrrtiБh.p\/_<++sٍ5c~q#."cS_P>\uq)FQ":Z'reIaE > 6PNOO6jի...aaaȀ>%B6%|3h:'UQp] iȴpʕ+_.\X^^/ͭ ِ4L{TT۷o_\\|uy]v޽*]aA GzbbV׮jP111wޅ,`u3g[u+OoÓpaqaz쉯׮]^Pז7@{C0|B-2(s0ᱱ>>>P2/C800 pj@}&0'OwiV#Bbꃙ3gB ~nn.:xo{T\N^'ǐ"N^b2ŋ!'OxKXtaa[-Z _Ð@z(cǎM:u̘16xm۲,J(ʱ6rآ#*?Eܰ( TzJmfnͩ 2 MGUN;e~N.P;pwd,8[yƍ¬Rk ʼn2_̫JJJ9|p$-(ʊz-xsDj//޽{;;;L"tҟբaRs& H\\\X6`[rTLPV`TT9߹Eۗ(++nܸM+ yzzAc"?+J&S[,_dDӽ{wަ rhuNjeJFQhV!{fz=z2d+#iv޽o W+ǿZN+AtRV<ׯڭ[ &Yeav}~aKܫ VUedWӜyԤӧ׃p1b-LgSb'ZD{^˸%NnߞԠA%E\B$_I,eWUi'wZɞw YyUd٣TV9]B'4eɒ%6\4eIH̱\ aK5;.ؕ2R7ՋrJ)yF^J;^$-f8.ޙ8kÆ SE]Ԯ[N_KsX'7etn5tYFJXT"ԅjgÇJrfD͉܍vvDį?y;{R7bgPbnhVxɺUVVV!T:Q f49DE+eғq`}aYT#v҈%nRTZ j%e MSɪT4CQp׉>k\<(f $Ÿv3*F\`|F 'Eye&V8ED_tȰ;ٹp(~18VUB)n4Mk Ufc2P*r3H|7"///5jԽ{_tG㝦q)垦iŲFEx;+vKhb$,#f u@YWj:##lI8yKS[lshg/yڇU(í dsqo_5=H,޻wqԁ;_TT߰aCՙǏߥKΝ۝텋q^%8Iswkp?<77rrrr6mz p,ct+Wzyy 4hȑۅiRJ3kn!~4g+T qHD2 gڵon޼yjj[XXX||<K$􄄄2Vد{>}7.4Ēn$qC9q`\S]Z+tݻw-[F6r R`͚5;vXRRrEhftȆ2L4mt6Cg?HnKrO5;׭M,v;_T A"/_F={;wz)q* .@~ox p&Fʕ͕Ǐ_kN(t̢U)))a:tԩSll_N}hxmGDJqqqF@mۂ8չs[n@|P-Z~㝲W~fn aaKKKE"6|7;v쀻 rED'&&ְ.z:dWĉ!iԨ>[dcOf\4 Y &3f̙3gn݊Dnٲ%T=.AHKKC~h{8}͛0p љ`WXCش29 v" 'uS`'d]BW\ӧOGEEeff&%%Aσ.]B޽;.A?!]=zK( /, h;T#H 6p@t;W_'OF C؛7o%3"",ޥK\UI&"(%D 6ki^6mA~!#B#\1bDnCĉ~:vp: K "-az-Ç׃p1dVL{===Ν ȑ#deOT۷/ BC7n]#~rC=dԅ˟~5kݻZ23P%r^Bgdd eȵ |~꿋]nxJ{~gx2T&OHH,U.̮'f++rY|[MxQmWBe Te?hРj5,j>}@#zoJf0O~zu3yeM7Sh<M6YȣZm 5Y.BQ#FX`AvvEjap5g4ZkEڵkȑPU++UʃArBpwwDv袣W\9m4{ʀ\*[VVSnnnXODSN7&##CF R^׭?"ϟ?v;vػ"VMC&I$*{|y߾}.]ܤIóIII8uگ'M(KߙHRDNƍO8qӦM8quu q㆝X)An0acbl>i hŋ۵kGN;yLF _ Z{eQ~zo~ذa Q6Ru/iСCPW^uLdHLLBLy EӦM!7nܼysͶс[nQ;7$}"88< ·Q™نGZ_v, vUP khumߣG?+L^r\ 7" VDZ4p\+lب +eEEEyvV?eׂdU "W8]7 v=;;m۶5@9ތPhj]-d^sP۷Ϙ1_jݺak&%%W!I  aN2XrۺlP#PpP)zA#x@HCiiW7x7[<(*8qO?4HAeZ]4ܹssXX'H7oބ#/?&uSL![uO...g``ݻ0WVfqkoS&7k!gNu=T}rӨ{"ǯдiSijDD?kɍ>aDB*|ߧK ?fGQ^zNY쓛F9U///- i];''H>~_5͚6׎-[fYo߾g%>MQD/0ϡ:zI'7:Z^^}hݻdO2eM^tY믿Əe˖ZmЮ{ݞuOd)YhxΝr9y@Dn9ɣz(mWʻv|'sέ= <̙3dZ <;>336ꊋ!l~ӗ>~:8r~~~ 6Ͷm޼Bo\pC(gAԩӣ>jں'3f@ǎ+gD3ET*yhhHs7=axoosφ5 !}]fڵkdR8^4AVÈxzo>?|9o߾ ӣ}n!@_?cڴiPD+^qM-\ts?hMďl#<2^sf/i?pa.}2(V&>SRR.]^dLQQQd\M{qOPÁwG9Y3]w1lG?TOZ7H{.\nʊEگsgll,+wuLեY1Ǐ?Q ǩՋڷoߘ1cu; |~9 ʱ;|aÆ? 7nݺ[7ހ`lfcNvHH?H?Ӛ5kK`\~ѥfҫ*-@Š˗?䉑7o)oe("2dK&)IgzBkHdyRRҲeW>,, 7({l_~^[xqϞ=kRނtIY.UX)!*X\PP;*>>̮z}}߿*2%y%~gIIIyyѝLv߿g駟BV qlKGN@fFsX!o(p^P@}ݢ2 n,`8,P5/iwff&>J%%K$|5 }HjeŎ g =#z &|7srte2+u[jEm,FD#$7n &ci&%5u=@PP/zjpd kye9luk Q#mu:thok^reԩv {キh"(M4G1fr勏JV͛=zl9&-i?>Ҿ;f+r=ztύRQ0V[>|w,sAg7K !/ {h@f{S}ܹD3f̰+ҥK7_R*T*eR׾5RQtЁx:Ǝ }.ȻJa[X.rOPJ`JUBr WMoV!7˯111  jˡiN+g\O)zM6FLb: ,u75`JIAJ iV^p)eJePGү:5)""bmcǎm2ˆᘭTR r]^ъDy{R̼۷'̛7NebQF$2(ǀ$Qɡ\5TI;gty)>yh%,W/+ޜ!vJ+M8)󼼼W_}z7G2jcu/c>n* R dO\"uiWvky aJj{EEE\>w\ڵkb{wtUi^C4%RDq<ˉ8C+*&}\߹sMJJڲeKiiijjP˖-UV_~C y(:7S'N79ri x %v#pV߁cz_ CBB֬Yt].]dee@8(.. m߾}ӦM?ѣ[F\p7Lc vg~\w$RE9p Sbކ8===|_~&^[Vڴis9;B2 t' '=c׮]ٳ5;>髳5C`AnNh40؃4WX̭7uTT Anm۶`n޼YRRt(|dgg9s?D=o޼]ہϋq.=ͰjEҽ scNҐٯDw qB w``{BBR'7nܰaCPtv4<7p)KMΚ5kİGW^'97ˌ./v&oH@"hȑ0̱0999P nqdܭ[K.!3ٝ f=2Qx|}}6zmTMѧ_m㡿ZFAn#ʒ㘘Ν;={vڵRD'aő Otdd$qGQڵG9dWBd[t)òe˦͑#-ȡ/\ xW ℑ޺ukPPH رc`<9 P z-Qp(h~ݷd>8C~ ̳1_~mȵk׮y`8} p '2@$~#/ѣɓ'coDhZWխz_TO@Pxd.y<&[ʴ([r!JImVg+XR)N\?p쌧DCB9uuǎF1ȉ -Hj%Gu<չHT)Xۀ[}ҪNqj|U)dYcn]鐐} C$rJ~~٢SUpp114#v _-P #{(VCINWp[mìV hEJ~ĕ*TJ4tߵm )|a~Ȯ8&#۷WU^$ W[Tsn>; I򶚪`xm&T'*@_+.rnv1%(Jxf*Ν;WTL%g(fiΆs>9V]ܟZŋSys?i*tG~|sJ^VÇag2iQ:|uFR)rbj oDEf^b)¸xp7GNY|R#?UEyE<^ApWqdiAV#hN![rL {]/---,,T߭[~F7Fs1s=~6>) 2M8pes22=Z O~P spa?3ΰȣ&9ʴm־?UHJޜ SRRnݚ^TTt}9;;7o|С-Z54i2bvVVֵkw~ٗ|2!SSƗ枥sfxVL#t63HfёA{޾C #LnJH$ t&HN{q… NrrrB"!Cu׫WWp@bR RJd&H̑3 Olqcv<3P#w ]D6W/rLvVS$P^+={,+++..D ٳg/^HMM -2#W6maVZzjTӷo_X{h= "n?݉WA}Pgx7o٬ءv~+GtBzj]-a}||@HOOOX=H ]&00<1Gԍe˖(>9JBp'd޾}͛p>!{嶄 JW&Eظisod-EF^H 9mp߼]_rp&:33,D"h| p5 CZFDi7Aud)C.R:u߿pp0l~?wBpF6ޱdΌ D C!r؈pP0 AZpl3IOO~:nV`,\th s[aÆ^<p .lܸ1ANHW P3g>|X%?SH޵M.ܨZVVک3wlp/8 yJXX^Jѡt/%qu V&9$H\yHp8v`;/8`>&&&>alPf)(f!mm?55L۴iҐ%dggpaC|@E!340v# j4s^pg4nw%겈nV&9ؘn{6^ 9rԨQ$ٳNu\N >p6mDw|<@~G"l8Ȍ7oNxbAq@ƍ+{hb|뵰7y/Ewsꉒ͋NS!7:`Z111;v$p'''Æ'jpԅ' 񉳰à.žÇcQ,q+m)O ԓlۡK"G  &%&2p0Bz |@0$(JՆlsD)>}Y8"C2˗ztt4ґ9..eM4-v4{SJPĕq;dh۶c=FvVB8@8 C#ݻwGQ2$W2yF[s@Q+Wۑطoߙ3g j{Z+ȃ''sf`)9\q #Q׭[ ߹s^>jUk+À[nM{ ZmNӄzCS@&H󋊊6VU&OdpQ%xFI5U-$ڽBi 6& —,:PZƒ̇v222ȈhdEmwVXx 0^ p8"{ЦàzII ycXlLF&ǃ [VjyŁcbb.]*P]QHnj='p`;d{ k.3g ;e@QW0{aÆ6lG \.%AD"wݺuSN6lXtt4 e!GI齶nz֭iӦ0`@=zmZYh {!ܻw={`<8g,j@:vW JE*I7Z=cK.]j՚5kk}br1"|͠K?ӗ[F;Z/F>yq 4h̙ua7ңQM4je&X[رc˖-֭TnjZZ6&1BjSJhi6QfU6me75QgyfԨQ0/OB],[´VG^˶mۦM@]Ѽ;*?߽{oaVs/9/lňV*&[nZmbfUwݡrJD{[ti/_e7^Cx}a-xaY $_bx>gΜx}ɒ%Wa酇')0 III(M |eEImE 'C?wv(^5'L'nQ aiKKK&m=ZH^KXjuxp79l˖-=ܽ @vmDd |‘ Z BL^"\;ӧ{XXhL2x^dgKJJpV{ ΂`;:.qI~h={n7<"ý:$'N?sLrr2j2zM6i*Ayƍа|ɝ;w 3pӡ!2g999 +VV;va O>pM7Jw}A _|b!*gOZZR@ R]SPЎhws[EhV\ҵkVQCoJy P1աJ8MFcbW^; /Y;[ ?Py@q Ў5,Y 쯿JSqX\Z>0kv+OB y-_~vk8P0 g3<~YFjzlm2釟9B/r11S2 u\SxfB zO&!ʅ @Jbb"Q<2ڵ_f'M}TIkff&lDcǎ=z*b1|9sbŵ5@/~ztDW30]!`~HQzmWv⁃<3<%;*z톃sA_=FݲeK;6*QC^`HkѢ@ݨW^0999֚*C y ڵk{ra8dBBn>Y|(:; 0 kwyo<}g}Fuҥae8q}.H^Ku֎;jb~}n۶%0Ƞ}# qe&v'OqR6m mtdK."M\+]MGiӴ|t$.zĮFZ  ߻Օ0ի/_> ð={ t'o [qrZ;k׮脨!ÅGh$">rȂ (n/_܄u߰aCys'z˛,]ϳlsQ j*HNkȱ:"c;G{%%.*.Q\{_Nj+/--fEݻwojUP+V9&w^BBBq;-'J#GS.r(6o%Grb<)O'NN8` ?7Of9ArmUiSJmV+V=￧Z ]g>m}z|6qeuCM܍{g9 ƙVf nst%1,M%UG"v+-Rb)-uE^9n+,Ἒ^N*?hL#\6ք%_DW^Jφ}2V"Fs>B;|0˲e\M3,矋)⫋x3NDQ^vPZL/?9_ܧ+Ai4Z*%WrvzU !BBBt0u]o~QW+nd* άYԩSeeeΗkʲX$9y%R;\:YAr3J/%-vܸtjX?&Jc}2|xnb<?Tr.Aꢢ2>Y.222""bDH$?yC看Oe9YLy"乡8_)>j~ !Аek(w[a :XH+వ#nݺhd2X,s᜜T bGGGx|f͚6maݻw_O(XH"h?gF$⟜-iq@=EWs~]ÿVSY7{F_5ӏZٽ[dIB̰իW]]]=<Z0H'̇@^g{((XBrP-))`rߟ b{zzNfff4 ˜nݺpNr!Cte٠GoJsy w n,9NTLeȬ8nVl/ 5_Jla%Ar^B8r\t z OIIΆaaa?J$www`A\TB\)ȀBۅݔLL]NlT๸b;qi sx;/^=7 PQ5A˼<֝o3pm'''LqIZZL:D9x6C;Xr BʃGFld](~3k7RW;.>9C;]12'f+&Ó;'k]To*%9"m^|O~_~9}||`78w78*׍ydb p 5QANb_;*zmt6g4ѯ%8p1y.> xGhAl0ב&;(\Eh) !RGbnn˗qm۶( \aSSSaG"G4hE!( VnBf` 0cC;v$gϞݿrr2HģҥK85k Zհ sff&8)4iݻzxx8t.G~R1yA@<7|矓{so>l퀫P>\T`h44iٲ%iNBZ0&;ԅa`\w 986dȐGyҥ y)6|`Q>XG͇C wy&DAT R1 ;"BQIzgP:(܆_@w6ac'VBlhLeӦMvS_ !l'oO<7Dyz[KAzwڕW(ܦ܆m@d8d^>'Ar_vb\>} a0xK\w~ ]{&AaAx޽W^Ap='sf`j *+yl$Ao$_ u__@d 2GĎBxXge0966VHy0,6'( Ғ7pIr8<R)Ѿ}f͚YPj%ڨXVB^+lCLM#a6yU\\ -W"𸸸G odݪںXVB^X&ړ6<^GXXСC{/@'' o^^^vCr P!\z 7g?՜{Rʕ+PAnϦwalh 2MK! I[k^&.@Ҵ_O=H4ujeDSzen!H]6%mee,QHnԒ"lQ8x3ZH8i4|TrM<0NrC˜vMʎMfVR#49?yy/VBZ&gI9IENDB`filius-2.9.4+ds/src/info/img/terminal.png000066400000000000000000000040451506574241500202560ustar00rootroot00000000000000PNG  IHDR@@iqIDATxWLX+*( v+{71F%F<\|DcrcTwr\عv?xO3|7gٝ~߭ƷZop "(iG--- r'p^7nPMFEn޼IHXpdѢEŋK,YXtdٲe˗KVX!YrdժUիWK֬YCyyyU mn"FV<<<ӓjժEk&///Jߟ(00DR:u(88BBBnݺJգK6l(iԨ5nܘ(<<֭[& Bmo&ƊԤIjڴ)_^wMܹswJ{{{EYfԼysjѢmذAw- ݻ SN۷W4lڴIw- ޽{ĸZz4d)\^OHCجtV$QQQԺukڲeZq4z@x֭{ȑjРi-[:nӦ mۖ5xzÇ}疓z7N9gʞ>uvJw^x 1~vtݻOii'mMV4«*i4* ̮*Ohb \ތLGng2*aHGԳgOի߿_={F;ە+Q.rq鲫 XQ_r!x16+ _Y1HUKb*Yi;+JO ҃ Rk܂aV:t( 6RRRDL^"ƊҘ.wffrU6+ * avFku 6(**"Yi}3+ a6+=j()=f oǎS*+Jc=~x0a\ Ɗ'NKɓ'˕)8aݣ1gA̬4IO2E~8m48cW@qq11V>}:͘1CrIx1VAx=16+ av>ubWT& a6+btcEiUb(oUGbA̬4JC@U ӧO8+ܿ>xeWϟ} 1V9}rU|GagAW]U߾}#Ɗ:aV A>+;f^X(3Hm-:/8 XAm̙[ K~AU 5m,(z*]r!x`[}]qNpob<:+kDRK"Wϙ3bcߪ>΁32 y=`PZ@%E]yCa.8iW5<%ruI^2vYQ|𭖩qL 7Ը%tj\+AD祥b0{y@1 qfhAA\9`KFbc߸ _wLj\vVTTT,*&Řݹ ASSS)8Z YقmleSdy&w-ld%X]le,jBK]xO5] ͊_tr]3ozIENDB`filius-2.9.4+ds/src/info/img/textfield.png000066400000000000000000000033341506574241500204330ustar00rootroot00000000000000PNG  IHDR((mIDATxՙkPTeKtې4c_jj4StPҜ(un35Xi99$/(*rkHKS$d 9{ ]M;yϞ?Jx͞=gnr%\di~pCN+]?08/;w)NM+WNGF[ 1044 (;'\$ɂ|d8'dV19MW_觾(%Uv؝Nl,N! TucHt<=?U@AVF){iI2\Ce A.GAX,F![9Q쾿R rIYS--vX%_A,liZVS& jEy%gBA<]cF%ŗ)&vl6~fbAJLf Ei ]L^؆vA҉kx#p-}~c:. KWs>AфEO&.%hERI7]4!8~3b_p7z *nH5{c\R$ I iAxZ]Ab c6tjJGo~0`OwW RbZD,Ui m_R 1zo_zL:*mQn<žުƖ"3VeAselbI^ܡb @ g:׋&$EuZ1Ay5 FdA_OޠdžCH:c{M݅.lޠ}.$b',<UBedgd2HO⥧gSӨD8^Z ^Z312^R3^S)4^b*C /1-HRӓxIqX8ޫ'G\!2)es B,E|y\bK/|z]ՉfQDoL%O|*[&1 hE]S; QD5v4B57|lj$PKdE(~J:QMAB Y]ȨRj$*kZՂJ>3sSTddt#!L_E]Z E-Fzn)rʐ].Grj SꨬBrZӋ؋2'P).SJfd >+ iygixYj<|UIpq!=}}.|bs [ob5<'Cp"<=}bTzvٟPPĈPVZfE--7(CIU+QXل&U4"y"E4%=/%+ke䕋]Ҍb^J+񺢔f(q%/R׈+EJe=+:pi ,4Yb?X` ̵8G0‰Lj~S7b!L7 mc;7Fصn&jٲm'3̜3˰̜V EH/FFq*)oAVq2NVuVRrErI [t^1 ,z˙z拠'8vȩB|=]nahm4+7L>[a=f8`ƦfMcд W"fq~45=֛l<)^Ԟmu _$8,5H/kBb~!4Zln!eE~ Lղǝ烣q-\"6bwe2VZ]s1v71o77`Ζ`>qm`T`֍y3~#~!R~:_<14D4b={LZ[Fgx%% 2ч.%;' +BPPĢj$<.³,R$T 88quw;7z Ss`7 ֧yX~c MČ]XF^1ofatW+?sfښMF'"Zy7 /0u,AckK]831*ive$09R9 "[AegEeFDvbq(6qv8mWbstzhXư>67ʗpy"&Auւ=ڽsZ`f9uXVC?h1a2ۻSwlM;1"È%C-|f/EEEyi(%@$Ef6. .@Tj63C1{a?{#9\?Ag,Ό:X5YPX^ ɹX/ !0ݣoiiMT N|e'Ao8 CG +G<7 ˓3}0{܈#G^˒{Jha% N^2!!Y8Fhk3+sV!A'ԃW9|<'pzo> }开GZmfe5\˫DoV,N}Ogp! (ޓZl #9MBG=}`18a^)e_Pivz!/i%HiL6lQ\.`2HsV78=z>_aX1]uDs]> =x ID+Ӏ38x uU{W#@[[\L".DYҀip>JHǞd̀Cxϥa?7efX0`p.g9m0iQ72"`HAX%V0:`~9,8.E^}0ƎmpX5>?pPQUOg0%K~[QxjQϳv6{cr! #BK Kΐt؆'6D2!Dt|ϑ`1#0@EK̯ԗ ;viQ '5LwRw}X~)U>M.EpH"F-jj2NPYyXNmҊQU#g4$+؅'c{`Xwz9 T"H@"` ks*fSz,99V R{-யVΌ1  ?$`&6r;'p(:-lb99}1-Q0?Q3Pz 0'lsA؞CHp Nxmu6 +$ i(!#hIwj OL0qSkUQY "@Q{|"${ay , ,L}˜F'=?v^0qr : fh:@Gc "+Npr1dLxviCV||Ӱc- \`#̜e ?>L[qy:EQ=ay.,`|>\G*(zU=뽎 6X;fҀQ.?9 Ţ##؇I$Hb2'?17}"k,^Ck@*}]XwÉ+~ ~ODO:aϮ=Uǂ"}Pˈ卢 6Loœf_[&qXyX~1|~z`% ~2ymp.%.]q؊2h.(ut4d1Ӱ}2%Cwh:h4?Q#k9jGr}{`X;A6Fl/nBf%={.(X 'C9Ǚ XeT4S`~{4÷r\Dz#5,9tKǢQX@i]ܕ轄¯\h\EKn#1iyLbyXxh;fخvmҔ1ȌfЪo:^}ğ1=z[ =a?]g~OF^]7:z9TTΦ_^} Y Y.4qN=vӃ:q\z;Xz$5]*׋4 WrUNQwUOc=,`!,B`; +ݦc6tV@ 6a.,ؾ:\0؎싳 ,h-`݌Vl]޸Z۰zb ,%`>_7(ojm%a;+e4`B]:=+a̜a~ k#,As>UUuz;3:'kc̎/.V;25X 6of',쎥[loXf$JG,m][l@YCwV65BX=~;Y6]C{\^ JkD;h:vn-5S p N^g{^Wl8ssqvu%Mq4;{{v'\Õ{`7ct_վs$r7!7rp L]f=1l> 3W' 2='aw( P:MN0O? ޯ`dxfZ ȯnF e@)]o݄7@B-PB4ӽB:!ېW߈z>r:SMV+r Y"j+AzU ^T!Wؼj 8(YL$肰 M-6(3l("@MTgZ@dwv |Q7BյQވV4л:WjZZPڀ:B ZQX[lLMËTeU@ QH5-I,.LYqi0G Nttvl@؆&p[dl= X]AmF ZhI l rD/jv[Q$@- h (Gznb% KEI]]-@qi JIfRZT)XEU#@Ew"T:4u^A4KoZBhC;ik@r[k[^I]@$?a7:i:a+$C BSnHX[׀65ԣI鈊~rTV@ D5 uEE#jk[_XzNju GUCEQQT^P+UUt/...uvvr3t"t޴ {`vН4`l6׉LEvt D ADv̄onE V VsSdWf" u׉f"7 I Y5BHMhpm*iWZZCe# # nG]!}4T4Tz K6Jʋ+.ZbSXX(b*g BN8iV3fxDPXZZ9 I AD4kUՄzn-DL -hꄐ+C}DNWր"DbP[^ 9RG쾤W0iCd)?5X%6"Rk"LKhiQE_Z7Z:!"y(EaA)r6ɁE NP/ IvW8rzx)jydd/ J˪5p—W!#Rr i$,$TSU`l' x` 6α9=*8Hv>:h[>9n~+ hIPLymPOq{E-|4^’",L~5v$.\fBV5rPTHvW!;f?eee Uz秤қOEsUBbrl[UVL@ YK@!\>@t]w5q;Q)jh0ɧh f[@44adm܎U äp^%j6e<҂z& ) Fy"AfW [v 0`oԣ芆F~;hjo8IfYJ 7qXISgSS[7EMxTKREDtv驨uUtZ:Ƀw s<FK{vرcܝɍWYZV w]?~4UUUqܻ"EEECqӱ`WXf V_r+WZen:{###k.\(GlllOTTT˗/{X=&&ŋ=O<鉋NO.MM'OJ=z4Mq#Qϲe9]]h!̾!%CWRRRw`+UTT8:V.gȑ#׮]Ý;w+W֭[|2nܸ/\ ƦMyȶc$^as|_0ݻW͛\{.Gƈt(d'!?kIB}7̝;w~_(I/ ޫ!6hهY]ѧO?že/s?դHHH3V5kIII >}^b$|&G]]]G ,---9G}GV&{4[OFL,Q"n&)))tN,yyy5֎_h4i(~@"X &ӽArRH/RB`ϳ=>q "UB0Y~7lY]z==x.pԨQ_KA~JF<5dȐ7h38ظ㑝(KQVH~8Z" 34+ fw11sB22&h4uvNWWח͍~"c}h|Aqɒ%=$ur_i ޣCN /fT&&&r1f2H^g$1H}3"LilfGQޟH>OU* X&ػPYb5o%hFSW6 uttN-]4e÷oܸcG-uww/=|pKKݻHlmmsє377KIϯ齣"aIV@~"BQΖ!reaЏ1/dϙ/䥇P-쌥N"LФ 5MG]HXL,%▒ito>C"LTN@1 a0]"9FE%rWb"2wI)# e2|O¨4}rBB%>"|Lm?{>) k˞z?@l"X,{Tϣ@&?9¿<{V>ٱ,`_d9J5&#aC_ ~?} NLHgN6P[6.ϐiT%AVcd?!AG6Yy?|l%ÿz_ :IENDB`filius-2.9.4+ds/src/info/img/webbrowser.png000066400000000000000000000157361506574241500206350ustar00rootroot00000000000000PNG  IHDR@@iqIDATx[TTW> C),(5$hL&"c"(콁 Q$H/ҫH>wD|kw` +{.DbzK~ì>S!wW&XB q)p䁑ӭw,V0z"&Shn"y9E5]{v= G? s-' |jl7\wAXҚ,ÉsOv uu^mǴV@HAYVUV,#׬_g2deEb5 eMvg/*׭exXvKvY7wFQ nƕ/"pgjk'e^ܲºݨ`{x=EObj憒eS2P7jvn8ܿYQ@w@$J q~/#U? }qp#" S[3 6اw>PH}fV"΂j|Z\ \{|S  {2?P:B.:]sspr?SW=GY;K"`$bKGؔ֋s'wXK[l ۅ# x%#ajlVe^}9E9 3a# _ .{{*ۡ&,oPSr Ş{iq+!S:ڭ}gam/sx#%Sϡ*we Jg)ܒ&oPݡk"'zjgkf Q{ 640`[ iʖ}uX0QUSA{7Fo<Ήy!_[UR'R1x(,]llv d礣w4\"CLo"l \PԟOϒ`_5'=H1XdU͢^J{%$DLk/*L%lm&_bSE/닣O/KBwNsB#-'Y:q8^:'狯R>'b.pO@uYkZ2O̬4l {\~WWrjy+%8UWǑ#(MjXdXgpV8M>H8f |9wr!>9` NTv:9M-GsCRm<rv'<ѳ1;gL3,ڸXq ~H$dYhA¡/pgsR'LϩeM'3y#3FSKڟRVL&'cu!V=gZX*C7y|!l;.;_Y0PTmaw6^>2`"&KoyϹQКsb)F.AnF<~xw`MOIu,/$x^$/(æT^cCٿ ӌ9FmjXAs FY+cܹ zXt0'E>YEurmOMGRM6k$=%L8HXhuv&OQ9L(zu࿗q?UO#3HtR? w梮{a_iawS={؞"P{y2N=Em#nˮ=Dmpe*ZNz 3.|Ms#o(&))DƏRf$C>KNŞڞ-;LOͳ#q"w.%L|G(QNbo'7Rvw*>Ŷy}6`Y~;ce`I5bx]e`d4X'40 %#)F#! c1Wo}ncqȱ8Y8 ' HTFbgɧYvtj{i%?<xwl2CP%EMh-eypS&^]'G6 ~C̰sg꒍FfW}2oqCx # `olw$썂S2 KN\sakuo!{aky_.=,Ğ*S +C>ĎWp|jkgV5oץ_" a3H<(H[`O߃PYђL(ɂo.6A`%.ت}TE$P+ ߧbџ1#=Nvn><ѐ#̢<ϕyc!;o;vl> |?&U6d* xe~< ?\43I2ŹdCd0ME4`p#@ӑb1kΒHQupI_pZˏ PY̴\$ՐTbX%nxG`uWr1v!M k0a; +kMZ˩'GPD32jcтDDc|n#Ls4D:q[sXBVb.` ~BX -)66Xm*[IѰeI8Qk wѣXNN^'IP\l ߢؔ۝4V˞BPŭ 3^0$U eX7w6V.1Uuui((.~盛Y;sߜ-R-1 >ϭ9gXbxԂTH$4?,O.oӧ@7Q쎊b::ef!`:I}{Vڭ Y|#6gb}|BPž9xn7@+$8/%~7|:auzKwJlNINfmHh5сuVrX<+v)E {xaC -[UfMX`Iw0 1 t;Ϣ\f *6rwû%F ۠64ӶGY?ޛSa4F,q =rT. cU#fPP GNv e6D Gc0pSU&Ͱ"YK(cU|+,}+~vR?s't*IsX_L;t3}AwE,zdXϴ{u :"oI5{lC/P <(JT|3xQ(5zpNTDzg- ;/msZS bt&BE9xIBPiZ;brTcޘZG۴2jҷs !TC! ]r/> P'%S'TKPڴnFAi@ oK,HكpD7}'X&-܎jin8 Orze:/NP،M&yuce']/EQ}qlZaHUI"gDA;HAB=JQX>7y5񊾊:_LܢwW@fIӹ_GEU |.#v[L:o<j9H. 睅6h9]-,kUOT’g'7^{Cݓ`j)a?!xc=h뛄\:7V| ]o*-6XȴZ~m=uT:D:]/aFtg En!G\>8DUr\rpIZFly,$".Ar_~TW&0yE7ySTaӝYؚfM ΗnاBx|th1j]<4$^4/|So }i/t^7FȚÌv}Aќܼ)@.XDiDA9PZ=>~8z=3$a;×Cp$dw! d0Uՙl@te3"~.XF;Ӱ<* h :;v{@kG ā?S-l]eo/%;W$gqq̢ww:D*||[X&lmE1 0/@;赣8=+?G/UJ6BW$G{wTޗY 5&ksD¦\0? Z ~|ӓd:L#NIv>{p0kof*cfw!n}g:Nxl - *lqY|3TnaFd"YxX$4cGaw'Iq7Xy~o\O1_%?&/\6gžw (eINlm̖Y <TjѠI%%%Ru OpZߜ#|')xYILΔTzN9}>LRsW6G2~> W$h2 {W6Z`s~wS ϗJ=[{Tq:ŒBwX,Z.trWJ_qiZڲΛٜ㗘_Rڸ}'ҼJρrj:|Ny>R|}KVQ)a:Й+RYysl]xTI=|Cѵ,AuEv)2}Ss/Gpq2X 6X<@s7*Mcl+lm]*14b09cK3'_-:l+ U~s3C( iMbzzLYM g du0fO`=ڰpv4#dg_԰/kehLq+5rW.&LVve~p_IYr%7WҪXLWOpKz#^_Rmxdw:M9`];dU^ԔIENDB`filius-2.9.4+ds/src/info/img/webdoc1.png000066400000000000000000002634341506574241500200000ustar00rootroot00000000000000PNG  IHDR sRGBIDATx^?_!INki)"-. +nH@B"E$@^.vgwvVy/gf}'W]^^jڊkWz2 tHC@[ L<[z\VVZ9guyye'}~ns~}GYjKhUSMM/MRMR-_$fm٨-j#O ۚRVV2xIךA#()L:ZjRZ`)$V6WffR_p2{&Ԝvp¼O9-q.prY5|ǯN=Ovwy75ӎ;׿[9?vj{וW]}?m"q9e1Vj"fL)|jZNȢlyJ(@{pb_ȚѼApAxLZUvsT[M%Wr*AnOflT-daꬊ>Ťh%ȵ0[ Y^5QW1}%e0򓛛 n*5s@~@*[TivA E͖f֐"Wx?c(lot/ܒ`#ձK5O͐+BmqMNZ7]Wo<Q{0 8pMPޫēNmh"Ig=Qu*]wrk6(|S|ֹ5_΃(|@f27^/X報E EL`5%%C_ċkj?G.?om*ĕW]A?\Acs@msj 9ꀴr7#⛓+N|e -RUU'%R3&('V_7vv]6睊Db ? Rqeh[N5n:6xqT Tг4?Ӫ!r i$؋vLk8usK{).6·MmM-MW&!"YY P)ȿ@SPG 6kE 8t[[zyw&jʹsg/D:0"n^8<"xOXU]ĚZՐD+c(TlIƋ2o$Oc&)y۬~ 9*jjqb'2L)OZ׬ЌPWlF[ЉF ö[.PoAa녭n[cvAd}d[eD@]A0-4컱g`I~[((b$1aJd5M_'2dzJ*}$_%GO\cs-kgM'RcY&[p֩aYoܾ[74Y2*T?SjPL+\HzSSaރgs.XtYL,"Y۲P5d~5Hpaf_t[׌Oi݀%÷K* u/> r^{9#nj𣏖V42C!@u@vʠB1Re~M]j_31\JJGF4^DaBu.TG)#GG} 9d3^sZ: KaC3Ug ho2D(kp,vY\W8"ʙhTZ^$ 4[GA>rx6+eTL|0rjZBۖ?+c/l cb Dd Q8ё<#謬o@,`Ii#$y4\m޼]'v>j5-gvJ g~ZEys&NG3f|NNz~}Awe ŵgfe=N~icߎw%B BK`ځ?UaBk8yVg]p]/C h>GɓP\aH%B']۠ƾR#b Tf dUfPuT$i䰓e?9`!{ m4%[sY]WuMٻ.il֮/S+ܾNn䜆0ݍipY MN=zjJOB&*Ή܃1Ȳ)JT]^1+\sn˨RJf$B*3MDUQpD"v c\OIRBn5󛄇D Q*zޚs5w;2꠬^ЁJ<PMqSMDCRnV%*#'H w!YNbA~8P.E4uF$&8kׁD iq#:i5XU$FE4Rܰ!08ULkI!4 ҏ4kx$%t/"vR/)M{JXꅩh܇/\LlZn<5$e-Pݸ},ҺT`?}U56Jz2|s9߼I@&rX:ဒzW% qb-J`O?9^T[&*///??~ :8.o(VX"@k\nya}RZq˼;{l}{_ կlQS-?} <1{p-{Gz\4T6]kꌇݺlҖO|tނMvÁE7CMH-?a y쯘`Ip??i |75tqѧO/\Px˗/Y }}M>u܅K+gܳ`:'ʥ2{x?O'޼*nWat_!tLaynXtYԲfT 4öfi`"4|#W]n v9W^o*ŵgmF-`ly/J暚5+"~ϻkj{̣ώ @PjŻ<8Lk[Ux̥Kg2'$wϩ v3?H|aRm*&D/U=:җWf}ou_oR2_>zx'HܥXbuϬ[ߓկ'/]0}4̚W9kZQ'scZ9:rD<Ӷ~pOTµ/wЬR5H\$FӈK uY Ie>Q ~B+r5иf>t35әP.n픭v=B^Zp".pPۗ$bY|/:*PΥ")e) MguM)SeiSQPUq\,-BtʣU kc+/"dx̶HH{֟d3/^>ngd9\3y}NjEqc(rS­V/&!T }b9ݻwڕ6z ν6D_>nĴpVJU(ȁ*/h BX} {~vnAP;~+<__Pϕ }Xͺ"A'=I|G}jes}gmi̓{|mCzi/DAm"w{<-d̹wnehΧJ'eV/z*3?ka+WSFϩHxۀjF9lDo=ɔ1MyKy +ίZ.k"r\~L ߣ_y~OgF_wAO( b]?pkHmQV|9t^jx\=jU2` 50TM#$=՘5- ɞ_Eh\ 1) e 6@{+*NU)+v:zSsWGja(! )0r0cq *tB#iu#?iD͗jZ#6L@f`5Q3Jdub@Z, LnI}c4-9J^cJ4 6 f:Y e]'5{YZa!Je?]g_CpYj0n D[Ɩ¼+0,}ri=dɧw-͕ǀ^mݼ\s*v*ug{baEwY} :w߼.];w̓o׮9]ʠNn=.p%%yesd 4Z[&w[&ʓv[Z[><[~SN9#9r$/,, :NO{䣟7/m>p.8Z"3̌#.NO\sU`duW.^ ~. Neҫ, Ʋ_͓>k&: %ndg{qJlyt}ץi?[]dFUACOzWeuqjf:m,@sn9~:E z~noۅnye>{1[ |[>>pԨ~7Þ#vIi[~"^^öo"_(G8[pd8tf[X6`VGx In;-ZpqdLX]R_NI`FjU&ˍnWq@^I\lL$(z; ShH 7")|1-dL]3y6U MmcW_׻kΝ. |8|DA[!0Fstue(/*蹰sYAΝt.ܥKQYBQ_q`C.eE;D? ?EPtDۮ!"h.gqn}jµ&mg)gWag*q6l؆sPm߀WCbBq{~x/oW%`Mmu# 7CynVd̾v_b׆19ylyFƵS:D%v}3^fčO_nڳ/@=>.?Cܙų99t_ |S+7Nq>/q1sYy@xM<'[] gYAZ|BKqqEcnŨP\dc@Wd4f])=/PԿ˔?zCøE Fg;J,Rŧu)&SBpHn'[ʞvu(L>BnX&Khy ĺM[o}j+1'3OG;]s(uzf Zst, yr*cvK$k?jwOWҭSvkJ)8gfbgz"4x$+f@J-G{>ba&D R2w$rNB(@O#)g hHT'b){Y%ck|*{o )VQ9ٛ0^xcXH[gLO)& MS=D r6HG:jTT|Pee0̀XZN8{g^|mΧ3˗qHJT+q#I +13#NLiݠYѺ(TP!̔Vhg܆gDvUЏg9.mzXRTl"6oTfNH _7#gJ+mTtM=; }ŲٽlZw"qWW$g((u&6Hd(/Gp`p)4+& O$V.+jy܌R~=ǧ>ZK}GO+)R* ,b RᴏZbʘ{PBю82ĉ(rXl!BMBÖ6eDS$y@'LX=SmTBf1L=LNztY頙Iʒ|kxnz &Rvm&wd@j<|*ͮ8qY9s? .5ngjC!pmN75B4H:l,۪`YjmueId!@[}uhhV&")F#O2naZf׺Ektlx^Ud UVu_].AEZ̅1S҇4ѳ4."D,GTx, QC$cl:K#+ggw'rY{*hD)fm9ԚʇDa+z x`GZ(xa9,+ήYgxHVb^+GpSB:djIsJ<|Iyشp \KLN[9n )d(Ynz9 49d+&84cz VaܜЌ%͢cY7x ms-WTDB)T-kEo@YAIvb4:W bĬ&0DMwb}.&XSk_@$Aɻ!Y[L@(Q%0Q:Qdeu8A<25᪉gcKf!Ju[8,x@kx,Ul3[oq妱j\kd V<֦$=Pe)FVQ ވ 5 ~LWyCpe\MͿD-Rn(2 MXUR+P.YZ‚*ӥQbh6!T Y v^N^"ך FQGȁ=b5ha%ţ3D\ю ҽM*REV/m(4UOT(_T Q0|N y CVS" !tCtmY(|/ClBh>c@# e,b:|b۩ƴgl ,[Ju#oLw*P$R2qt1<Ɉč ?eRp gAd pmG4' RR$}F3`|q@7"@@V6^7ka]]nhxX BO\D|p ZlE|Tt,7rJ؄t8GUGlqj5)ۮ6"IS-B3u@[6GS#_8pht'MЬ~O<ד;(Nel)#"ZlR !#tU= 1]n1vf{{e, @-nufGaFf*aKuI󏮤6#Z.'OD<3}(*=0fk v`8Խ{5}VAIcD~M!NճS.E *RɑVi:9C@mf,NȭjvSh/yZsjf /fuE:zaų+{SFh ⤷5vg@h6%>kh)E8D˷CȬWXs YJ.&  זA8! : ]O4[)n*$(TEC.|YX4*)1-} ]ౙ+ffh̰r()Ad,KI׾"HyczmkJ^㈐Y4nKT~:[E5z SZ5]gVXbfURY_gtCbť1RpmC'~6h&GZg4pi]MMwse`p^Xӑ89j66iŖ̪짆Uӧ>C!:tHC@:$^{~:~`}-D:0 x&2Er‹Kcy!U[cvƥxnzEѾMAu|Q׋%$fF%?R2Jh$k~ 8=sE5ɠ;Ȃu%]_6"FW"% 7_uFhj=nX'v虍LYJǦH"&E#4Rءr5+LPڊx@~}TR~oTbՎRɤjnjg ]M6%A|)͸V鈡u`4}l8F_MA/' (v2Hk_4 IK#`>•HAV+Y~B"#, Oz ƭSqdV ԑOy;8jj\TP5>J@P(h%DŽN Pid|gjȼ40XSVdG;Ny5$#%0!d %sM3.$9%U& rVCnƀ'A>K\lB39逪iR3ҜڔM_$: 7T*f2t(e”P UVVrRZ4p9'|]8B!'ȕzR&#w#Wi rC3+5M2Ѭ&OlfE y8AtS삺Ut^[+e}@2mP8ZrI*fyD++ C.",Z8!戬05OƹL1C6QɈ/ %k_54n+𬱭 < 2jj\f;N#EOmσ6b)CլTd4j}Pq]X+#R|ti]w"(2JljzY<35 M8z.䛩c. qBR" 1.QB_l1bMf1:[YVOө% N-Yʸ?DvMz[7|9W˛2U G6D 9@բN?1MɅ< ߕsU_WĀrڙ ! ^f W9 o[\IՔBl3 ü"P V.+5W\?L]9sh멱XWO!j?`&18T%蒖%,4 G/%7IPQCSu-_zh6sRpAsh)-#c Ʉd ǚAzlfÏӁ` hau uw]h3F5w.XmЗ3+6qFad+6EBL+H::7V[3*[Ǔ wjtCJ&@=N-=kDdQ K %T|*WV"(^ECc!:LS=.HJ3&cphjȆm%hoʬa2TGGjc͠lW:joE/܈QyTgZeo4+t d}0Уvəz!%6n<#͊STN.'0ǎ#JQҙ1NPګ@Q"|"yp #Тd/6c9BREH8pp(>nI<ЮԞUXH $ <]R㋑-l]8mw2юԠ[XWΒϛ_9_+1X83g$gF @Dؗ뗥f1իL-bEercX0CƘ1M sKAvYJd7˭:p:C4OixD+X5J4elc6ʬ( -x/HGS A[y[uJX+nIΖ]CpĂ,D$r9FÃUteh@*L4yR{Ԇ]͏S3'TYʅ!,Ӆ|(%EdK ,R/>5GUn)yUMqָ Ɗ6Ư ƞb1‰id.M;)öVДU^[aFk󴟫 3O♗[Yq Le G2\3LbJt" Z75wG0L*bRL"R<0]f}sq@Ugyoͣ-Q9{9J[M>vZFcI *(%מ!sWD2eH&OW"ä Ib/ TǽqE0S:8t+"R)vm#;Jf1oD<[P)k OEQ h`Ȃ(DK#c1vqB[ (ȱ[<F{)ODj @[ qd%}TϠ-Xm= j^⇆ 11]{JSÑZ2A7 g4} 3w G8kώ%dGmf<({ϻS0;thG4"?Md:jj`pmqt8hlԿK<=RYOMpSV)@_ˍ4:Ӝv~OYbd%>y*SM4')iyd`S@AXR;Dŕm_p*jqFu<+[9ȮڛKkt`ߟK d,GʠwFJlG@.v5;bFJ?#ͻ/`'+F~1M="5A c| **x7r(=&mB.9c4$ZU1i,EKQ$a/CGbv&Z9vB^mS黁(C~`B櫲 EK1'9N͔-\L,c##r&Ld2N QQh%rTalp] eb, ) l\ȆrM`kl+N?Ĩ& 9;!Ҝpm1LIMj"!Kz:i,W#CqiSƐp%¤A^vd^\*X J;yoIM2$ضם KKӷRUuszrlS<::"īZوQu5%pf[M?QMaWٖ= +mO\ iy>ɕ5Naͭ6v$rc̚A4tC˧^ǿlX]nxG p VXPE<Ü!B+T=qɫИ: JG{z~-^p տ7Vքzy3<ՃS*Rc8vVMz$Ӫ5zIkruH-xea4Y.+Whrj<s+׷ɤzIF~6MlyL}*|`}lBPjF-y6eaQDbpQPpCO01>0H=Kbhh0\:jR*XSfr)'')=f8Ao\䣠:rq{aOՑ`J\:M2YJ9fܧϜ*ZxoU9kŧo?8/ b6mesu$` dKʴ~ˡѮKT]t χN6šFQBW45zxD6TtMLNImI-QэOeg}ϣegP7q3obuXzjߕVqY|TYq!Ī#;,Dt5`mC2{V=J;s[[ BD&@8?յ 5/uɒ ږ.oVL(Йp$ epp!TՐUDhIX)>vl0@B(͘ިR@i-p!AͅחET93ĉs`#:Uak8[MPoxNkXWD^t2E N V͓3zuM2J[Dh bٓu1!zFe@Hl: D֖)+)>q)β^][gV#Z- dFҡA؂qa3.VgArS(" ۠ĈۑMM-C$gG3.k 0`Y_C]znw>erM9R욥?3=$[.Ys3O's2z-g]Koel$bY(oq bx!vɹ%-wlOǨ`wŌ6R2>/djT|#2O6AUvaR6<[9i՚X{4ӽk+vۮoʮrqQ-#70a;LJV5h{TC1[ ӫf2~cTaVSy+ 4Bz] tnjjpK cvQ ñfe/bjp l !fv":2g?jhs'dmj}AaZrN oiqt0g(vYΫf;5g}T,kZ4%y \.zSGi7| 1HBY`p/x\MpM̷T_!6b9uP/|>0E;v9dueб$9$l_ΔV>3ͭ˰LfyٳW^^UUU+cMlRPPD"CKRIX0iNmMs=QNUB^\{X5}1 5Yx{BQ i3TƍӨWJ8VT\ߩS"<(-[p_5&.OW7ŇF467(nij?k}F)+uI[pH)7-(+PkgZ}Lh4q(yk_PR e ?9DӦe3A]v?o׭[dIO{^-:d fPw2"|4 >FZ]iHҳH?OF".d+RbdCYn[t7}^/nWU-LS2q>icY5f݈&%~5U&XWX5 W9Ҳ+r^(ccY )0Uf%^ ;o7'SFANe2f]KO=OmS<]{(9'$-)C!pMLeTfLk~-X`pꫯBHJ/[?D* .|{™3g` lCC`M x ?O8#?OϓO>SO[)<!yj\4ڈW6ɂmqNwYdE2T;3ңe%EӖƑm񭧟zTIJ ^Ȗ1veȧCpn}\ݷZ>س!İNӉU;~Z:r7-a|زKP2ح%+JܶVY:a+^Jh EM4nώ >&}n$!eA[GFW . ojlyJ 5l١GxkON{_n;8nféa'*VTPw0x&G5(D 5tˍ(I<ی#Buq65reyEgL3j^GN`gG+[;%nĖJw}w~~> -AI50?bĈRK| f͚ˡ_ܹs WYY%@O~)?~u]7n8=-P3i|w^hyde]!3%(2x{0Q3˳ҹzu}شį>~Y7j!<˘ ?( 2_ZL_z`cSn kg"yyiy*Kgh'}%=zh7Gls!0*jiѕMO-aG%+,\*%S &: ̨V_Xr e[8ẙ6YN,~}2 5*>+Th}c?nK߶Vt &>yGS'?Pێ_#}t =jjuDӑDphNY=?H#'؇~FC]NT$9?hѢ't1Cʣ:b?s~H 5Mԃ.>doLm.m[íFt4]8qtTtSs#|IF$XH<*n/nu-mgUDo6) vUQdrNݞ!~bylO'vvyTFL[J.IȺew"ܷ +Yr{d0NJ4(dab&_IV-d]wOA=*.A$ ;Zf~ʹogzDG lX \nګ@Avw!֔yY8.t))ۿrőSFO Vom}- GЄၢI8Z Jۛ+0z5 +&kQQrN;Fp(R$֙8"a},夹(k׮=cgң+6_Zu͑w5k~x(䝰,2VqdUf@%oe Voe'ء(LScӜ75紶`*;X7;@cN5[Dl&u#A0HC{{nEo}tڴR!7d9b4{dpE^ko1HFh*OB"ȋ~X\<e,YZP yo t}S-)[Z)/S Rq\9 OxVL\d%.:.GK'B~ 2t*7)OE]2goUk{Oa+= v9%=nr\䛣r%jMFH-z/$F5 .L/OC\hrt@z74k2R4X_T;Omsu:Ga[ |S=ij[ZWӍ/4Ytn|q\-{ǒ?Qّϊk jw$;;<,?.Xꂬept8e4eDqϞiWIEؚlHxb0aF@\!@ƔT'5;JSy>4ڈDr#Fh@54g8jp00)0[+=A7;m;f?.m2e r@v0/ŏ}p`OxFJpzOcI bxDp%Z (6/*1NF͋qpic'&Zm zQrO>p؟I?u555d†y^׆C 90j=%crXt蹁R-~ĵ@1ݟJV4@#W0CO: -`_jp۟3l"!”D"1ԅyEЦM;: {#6YIs+^'o%YL]{Ԗ]N bUA3`&f\-ǫ9VD SIg /,PNQ,JjFVϘYkI0_  ~(q'c< ž [(_q7|ob]2}ޞř <3ju*bsF38Z3jjb{1kIA`Ҭ2<7OT j2s~&7XQ+#jFZC7?#ZYD36L^j iRU`6-?Ajre|{R3u(N [ a{Xl;<;p-KXy(AYHbVIij#{"77HmAcv%Ց Ȑ8st,Wɕ$CΣ5v&@WZt$FhTQw:#gKYz3g eE8 c? vumذ<[b qC}8 X 'r1 5fdSvn˩4Ϟ={bBO<1nv13(:8%c4-#68+w'c'LēA^M-Ujd˃!0궢 8Ap &9W,Z$GJcLj3SYWK!t]){a vc`3zNt5 Q׏]5 ދf q[W_ܸ @ < wRܷ0(+-ó_Csil]V WQ% .%'Bn8 e%=1o"4o0ǯ,ggr2_,,^'+ Gq*98[yk3O{˦̝n _u @eAG{\W3|/֡5j:V9r0m98~=ZNAS9DI ÕjmR}56HmD) @mO>-Ӂ=rJ}WbWre"c'ZҚpЫ 8+;"oޭ) v_mnf8ifr3Mk-3]GuWRУSAݺ-+޽p\Iy[qrR:QK<$1VeAѤ8FWliPt(A (C??41\3y'ÜLNneӷXw?᫑ AJ$rh✺ |Z,I+Nx}q6Q)Y.lYQQ< RM@,<vmYC/"~:D3ZXpU|Zec"\:1 ]\qz`-jhlٴ9+Q{(K ڑå+T  eKبǼGn"% 9^# gH"hXlҦ mKN7'Q6ut^[7e)iF'`6HIp;L+VY1_"2)lRQ6K\M91džc1rm[T$ ?ښ;=yչJpi7_xNM(2WmZzv#Kq,Y 8X@ܶbx(|`v/Ў0#Æ gGI/x7L\&Tc=~yRQfZ*q7)Nւ.pڢ$]Q/wY%uzc;!^z闿%d 䀜uⅉꪖ΅^JJ& DY'qY]P>$+%"Sh,G=xfNܟu֞Gl'BPP-̱z1x }mbw~cߠrWMwxowm78r8pcg+?wAimw[p5N/7AA9'~-08h􂋶YZp:}tG&@.KTudT9eZX֩$gm-U|t*߄D W=N|6R0!5ⷽNieKx.sA~e~aGRPOt^ak87n-K`(;͟Rא%$0]21Qb,o`c,t7J=xw\%:e=(Q!FMmԏAi$& WG#0tL8`R˖F%Lqo~OTv;RW_nݒygv0}:볣GdɍbSl̥Z<  NcRh"Q;ÃJ>_~/ݧ zテxw~z ~jҵs7;~۾e^!D$ yAn K58 GlKQi:yiHe['lEԏ90lR Mk.sJNt%"5W\X=?c:0&ݸ KkFUS]Uڹ 9_N:"Hq:? 10W.~}-NA1>/Vp-< [Rrx<*殞yq0˔Nqci tiVC8\e#lcڸAa>^B<QbYi ?3 Q-(͗ _.BCh/>9CZ!lSzw?zϝtI~="=vÌov!2%(e(jdcw(b;Ej|"y @eM_HW_<ËW\ P Jc+?gHPJ|~KśTMgM#cf>>JV$E/.dREy4))}OV,Ư{B 5ZkN{Te8|[mU+?oێڢxuc5Z oXaVTկ]߆ q^݋ݤ}Z q'j`mX[9\V:vq #<*Q]>ݩ 6 OӠYJV#E>\w cD?T3n8'h1"XkԿ";ehX Xrlh$~i#hdN^vMd%rBgHk\|xe+f̀f RMMh:GaRLE/ @ZFb8XV$ f]DpM=,ݻtwޡBd`[ Ss>GiٲePR]]  s/n-x\ GR$Lw ox3էtVA$p4gQBHRfmv|5fq:DvGϝYi~ dǾ @\#R}S_~K/Ee~}7VUlh]ה>S m]WYABr*+5@ VgʖѶӶf1䭷ނY}'pɢg[rfu p1rO:U+0)9n0^ku*Jg?Oin<[VCK;6ߐn^y=.|`嫚!Jlk^Wdr{|)[TS/_K9WF\dK8f6sfcc' DR)xYIwC*)DǮ~x: VZ ksUtLb *Rȃi14)ZF]3Gp\TХb:Sv1tpU`M 3۰SF`$03<>| &Mm&CKɕF(IGehkO^yqweT[Oۗ﫬n*5g`}f]Um]KJ@庶u-U5M/6W/쵴ۨ/ʆ3K .nȩh*XӘn^{vS٩m뉋s-ot11Vwe%[xsza&ˣ* R807fK,Fx^` %?h5[V/|S!tG 1v.3q}_(9qC̴#:tz獏_ /u;.V64BfQsdcB[| W[[k1S,`7,]SIDH"ڮ<=C3!>cb %Yp A`I?]wuԋuݐ:*.yךMuuSsi<[PSV s+*mZ44vj|륊Ub9ѺA1HIГ1E'!6޽GlY)m*gm11uK\ɉhq NbPHzX`}0JPNh=QrJ  vPUJ D*/b n#^B|0Q{\lUhbJ3D;MJyWX륨Z6:=btZ y&͸ؐFW*a/]aQziͿqnN]7U:˹j\W՘SV{ںPWTݐ_0\/Lӷu|!Ži.tTW'/=BwMJŵ_e'$.s8I#`=^%jmzַ^ɪ#؈Qw']{ª`KSk#M%zc^_bm 3q\]u1I_Apk w87WtyaRsKӟ|C TV׬d`ƽfeĽ[y9CpUc+/L^# NJ?: 1 Ḋ_$@朆EB v =b ^T-ccK׼ɐ@.Qw¢{&\[ΩD3d\~?B5}v+sڧYvus7H_y@zUZʞ amK? b`˗g֭o7 Wn۫[`XBi=/m}liXWֳTĶ 8ȉčd,Q'ƈ>U2jAAjwosGE]rEpdy 1 (Q"ha.U|>U@,&`Wym0jo& ;y:Ab8Gss tBx vzeDC< )>;Oҥs+!  YqEZ[㱈u0 jCVORewĸ 0#9?i^-!Km6o nOdo@\8Sf '^(}~%T,վK`a֩TAQ6ګgVc)rNA3n/ZY(8O le xۮ*OuP ;4Z.oSQ]c}rF6W攭X$wCsUn[)n+nn[e m-yz| m%s)a 1V:VERlH]"$(ɬwT='y **@xM˪IUDnk mY:++loeV__|-f05p}gSXT\_|iEet砶hNpP(GJ6M %LFޕ^"kvUe:UxN+^+*|[tGt;>rܔklflO8[A#6';s&=jGڑo~s9'|*ֵ|8ԆҥF?`٬(W5VT6TמL0lykU }Y\]_w^U߰pE3G!s?`C~-5@M̗ן=‚ RljZ"S6y ` Sp"ʭ ˨}:R2[BBp{<ƍMz0QGZ"m \*M "R,.f1-]gOqI_374BAߺ -n>܇Tޭ[X8+8PG_׆6qfoۨCzX-uu-Zkk[ܜ֖۹^ÆG׮`z y&,zƍ`41I`Lά rc9[Ad]??eq4)Vw&$Ѻ@ZpnGc,כAnPGp{. 0xƸTróGo9n;x 0;oi +-erPpݢ4'(k\z)VM3\sOztJkc>As6+,z.zp=RR &@Z }赉|/enPau5y H)priFR #(KDBT3o<;uɮҲI+ꘅne[r\K&/ki"HC2.ط -:RXLn( #J!NJKr D'\K 1E/YwCEW +)8V0 "’>eJz-z Kcڞ45BAXPy 3w|\dT 3zB" *;e\L\j:=SbIXXpW<\v~R1< {9Y~7l3 %kg2 F7SRx4g£  Җ*3G(QޢeSS۞WbSszkeMde~/a>= AnwجO{z0fCe.L6[k+? KzTx4k-p+1CM NP DPz1E%.@|lYW Ѱ/9rIpf-UG͎x<6OTgxPe.8_WE@3f:ZHVlU6^d9T4K!Wܑ,ܷ_oDccI3sd1X**v1,-"t@o7- @\~v^08`# 1%~3n {@vHdؼ뮻A>(/ǘ &fwk>7QV  a G]tI':&ӻgei.X0pAa݇:OmsysˎC7a~a}震uKmxbL+, F o;qNlQHk3Yپ0bcm"4xbdmDo g|raT\~guW)c@]G:kH5Hc ]ԍV,B0wZ $#*#Rg= 1w*\ҩkIqגNݻw]RR*.Zgͭ"(8x/w;a6[3Uf\foJO+1Ҡ8G,E$z3~"JJ=;*& e8BECeI?~<#?5W^fUtO;~Ayi> A)dBxE)ImDxH%'Mv*crͯgEFK-irT&yxH$xG<I67e/WL5ݧ84fOrhRm2o V3N912NBb>OL5"#[,k[k1 mg3MjlQT4e۳!P)xǑgIA}g: 6D,],2:zƲ~,DDP_n ; b`Ƨ=_kuCT+-lk˯i[ .Q lpc+ ҄b<~ͶɉK(7nA}XhQ KC+ήZ@pSk뢟>/L8O Map͛rĴ|GίW<`tOa sZIp ġAǚMW$7p gRw9RD]pkS53tM1\r ?V*z- 0W^KC P ׮|\+:;4Vt_]Xl(I˷)ǮE7`ؖkidINi+ˮr쨊wiBVkMd$uqxY-fD,m:1Mt" D"x$M\{שٕ3;42_C8Kצ x-*4S%@ ꛹)vk\~  PO5-\b7_zѤ`"\VN#Nh9oV! \~>1}-oK"ӎ?D(x ҽيTy`PoBGy6)@n|$nŒo|0d {Ö1GSOf͚9ٳg'/?+Z[D&  gR;uJs=yTLS {a`Z rk9V|>rÚnGkK޺[0Cdudmbwu ]+lN^kquEB)y,{ 6&D'.یc"cP>a'C"S Ql +< >MQ*6h^b38>6n>]\ķa .:Iֺ OPbEv̀|3܃-K,p;1fH?a(FHjVh&5闰BIL"k3o%bvQ/ #?$UU (}˧%obߚʾ?`kxCؿ?(4iyO0)z oTAǕT8l3Qi]^2_iq[c0 L܆ع 믿& V=7 r=w~ɝK>t3 fcT(x7ŋf_䵖*Hq6xlX+_f5몗~>kKW76זlj~힊o=4憰'A;}fG5؍ \RN馨f],Ān ߖl*|Œq)R4: L#<$i)sg??c]v,c 0#MaZS?lDU)mj8a )L<4dꫯ< 7v5ESMW֯/<(S: *Xt }9>X`ximF.y;KWӃ裲mrno8 ?,Tdh#v%Mf/7K !l` q_?cia[b.Ch5 ݥ#{yebF$Qu0! q;ƢEB Oy=eܮ%3Yِ56#c( K[ɔ FƈY^xhD X~Z沜75AyrgPH]f&` wUqo2oșכ,{|m7X{AC NAoJnIW/"m*:wʄO[*ҕړQ_K>d f]\.6Kۯ:$0=X꣎: @5WT\T4q⤫8GoXRzAyU^{5W׮vY}ⳮo zi}u +Ͼyە .-nkuػodyH lZL{9~>) _(׋N*{O"í͛%M.ڹʯUƧ&޿a1kwʖI2oU7Q|-~)֋Ke(jE9=06&-a9ZVt2YO!# KN 8+3cxqğW,b ͔֦Uʸ.7,Ta4{jX4&a9193JڦcJô^I_Q' V\^꟪N@0g z^4cxCà zښ]ڏDrI  Y8q!3!<~ԀH&cwiY~ޟR >Sić\ZvT _z3$!j\/=C5 Q5lDZI}Wlm 𷸨t7o 0缱 *0p7_ԹC-F)**dy=PgsokV7h Mo=@D0Đ:*l4fCd|O(gPzSۡ5|z#>6Sn ul (dDXpj6bE.`[9Ɩh`g@=B ! QM~>x/1Fmnlhn[~/מQWg|ǀH6Ao814F~ʫ4W\o);>J1Nv8z*|xG~._H2~դkO&K)vl=M $x lrK•ƕK6NE_5+Ço]fV[m }H/Y<vvZzΈdȭY5kVx׭;Ӓ4fU(kAu xFh;iȮ{*S,jF`]sؙd2ě쩐2mC񍒠!\>Q4% q$:Q 9kk%x_WU xHLaBaxwCcO 6a`3|9_} 6+S-[.0ρ&Nlji)pt:#mpj X%6`Ҙֆ]v ,?ڻ,IА j1::dCAFVcpX9F2DE)vlGom1f#~H")+Z/^OWPm5)AYb yh= 5 5g}q)PkxŴwÒU?ƒq>>xRDf5յ'_pb*aV@}pUdQ") %j>KSf4E!NϹeM7h6-'+/j 9mؘ |x6!b;p/Eh 1'}ڪ2mU Uߟp5kzB꓁&Es2rA|0kWYׯW)ӎS WKpfѷS~m+-Z?FDSw ?#4c,C1ISࢗ#XrNfkS5("WMˉ^jr6w\a"U+3U;K8vo)bYw|حiC~f6Ó>Q0P7@4YgA54Y Hw1ؘSgzN%=tӯ u|05m[߷++ )ۥȧ\V 5vp!]`mb'᱅:뮓Xaaњq cpQV+VԒĕb2Rc<&N nuL,g2uDy~0Vĺ tcDUB U~IM%%|c>Tޑ'lʲCҰGl]J9~2gw߰rx$3~1e`Ќ k|84"*qB:&HhXYIU5%v*0;r)t<ۋ^" 0>|Nj+Y{&6$^|:e:L%^dߙ)Z{h50Rݞփ:z7n)ڰGJƌ9s.6C EwNl{Zm>&'t/7eZ{ }ь>mYڵo.yCO>-klԜriI=3mTQn Z%1˃rT:KY4d{ K8]56Yqضb&1;LSZN "<n3TJ,}QO\ƁoJF!ʔmh)Y>?s_.ya m6nC5 AMѳT(_/kَ̔J8p6?U:+-ȏ^?@"Nal[)d)W⎜YMwsYLY+Rz|DrݺupK_vH؃]Sښi5K~OWvss^7zgV^vԔx叕 Flgp%_~ac ~lHcmKҊ! "6}x5@,xRۀ4(c֚8L_n.?aQVf>irƟ`Mɸ:b%Z+f1|:siEʞO?wO.792 %[jY­,"]wIc5atT'Y[SҚ[vn9JnFRtqnwƫp匯FbkjܹPؗV.m&[.=;ͬ1Wx):fIy1l0yBca''tڕ+W{ ~1ھX.a0%/FY_*Svd)[̵ؙ#3歫X wUAK zk n̆k ;/`Mr{/K@sr/xDV,{rr~JC{ϭKXֵdZ[D!PhgВhxRӨI|$oovI=:;^,/y5ZDSg⻋"r)lp=rkk3Gc'(x^V)URH/W>LXLKx!4"J_t/69i; 3ةekohhYz}]쯫*ߟW+iko]Z@dߝӔl7~󼯗>5c WFq:CFA2WF4v22!usd%E-> 9Ògu ,It˸>2`e:n3LpwqkpWj:@]ve'M.dm.rQC"Bq)5S FYТrN%ՑR[PPG;5lTPd~6㒕S61kkk;u*i֭kjh=1'ڰl %q˔*`1da3xN!eFu%<) S2Otňb3#(uUIO yg=>-=p$8l8^H?buG2DG#؝FG)D=7skN¼IIS(tE[1蘵  Li`I=0ݴg*}O?˝-?[z`c-ḡG*@tM9JNZ\)'*)%UDM&FՎ1STYR850}<(3kf^kYpU~q鼢jͅ'hcHb\zؙVZn(EuT*ǸFAA# 6ۼsGby6@H[ؒO9횚94=چq]ۚRa\-.:" u(suTFl13@ά>S΃T#B)0V(kT 8l۽qܺmcn'V7N`"5MŇx ʒB7` RDO x[7@E5 {Hk1ģhF/=Y} ڄkyᙌn~:S*z&๓gBotYi`^X-9vb"\Y i] ntX Āk]_'hp}$?8;Ï= !cH C3+ 3FՓXq|#+BQ qHuq 8 Q~Ңfzq\tVf)zA~|!0`?ۼ8ڸaEwMKHŭ_;ϕu"zFExjsY٘YDKb+n:=Y}!1=)F@L:lhI@]ɜN9Ɵ8i|7lA-༧ԁ ds JVs{hU/|.{'._l,eW8ݠz1pbq}S6&Z3t1rM3NrN|!WLؗF]<7V!zr_G,*~+|X EJ G߈"~L45AHRSG2 |K@; q\H>I(X Vyߢ"H%.{>Pe͘7ɇ@DRBA))})Y0`\`w7G+dIʀW:|z,ash,)ѕ_)h_ ݂h,ݝIHV=GAPkʕj(!7Řs=Fdg8o|";h=qNv fھb/3~ }On|aF0ˋ&.f,̘?Ȋw_MdP4YoYDU7cQĭ[%Hn:$EqEH@LۊIHR(6YÛ (n;[4" IȤ1(ֺ'4zՁ TTtz0]PCh ꬤFǸU΄>EX'bL![&IW&6a_(~ܢCCyfcsBƖ5QWĪR c4_|C*5nƭ`KkTc;#U<Ŕɸ2x].oD1c:cف.KQAEe&d=2F6ɩXtRH8Iz'|^[7pEE1i쐷;`|Xx9˂+]cU"DI bpbBF*1) s#TKU 4:LS U VDATӥIᓉ U_HPo8Bݑ"V2uܡ6D`!gj q{gBsdMD Lp"\[MPW%@5rW5ʸ;nH8ly޶KAHi5.Nc-3KE]tձ6QaK'\JЧ0IuIqL8{" yu:YCͻW \ ?ħ54Ɖq(z!%М.h0ķ`Y̓Pza񻖽SZȾq`-FV1QՒQ:?N P TnD &k ׄTkޭ|ZvU}X&eRSQ*w/jd>bF<*!Q+ ֌݂@i:U}b8V~1Mj{i'7fpmdocGi'X#mݤ*CdV7,;KƯ(x,2xZnbq- x!iF:M['E!"Fk= } ]Gd"{+'ƈߖKތ*q8wdỊcF'F^ _DyjXP$fSi0Jո %%.r 4*sl Vvnl*'[Uk$ I{נK0cV)+h]EtB c,CK4hNXԋM!j |u{܏YD&X.Ό o/MYNRSFV( E Zq&uq q9vAא;pa1*lBk2>Q_e|дDVu|2Yx?{;759s٩m3c\~V"p.2Qa Q7u}?4'f Xn4eY# Y{yr[۫_<}<8ᩤ<[Mq0:uOxx ͺdU9m?4:.|)fK~ۏ h%[MP^Ug ̅^ۀ-/c}yiTC*ols2?7Mi[Nۄɕ7p1l6I+*S܃!Cbo+3$)ph*u6WiB$cfRJ.?o%ǔoGYqVk÷gjuhQ5Lk\Y#)W3ƀ)0'9[3b㥵-؏ &j(ws*{9z3zq)qgA9ı"g)>CĜԐ8 QM7?le]4چv; $vh">AUuP듌yyN;mCw w޴,ه# ,# ;3@xv?#ۏT&FMr/^̨|J#+mgJ p^1ѫ&yCIR2"c[6{q@=׻#zUbyJ(Z&\-1q2T'hIt/n> w=Ǻ]\}˖T'% z-FXdW~ ےz̚B7z5)uL=D+:S8i'U镽 (HFnn. [:bժV;@7'ӿPv4 V[c+u]{fSlnPH}:V#ZpRbs{[% $xxK6wI,_1;›~220w(|*~U t@d=zģ7^Dq{#ǽ7013ȟ_qOY\O6tQr1W <㟧>3;SfD Gޓ= oKϫSp8qϪ'AD7?X3~CgP\h)d&vX?ûg2=j7Agn?~n:aQ@>Ouwlݝ]9Hmиw3];~Hwr O>fjHtkjyq"J޲O-rXIx{V޲ʮx4ɕ/Eͮ&xD6+o:Ti8>8˕MN`"=/W."2~2 fZIQ H!/T|[ u22r5LLsdt~RyfW?]ѧTh BoUkIBJQN?wǩG%M"4ѵ;W\;\qt/J./O׃Tkc#)?zH*8j gN227'*<`|)"z nOG笻bݙot'35e=rؑ3v{<9'x_u҉ܼoS}Wzx3+k.r'ӻ_,HIqr@;uYZ<gNNEW>33>oԈOgu?=^9<c)57\_}Jӝeװ37Dd%lN!g$&Uu aun͔M)};o ]?g3,{9gQjAםxޛ{Z;bIʹz _ϘvqyC3Lj(>n=Oj3[>J[>m7d/d^wϘ[z/ZN >P؀e4Z ŬRKTvUb 6iE_ }mcAe;}457NvP>n90 íl]9_BzG>F֞ɬ/x{dz.N!T5873e _C12SKޟx ޿3oMo1}1@7d&m?bd*}-d^A2g^djf޺ϗk#xή,8]`U83 𴡲IxC|Y ."r,͝x62n~k8;cnLffy+%m~]F j7S鄜!\]nT4׿h% ȬnV52H}8I{oy3\/ϣ?[vˁ9@ę Tٜ?<@E2Hlejn~a%qL}'tQ"t O HIHa62vFi"l nƮvck[ѷs&>Wfle̪5ș0׈Ls7@3V9 87w쵈B#rT[ۿxpE9k2^nvgW,̔韛;L݂܉Ӻ?5[t>3w-bwbe ̿>Pۺ̂8Č ќI ]O1/%3|\\L$nOv5c#6 ^QS`'&')XGSF:0^5rA:NT_*q\Vϸ2ϼԪN>u8 TGNH.52nDpZq+'uwP}JXTNmyl9 M[jkRKmGo=ETJA|tf(ɟӂtTRy!뎇,WHX,|,  ڋot~5 El>V`:MVT"#b%|-_&5vY Ӓχ˗ /T$=>%}Y SE[dVmX:Ԟ˝+iOzY$FG^p"dJr˗N|͐fNJk wzuGºȌ[} =ϯ*gl~2*!|JNkޡB'ɓ#7I̖'xa2jEŷzš0C %4(+e)m>GQB=)uIpU]/h{mi",MO>4 ;ws2Nѣ0L*XoP+Bv%a^jЯ'fVٺZ='V]3ID_kȃ^u2anGdE  %?y}=SjjZ,k=UFhUĹ! xYN$pL芪O>;Z86p9c3 ˫GLX_jBۄW/5^A&(sȼv: (U~b~1V:bL4󖓋#buxM@D@ܧmz_܊;H2CتQ{_ @/YT o=_!$Pw_y{r4r9s7<>~>% 箅\B+j몥#>̋W6rw[n1 u'H9` 'H)J9V@1sd5z90u :\QF0`dEu=̜G:$r&of9[3(aLSWVUb}. r3*PlHw+ Ȥg_-tw;q{GW}%f(g@3:uia=pu.$94[҆4A1"Hx2QV:cg4'N<2SwUܦ7`E98|'L-E~gZ0e ]xUu7؜Ֆ)GyU3o.r[7p.;\>.P2W 2=+#*Hh1?m?l F3&_8d_R8R]`RIǓ³6,>g(lu݈JTxv vgl[qͶW\Fl{<_g׷|go@1͒}B웻^^5Z{\ϚSpoڪ9 S{Wmsy?Rr+~h6OYu]UN9U3z Sƒ( e"hbZl)c/1(i磘>\HcD2WpuWp[UdglY$&CxH}s_g/\p`kbcP oӐO >LL]&Dw}ps;3RG>g>P[6#<bm]0o<8~=C:a yFoylq}n[ ث5c539#O#tt;efj%Ɍqw"'g܉W]~xZSC0Bj_RScA嬘?#~\6~]^M؇5 gͼY=2wcWy&.={Ty>}hB&w_׎nT g{oXP^oxb#f@!mcKRKSZDMܕk6dPVm| ڧdĪ'eUK'?v{ }{D-? Ww;@@aΜ=Us5Ĺl(óEsMi*l|\NصWNyecZ9ҋ8EIfAM2D"1IlhyuAsk} QcLIZ4WH&ڸƚx,D9fܼٳ*jгsQIgAFG+["Ԋv OXAU|҅^uVld+b":pQePX%u޵*D5ۂC!i} (ȆK?֯Vޭn-ye/-"FTT\w]͐q9áhWVDY#kK% Dzou5-zf<]{VHU#E+  Ѱ ybyb.\ rū/0G w#.rAաdN( p@hhk~M+6GX+55ɄfҭA: GD_M9̞>5H& 8v]JʼnRFMa%ŤA:Mϣ!Ur&l!W66ZϘi|ʜ$|$FGp)6P΀锖[#44kW0&? K{EXjlZђv\* G/R43F75gM1j%;VunEi80ĕS0F )aO>M#WLv>a Ѷ^vWВ'4u)DPƸ73<^9cR5PC#ⰱwdee>?}̙ob ӓ_l,;"E:Zұ K6ۢwSTlnSO@Y"ˬ`YU?阁gg}}瓟&{ٖ!VHElf+.*ٌTrO8g * 6^Z.׵"BcLϑ 7b|/E녠>&,gn%= 2 =+0RPM7L`dZ Vc.s)l %U Qr6Mݥ#y0yw+a##eA)H>]&ᓏD%͌&r`t4҃@iVH鈖&.arz7i0ER,2F1מih;iC9tMRN3SQq1i(=*˕rI͸P_2.^|2:rpz%YP^IuQAhĞ(x %=ܲOʝ8N1`oJ`Do6ae1?7FDD+BT]LdLk)Z2ڼ dUq"BB*%3e*Tɍt5 D/0XN`B6#6vuC4d-l&8'LAFV <ͥӥbҰFs!ٰH@$3gF i  Y9 ڕȈ+6(Ť=Fj>LA (14ʩbxi*åƎvXZj2:w0Yp,a*lq8D # (#8 Dl_FS*f ç;FHݭBOE$n87ȊuV򓽳VKJ[䄺Ķe4۪di2&M/yó.HDMRI_?# hK܎V"$ykv Ì{g#?EB2\)H3HLm, FGL, x&<=\1p,gY"EK܆lcKoeE KV˽UM `=W FpIn!9:Ź=ݱK 42SyۦzL&7 oW{`G/g)9Ҏs֜\/8 מUk^Ŀx!Ɲ/c'S2).hlkeeМuc lq`(BxښcM@QVM'c~ZU̼tp⸊RNsj7@djb_[܅D!$7۞Ò3Rƭg[HCLڕPEa4 쌂Y)lg*59b(&Ѯa,]0ҫ(/q A\7\'D&ۛ t誜\[$sfkK sqy7_{ooċ; :$! tHCRן1n䟿vuxۧs{΋0/ $oygaLwSwt]]#zN#:@:$!( <=v2:X9_xtG!ux+ܾWb4<(-Wbx_׹+ ^xo/;>@:$!w ['Z{D %nvW~ ~ذ=yxل <Ϙ1EiLt.<_u@:$! l \xY3 I{s҂԰TKՖ+֝>f<Hu7p pU^XE]7|ǐG3ԝ:lD~x9sQV]̿nqz}@ ~zRm.T;m𳦾 t q!Ёder{v)o֖ma!7;im6\;?ns QZ ؚLh'y (-H ~<>:6:I@§"'+o=gNf%KT__߷of8.,,lmm]fnz#~yͷ56`rЁ@mcQA>mh7~~xvuovn&|5V0dGbPw:. RnWDCعlr6ACmՏ` 40v[47?OӳgچFO<ҥSq1ochC:С7hlƫi$vKԄV^ g?un[x=?pQjYDk9ЅWNGuD:*vy~Ye v5m~%z[n=rxvgA xCnn^^`3g싯>twc'v'_D[]%;쬺o]:$7=߶㎸)sƿP~N߽ٽƹ{X?OH4j7e!9tS|˹vqwN>[QA^CNUhr¥]>'O~!WqnMpZNMNɜO_|4ņ}V?|l [ 7+nDsd ſ YޜSPTTԹsg\*?pɹa.ל[OuϦ/y߾?gAJ$fرbmhz#n4429aM}3R8CtY|[f .G}hWohJc$ElqUo #6dZaK5 ]0/$'l OEt:cNue?+(c.?NN\Pdy4yفK[Չgכg׍ifMvNA93e?=z<[3ͼܢ'K8;淶N{s/o~oPuԉsU`\349 Q-%@2Ȼ|S>_aPr\r 1w\XY#n$Oq:tCo:Bn6=x?(rlQ|v`7R ??yѮoo|N<L5y/Mc7lΫ> r)rZB8ȥ*Ci؟Ȉ/1tJZx%TJ/ (!\(\ɷmoymP3g?YkWj7[^d>IgluY93nsvufQuHmE%9|=o}qLLUj/l787~ W&YT߳/yw]?yc_k5ōm\v4-qɁ{K^_G0?rp\p%ɿR03:|6J/?Ia=q}FufSg,5 Y,bndZ|ٔ l}6Nm$;]Yal͛}l4Q:Xn']L4v;\yq7píǞw?\ؿo}/?n]ДbFd_sX>{S6n5r6۩w)KuQ`Ǘ7zRGr{zӡ́~,c=؆G>=*/FP?sO{ݟ1$˫~OgEb7ܗiƢ½oMֳyV.GDˇn&M_M48?Š9?qTq"Ϊ$4#Q$n[)H7!`P"G{;ÊdZJa:G>Zpf)wo~_ mU-&z/O?=gPYާRn=y篻|]1=/:_8]PjrK'ة&  ț%7g56σ;?+7(/I$xwp3J8l|r.)מ C+=Ͳ)߽~ɵ>׿}I[բRb]BuQ+pOh&>ko3{^Zl_k>jfZO0ˣ.z>ϧ9OUA!U__79[^S{^{y}\>BKZP857вW)VO3_f ߁@́Dq3ո0-WXGvǷ4.Euz<"NS]& dQGwWrJ(3 5/o]y7~R oncw}74WFy~ч?{1/Ǯϱ.7S QhZ-W3ͯ$pm{ӻǜeQn·hnB|<'$h)mR*90!i/N3ߴ^Kgnm_x^Fi}@"1fҊBj/g^vp9YfSh"ael԰փ32'!z?caj;Oh>7 ʄcgX̴l֞oxswWbU'> ́~ZRW~yT?"<6dFS>D}OQxߗ^u ۶+CLh٘,tPW\Y_ջ4\s-hjsw>rg>sϜx[R'T?N$7N XT=GUwzʌt~ۯlƎlQ"+'v;.xZϹ??~?/ϽgkF<[rgq5ɹ~b@5@++L&Ԃeeʒ Q`J+m|4#wq.=g1J+CsׯR~W^>W_|o(/Ra2&;>@ hܮU'Y8M44|m cʂ#>pIUKGvHֳ^{N}̩wL[o;[?~Rŭ[^t? ׏|-~Ç+"@U@㥚@~a^+نhQ+jVp/<r^zH©wMsy}韻lcI%Xd|zB7iWnYڙn#peO%9Q>/i*ʔqg!>޿w+ߥ1~ EY#qYrS&,9 FlO%]P&֟y"קݙ.=^DG֟H0_GfQSdw%g<{1VwGF~Nvfax@Ռ@Hl k l*ضlS:#7w>'1۾~P&\lC˚,ÑzV oן]z=Ώ?',~`mRSw^6`s<4-\zZzXk 2>*bǂBiU\Qg/_%~غ']6M( /@ Z@2G9='](r q c&sT?I EDVZ#|GŠ@䙭"SXs]Zuzs'}z!_/]{ @`"Y{zw@Ϗ0^A%.Ru׍ٝW;6x9(Qu| ^!=zʌ  b9adF\hb\iZV|YS.?=?F>wB?zFfd+'B?E<}kϘO@ z@9H*K9 ,OKa_%5pg^$K=lA0@D ,ĝ"I-=slf2+LeĚߣ`Q nU$s͹j@ ;Bn+h80;ZD|'\/2؛cC_fܻ@ PhG|NJIS8å.d6VYQ. #32{ G- ;Sy3ͯ:6+䂹/}Տ+F}.°/[9sAdp35fZFZ~A>FXeƪ-O @ *]d݊շ+a_kpQ$Y!io&%@Y+[G\)늮酎<Ɇ9+Rӛ]eӃH1plB$wݤ͛7t0H–-[:Ah ˂#HK`O Uòֹdz8#=:8jux\?TkP"уeS X Ɇf *>[YRuPSbaO+j55UH+wUQՎh#'NL xL[<3'1h=:JNKʳs(:K1y@ Hisb8;vi't9 Q=랶<ʔ19lpG7vR[7#y:jLC;#GT8{U}ߴm*=s*:{h1raX8]qwRV5HW7 fa_AUEPX$G,תdYdEkq!#dC>i {UC#4|+ @h\iQd⳪646 JF:~!W븻+3641t`5LL:S V1aQ6GVgRFBɚr]N\m-JB2TYQ.13)-20cGOqw4'dgwLjunj^>2}h5jj֞a4tQP njYUE6hT-C|$U1$,kOG+&r.Sf?>wqk^yϧ+}}@SsqDH>5LY.$8)]*%#i"eG4i"ufQJпm뿽L*@ $j2F[>&JՐ.Ý)Y:u¾ls  @70#鈧[8P2C5ftK4/֊:1PW6rk>rn\@1:ioUœCdYdGz02ͬ4 -RֶRm mXVg @@$tTmJbs%JvAc ;ݶ=mqVz~/Vd=bA&@]Lɂ-wKf[M-EMc<ώ8}dݲtp4X$ٵ*3glT@ 9E|Y!]Y-˒!.yn+] h&h-&6'&rii9:gIDATGEG H2qar!qlIyn֯HVÇ@~@וsTWUtWrdߐ?pKIc!ҘsVҟ /i)@Uӗ!U?d 2bCUjr{CK򹧲Mi&--canTdOJv;&s Xd:Agy_}ЩN6t*I5cCno 4@,;z4+{ŚR{Z[l6`}(00ZE;9=./ Z8`E9jFqja]¹mVѵƟ@@9Md(EACS%þ*6xMr[mشiu ݓO .p04p"ۜMgk)|i1}1ˉW!/FL=W/EC@ 8#,SZA?Em1rRQv^m}No%;MXϫ~c@G +Oq5mft6C.K+XrMZ[Uc, ѺPL>-_AHw^  V;F 'iUo1abGԋ.^YK_S@>D@vXѲq@ɉY,q<}MsiOJV{I&>,94=qh}x@R<;cvϞ\upqvt>fN^,̦ӆyX0Yffz^Y]e'ntJ!ktjGXr ҃>7ů^_VPC] V L̎uزuaubKho^n%7e0]}pU9҅rtrbbXjIq(*IxE'5M8}81Դ I?BGZyW${.GgH5@ʖռ2O)YBӄ51UUPn3#W]D(m1l<}s[QtGq1 5ʤ*$Dd:4=2 eJUTGF2lbCz)׻.LkD33>=r} ZJ mа8$S4 ;΅j abI$[8mGɅ;8my֘!'lV󹭆'fo޵iY5JąSc5r .bdzt@%P<ˬﹹIͻǕ2SU1;,[B tb]`n }9P< i=㬶5irfSt쿩X2ӿhskU#;8[8 4r]Ev}Y()2ݤ ԱߍԮl:Xo-5WFzR\ ɥ?oZ=X>C Z6j列#:CRwfB("efmK%\GFiV W_Qbe]hrs3km5*(gq:5tywG8co^{Zo[ ͍F 9gn3޳ |9jC@`JQ]%ci {d.ml#)9E?)&wnRb6{7ׯ[OycW^{ϧJo<~ous]ʋNz 'bvL\o-7vZ& eY3mjmn$9.>z1MLs-VBΦ]7Ld%IiP WdZf]@rΡ/ @yYEҢWޯ5=ʦ( 7PN]Vp8UQ,ݤpoZvnoIa μqH C- @@z~S  gQ{;IKҕ)ؤ$u'iO%zǒ+U@"@np3fKy9=@ ^\b OqkA$-,]NS/h5^ki8Zj%zyn% M`襜  < e_{%Ht,#xAO y. f8+i&[J ^zn.^zq'8}N4/RIEQC% &(ЧkK|d3rr4[\gdugs6$- c7 {Fwwㄒ6*aِyj]!*CUK* @ Ȼx dTRa׶f{c:ʆ~Lg|hzQٝ}Y!U:hFyqf@] f~ϖw\'6wf]lOfV.̗!iN*cE~&ՉIxJ՜d&dclJdG   (@Ҹ8jGQе$W+F9L6J>-fC>ixn=RšS ]O| Mo *ɱq2mZǕYDl 6,2IW}hA^t[Ȇ]l:SvmcpgfYy1  @6\X%֧أ8mh G;ϣΠσv.4gdDęs3Ysϝ/冢D k)3RNczѓ׍;o6vZT6*-C/W]c#cjMrDvq6A@t4%%RaѧT 2vsOE*cխzS_1*{UoJ"R3+U{ΏE4i# :;dPZIuRCVgJ}Q u\zMBI?JrhXh#R| JB@:y6i6浔Ќ(zm.6[(nyk{;ڻI=[y+zCW X qsY i5YfCK7_ s[ƋE Xr *5gn+RBG XUxb3F-ahnڿ8׃og&lE$,-Ōmxˁ%b)1zf=AVcR: jS>3"v@0jkFb{y~cwltt[9ҭYV3Yb۩*y:K:-"@ Gpʙhxi$鈪sA^v'|ח4skʖuQUn$X/ߡ1X{6U>⅖tf)Oy먩Z߄ݴv :_S(gGN*jU.44U Kz%j XZ3;"˭[j]XeJiJIu˗Gv-<%x/w/|߽ajO6RZQSH:[LwC BAK}P<%-鉴`=6V=39lfK"?՜JyPr5DRR GxЀ>p\枣G\zvVHz^Ngs9v0'҂u6 gIuEhs;_Uwp{xRv+f3ɩfgGw(c jvv_C5<1}h ;ņ'mD_ :#')Gn}Ҩ2O?3^eUTIɲ&MMqQ?{Ω; D]!3Ɯ6ӛUO: ?[Oך=ZH Z@ v&{W~ek2=ZdkrF˄f,W\ k#G1їa_GG sfŌU bwct9MT׫O>IGO7F.v&㕑CJʩ離ÎpEv=Hƺ]fHϬ t#-X ћ#?22}W Ll5n^t`5dXKd%eQP5v;7/%<58ɾtxM7F&ߘ]h<F%jc\kia~nD܇p9 I<3|BBrI7M VG] E:%-Wc%L5=D6,5\=M[IQeDKsNZFjjq{|MWկnݳ6=Rb,=wkmYH"R{rf[lSm P1tFtm&l\ .6h vp)cɊ +k]n[ OL uۂ! ?إYz\6.gL:YX6**05x ۗ9lMW)9pe[8wEμ&VƳasŊ8af}-u2%P dH0+A^#L-/6GLwP2vQ,',Q0rss /=kyн K[Mu46 J,iIqbRVpV{<(7 saęfבg0OFn,Z\ιSjG)*lCoK f`q1`~6Ƭ@Ոw&Kt3w]1@`e#v5ۅecz;`qfW.E[r=(X ׹Lv^nn~zWF.fr\P)R'8īMЇj+ l!ȴ Ww/+Gj_Uj,{4($%CL#s A]vd>琉$< 䟂 0sImDz9U!K̸;{rp4贘rJf,xrV! ^!j"TƴGMѾM!:Fn/ڣc\My&˪*7eLok6j`+/6^(^Lddۭ2G`-Z0gnÕ8S'J[9>m~U߭=gs[sT;Vǚ%᭓'n۔Vu3gM4>GkNJ*}Z95ek8LK|Y4rjdM%T4Q ]:q]Oґ$dybn+J*er[ݼk^C;5[M46,JVP5IEVvD=6ݵ-eqsRo!t9bq+tBW>z퍅S3tԺ=9Hi1`RM6ڀQj0u\g&(K i}"00-Yc Θ]=VVD9)O-Ysk^|n+xr*zmy ssE"(BsȗU=4ƝW ҙ.*wʮ|1@ PF&ҫ%VJhK LswsYj`ާe36͋j+ v^;Yvkdzq yZb;;V+".$z/wX&UnC3sfDm|nPRcmڴ@7?l~AiPu@k!TR'vÎ?0P4;==o:JQe(eږܸsd^%+;>=ռԼ0r3y榩&k1ZZnz#-X/(5`y/h>Y7T^,f`~jϻX_TSW ŝd3bLQhNqۭ<{iƪƤ\Ud%/J* 2g'ʊC!rej)_kkVjSwSx]|HU _5 I}F H57Ify̥hdz#Po=ڟU#'E ՕD$OABJ9F:qOu&!Ǻu~/ra)֠s[TP%ՍCZB,W oY2=kjGa:QWJn~z\D@/Kwqp w\h^6oa9jPVep.luh6͵1*1=/s[ËX]#ޒKLiX¦O"L, f۟:P L _WuԿ9(23zuz -xL+C{0`_} P/kY[p3¹f-t@r4 >jU k-WeV|k2&X0W@1zmkir,ŤQU,8+<4Ie}zIZQR_[s{ :@OH](ѡ_XX뺑lIꅾD5 S${|uБbA}27@`m#tҕ[3Z+= #Xdz]d|_|;wY^O߉}  (~Yƞch0t>f g̖.0-:<-#"LMjbB XxJ=y aiZC8V]ΩkbI8VE2m^i["z,cj[E4kZ>P vϱarQ!3a3+; ^2^liz^Fk`P2eap3`ܹ72#%iJ_E+ u=$ϾP &rYڦ˽mS3Kl ZF|6#>\#%_aB Kpnz @'-[l<>Ӡ;}#ĆNmցңY-T*l;q:jwYgjq`18%{o Uuen^i+^d3 I򐰙ti T" ۯ4]L7?J;GNh21 8ldzG-jfܴĒ܃'Ͻ8+aޤ-(ǜ= Ni+:Uk^^SM#Bݙ6GG-$j/zi}JVkTP7#6$qGkW/|IORZQx厒T/tXo(Oz3m޳7BirE63>=ry FՄgUǻ3Z3мIpv_O!yʠZ`q^2_b9'SġF_e^M5& Ei^M #aN=GL{:ا۞8<2}zfi'){ukӦ݇l:3NJ9#Fwg.2V3oo,Yh ]vo\ܶa1dvv_cAs R~rp@£,dsvmcq@;Aٵޚ?n&7mr%=xLBGYW0=sKa3ܷmvj7Mg7]͙;{Za[ǨAUelV'ęʬ9ު%;WRsn6L$6 >gzP҃84^WmWVЫnDK̢%=up=ӡLbd&QW)n\hx@,MżQi 7lצ@Ё?sn-1}CdIrM,e+#rNt;]t#ESӍ=YH7 8B6Zby xOzzĽAmۗ.z[G+Jet6^͒Vf/OZ˴-qȼǧCKubEl45$ ܰ^w4_v8L$6{^m2+Bj}ב27qT#t\[7 n|\?U{ϧoy77@;sǰh] Mn.,1Ut'+0h hk~UE;NjPWӔ?fz)\$79 p^!ye˫< W)EHZbgEyԈ=o=f#|JaN8Ku1}4)aY%F6\<1LEzmcTg|5+M7%QvBfOG)h"3Yäg_hn&LŇd4xv _;*/I)=<8p!㜽޴ߤikO]ؤOW͞!>*`}|3쎇͈9[{~︥곗1=З a15@pg0 E5-(-jy揊04)d:ЫbrWU54΂6ES !]ǒ+}GUrnQvg:V|Ȼē3>mK~wnvqQ V$e,~u'2M::hJs J83kӞ9@lEN6( 5PZY>0eے{< {TNivGGi3`&RZ@!vﰅd XyV[6n#jVل4jElW/gO@!^L>ْ[$n*r{lbhm"X-<&H7YaFK˂_Exd5Um.T;&>,O%ke4@ * qA,) ג|ftBwYjOH~'Y߆s @ z@Jψ9 4ovaп]vq%m#$kKxN{6^u=g4 XJ*V<U#XK`]^Ll9:@,!<$2AZv]d9NOŪAK8@NخHHqDMȳmBoCmwd  X^ŒU˫ z@ "Pa^C"uE'(>Uظ:wh욡-ˎ@D8 *j#BmmtMΒ,oT3N#F4@%D Xʥ~ |\bZksGeRoR{͌ cBۓn>7:\kja<[zZ0JtQT5!x7oǸ0"+ M;5gFmUhfl=XCN~V&q#P(f_v| ވQfc B t6"ˍ6'{~ԓ!eʝNڴm<'Nja:x1$΍-]Ս4MB.rpt |HcNT1̦wCfN#ʶLU]yhr{SQ׳"Pb?1\E9FktOͱcz1 Wz2Q˜wcCYƗ?ytDRJ7/ަIjmLc4חΆwZ$F^yQ{e>4AʾY :%)| 2ȌS[fTj<æ$ nldI[%j*tAE?efƧG0]x2ta i"ʌx]Ulf%'kW&}4cWVx%JfN0r3'?l-#l9bd޼k =.,r*TjM5tz dN_dzBir5s O=4 'h jcKNQ@4O6ƦQՌdsC@l44[z tofuS0Y(_w 3* Oеs6HoBtIks:OܠKw\!*ό5r ?\rX?VHeuΆBSt3'jWΆvwUN1x|ja~GEq`EU}XxN/\ol-dMפ^C#oR4"Mҍyj枣7+̏B_McewFmJgz/̌v流tD-5Omyzf ՟`E=v+> y+©ɓͻz@Hhג ;O/i^5TҙM)'{w5$sS>xF!nnLς#J1bچMv[S٬}_f`J$fƭ|ΤݷkLLd *jCW> pIf|ޕ+ůon_PVFK[0MԣfD;iO,LC1s ζ]'O64=u&u 42`pe~^7 nS뮻O%o<~ouTsΌh]$6I,.n42 [mst,nW dnڥ1uRCnKŴbh ]\ilWJek&x"oyZ8c_o/nm^QZmT^mCC0Om =nGxN [UMF\!yf= A*C#pU2=;"6fIxi98C4٢[&|aI[m"{Ǚ7  k OzX*NRR{w?k)*sQl?&(i~Mϲ7A(-ܝ ȑY}\eaZ9+Jdeyމn9? \"ya-EcwPH&܍ t"3 ]@wD,1= -6;N'~uo Z>,=3Gy/eQѓkEh/z+ϙcGvW[J;yAǓa5΅0Z3It¥yUnW͟/[CLxvUW0 7`#' eE(N k#e,>ܮ3j F hVf,@/gnP(@1dwu\%@"leR+=Х =/$=}M9:3GyEeYo/kog*@dW#Ne{w1ס,sD @KR$ !$vpnK @>{e` }@d O.$@qZytkעu*ns6@  [ T@ 0)1-TgڦX[h=w x@@l&V)xuoP?Wd@ :dE7pLɪ@ Z@F'B(@+v%TUu-Ye{@~5߼yLjJ=xT͛wgoGuXF P5-]', YubIR*ұ29a_+(O5l='N [⩃'f΍%qC 'jQE5TDCwڷmӮg'/ٴi׉O]=A);iՌұܡVB] ЯfK*9Uk*V)Bgc1O^cgK?12#5R^3mXZ\GtJb˽L@5/mP: K_K#c2! T{eG3F)\[JM ֶu*H[hܰ*(eflf|z DfwG>h"xYaJ٦< My#!yhrXcҘDL;);I:\ðun{bqp51ݼkA!4j!ꕁz8<2}yMt=na,Ez$<;At4;1>sVql ͳ(A)\[s'&GzYY$Ħ՛ۛsO˱(gi:3>ָ*p/Gˬw}=A>MLF|GVn&Wm_8ſJO--{6}Cjg >,hHB6tsI#cM:Ͷlqg,8fPJl744rO{yjLeE8iՁySLVzwG{X&!PnMJ.5m1ȫdc X+}DFaz7EMyW#l#%޸WOXu߶L 67E빤I2Ho'ѐ].5_$@zH{ԑ5oݕ.z51Wz̤m45Q{&+|uK@j\ma ?7zS}%wws >fogɣxO%o<~ou¨;:3>zL՛U\eߞ-HSmqq䉉OW+FwL]GĀk uLNl!E-9|ScG53VS*oz7]zjXcny {Ld3o8c_ox.n_UŰx eG)UTiP4jN9&'j. pwJ)/II1:V,xvɕLue ?׸0p[Gt$VXɊQ"[톽{?u݌s~n0K'z^h0  ^еO^&3a A=E= )3+Rڭ5YJVb$=SKWo^-$h5PsnG󃢰$s @HVUM!k5#Э%N&zqXuXu*!l״kRj!*]uTW6&\@/CU k!I*b-|Vy;nZ-+G= XR$;K+ZɁڢD|?%"|c~/Du )Ьmg[47qK,u|Kmw:!蹧 @@гKcnuK-jϨ]B@Y ߸(m}ǍmAɲ-x=]i?[z^$GmWQ,T@w@ 4sR7 0}9HO(sf{m!x蹩P7ltN>WԆ9ҤcRxU @  5Z[jQ굑)[zuZT-9 6{PGZJ49U./,2ȩ93CoPi'(10}5?dx s$E]yZeY!KYٶ/n^tSӞkyP'~'<%Cr|bSkxbd&2G-@t3=@JCcNQ#.3YzֆU*M#6c1#Pw "U~ޜZ<4)Ff$kQ.z+& pWØ'\feJ%?24l2)SHg#)Mo?:1O  AOpPUjZNG:&K5ōg4,r3jw3W)k?7'a#?V@ P%MXCTswm @`#8x+Fo W@ͼٵ4hS%ԹIxkMbm{y(  a2szs87uwG:4]>3ڠAf$$QhX =/ t( E/#lK/od svpIBl/V):7w/WK7r24@<گxEBIٮj$9zwr`}^Ν*Y q~t8 3zvy:(Efqj@0 떜e4]q@Թ{h1rI + P-M^E@2D#έ60ę/** WU}yXǒW]*M빵Z'ij.}~@^L@ UZEk(;;o!bU#]56yRpvz.fO>^{9?Z~ƮwaRй瞛KvՍCA>P ;$7a Wv yzv\(Ol[r< 5j V52 hzfw’wOq7xą[.#*m&Uڠ@Dr.4,dmc/-o[ue+G"|VKCy3}DzOȅg1t@$u0sX`:7\:DS0oOƴtS;cܻ DH*tcp׳ӵ2-0 Xv.9䬦rґMҒ% (:;$Z@`!#i)U:3XŦ*{C 3moֶׇ{ingsW1@ɂ  t0&+Udrm|B:ڹ2=D\wBK/@t,A'@ v6i~d]x¶|90.6ԓWkUr,7t 5:;0r 2zwìh"ӳD]G鱚aLkS^mic%gJ2p v8 hHJu"ȚhU@ P ( `1lv&[9{l+J }8ֶgVjM<@ϵ&* @'}&*[;ǭ+miISxt`Yȣ=eVwᡞם@ &̫IotXկ+6zo%61 1N琄_Aw/|"Yf@ =BX`{?Zc;v\߱X؝Wk {@`hjU=ϡ5Tk=A@<<5Vn*̍bl zmUZ۲$6K}oInUAΞ3n 7/69S=6e)Df2-a_9baIW g" ~ HoAU َvP.QMysw"ٟe?Wz^ C"e3U͆V̪Z93,\I!ۜZ=K<;;ۇ* *B`\[5_EC@AxV_Tmh=#vVsokY 'K:Y ˇ@ 2*d5FS;o-Pt- O2z^鈞@#PCMaeUMUgěD-^$brT׼3 `i#Ю5=՚"g2E9ULs)i8r6Rط}Vx~ j"V썂#c]]m*XiȕH@0{l,|qV:yڧt\=z/=+A_(O!}pՙsvO;E@s  ~u>g$aA q֭:6\үνiǡE'-"N4 XZT΂9-Õ!$!QYKf_kls%yȹv 7 #cVt$%Iš6*i=ט3E ( `k)[/sƐeKW1;k38VʺwX<\'u" ?ҁ@@Zl Ni^V t}0m@ )֭qGد%y^t780沩e8^k̀ 84̹+%f禮+Rb}F7f\gg~铫5@` !ZljN|+e;K7d" $J0^ҎO:k $VSf`o^.Vkh`@ [DӘnHCB؈2Ԑ X@:ue6\\+f]+Lj]" ql.s7ݜb~6z%Ƣ%8:̯?7!h@ 價Xeǻ>+rng7sYW[~̣*9%mݲލ@("8+ jD0Ӗ[ g#C`V'g?ù݋@ l0*i!+j}L_Ee0# 8pS뱸a[^lqO+֖霋 UA-L T@ `Y2eTNiٯFfCfte҃&la ͖ܬ׭FuO`>y#+_souAO.  C?)}˽7vHi\.fC|q֩7K8z9ZN%~⚽{_/À\G]xl=sNy "Y6僿D,(*g-K%bȎbǔ'HPYiy]Z:K;@h)JgqCřVk)vb"z:U? { 3љر6gl\%Plvv  Q ³b|&I3  @+mφH],9G09bsl3p0G*& +o$iΗIE%֞u‹0ps*аo;eCZAu@-}scgH37H/֢ē$lm_Elձ`.*ێ$3fg7=աa#-L)T@.!v4pn}M/ZN 2 B g"F0 c{Y0IKsC[F*пu@ @ g*N ƕMT&Em^֬-RC1%^c~YFi;WmIU_btӳhũazd=wI=hL( @coggm wC6s9ia7-蕙8<LH@er[?y]ֹ]{emK b@xثM]K)7`$/iZE]g2vͧa` ^A* IoWFy>kwhh5sG}&B2GC3YT3}a¼"عSyUfW!+g 8Tsm[^i5thX|iMB"yD~iǽ{:+GxL=֊A":W^s=fP[%J%~ep<(TWs.űM ;2OmyA%l/z؆:NEK|?]ѵŒ%g-eݩ.w 1pjg b/=w zz[,܎ɭKܬuxrc&7wGoZ}=W4ݤih[zbtS||KW¿"˥H|,7_>6s%2arO\2$WouùӔZB9eBpqwPʱRSoRI>e$iq _#vti!tA?4M9_oO ϟ4!h⧪ibfBcѠIe/$%.{ssEU6#[! B~IWy5=~z:v̬0FW?br~^xpyeicCy3FvSaq@ZnvZ>=|KI;X̍=%.°嬍UC+ mKx_!qwiWQ?gь1T0/ƴ}Oz.\oUe\}qy`U:e4l=@@['Ïx{FؚԃE_Ly]" JV*nVKx" J;Ԕdׅs"LQ rZU2B^K+Suc% f!!";%S%xo M*UM0yʉWAW3xBtyS /%5Io%%gg濾jpdgzW=KamRNA)Yb`\XONgc霘ыbm,HS*+ ̍fڿ;] +[秪&jQ͕WG{H:}f,Җ5[6aSʚD'l)ՔE;&[Sܪ&,,J`hq{C*K%0,x.iYn.?%w{ N ؒSDq\/0jC.(q:>_zV)'+,i&:%“;]JozŠ[AfD|E ӡeod{OӾX^b$c_]VoL瘝ۑኪ&+V9Cfgsf* +4A1@ vkjKҝ  h!@ -sMCr@j,=s@ SÖ]9(@x"h͛7יB^W}pK /P[|R8Dh@ vX\\|cǎ熟K.d޽ڀ"]_n﹍> Gȑ#7ws;NSz۲;@ V9?~cvǂ¿Et!  V)Dǖtsx6*@ )DD|:Yx;:@` @Lɧ9Z!#pW׽劖=9wGlWt }GЇ>tĉO|GӟN4&r7]> s=P  C,CY%X@ LJʋ. 3݆ 4@ }{5+R ~o~62ֿ%m}[Xkv^Y[*  %_Gǒv:@ @[)1\ow ]@ X{n 64@ +g+! @mlhϨ2?ح@ @;l>(cE6Pp{+>"t:/ woG67]pZڕJGU_?~m2vkD\./z'W4?Yh]U>bl,擛en:<ۖgj ;<[uųOjB|~jL*u϶X؇$U;K/&P Q0LP_iӫt{<ޛ|~poo?e˖uO'^ċ~;߳2W^/w̕ienE4& +˝_F>r~#SgW\WhlO<) h 0"z7z %-3t#oc{7\oxhZ&ӣ$ u fŧBS5G(jk=o3,/yEa LPԼ@< Y׸[m糋/S5{?JfLd6Wpܧ_LJ{G 95grx j)Ye{CP!pa\^5o񎽤hهYYeQtmn@v > ]17ڛ]0@!_I{jnr:]EqYaNXh^G`^xc a GY1@ 92 0te.0Zr.^(U1y"=߳Ï9~[0-#W&}{ɩ]GIOޕ "v=Dn%dr|@Q2Q0î- TwCM2rt֟,ŧJ"➤?|3v*eK/YSS\uqkX~b ]1VQ1[_Zoפ_vUw2/N֨ύ|oyue#}Oz?'[?{ƥ*΢FzBPyNֹVfuzA u:jvm)"bj8Z=os^Hy-۹>^ls摡{'i-gnxzdGgyj䅿%=aV _Nkٕr7 s}5AYoί}kԿ+il0տwnl͵ *-ʺw] LYSZu}||+_w;K@ >1~?SV١)IENDB`filius-2.9.4+ds/src/info/img/webdoc2.png000066400000000000000000001655041506574241500200000ustar00rootroot00000000000000PNG  IHDR sRGBIDATx^} U3þ!HX&B"a*Wfe **&i.Q*J(MrqD47efssgyyg?>Ͻy~9{x۝Oo~^x^xu1#0#t,;A}׷_Տ6o޲}3~fzT⊖$"?THL}wzz&2S9ӫj#OZRvM0t)t*OF U&dk`&kϻ )&w/\Ҥ8s mѿOifxs۲KYVRWSe??Z}7$9z>nU <#vڰo]O7},^ah1u(Ѵe,Y^ ȏ=\Xe ~qjU ^o@I''.&3W j#~^ikTdW5غ29ISzRRZ@R-VlخZCs{vJeS]]M15+2v+ۢJ6lOmrDp29jrkL)gVp)6. |R*-P!O8%\O7Vo<?G]үq HiBZOiWoa.6Կk5W^fkPx׾9_{m#PT E%TZ~f  Yឃ3GW=ڎ]ͯ~y>c{7E}'m#]C[(kChm}@>o"[U-PP_y TUe|e$/J%V(Э< ZTVtcŊ@ԷO(R8KdVVo}B*z*pC( ϋrjj :UX̮(H6h7ɃnvY;q'+xkӇI!V1WPSP>LNȾ5"B9)-Ply.is^(3;Z+cKKҼ;whپ#ZwlKvloٱ=iҼ[[ߴwU AӪdnkR\(yNi闶gtuˋOOO|ˎtsZ@zCH%9,EA}yLOW% c,;ނ$%$xoBnD G+muU_W~ON _"}Y̼] \Ø!1GK_ /}`6mxWMnMI1҆;*dbkWh')ZjWZ ϵk{:/iR5I[6 ȣ |@FHҘ !Ok¼@kXl& 9ϋLNap줔r%/no-ݱuGW\UR8$E~52V`} L!Y/,Ҭ;j11ۿTgw^y'Ně;&/wh 8[8>EeP{]U;7̪J}xZ)vI%~vk?6ѻ >ǭ<R)kH[Qٖitsm@HTwDk.=b 7yv _{J=x(sP5foeM@'Jcy4}Bj!W-7w8t ~|fvY[-/dt=K03z^L,,k 䙟>ϣ5 \lʖ8f,~'#ncw׮'_׽ Wu+/C~=O0` X4:0cmQfL1 7l.\)crER}u#_pS_j‹VJ-IjkRE_/u)d%U⛳rf!1㘣xBKјFq)<_ qbƫcY }#m?}Wjkhm[^8,5;GՆ=+m嫔NE+U=S7aR}ft OIZA䰎QC4і5쬞V4"idLY{~t>]^!K徻 "I+cφ[?B'~u[NZSlQl W9~-wz6;L( GӐk3R)s4aݑjc(|SnMgm<*\gHi.'pUlsS;9j(b4"jHZ{T=B9ϤDW7qHxBH`XwNy¯=y ݹs >-F /+QvB8pgsΝ !Zvl۱Skmk޾ Fwʱ2FLŰ1|Oo#Qg<\4P=q;LؼcɲDP:Xw:3D;Vwkvyqh=c VS$M˜O K_,i,4 L^َO"94& ik5@92Pm/~%RH(~yR/Ru)YN?l_qRފ[xG~BS6(Y[F}|NlZm7NQ)i!3+dp ;kHy&414ZDҁ\ vJ>5"oFqbBy4{[ 5PWjx ,j,Ib-ar|'Q$ ґ_qoJTPjޘjGwFC-!P IVӎOgw|8Xnf.Jco-5s/ӧo߾e/|x 8޼m9%.N[8=`n, 2{M{[?w#n1x~x{#>ȨH&ݏMWNYZ.=\=L%Rvk֚<ڙdL)~0RyͩNXpף_A{HD_JM&Z/>HǢhΑ"U|B,\]9OƓ/uHiszg 5槟θ3gS>jHGk1+?gıgc~^/qd=vG|r ?'٭rB_W0@'@}vxsǶǤőfnX/,< tk2?[ܴE=ځ{wZ϶4B kV1]Dzc[_?wO<Ƈ?{SD'f:.5N>z|ع[2{aOkڇ^{~N>k t$zӹa頷~h}k={g>z%zyߞpg`޽76>5䯢pi$ϭL3%Xr%x&v0ͯ7>ﵦ:U}.^+>r~Ug3tgWI3Y|>[f|N9w$ޚoOS~j"ӈӬե?=e0/a@y4?Z9].Ov\eę7Bع9BL_,)^n{kr e,\vY mKʥGpnZO%AW z)V(W %Z«4) 1`?_N:~9yɅ}*9b$ UɈW_ּxfs=oSN]Yonz'dZn +!ױ?~sU{Og97=g51駏?N[W^h\ v>c5ˇG~?;i.H 55wɋ~agyA_M~ 3ZR|݅çl%v kQA:dhל-jk+~Ps_>2Yu5WiZogAmQeQ2;~!E =餓n&XsAH}/']{???ջK:=dݗ\tȨ՛GRap?!Q{]k~1˷>256y{uӒokrҨONwuA3CU*P ^TM lkʻ1Jie1]َii&VmgݕUNM|%}M|@+(Pԕi[Pw-%%䒴Xۺ"Zr Fj"<&QAR%0Ta? 7UD  %aE2vo#BDm8jg0  F|[_hq/ (">ppp5_89z>J7Ϻ;/]% ׭[w466tV'y{Niyra{ ZfgnyĽhz7|9P/Hz7/+iםp՟=^뛞g9ɟI{FCn)h26>oZrev\<:;OO(mM튯MgnwB&<$Y| ?\b;//L.8'C@᫃SYmߡ\lO$/}?Z1Q]рa-N<4z㛙A7T`7>[+q+"㚢̣GhnN=<@VRTQi/> 7IMб![Ͻkڪ8Y3\{(Iッj &/^#3d|o$ŒX. #  2z[O>RU X׷ϐ}3X4jC  ߪA>| wF!DU X=x0h ETV]LiK/]t̽qy͓ǻgcWsw%G d'k{z0 ?voÛ!j}{n~oO' $ 毝-\XTfەܠͫQ /)C=&]}O$V3R;㰻EQ޳%_$H䮦g_l]Nʭ}J[EK|yߙo 6MjzO5Af`5fW7}abuhX!YXב/д({Q (8(fvi輂mdnݳPHC,XYF]H8e2I) @csvmw6ۿOu?ӯO ~wOӧnqA6jy *Brw1b_<ˮ{`JG,ۿÆ06]w] 4ŷێ xpgw>J&N .ë i}R=\ȇ/G-o2ou3 g+fԧ>UWW?U?= k.Y)~N`:1^~~Vק|rԟ7鬾LD#8.?#n'|;pa"GY}#Ͽs)b,CUKp͛O<7rد`X+yAV`wp[Pw~߂guy#j?f/eM5ZUJn^s? ށTT ,ӾC]汿 rP4MֺOp\zmQ HfKTcwj(O틋23ɫ=SemkMPyFUJ.5Y>_fHZѭ'I[_ʨh!0t Ov1v>*_~r+Ʉ0U XU9N oo]w?L8x. <>oW8(+sAvw8E]Dv߫e2$3*n;4DŽgP>{|_m~FZHAOPgx^쪹|n-2Ϭ%/"-kNg}8@Ym Y[Zܤu$Ʃv`+5hGQڅ3 WVB㴰^nxﻬ"2E*hKla+6|Сҿ ^(9".9>L9Ç ?-Cgw [ƍ{!3wqh]- 9\19&A߹/gC=օbz˛\ͫN +"~[2jB礳Z|z.'[ vo59AkF/5i5` Kl{&Ϯu8Wp=OshX~OGg L,~V}5[xgw]=9YN3'Po݇p%<[~v՜O#&Zp׆/ 1tN~yŕ~Hw:i6ҧX3g=Oq+'Y j3N}4hWџyUht[)*ȘDkZcuLo;%V3vȀt/XvT=:XYӽ/$yeu!)'OȨKH6$}tOű[Du2|bX*%qhũŴ0uwcxAS>{Gg ~O^[bOy~{xґ3:QjZ3J}E|:t Gv!Ioꇗ>68I1,KIJ\}+_s-1(JV. I侀?|a>׶W[o؏Cύrհ^zJ~ΔH"_h,rPblL&SnRd[h{Zt e@˔~DzVR0TQmF h1^iq#M#1a{G(!U&uBh̨f u)3~~g4FaeJ UO[虢oZ!+d3m33dΆVyZ\gj3=[~%˿QhRI:J@`$]Q6V4va}T7"i4zlF$1&pZgie34H$pԡTDLbJ&YY~BxQ+[ҥAe" s? nuՍ(x O|3ihAX_7)7o(ߝ6(nc=[ ]| Mg43 hzSY' = .TsIoI[[T( e04Kc:Q}Rӆ^.Uq($$ytQO!i*6-Cj+%ۗЌ<|z2NŐ2nUDG)YI}}ǯ#<ܵs>h[<j]â򴪛}SB \1nerG˴VXR%jndVzJ>p6Dp7ʩg DrC.3¶~i\t6AU9ExF1&Jc|zv) rQ1A )AfV!-A9$ucCyU3F<}Z0#U- 9l5iEN6SZ9]o\zFYyD=aOi(gVS5}UG>0261\rV>Ss8^r\*S&m+I?[nhp z#zi fNELN ehGr}'苪kh9>Êz.eh|s]]`9fTe'.g1u^lxZ: 94j =7uXU}Kűf3#W>sQn {<ϥsO׫`ƺPvrZ-Bzlϡ83d= ~xyUC~3!-%P JkQqVh(9sŌMc6&+FӵPaY( Ru ~qqWKB\4JvfT) ѽ-%m[ќV6fU @ ܳ@g}~vt-x_wB2iT01ׂR1.yT'̶3j> )_|s>~HțOdOSW >>dxzG)?&o"WfzI3xpJ* F mJ׀e3Wxt)W;Olİ~}5C %LDD f9}*&zl"Y;fе^K?7D^B?, w8ˤ+)_]Wl1ʑVl♝p{>7<%Tan.&茑=W2аKoe:WZ.-KRu ]!%BtK-2R o&ns6NzܬZ&ٚ< jLҟqWYF`F8zRN罇{7\|YK/^dF`@ه<22EdqIY~Y7@a'8{}?/[9PRen.4'aFh+nuE2,- jcՋsKB[[3#0QJLͻ@a5L Ӊ/E,_WuDdXeنxښeF`N@k94R9> 50QF0t..S2#0A&!ϱM5Ҋ$[n/|{ }64޻аA#0qi0#ЋX^o'\@ B>24l*.Jfy_:P`FCCpʼϗ2=SV\8C4z+G0#0z vs.X$ΏHǙDHea >*}{J߿ڝXUU7bLm, Nun--;`۝Ie݆xm:3>}@Op$""r#X}V ͡'{s8a1*4 O0|s;*5=+FѿouO뎤ͪ֝[i3u3 3>}b'-;!^r Wrw}w;!^~=;pu!*/IڜҎb-3޼ȲH:7⢞rݨ|7LmwlO>;=}6>=ӿo;|8p>S0;˯ VZ:oBsڎmǘojM[96v1\)ZZHvN$aBW 6y ]-3՗_z[Z3@o믿jD3۰^|8p>SNUpr@_hwO>ۯѫ>,rk%C*X($'jQ%M򛑗֖/}z'.|1jOf%o?Ɵ~mUq_`ֿx7sok۫Vrf~%cm31&Gaqpg0%漉{!/ ;`ZN:*wr廦UZ}TXbI^zЗ.Z dž.bTB@5tѮqMe|2^7Jy9m6+ߢg?ߙ={v]] /m-A&6fDUpz;.|o|ɟό_09[>^jtݬ\$9<$Yu,ݫ N@3nzQI \zMÆү_+?!o3{3ǽWg545mܶ##?45=c'iiw˶+[Oz13 ع]q76|;RtvB O<[??m'#ٛ[Ed£?~njN|ۼۮOĬVmXY>fp}{ QJY σ Ie==KKT}3Sڦ^dIOT"*c1`o<&aQϯc}Pέ>Mʿ)m;/=߲={y[[7j.Yɢ_3k%?;'#/8?tS|Mk~Uw}kƟvnr|'z)KS)>p=/fFq}ͤK~ZHP/:~~uĝB\^]}˸$cƁ_^zūlppzV'|c_|7 ~w*j*5 ªKSIJj4 [10~n߾pM6}x񍗟ïk3ϛ߲j=flnv=MO$wˎ62ylOl|\]|ߚwd9|'x䀉ٻP.ͯA?:1}@OQhnˏ%̀cϼ n8ٖf/g ɹ9F7BӠk7Y_]0v1ItW 3@@Υ-r#?v8vPnC[WZ>|p_n?~ܷ_ͯ0F,ֻ|aro)b8jso?o:K|Hm\h-QO]Y?Nf\F7ٷ#[}h!Qʦϟ̻q/4B1z~1}@O~!9}]n6OBH'==jӯ߹5gq㘌ł]|5bVT)Y^op ӄc*+I7r[-ᷡ_'Nַw}~>e}G}7{!w/ԋ9yw]SuՌyuoJgo5ɣs?S?5lmQΖ}cr{?o ke< RnwmBNz~F<26<nj=xO?x] 'KPkOo$^ T3kX׉N3)ˁַoc_Ǟ&ulq')W ˝< xw}}W}+O ߻}Ƨz{!Լ{!me?ߌ6V|0h}Ϸ?[[jNO- fdxfG'knyzO/t)'U;:᜿Nusj295 ˻FM/>fp} !3MlL{o O5lPҚn7Ì"X zǯzJ=*AԢ 4h})o-%;s5}ysѥ+]6CΖ n3<쳣R>tܻ놴<ǸFn;|6XϾeG.]}fmū;~~.{39'M2LsW~ޯDRvwV/{>FNA.Z<"3<nj= :Tc?ݱ/knwq߆5}q/KRV;,L~vހtA Y>.UTʕ`KgB]sK9{۟lcǎk|1w޵vԨQxH_y=^@q>}s-bIhXj1 {#U/V(,1I8~q_ϸu Hc"5+~ĦkC8~%TamRNzA:%Svq!gjXUgCHs;pgϚ1/ƍ+~33<_}ᴳN|ɧF4en0ޓN۰F|8p>S@ܾyt}޸oмQ<)`A.ohʊFK2*!ǚSu%!4Itj.$Z39F`~0=( t2ieC,'IUzN_ѥ OTv`Fɯq+gN; A<:c)z{Թ$uiO`F?uCߕVBR QD$JSW>nϗz~Xg($.XN0#/⒢1ꊄ@[e"MHr.&dvm-_z܈йmTd)nN0#D8k|@[j"QgR =g"4wL0x7YH- ^Ҧl*bƄX0#tv{( 5a%@uj\a<=YqOu•ɞ|UO-R?u#j.`F#рaR5~S$=Z*%{ld1:IvPP) `NG9Ţ-CiF4ITζc Wcz 0# ZѳY(8ƫM^35i(\$! ŌF8$E7B.^ɾzmݹ#T/4GĚHi}.&R`j4-<|`@R ˩ s>* S|tC"Խڟ1=w&cmF "ÔQ̠, ZPIK 9 Io}.k2.Y%F`=G2`﵌b7NVrH XVtHN-NQlBEck #0]CZE8oTtpd?E*wJΙXtiz: zg4ޥ~FK3c_M8{)=2WfZrvnw@p#гۏ<usR"ԯou`RwXR+ "|=FTUcO`y`j< KEV68wE0Xv=ۼD"&4,{h_R 'Ȕ* o VyΩV,0 qUU?=ki,8= X,-7m'CzΛ-;  ۛqG|fc;3c.+HdȂbne/g 3LfXi-F ٘--b %•"aҲy텟ZX[M /KWUuշ>ԦǓn C(ۤWgz7nohmW5{m-zrNDnCgu![9\hS/f4iHz5@(D~쩨R,fIA7 6Ў_Aͭwl\w&|abMם{zFy^:ΐM}ؓ;;7lYW}}k &~s?m?| T 8,k[^XSw(y$i &)Z⌲O1S꼴zE.+/%SsSt:_/* 6u) MR3= Ɋ@HԞ,'@%TH HM_bfZf,MTnvYxK BVa+yx47kMF'NVWoOecSYo\mK#6ǓGpsNS>[;ߙFa|+[OR?f5[`e z.Gk?e#/.if?Q3ìW%.cqq'շv'˜NEkYWLrϿuUc)VܢPuR%1K+k.V06- zHz@Ս?;u´㛢ⵀ|G" KڸE ?KC6|n&L>ViV OOSZF@j̹hae"WPdhg)UFxSK/o{B.= 9--|[m'FFe JH/R)s1PQ]K *apono2'Ȳ\ 2U?v{ Y0Yja؝1 L̹ I8/#PEZ-)K`i @3UzZ X|uZ,: Xſ;oT]o|wBDֳ8~'6HO2;'$Mp/OS%*SW.%IrC̔u'6cp;7zK$|oG J)ѻ"-{K)~5'p 'WLD8FaOn@YGE d@Z4<(sՕ뙷f5^R% zvl0S,|{(pkiw"B8@\q'ִI; {J׸X }&$k޻'/8M?g??}g/` n23V\x lSa$ FGaX2 kF+3ל+lfE~V!_^ Zǟ_m|5wXRͯ]0=zިk $aCS1nbPp:NɬHKcއ*M~^VlGb>d"7+zoy,U5N(oT&2ʏ2Kw)'x?nOdH˥s[~ág=,9yʱnwyp6;W`*OOlryJf-{8^E$r{-& bag;TK1KE3#@վ2Ǜ]&.UB)yJVA18<cd`FD|ҒXit=]w,E.ܢF~Iwu[ԾC aF`4 %}LRqaltf!G)iC8eY4]900#0@q:e'KË^lΑOºk43o8UctTp9#0@Ȉ$ؚdƑN)Ə#"d#&[\}g=hIvuSf_eP)̸uXTXG8MC)9/ rχZ5V+U-x)D-s&5q~vPC[.u-P}ܱӯT-vFyRMs192! ]5`hQCij2fLfs n1o1Bհꕺлr}~RIyjIaP٣[2%'1&zI9j@$9aՌu o0ۂ0= (#&fpV kG29_+32>Em7RL"Ut}ePGma KN3oN?R3WOـHQtؘX765Z Q`m?{ΩjcjG99,@H`N^5 NWKsQr+WԪXڃ1=[R>m^8MMQMnQNH"i`R$rjHDQ0gXD| ,ϭ,r&FVORb,!U/urXoS+4m U!݊e uW j8/[<~,{ZI.J.%ɉTRsVy3@(rI]13;d 4=7Ϋ$Φ(>_!cv Ny\׿.p];&o4(\ sGy j]rnMfn q5Dk٘B@SρO5,\Ǟg+p,M1it[g.EzD+SXNX0@6ffY*JMrȂ4Z  BQ%`yKQުaGܵ.#kf,x&k#ԅE؄a8M - ɢ?l3 E&V"|*t׊,K)jy9 %fcUZ]F)tFSGJ̦bjD' uq 2eT8ZF,#WZlLD XR׉S|] -Ͼ,9ˠ~ݴDӚfuJ^)9\$#н(˅!,Y+^VN.dRylQ%F%"E0fۖ.7q*1;뇆IWݻ7r\^l*Rr*W30GW xGIX5CB_5PXA3}>0fĻkv3oc>c]j^qd%QrǪ%cB@r"F`Fm7dfcOC<v"ճ(TLbǚ]"Ŀ*Un[pnF`^Nޒ&g|uVǠꓢrHf3svK%̽jdo3`}SΖP!*hY}3*Uf?>˕A%эb1,O\Xqxm9%#}("ٜmH BҶ؊ { [e5hh%d.`"j/K-km,v-`׋w,\$R[XtH`)zTleyZ򬬮řF#$@u\+7%I>&X!ib+k4\xJrolAAv;/Y(upNt n#sy粟( >iC鳪 $gT@0ϯWFرꑇ{ޱc\Rsuauz0c+lgC{mkּe|w?~cxd[e htQ)PBpkaܝUuwwjǞ{prw3)<Y+5 zSƶpȪa~YٵH`⇆I[|.Nnረ\]f=n> @OE ſ]sf!#bEҶpFp}KAضjEȗRYSW{jsF`xa%d13WT%=Lmfb='tcF`zY&4|1\:qIbx`ȳz uU_Ò[杭0ߖF`B[#{яhbCN[3wհB1~{˙1d!Me] ---@h1 z޾-۞޲闶vvTp9#0@`brFp4YNBҬs/)AKoCMv=39hքEYlZ﯃uS2ڋ&pd(_e~n8Lv+sEb1X2Q;‚Y~nWh.``-+ÊFʛvm:d e+|ƹMqg ori"e͌sMbv.WPF>&,%->WnX$#0k KLǵ\hUX޶$GLI㌋Vx+X,\\H[s[l&0'44=. qf ]j}Mq%܎hF`-y8`s5>+MqS!~pdFΡMg%Yy%Mc'E{J45a^eБΒLnDnjm V`E :Orv0h-N渱>׋7xx-=oa(;&8ݱ"Vu|ّc{/6]Ni`^@QJ=VG(*DDuKS ES"VLNKbVв=)wZ [6Gm|ԶȎx&upqmA2#-Dkr"pE-ɕ"a8{.A1 ѓɳK:bhŕ dF $.!R\=%z15Cn6tQzn7aijV1LZd37.}B1#y,R;gFƒƢQJ5t/!]Bun={{8߾>gcit!粫o=1#H{Gfw N; OB7 %Bl[h\tf*G6_E3o ß0=g4{dJ1#@&ؠzcFTb*x4K$S{8ڵבtzUtR3b&[)dF`* yNp*_XxEK3Fw%PӰF`^@ξ![7Y0ao*l WpPX*^M gzuwW`][*e8ݖK6|`'Z-BóڛY$L]>#B"Ҋ3bl{!akKW-(8UrB(hrX6y@)AppF`:L$\ ;,w lHsmk cGhs; _)[,`E?E& {ivo(l՜:m/K/>J݊ 9[Lr.cF'#{H3M&MR0p͒.O[\H$ 'WM^pn˓0CYrEEf[g+7"xr*hXa%Jm나J#;Eƕ#Q`{Q7&, Cs0#o/,}ZbPiCldGk7T+Y>g`F"eӫ "¿$p,%;+Zo//cW _!VFcޥg$M u5R`Vg:-h:F='`{kz4Z袳K*U#t?/Q1,]B`nF|N,罕e:qԹ]oYmRt`y`CLVpncP Ǯ?N9RCسP'`*q*%Yr+0@wBS ::~&+vڰdVZYRf]QΤT:a+MW` -0=ۙ8#0@Ԙ5MzT], 2o+k3c+ՅX#0@; Pgk{y3v8ӑ5kH;֡G)_ ֦Jښzn"F`*@vT~ XF,EO%RҰd}e;KËS3#T36=&m@u(QZБ2RX-R<ީX2=WDF`㢑F0cJ|xl;k;DjGAw RL%u!N0#Fj$b\(L'kpjjL6]iQ`p;azn~"F`JCXÂ`P&Y*$gRmǭM&Lбn&7 'VO85#0@ ámľD{ScNE\iZbؗ-*?X\*#0`ϥZƃͥbs3]͞"hlnGaLX7#0@g#-<<]lh/ht'F1g4BѼAhST䙞;Sr#0b Pv%M[̥7S+n+ǬsJ; S0#0]CԇƈI#fL`beyH2J4evz6 ̚ɑ:+Bs#0 ?Eud,dl>^@YvW* ᩑLϽs]FDƕZH ծٝ"e3frlwp=)(ƻ.5h.UY-F`zgǹ`ީ3\eI_4dmV{S:qn{ ܋?W`.3ǩ?T5 ƕzfnJ4jx?Q|ߜ鹋UV`ޅ鳬%.F.e3l\$T:ՠuNdDY1o̼Xn{3=ϵeFK"nFƯGe×Ee23KyfzbFW!_$j'Fk\ k&[G- ]d@!ﵭͯw["v[;`-[dw֒Օ{^E1.pտ%{\ Jp?MfvlODc0nqì\#0@+vyQi˄q߼C&'mqeGt^T S pAΒF`\N[+6>EO&hTMRH.]yW 2o0%J\AOJFYй}0# yǔQ" FQٝ<6b&Ɩ楁ƍ6z0&KAl,`F00j+.𔉥ѐ"b3"LEBhb>s F` $fMOk7>3ΚLWVv2]yʖ3+moXֶ5`[zfv{:0#΂O[:c1N%QPHKX?y а;  IaiKaFnEKvD!')IX?}.PbqSV0# SUkVl M aĵjgNU&,Y$*ѭ&pYoJ3i`Kx:vbF fllhT8721,UIb.Ɖٴ{sE ׎^jDT26/xq2G/͍U`WۥI||q>rY.*edAe+5eq-/_>]jBbe8u(9+eNZOhQo8{ 'dnGANȼ( i[ + ٭qg- 5 %}n>l3.L WՊ믘">e ume%{Of]sr[f$X#W.^5& XJn,;Jml](u߻t"zi(1Gaݚ& 4n {]mA,F0jFӡh2鮎vpmjh-+Ar tʶ&[r e{bFrw&ISVlJWl2Y1Dj9~R,mFD9g rwZYiEӃ3ǝxAV~Z3as CkrF"`ƹMi(.~Z/9c,E ݚthdfՔL]S(0!Q`T嵗fs]352@Kmn mS2q$g E]m\i=bT[jY/pS˪8gbF{" GYh(.C!e W1W)TAz!lj`qN0j>QI mqB%';J$☎,>pO3my: -q=cuZ@\=!֚` !rv=ށ+;2\r'bѕȌsHX$ v~XeI~a=.^yaziykPb1#l{~U'~c25d:{eNb"I7i?c=b,uQ+Ԏ"Z$ty}^߳ |h>iDyQ6pX鳪@^1#0GBj(8E4L⣎bIX0m2ríNm\8#0@A0>Ѽ6-pXm3-ɿ#cnΊӆ5Nn&]`d#0톀EȣtX[ p017ҺanoPo6X3ôrcX\3S!U,_>]",`F ) JbkOc ;k=6P8wtiV&떑L")/Y)|0#tom,p7I6Qt]n$3f1t{ڦ$>PJCJ6KvuH8&@@(΢gMe2#0RTPEey^"hZgƅ 3MN`#CܓF`J"@CbFCâ18g`Y$s mJO][[f4!oiC PYi("=JBͲF`aai0YuH1MB IӗH6! 1զkx3Zh4 1#Tj=egUaaM_Մ6T۲1Y6uq6Wngnn6k(g|`nLc)#0"YӲ)d4)$AuSl`I$ۜ1i-u;2~"7KR`F]HJ#Be*L-ovEqS0#Ԛ&ǟ|edN23L &}k܉F`A2qv'U8;ǰ3GeI4;wH3p!#0A %V!j,Y }jWٟQcږm,stk$锵a^;l*݆`F8֍"MX$K AzI:}˘fQ4-)wqt'c@y:GrI#04V\(/ fGkFX5L0."#0#PI$+׺H1bMFҐ:ˏn% + oHVIJ6- -utG$J̡an,`FjGpnMt-f뗶Cݱ8m^Ϻ=PZ Kzf@g$#0C1@mUzVFZ%nݬ&$m οBnFnNEna-rfu80#Pbz^bE|X+Ф23t(R:̿2.ڔM@ю%,K\ذ˧+:z2^EZ0#tK\K~Y'Mˉf.î-'mgdgvlw˞J3#н@KWExI{ΤDk )+\SFݹѾSVEΆa6eֆVW;N~no;vM_|>^|#\Ǵuǎ0#й̽/\f=w-˖\CmPP&%iSe :v7 Iǂt[ڝ?_h$Wέ"kn۰ :v^~xvz\1eR` V2eQՆM՛f']m9. &pۇ32#fϝyЈ2ۑՈ9j!7_x7ԽMTz@x<;> %gӑeg؄SgQc6̙)if1渶.il%q.h:.Kp6F`@9#ϖ$*8e¯,iRV7q^3 -)SgtQ\gxt1 I̬l%J ܨi^`F-i03u&PL''D"@-"CQ%XdYk774/KM0#P.b=+17 [n61\3d.If>k+(`JwF >xKzvmrM4mƞ(Kf1P0#P2c=S0%iw?|)IP V#~*ΐmWqZg\vpZvڀnlzVl(J Wm0#tK^ڼM_k+>R}Ӌwmxg3v8iRQ%xg]oBE{&cŲ#Sqn+|*U#4E6,ԊE1#t5ejcX0#tHbDdZ" N[95aM퐺N#uCdûPnh+a _ÄǝX%/v a-1#tNV欦7ej:-c-~m(܌gk5v~pM%شUl?^I\nа)XGF`NFa&L}HbKeR77suXD+LlƄ%fռ c6h}Rм&F`zY+&Roǖ \%pvĘE3#"๲Q[47fϕ-R Fۼ: Z*"DYRDLbE!pb^[ ^JO}qݾmDƞy9qF`"@֏Ƙ*2fƐm‘GTHN(Y;ƻPue;rx ꄡ-'+Y;@c bYm_$݌J"{E)/k'x4KaF(dZGx0^5Ģ6,E !O,9!択ۍseشQ|tnIh .|󒅕,C cFW"@ 8;hP&2A^1g -lVYZڤVr4UuhVqgGkמֳIqv‹->zHa}cȆy7^y;sF!@%G Ѫ!aw|y ǂ̨aS2- ;4W C㋄C|mN'M]tFü\R`Qi; _ f`x/xtmT7i9~ɫdʥ;\Ri`F+!`\gl-Z.! g 3;Aݮe$k9YmnMl!~7uubۃO?z֞m*w GɮXF`2 K/e$.N̦ ̱.`Mw"B\4#0@#9w8;/گvUR9] h{)j@[YF)~33rE.`F2:ˈqS]vBLT; 1z0LٷYj혱]&:i8:j0#0@^~Z~3JJu:_d rS9hUqk%D!/ dH\Ϟ̝`FDD[5J=5 sh^èؘB6u,w5|ir<{ubsь#0g7vU{yfF8 (3֫\zJ}o0ecRr׀޲$S ,kG283ڜqPMWbI1=F[^[aWJӯj'7 V)Fh7Pʖܨ5y.q`17flXU`tAQf5e\S@BÐvk,tJt3&vn6 <7Y$Ž2lSAkV%'+Fim33#Pq4Auh\chA`=JrGxbv6 wҰpJFÌd&uf~1kJKCFٳ5;oY箅]#oykEV#8 Za]]jFycS'B˗8Ʃ=-EF(]vTՠ sr DJMMƵǪ dF(oUӳԅGsFLYT(Y*jsk:}GIpЍkj'bJ{Nb^<R3ab;`Dk$h{U90#Pymj  sb;-ʝ-cW'CT]+!x͛ ;p-`څԧ]BCURvU(QcJS`#i8 *,FyMU5kZϱ6)-~jlbyI{ˁ7((*4OOZƎm- mO[59cBr:Ufqq. "|(XP^2 xyAX #0튀&۹ƔB: T%H$T*D Wwًﴥ@ɻep3DMǪ:vWXUFfi#09JY^E pOܼN宑_56:,zmiznǕG#;eǪXF`NGD+):je2 (8ōpdz;m5$o(q2 [1vXӛ`FFx4F5.,E cѺ,W&|m@ mXڋ:U\ \h|#tZ=z`ݳELO\OH1,`F#;VӖUY.\YeSYS9Ma Ńt0@#sS[sY͞ݙPq>{F0hԘƯ3<ӬwդM-hg ax5=åS7ovLkXLlM!v)+Ŏ-Z sUǦ+ OQ{ (湪X#6TЙk6o=dy0wۇ[o.`D@5[B%%*$:mq Z?$u+LF 0@*MӑJ=3U2djQt~M:].nozJ7zn޼z[vt*0qeij'vb&QKF8 ys8NNӄjj-L.6?F; @)ӲiM^;X(ryb55Y-B ԒmEP;: MV=)!LFKi[~^!drci)+\*CRQe)9#0] ͫiրFuW3 n*OQdb;ӷTm@]Uw!sm?k_ry,ץhˮ񢳾AM &!״YvWRaF לt>d@!ﵭͯw["v[;`-[dT%ӫ>D}oc0e/L^[>Q3Rp= J!uhNbo_|ռ=L|vXG9+=7LޙxQQQ0#0@Hϕn!e&8f#0ok)Y- r.hDƭI4HYp*Ktd#08ya١12k Z:i 5Lv*0#0@M" hg8,Ni%+AvzԖBƞ;gp1#0@Y4F,c/Yy`PGЊB%:%8F`FpwTxvV\mfS@gs0'D UK, }v"hv߮m+vF ,ر-JZTe1@12KoCT_Wjܮ\v ]ֹ [%¢%b_)lbUYEvӢ"iƌIp.3S X\&W62e ]`.ozVW6t_<^(Vj5$KAwFRxiz5=^qWw e5kju䘌fh߳4Q{T4{6%2g&ZDڒy2㲶~BUs]Dj)_fZcD Z$Hū{۳G="YZC=vKw{l%Zrr6[@⍱̌@#(4FuIcZ5` ?zHq2,gǪ8,u=#V.bL890eezg؋b*="YD8ר Iq'f!fQ[GwުyҤMHMJ'.\km.zhێziݠE"^1iCdJQNmZkrs!@!$ 4^51\g)G):̎sk9ϩ(pv9 gPtF┳c^s"Kڻ[-Ϩx>,JUFt֜i Ŵ{\rL!S\`m(NƉg=zza==Rk؅`*@>zc%(Cu ;seT)v8#c KN5 fTR妚h$TO)q | tA}\ A}L#FSJn/X]={$=®c&a @#2><+bXr+,ILJQ)ՓTXjlg:z*zqKY4pkՍv*2Q@]F{lCj6g:S7ud=ʺS=Z1v-NC cXq6gaM9{cv7&^bFTm;*оbu8`~,Nآf:;FP:͘];b*T,[5Txu)C t)vՆfTsfV&yəK@nĺGhu-2타Kh9a4qj*ar$%Hzvs[ܓo>]DX8/TWv#gsnٻDTv&Kgf {kT@i=e:tsuF6zntr4rV.` Ǟ:Y'ȳvcesq,y=?|~;Nsy={.70@~[\dhܮnlMВrvwV@knœ dc=66\m='gFG 'ʸFm0v&q~jv(fsk3֘`z+.;ed8g-,/֮SK`FR"[ j1F)gv^vOc\?F`vA A]jy| qJ`z.19=#0#5e[^5.`F qщ"(qF`vE od= S\z&ڵ)X8#0#@жkCJB7ae^|Y|Ug;F`:8LBF$޻dO2g6۱X4#0#P,Iq8%#0@G I׹|ع]>v`F"Bzn"{k[rF`zm%S{O_2#Е(%vKt*rJfF`"s>iF`nmH h?];{Z0#bJYRF`oV0xuѯs4~fz5gF(XtEhzͪ0#0% a]`e#0]P.%*г Zvv`F`@yShtV_G)Xs1CBu*7j"@8|cA]l2Qx[kRv#&R8 T$lD{F`,"P-nfɑJ+[#snd Vݰf Ɨ.yT&ELB+t]:)5^~3jڤA{LmR32 m32A02k9Z SZ@6Ew[(=w[X h Ce`5v˱!3v%8c |Y3f{R3arjg񼬝TKg!PJ "/_.LseJ3 %+$‚UZyN[N36*V2{7HʗW+_Du!vC=+7 YdSSC]mMO N0m@Gtda[W.^5-&/LI^)<]7nׯ_1qy6LfC9/K4{932(67Y$ 5-\4Z!sSV.umh$ljkf<е)E>@q&ѿ~Q2Vaq$Tc)Mke 4zSk jf̚rf/(6.j¸/Vjs^ȡJIt < KN{ռKkWhS1HOڠ $^& c2"GƍZr9wjy!iUEe6S|NR|vdzֻaUo^b?6ˌ@A.9[BŜ;~zSw}i|;SmbıXb~n=Ƣ߰D3WZ"E2}iR{uՁU-=%!<ΰ0E)*? 0jPNzqvutGog]Su8%#ȧ" M`Lra(=&6t;;p2sqN=^׶Q@]S!g̜j;F ($P~$ܮ{*T} [Yl 4ST It^;![.O=u4xuE <{֖h7t@#˴h>e# ;mJX%t:fa1[M%EG_|rcWOyQK6j: gсgA3jgs`dDҝ+_W 䗛t}W"euiT7QXNl?/9s~kO?`*Y3ԻwM>xTjmJ)jѳޘR3讔ڌ_ R7lоoHs,Q h`/780o@Owя$h GOfYV&,ќF;N CP@K&O-ZHq){4SJSզ3 pd,6ʭƧ>k޳3 1mkВHtsRj\\lfs}W\Z*"*ۚ;{߳Lw6A#\:/:X%$F\)M]ӛZ 9=|Ȁ>C:k[_-;໳EǷw[lP%ӫ~sYLY':ri3$HdHFa.]R$4δ&V%^x |Uw~ظúz0#Х)5MSdb-*t+O?"$Q^1y;S+)NNV-m,iZT3RWt1$˱KQ\+0}Y"1rmuƱԘL_jf̚ڰj sc 5rIycF\ U#6-(X "'%JRд;4(lHd$j44mJBR q$¼c̔`s\[Clųfn@Y u]ѓYM'm(Q݌2iyC}K)4I~2 5umVJzgD^S;JiRݔzFBvnak_ԘXdKmۊKեS7*94gKtV2T;j' ֨QϢt%HJ\Q/xL}S[vǡbxVk1WM^vtǞEӫ. ̛Oq%r)=EF/EDo"g-e*@0kAʕ*f th@WXU*emb!Iv~fUb+xҳ#>c,} ;StoO%/jY>W?K,o\R,4K!y*y튶uAiM=5 MN#6*m4-d'] ˔2;YVlq/o+;pZhnX-Sa"YˑH化9k1'MՃW#-ܓJl,e%6U`{vMEzѓ&MGޅոIet,%UɂML'.xAQzgubYMMD!ywS=0G >RSW`oRױ$]…lΨrʪΙ`ѥK:$Rzc% ]QPp%,XZXd<%s WG-8,/hn0{^2yKYFԎMEwPui2Ǟkf,x&kvE9|É$ ^)]KkW/FՏFM'x_+ dBn NMVa5bm]@1eִig9IJɜh< ݟZ>"AVvYSgK]2fIpQ 4 #q0:|ٰCw3s4XهdeWxY_,PBzBu' TrM=a;Zg_L=m(ehOzPkGU鸂f jjfQdϸy՚_2-p- 쫊@Ha2FO{f/XpOjwo=EڄTMfre$F`'ݑD廮SF&ڶ]3"c]9-6mQatPyɁhZgu>op`mmpG8/[EPxv\;֑`F}p}?e*%rsW:듑uB[RxԯYn,`F\|5@JoP,&67=C\+W;$*̯Ex7Q5SnF`*: ҙK}>"7kړ>m%di*ٽd)Z.׺eZ8ex.,="s: bFhri+G;LȭgJbA#0@Ql<ɞ ?bT5= fuLacR)g%T(6e)s+]<#0@B@Cنt OQm()fqvJC6눹g,ӋȨ3v 9{Μ [ݪDz#L4:AQ X̒­L 5oa֔X41ߝrj{Xn4UØ{fOZ1#ט1K!ߖkXnJ7V$-K층iYti: k_sw/#0P O^5T>|`F+#`g뗖G%WvBӟ<S ?+8UY4-*)0]Y7F`@ tX7`4.[#̸Uk]o㦖i'+9uHz9W`n@@03qY8YI,Zĉxݚ)>{J 򼪨a[6&+0#Sp&;T^?>fʞ3y{0326(36&XzqSS\+v6nܸ7YǿI-5K@r>*xuJF2pm{e}ڀ#Rr`(՜p0͑ۑ/% aTQb+}e:#ɜQ<);VhX'tz.o\a_?<=:ٴY wf5>әXpٌ@G@2@RuΚ:r1g(__Hy c>f'Ma^<,t7xۨm0&tѳ5;`U8go'j)^G0Y:^7 7MQդJ}O;jRdYU=yJ+nصh/֑cS7jFԀ BWRQr`ȚVYq+LyI%uELvΨ0${cs;Ө`1& a"j;xd5V.^5-&g<ɚ9jivܐW6֮WrV7Ls+` V2eQ36)>CS?~ȆG)!m#~Q2(߰x)dUwÂ*<ի{Эaq$D&YcIZ3cԕ=F>Qyc֮J>&?ijê5:Q5P* !ҏ3Sֳf1P2 GeȋTKO[E+oMcjGe@W9:a0>vu׋bo`3pݑ`uQMR?L*j h9Ӧ\/JF29#S֤'0}I5kQ+hPh`3v + h$iXr"Ɛ=s ߦ7 ŪJ.*P| WL]i=IT6q bǑS]t[SнkVˀ( ܕC\.Z75*KퟱLrֱ̀:)fjUZϒ}iImͤavt$.̆TzϨ׃sz`|Ϙ9y OZ5 q0tݲ+l̸i]pS "J5Ms[t,My歘Y"C/ݱՍn=2 ؖ ;AFw }{ Vo/gEh[6\a:其v9qGIS*̛FeXK%%#Ȋ-LM(03ZXd&9gJv켦:2\*2KeHqmD`(C 9UPkd_z2/DWll:0nS;0N_~',C0e!=[qd :<+PSUw!] /Vg;hKg%A@YXC&0ZIdUR8#< 8C!;YK~f7iB]ʎEc LF5͍)k'6RjK&]7(W+mx"4L?w<+F4#0cR>mOvlia_ʕЏu7S:t\{4no%5 r2iEd<@Nv YBoA[n =Y""Hcjn)fojE^I|վ$EZQXf zo3}A*)W?61KϥKĝ3#<2 74h/i&V4 ƖM*$:b4gJb6`]w{>rηw#0퉀Jpsh^ܘ,J~)UFL6Y YglF`.sŸueJK4zdq;%*V67H,e4 HoKZVT6)=}UcFYhf%!]fxr!?:$LǛM)t7 V+fAbɦglJ**ZCڢL 3f(aWl֚fܳ.׆`cɵ8Uņp-\}L=ZĖ9]$ 2"g {*W`ރ4eb3Hzvqh0+:XնeY[%KIBwQ$Z4V8ҳyh %V;63:?X(ON_$4ⷢaa#6`"IB6 TRHWpxlȘ*OgTj&v9町jWpRZ YF`%gZK 3qLa2lL-f($b]S؜QR#M^o2Zrۀ5ֶf2 B}7.6aʧUA + օ`F3(mQ>spj&$v_f3mL歂bAzVQDŽUɇ*Kϝ lF`nvߑޖ>gA4fk9QuCH7L|E`a_:Kzc zFysѬ,#0=ґ^d"!`k2QnN rYѱKuI]-5ʽizʵxp/s,&V-d\dz=0@wE #8+ * 0kr g6!``J'MLݵތ#lV F؉V6R̎FA냶C&5eam3wbbތ7ۭ/jlO L={sEF(,lifM\i q03= *K] m{B+Ic0\xհn۴8#0@7F@ P=PKF)7'(bgH7FUS x=g PwgF`uF.kR+N%j9̑MR-ag[6^*m4Ku}Ǜ:AfFx-oH(ocΔ;pb;#0#{ rii+6VˀXRae:[Z[O?[o!2qa_&ց_j7*ʅsCܪIаD{nYL6C\u2 Ǘs=QgaF`z<+Wp t~[@pYo<0#o߾ [3Yz]TE1#0 fco#_b=[:u/X[F`Fb~3Iw{IgX \0# zj~=z`3lv5zA*2#O~3i֧O6gKΕ[X#0#o^vm}}}w]w_8~衇ϟ?lذh <,%Y~ݿMޒw;V[0#[fWU /_~`={4/p((,Ȳ$qa9al?ndFh0Q}[:aK:묷#l023?"gF`_OW+1 0#P`A]~/2а\ ܹsgn|]z#҅ℌ#0@ExQG5qā=$ta2Bz c 9 #0@D`ҤIGqD]]ݘ1cpVUV OF5동Xņs18#0#y7R{d'tSiLo뜮ာ, >bC棪&#)еIB=dD'CWT/1~ҁ I[/ 2*MK^Ż۞uI7ƍWu;yW~}eZu Z[Jn4l)խ9Pd]ΦpsMG-i2^$^ ?Պ3ڸ@gUȓ2 "F[v+py!A\ӶP"UG6Vw3}Mѷ̼Qj[#MsWa;_`y/>]_ !Bz7g-i*=0};C9Y#u6hԪ5=< eީޏYA02W-귿}W\^ }gK'xn[S2S zLY)~cAUCR-3ԗE>kn};8#OPÐ c]QS*HdWR^!b2Mj"N4 !IVͪ_x7Z ᦰxQg=(6MfοztNX.P*{4c0noݽz-m7x*g5/Ô\7Of[%vaizQKe୘cFY/mW@] 'WfCnoc&8kt{֭;vq*oqxg\X7Yg;X/x Bwy-Ҹx+uHy*Ɨ\4ve)U"0qiO_y)jHږ崏|ǡxrx6Eh!yV~x#*@?:Qs%ݫ ),J;-hsRZ2Q+O+į{5:-Cg]>gߪs[K[yg3?)4 t˫L6G 霯U! (Cڭԉ:t{'1zN S:HU@8PʴZmnū(^؅žDc9{f*O;"E`x8R%* ~r^7loW^yW_Ok,`F` 7h}u}IENDB`filius-2.9.4+ds/src/info/img/webdoc3.png000066400000000000000000001373521506574241500200010ustar00rootroot00000000000000PNG  IHDR{sRGBIDATx^} ]5- H`Q)>( ӽBo!t\4e8-&#ZH\UR '*Ԕ Xܜ;)Ak _$ vgL2fS.> JRO8.xmҲ/;S#R&RhZQU^D&#$gRPfFGD榎!tSAQ)Pa XM߮&] E5\&nlʵYA /?;L 8º?*aJa%?Z`]#6pum:= vۅt;&}Kt=9`|(<3\~:NlL婊KCI#%POJ H? M х2EeP:P4:{8uwgq`޾ɐcpɌ ^]  /Fy <7_'4:\,M3w_͌Z /<])S>z|qEMY1t}i5_Y+ PJe=SUHWhkF߁vSECos3[ [GI"1;p\Έ6Tv]NA a_"RY, ۳-s( -g4'h[8=2fXs$&׮qmZBvzEls|&dW֣ӑZWX2[.h|#[clՊU+ŪUߚ_YJeis9%w9?)mb.'HPuHkb.(FPB}+VtV\wWlVVQ>_Ck"*OWrn{׎;xWϏ-gLZp\IR QaGhh 'ap[SPIC&^殮Ev@pslF֥I[LZn8fX *DYU<>ljVAUDk|vi05 ˲h'5ڸGѓH-o82OKt7SqKf Z鉹%*(4D"$!L͢\vg1̈́B$ogp驌Êu sVz(* }TU&o$,y#'H+T;!&X_c3{H0VEF\[Gͦڪ|хzs*\:33x/"O/.{AFlћ6Q*OO{q|6+Ŵ6dX6 iUOܲ9pzǷC-;Ѷ{IYwmwcI.į$$pwEK?D*)#!Tbc0ZJf/ɞ" |ь_/ף؏<.ʟ_Qo#rڲޭTs{C쫧Sn<?1C#6u2GkVB?54> ^Gj[?mYwv"^K[1S6X!v,u֘lAOm%خ Gݪ-lu(-ͪlkM_ Hekm^j c-v=)A^f*a 4gcvuòi/sm7QFcP %Q ~ pv!%R_4#;~#?r"X4&81Bdҷ *ac6iX^gD oy?CxfRJ%ݞHVφl.ԈhXKo_aU@i4!pDWV@mqwfU/_ŗC^_\}WQ oRVo<*QskV\+oʊ_Y.^ye+/_2_u||0E"ڈnկ՗WWM_gT]7\K)Ek|x)QʵUԌw <˺&W,2 JtKīnYܠN"B5DkVj.WhY"v~Oy\ wc^ӍîdrMZ`}ՒTHYayݺ~$ hg¤{y*Ml[u>X#~$?f6TNqiH3:v"& 5NG8%1lfB5X:^CLy&"n,kk:iEGLŒ{Dk֋- 8E fӒBOWGm4S8^vz*tÄkcZq5ÄJ`VZ}^}>V^ꕍUjrWU+X+wߕժ1KW!:|pdfWpUX^uSGL>yңBOyHl4\k,En5u>;$%|7'suqAzpq7Oi3-]Q%&dJeVaFհP#1u{G8ƇYfe9iI& 7֤4?:kבm~{GN%yRS+yPjBuk^M鷅{R&:OB?Gf%1Lu{BX5O-Zp%SH/sq'.Tn遗( l9GyBz%X6jbh_}W믢M埫tH|tz}K%Pz{hP܏U+VW*_K%]eB>lirxho{j[??yh_f~ll{#`Y?tyW7nO7=oF]} l|~dcD~ 1gOw˭O7$ͭk5!gб;3;/&x]ڞR|瘜NeQ=˺fRi.V+)ӳGF/+ ̏q1XZxR ]d 7W^53WD"~`[)ZɚVpcŋb^$PNM+=D"´PMYy}7--t# PPksHAFMxeX8t'B)#F|M l]W['I\L\eUm6<&}G>mzh?Y؆Y[uGm[n~zI}2vKiWnw7 ୛mc;o.KmJ1^h~•ڍo1?F,׉gW~\hVmL{66Yt!}bjv/rܮD.zO>tA{ߜZvu(Ё5^z.MCI)TjuwL1ͷ[y߷s^q mN62Xwb2ϗ)7?]tϋ |q Sٯ 1Dw'=$#-ˍvvom'򯧈ܷ0Jl:+X=W|̕SNf_Ko={}W^3cwԂkgd_7d!~U;'S\r?mfց}\mm;^eos;<-zkY6.]6 ~@tiƷ` #y:Ǯv\믛l;糑K?muO}W_;CޭuOJv p긖/If1! uNɰlځ/׺۞5/=!pc<RFWx-.'|(U.=%{ )$'YYKjm<LK'&]A2h*h.x,ɱ!]7wlI2X,.Q[E=Bז~&L̊z@#$th2?۴>ǬԱVAX r1_}ցnמoӅ QꮍS*&nx߸+yţwZt|˨Sa~{4^PO&lvPRqW>ș_=mCVh+w>iΨŬ-ʜ~q>:tۗs.&;+7?YsyܫB:yRvy٤8syUc? ůxG]elSPBVӐanωvi NU̐Jzާn1,J]YnIHۛ)hפ>l>[%_Am*AΣ >+C__Sߡַ?^Nہja \w6dXa7D}2@_ ͆eOƹU wbcT*CԿ*aC]q6_ͷ*ݛk>sۄwS? P݃蟈L{3k6gy\vߖ%糣N)a? AШZˇƈ:cDFF@bycVwX-,:V0خPĹr4oŗOMP§;@Tuےƭ⺆j\ᖏ}1s ໨w~8qYB]dVn3mj}H1eS`ܬ ~~w菴5ZցY;,SVN T6pw8ǞcHOOگV߽Ǿqv Sn>}ՂY ,h?0EiӔ# Xԣw˚G4PqO7}様l1?7}UOoeNYyĊt{ź莿[vnNjNQi F97Z Qo\Ͼw]ƍaRxk?bߛ֧vl2f>FJUA~O_?O71%@}AHG>}mȢn-3L3d̰ ,6͇J|]6"X =u0Xo(w#-k>vhCH)~bL-t;m<<8f4&7AA)rn'wn]O7m [wuYcjd5T$1dAs bU$kyur:?p:;x _z ^:Cw *믳z*yWԪG`zJi~~ܗwtIjY+?~B\)mQG}ze֣gqA*[#]UhקfުH?g߸LY #?s :욿^5z?rwٛMT_^ VWǏ,%T;X|Px/ӶeSt?Ox/xW{g<ᝇV9tKDmzXI'r/2^(遙f ?X'𥳐o_͸HY?pS?t呷dl>oesU]8@I}}We=sө䍔ڡ{ vh6s ޵?N_/n8?}U^ߛ?v?~:fc=w֞he㖟izs2=(fʌV/6Pq%ںG Jy2EPkrzY`2#nSxv$i$5c(ej#$xS ,o`2*?_,G()9&%(C.B67 {iyLm#+r$Lp^Vi &wvz %.9ݱl6=ALgN~;l-pn`Q1 Sߘ1c׿r g5$'s_R:۵A!Rs7izC<æ:YӠ o< _ذaː!yA7&7Wɉg;FN uO5jK6Xcx,hqyE[:=!둜o3wik_`. Ś a~qZ,Aɏ qeû|[JBe՞Asue\퇒eUz #Ihfm)89VuOB}H@kA9Kk ڙz޹LX{Vo5zS]wGNaZ1<>طhQ};KJHi/lĘM;zU6!'S i`soNg0ϑ@I!Cj<Ֆd5馰E`PHqkۓ_#.G&8Ci e GT x.1lo\^ĈRqQ09u.|R5[X vy&1 W2F3X-kؗCA4dgvl4A[ͩs,.Qq Kn@6wd,R0mDBtnĥ'dT긑$7'`Hvkdgi)ffʈw"ɔ6fy"j[6OT)OEj4(H%CԻ~eb-k=4vyؗKmD̩!BS27.<麻XO.4XSDZS15J@.ZGxu ÁGNѹ4lAtr 4+AB,- 2:9!*Aѭ" &O1-1cӎѯ3u# I:3qWsDĬajl[QfL2MwN{dj/rt܄`&A!=;feӧJ-/ H .a*O\!Pi,[&|6d wQ9wVb<#ҚI^чboVr͡d:6B8ؒnPf餣;$ Y/ٞv0:.Qpd}k;`Y\J hJȲ~*si)}.X̸J0!;@O2 Vʨs:'JN検r6\;N9%[o;: V =bJC9*YGG(:W]RB2nR$hϑnnW>0y^͗;<2%I5i׫A$jSN. %FK}F F ŷsgD ]Fr1cea p1ڄlA'4BNte̵+xr?z[$) c2D[T~8W""Cj^jO˛j,dAǰY(70KnsjVZ^z*kqx]/gjFvzh;cCH* ^M kϕM|'Y)kg-KP&;`:"4 zqڙ.\TE rC\kMhBTJe(O@= g9LmxV(KMB "31AK ecGJ6KѳE%NuךC|r8|(pL&, $ZR RKёjyHh] qMSc&_ hs;GN&[1o%V SLE!CvĩT2D=HDKS`YZ/v ] sCȖ{Um9 y 0cGA˄QKkiͅ3|g^2 =I:\&@5Ea:}\8~!ѭY>.bL`gK1la\~@xU u˻r5%1-̶KhRPEEST+Ikzx2V4yPv"QLsl龣&.}2 fl|6*3Zۆz-Y?qXd @jZ%[! ȒLRk+6:ٳ:ѧ|RjxFL_rvm%. tà!J6H|6x>@wG[zɘ:\ASC~9y T,t-Ka:uns9o<_s:Ϊ,D8x`{} \Lvw/Z}]˿sӫ_~|ZE~Ϟ'{!,_ }}'fG7ufsȹ;vN?ƞӡgs琳w<{;;wgo07Mn/YImEݼa;}I{ߔlUq?mY_8>Z>cՐc_[+5g6ΏvՏx;[tT?xV0=4;<3hJU nj?_߳$Ϻ}`ۅj{KK*n&\OQzvPTqVϕ_lԫ|O=sA/.xyEk+wXx!#>L۷'Qq zBhjUtKɨJ[W* SnP|Zu4U&%GVq)+;KZ;06pcoms] gooy6r~Uclx?iowM9>yׯc/^Ͼ-ݸz}Ϳ́,&j|dAZΟns#aGLVfLɁkm?53h+\W2ynʘ|׵T%? &}K{]tYMkZҵ/~6o!,%*ܜFuϿKVi쁕rŠgJG:jM/,}Sg6kx;W~ߟ޹^n"Jb~6n=+WrѣY:,%\9[1\V`Ewxgm3ᷳov\3g\ zc孓7UQ=$G95ҪעR::KX)S%tOr+6ѭB[9X s,Ȩ[v%r0h%npT)l0f::3^|ƇeΣߟT wX9kw-uoZS-\lO_(7wTUVS 0/FB֣,N|jL&; /w++v#v7nѓ/鋯jo] Cm:xqrjꎑ[n:V,zFnѡ|§o3?>۔>jLJ}_ӯ?MǣHuyo˲/:_.ru))1bgѱM"!RJ'Q=DlUtn_~-Mt4TR.m(\) W}N1ky;+|MA:8gU'M>Jyï2;p_ȭ_>ԫ/0taJ?w.?6~aW'}avz;la]_d9.>6qwMaɷ8|(Ol?8妏N-H$O.zҖ'ߎ/+}s~n|ɘQ#6^wsli57pk$u 4dm0E: ~=F|Ι_|?EVY1Y_$·p=|uZD1ڟ|eU*iqT[tHMD55p@@ } 7&UFɄU wtr{쩗՚ܭ•ѱJi2yLfFY/ QD,˵Xr6I,ke,ܦz3XM@zn闾qE{>٧\/oٛ2$2kէ?0ǝS.8tKqYGM郓t[Mir^ܼߩ?mI'.+WbνoY9yXML?-\C"nr.huGauũGn@ڭLKkq/+.ٗܣ;QV}VP2^ONMm6VA-&e @9Up.U]wuGC~{zI7ua_~{M{WeLarcG\|]bsǠ{;gJc?yбW/Q-iUrS[ /Ws!\O'utlJ*8+w|ɗTO=D#*]c8$J{U\[ TTؑe9./T Kϙ*nj[>J؂W('}=@cU e /6x _ް=G_񥋲[Ȏ>2qCrI"U3G]k>-.HJI*=6|`ɋk>>@t}X¹Jh,\~~ܾY1}לuoM-nG+#+ԉ9伋&}'yT9j:_eW)J"n3ɥZFIY2ᄏ 9x@[lweC뽲^cu>?qU7yؖ;97]z'6hlr^uG7owW@;uҪh;~Kڎ$Q*SMwK U*"3E/>D=_"5ڕK R$J<;2uæs|wչʊyC0W$OrnbˎD.FqO8r<3+Z% h۾=Hl<<ɋk>>@~UR n3FmvJRڭ”ܩtZM ^pR_euhq"z_:]v3jx8vY)tbKfʣWy5}%~[&/^ f[Hշ~9Փ=G]%UɷQG^Ȳ? _0>Gqr)"a99%^9}<)5)>bn R;"Ed_FRޏ;ӆny?]@ \z$wSjt=lyM7U3#Ow4㞔c}l@" 1?ꡀqU/eh*y.rCWsuƢFM2QcG .0`*cj>SՊkGN/ZPʔn:qUyÄ%# ;2M/~RXSTfl ߗ-8yZo429_  @ p u܈q#WƋ$aߦqAQ%2mOTʬKf%" Ut=иe0IUo I~[Bo'`7m "RWhXmoulUz{h'O!$_B(ϜvQA6@ :Ǹ.:/JD*=99*W Ǹۦz" @" Y-TmȌ,XӒy=&U>BG3U ϩȷmO8=:#:+idyN9(Q]"ϩ+ aI)ưIʔGrH*IQP9DQ>zckGX$͙SƱf)'  e˖mF_b _8Jl d\ yZXm=***bGh2!xwPu5r@[ݸ 0YiW%)u}|![$ˠ_.9yR\nj 8n 9KU;܉-m mu@ @@`^[ɦX'%q&*))$]fːi|R9$5D:yG   }Su9 `y:O-N=_vCV}̸LA)@ }qdwkB.ggr9kTV: :n Ik A{ݷ,ep_3`*?)bD}b,E<`ʞ@U[2jCc H̓9ܼU̝c?;r3ntɻiU#Xs&^,(XCr{io;)H VF7D D`+n=cK3o]M9xg95\s}>6IQ6ܫm6ҦI$T6] ŬNFY| ~{Tk/TY̟7m3'>z3 a:pB|T?^'N;8#2w(<)a\ҦRbX^qtlS'ߺjU~?~Q/nie_ls4i"IDL﷛%ń*.KGwsB:n YMN7\*>@nvEI~/N=[UvӵyC_m.f_Oܤ^27FIFߨkoEVw@@$XלmؑZϘ{-gƟzS{{ _ x|e>{NcXbS>c?>~:ߔ >oO* 7Ud5 n$XAAm}^ jx\k}Lbw*}qoNӰR>J1 G.@#:˯c|Ʉ.9?ͦw5- dI㋞kg˯.~2hnJv5O罥]:v(RI 9(CkϏIYEUfQiәSm·`üRLTX0,# XwG}mS?M{S?*à:5q=ƞ}_[7:L)./Fu5}ʲ⬨D|ZYb=8pcnnw-jh"nahC&?م_gnM^Z2 >JAXUߕ9W}a 3@UJ%m.lҾ-]Wc霛e}¯ab )oVJ5iĉ( nxϬb?,=t2$[g&k_#v4rK"nAnJeemU&O&jƁ/V۲u;Li8SmHmUiI)[8@)6]zűGP |bQG6[l31Zs{rb;Y.Z4Y_:NR Og<٦ҕENT.9**䝧Νw^.t~.Jn*jHx%UqnkhmA6N&_i/]XejJ{k 9wܞ6N1 [++9ؑ@}s177~}Inu4W~7:6#uώfchGL2՚[wUG0VQr՝6F{}u SN*kN9i[9_IBފzVI}1}IPlG~xW^YF+-58chj/UV^Vu'`@@}ͲĬ:Еbw\[ xQ䍕R[ʤ9[_eSN@ã%i1D*'t&NU.@BΑt`@ &Ph{ݷN&] 5UOo?X.wU"@ YՠUY5ȕtzc$vSI[ s9Z&@ 8)1ܑ[TvܒX FۜgVT|vX'd@@5)Nr&ۂW 6ܸ"qqz_ k/+ TfVnSK F`F!@ J̊W3iL|7 'snygkNɢ lHE@ V 2ţ><)Se5Wk7ȿ!fܧ$K%,cRJ.Y oՒ8+A[fN"5+W'R%l/hr}Jp(@t7V!kU$C?)347&^{|U|+*Il[*8@4eN9T)fUxU~RJ8vJ*r_VT <@ (9*wSxOQJ%-?"#mko(ջ5 <3hw2ܨ ے_ͨ< 4G3z*f\KJ2J; F:iG:9& u\>YYPp7@XXU,$O*wo=auS5睲ف' )g$d 0ZWE(vp* \<^Zf"=!w3 6o4.wޜ|BU'U긵*~8@ZUZfTѫR]nb!bc2L_ckǡT @E\@ 9SϑQ}"pN-r9'm´+J-' >S6qqd(  O}1|ppS#a EDĜ nwUèkU9>>*PuX}Ƀld:[P*]Jrkg22KIsC̍*"@[r8ERV;{Gk"E܊WuŶJ|#+ZM7n Xs2h9 CZ ρb8br[iV9(Wb/UeZs= %%3m]wʜ8Nca)חX2uʭ4N>9bLx`/{D%l4T K. \]wIReJ'@9/) ZvY+<#{5-]7֖?kv@#_<њ!+"@~uŗkuV}F:lS8v1DMT{P^%-x[_.|+0zp4nwP\@s7?΄2Xz‘{V/_6o K*r[v{cW޾ͪ͝o}Yi(~P2Jv|bd%Vd"S1(|8>緩9*bB>Parq:$wxw^VÔ{/]$*R7uϛ3NMSlibg8y3 /8y5qڦ}yL{"~N nϾWKNפvF[M<Ǿ}QZ]{Z]ͿTۺ`ᓽ@>@Jz-ݤM TΓ܍{UՔ c+*쳇FՓ-G& M;l(30}MkX%E*r1GjF$^-V&O%=jޚfu v/ꑟٲ ݉@k*T Eq+r*ڽϠ;E@&[ܫgs͏ymѤ칏ܞvV{ciRE-HzwnjQ4)Jn4T!2RH:@Zd9)qJp Ҫ-7ɻahJWv+?);X~f⭍r&eWVO.&+(\;b{xl&ֳzs^S)RB@Sع#7 5]I-Dʴ nv q"oAV3m9|Dwq=m'iV gIiLfR8qr37O2uH\~Rbnkfaۓ) D(77ҷ;g8Q$=+4z8 ~6*?^ZєڤEvoOe< :aͷD [.W7kԪ܏O}-rgz$j!J@ }^L)/:|t*ᢘ7A`M2@ un8kցjEX8 k(uw9 *K>lwiMWk~GO} ʟ;u򄳎>nYR4ژ;V$Ӻib$Š]w޴JLKY6 '·@EtLxK |1Z@ ̻@ 54HjVM2Y'ض;=x=ϿBXoȠ [B攓L/d{>~kmP$@fPCKΟj*Z ]9CWYsw&DE@ >RH7 ^]&v^a:p a\_[r @@@/N.NeΗ*!rt{Fh/T z-[q"nqQ%g\$ &0% jO*P \zmkAp Xǭ)P|Peo#h65P@@ vM"}Rmu\K)x@`"YzeSDusʖ88-^YBhҀ@X7ϝģgB~q(&88dXU3g5?؏\|@iv%wssXX? h3R!8x}Gzx~\t;@Gm%H ㊕.UkqUp@`!Pn.kgSjpV]p.UHZC@7:bl *;[Kmዃqc#@ #HxH |rOisw=ViQ@ 6<¢#Uod=qv=:5R'`+k5P6@] ,*P^ʯFGGEce=|wPmM  z,~ޙFNR:JtRUB w"/ U$TXu>Y)MAnA=) @ M@3Ud%",lũ0^6m]weL៻Nh\?e/[)syT}DJI]9gww\JXn-Tm-&i,:hQwF&ssm: >@tJj%+w9u9^YylCK8:=KN}Rc,{IRG8&oL=,m;J93'ɲ1g9j+3=nڼhץ>u9Wlb|EHg[x̼Ƽ獞CItlp"kNйs?ٹ 3]# i>͕k>C[i*@S0+Q)s댫ʭ zʖ#ni:T=ͺTG~sl&G}Nqc0J5zZ IV/=T*ISs3r˵-}&}S!s˷ִ[u)E B,!wf*waEL2Nmeg̞kȦ퀽ff[C܅Z#c$SDkkv)׻8$Xe'4FI0%{8g5A6s&6ٌwMʭJ%M1Pm!BLd˹.pL첩4*_8'>0nۅO rIy2,^>O} sT^B]o司2tF%YLq縩 Ӝ59؁߹So u{k̿tV&l̟"(5e26Ci5/>Dv\6@;eu?]$˪vc^坻(cDPJI6(xDlid91:NEіwMW{*< N=7^{켙ͼtKGvə27!\$vTqZUM#^E-rȴdNe;ܩM;f|tO]Xz/Ѻifa+8s}XvzD$Qs Ѝ83sQ%P|+#?ONQv+E2ҾT[dF7ESYzg-8ynm[ձӦEmjt<3#WH/ZSk[ܥ1r hcl` GNfWâ EƆ\i 콭ls5pC:-w1 _EϦFEJbls]MC,TJvuxg?oR* WV Pܦ9dZh|v ;]sst6-<6u{gսLC>y{v2>zuAi,]mu!XP{s㭴~RF#MDR M,K[y$R-sS.$Hȟ\90cX~ţaӋ.&e-Y'q{ۅ饋u% -[| 듓jH_d.TΫ̘^R<="粹.ޚ]*SuCByQ7:coO:h ē+\,wWK,X(E x)jqܟyzcs [ٰ|J=ɳIg4z=ijxx;f0l׃;**$'g?7F4+UriRn豌xy{2>WgJ;S/=*uRy>hA :@tSm?bhŷpG V1N~\븚˯V߉9sR[xsb 6&yg`ǽ48}F 9nh`L-cVO+OR@{/[J;OiVf VdmEL1nkN5ӥ:yRP'R:p- >@Ϊ\';dIK8;kzv.WSUKYp(T*;͛?oϹq38s{9)o|i'^|뿚hՇ*iGْ}eOtGeF)ӱM4Wߨ.Y\>o%I1;]nMvpv,D_>G|y_4UJ)bw܇C@ P@-]ݜI[\J~_N[Rf@(sxB| "w @ȭ)+_E n{ ].W,KENR\I +Dр@" G^qLϲ8ɂ 1EG3QZQ]C"o8x@`# Pv,HQAz%ǔKꦧ眗rٗgC3k%KmzA 8sM&mZ6h+J91,W&2"gG]+ ;$D@1mVͰrOQe|L>)8G c@BW82}E4Uxdc4*(Z4 ylzr;ix[_/lR =kL`q@˕\ReeT>;dqY֢m"gv>HO8F׎) ~i\o% Z(=\8*-ރڹouS@]@ȹES^=I:ߪ$&hOQ&q8@ Э8UPlVp+Nq'l%lk؁zzE4[2@-zptD g7k)`plg\ƭ ZM5]Ի.±wӦfcNn(K2 Ń@<cߢ2£'1RFί"S4z\ ݉\7멁[:$m q`R@oBæ4> @.̚q=?Vݗ)ўivcaJN5rzѺ;gJۮwe$am4k۔97LOYMnt]r!kV!/Ԅ9D]ZIBڒ$K`R?Ȅ&/[50vv"0\IJ|Rj%N%S*>1oy1g6.=jG8'!q grn[0i~cF*$q'/{$c?1or6y7FD4,j6m)ݦ'6La+[f@T PG֟}AlHT$_ZGZLJ[lv:㢕ڙ[q-v;n=" c[vG^UlǨZ[Mt׭BN$UKt>.^0f=F<ﺱ-eR9!ZoP }z̜fv@kx6%[lxth;[Iz~R 9Z`g֖1s $V;aUΣԅ[Ss`Y):i]pƉi20eji& tm6Ͷ%vik>RBKHe UppzO2OkpeS9o;rܩ㸲ZSt@,ye>% SjO% rP/lgeNLri}\*jTWKS«Yؙ[g @c' `Se;k}b^'K;=DbeSJҽ9rsyHy7ֹЖiet6Y'~.ZEӶtc>n*a5_K'Ȯ?KQխyO|zGk.-I[ p}nI':ݸ*Pq4 ̉csf4%fV#xa.$fB6Yd%f"BD%R ֽ2%Md|Na1Vk唩yԋM7PM6sz'Y*aWKy]H,HˌȯȢw r=3};c`_d[OyJ$3 e-Oy1,xTщ[;؊5z'{3᎝6}woZpv0_UI4aĨm³mTFNW!3J p*k4.ݿh|lJ]3\|H@nXhT%*'UeHʆ0+xG#FljWylp)G$n#^"c$]E7^S){WѬQ$|htO)B QHb6ڸ טR R+*,]tʎ̤ueSWd&͇$9 ic,V,+A@´Ia ///VVt<5lT+5ĵw#K[W% ՛:*S lLisPvC[tt\%M Z_ZQJՌV-C7T'g?7sRh̤ۓ5⤍>tڲt|ڼ0%WSє1|c6[k:2vהS!Vz7\M,X[~e_o@HXJؙ='vЯ-~3N< f.چ[0k׃^~(W;,6鹿$ K?Vw*B^pd-5]`urg O|H,X^=k{P>Tn 26/Lyxթ5&g}]e&j!!v!Q}%,ςcמqWKq ̛gL9e˖I(N}ꧣk7vyT`?O@?ZYuA(.C)u6wVFqD>$IB][Mέ3{Ғ˦>yDr f\;/)ieLyԱЛdĈwo fU۱K+Kett7b;9;to@ LNqVtf*J6Rxn\]e0{nSrQ wuw[Q϶IbB @/A/Z~W4U픕PsH:y] Nў]ZK#@]Į $xN=i-6cRICVwiw_J'⧯u0n¨@ \e]kHUFؽY?uGX}D{*zYO 6ЌI\fu㸬;BN @A p8. 'M~* R9?ocمDQ2aa&qKF==mhjQM,=cL:1QVUû*%Oݯb/*=A4 @AC',ܛ59,_CYrH~ MqMFE=AB5!5r;z+R̚-k{Ȱۣ; @ ;0{_*C~cE))Qׅ޾l.2@vi{8_tfT г\,qJ)'$Z^Qz=B: @@ 9%Y*mr(=Er}%Uc1܉TmBno"0n˨$@G#xeX Ζr* EqBe.ԑqtȆ?ٖNZX!V=B8 @@@hseڀes+m2nܧ+%FVI2 Cm8e УFekVxNi %{)bh]eP=}\apήʟo6#HuzŒm`N1q{tp@ڐ;zMbrV˕̶͊8<*-Ny?5@\T**#;;k&r.TҰ1-cv@H/=C:zWJ^/ϙtt ö쨗w* /@`r;9潫b,Gς)U(Z_ݼ<5I`!@ ElLN̑U MG̪ $QVe~돝Ҷh0n?Ψ&@w!PTԚTkR?@ɲ1`!-@" vȗ8QNŒjgMDRuٖ rX W.=t錥rW|Ij3<`@R}{z#$@azl@]%ZYT^@ZNnT}Y.?IղPf'UN5ە%BL2eNW|@G"diw0'+G[-S3 +ü˱-+lE6j#XX^ rx9B3ٴY66iU[6ę|T{69U٬T^EI*@! ]t7ua*I8%sxd<'wvVT}%щEIZ)i@' vXszGP+FNgVU&Rnu6dG&|l[1ٟ_\8+u6M?aM*Քgw'Ql|@# a{T\Cmȡ^ ~6H<"^td \vWcoxC2hbgImDŁ} FY沼7ݰ$eͬ .P y1)*dӯ7wqnUvm<#g>)DFT}z]9(\DVjr>1Rr} 58$or:kLMׇB]OYݬ*(?^Sa^QrhK2sO4ԥg f!cN^@P @Xz#gYhR#;7r̓KҶ!J=VvCEMiWdf\'C|iv15ke">f^{[6,m&V>؍O<:Skj#/*7>`L.IT4Md*7lH8ֱZ|]Pzb9fG.;,_I1v9Mzi;7ߑJoGM,kyR E[D&zIp&MQ馼U@@mu5p)62;2ZLD7dqmF\li`\=C0 @AΪϗtS[ZD yZ栠9M I@"VIcЯKf^b:+L!inK7`Hn.ُ " @Bj39G[ĻL)Or]K+"fn\9+gn^iU/[y Y38NYfl: t  ~;re`~ڵQm @B2N"DH擯]'A zq[A i@ GE,HڤMMTv w旊]Y 3;]ֲz?%i{L>@%Hp 潐,X[͟T0%ٕ ]K14MĢ٭[E9 lk׮zPIU^˔NXVE6T4rszؒ׌\aܼɺ+qD^@ J:%"Q^d Rd-,_mnݘ":}7\g.ߵeZuiq @A@PD/t*4P69@ -V\A;L9m̼ Mj! {Z8o-2BlђY^^A@ WM2xrLQLj]ڜv 8¤rᖊBrݼ4) =̫μ옕XK (/zu52O,"g:7v I}i!q_q6V{Ҝ띴,Iꤑ*n_Vv8 /jxJ[[] a+E^@ Pe=WzSg7eCf!jN[u1XZi">U.Dԅ@468=³,,%g#KHױհe]oSnYKh |=:v5% R )9w%N-%ZRNYefss'4 kߐTGI =Xt{t&9-7w ;X ^k fsVevxj{vzg 5ca7Ke͓+uDY't\{GkeW( v-+b{jQi|65+(mWjff Kboq߁@`# $QilaCB fw.\ڴmSȅPxN& iZ @?C wٝ7Ĉ>RV#Vq.NxD͇.WxwP?Ԩ.@"VdJ]rdfX0GFfz%UL^`ֺs+e 4ZDxPêS;@i e>S$;= :˗bL&i/NZiZ5ٹB`ܵ @#d,ifL)#[tEѭaפ6hqs<ۖnP;wcoukGJd*[KH)Ru 5ZQDRՖa XBB@ U?: y8ckІl[V]avcf(lv=8: Ra"{rBٞym@#ͯ!JsTx_#oo*U j#q}]Q? H˟M$|\&qFF?#О:^vg X" O8̬*ȕ4[>QhJv]e\5$do%6u#@f+k _e{ Xm>P :OH0H@eYzgNyk3~@μ\deފ`MTZg=*N*.knغ}Ek ZA o.kf띛$HVJCtltMsEz*r@u\PZe( 8*FJ()^yeo%Cn E`闀Dra]KxQ :7tEoo4'ѡTEC!JY,CrC Rt6d6Lu\d\tF=E$-a1jZ'(M3#pK)cO]=A @D@`Lo\jCC^ݡ.wZwd vqeF@`#]㐣@ jcZ_CuS:7ɥYo 6JQr`TN퓍<\Q:.@5@gYOVˆtә O&8*Zuگ5.^NMeB]] E @g̈Bʹ^|*V:ŘHOjޗXj^ _+t :=݈!Vb9Ot~F?W3LN2}B5ׇۛ6"w30nhfT a;~!vks\Fc[ Vd]R 7.#xN>}ygG݀t#ge`Vg_`S+G31yR~նLq~OF .5] 0bSC+VYsMY.@C bBD @?@Sg>,3*@9Z&`-7<-ݛ&HϩB;T@ Z2 9)0Cx!R3Zi(+=哶#@gSE_.{9aۇ7)m6NXmerluF瀝9N، 8z~q`6y_BH7 .%ܒY~Ny,&rӤ@<WI5STJe%4y96ZXΫRqJz}kH} >ŇZҫKlB[UXG8$y2OJ['@ `F@ P g-uzY* Ekn8"[gaa9mǫ`Z@{`%$r/ uzB1#_e5 b @"`hA'jeZ4;-Ɗv/2Xvm'Bn@ P{i>R"u؊VNknjv@t7`F @3TmVaа\zò3{Sаhؔ ƅa@ iJSN!F*WZUKE̝HH[iP@*JdUR%B+e*#ź>sX@` P2Ej wD) drќ@ OylΆ @t#%|/P5wٌ|ܝB+KCˤTa5@C ŜͼYh 6V)Y8j/5bBs#6@.Gi/bW's][-ɠvyG@@ ,OJP&RZ˪SE^f0n' 6T݄/&5QXKhe𧻓Cup Ad-0lbp=j%~wPm @#rT,qTs0u֩I9A^@+q-$2YKty&| l!>@V㶂 @OF |y_=sj25ۓ d@BdX;`ӬkLbmॸoD@g[N"(gr=)T,tk@ h-9%pkk.: J@?6 #LʝP r @ :U5 -;Eb@m} A:ΑSǩ3s== @}U3پ ܅V+R3f\cXnHd@o"o+js$ϻMւ@ 3h( 0nh;H} %qT 2@^@3^D=ݨ  @B۫  ZCdPЖE㮵C@ BĻ*O@!9]Jf)7kz@ PͩvT@@s羂 ! M&l}{ Ƶ9Bl^nZ z3;v1+ -,ۛd@ǐDfD S @" V9@4@KiN3m.\:ԋsc>zG @ s,`SNpQn} cvHmi zu:Nױ$2O4=@ $fxΘKW&bն,)ܯJ<*0nW"0_>cY"IfnU`VU'g.<`f @w U~R]O"G h+2p rOE_ 6DV'F?fϛ\97Jq[H]]m ]=b$Tizv.N)th v>V.NH~ K:nud]@ w ^8 dM>~SqsGBjŜ% @^IO2vݜ-ؔ-:)LcǟL }͗Sk*^Hfw0nR @OG y[l&m@g!aZ8ZK&.W"G2jZ%͘ ٴC=A @D@G%w԰[sgr++Y\-D¶̫C8`طQg @Fs2b6\ E'kv»J;bYe @! 5HDbf{/ S)V`6Ql>,dkCv, SZ{K@@v)1krVe>I!)}mH}pT=e)L'PJXSkuVvi,5Z/+LJ z7f*}N bYUpbՅܳ^#ԭz2Ϸj6lVyJ;ҳݡˌ ;s|SP|rY{Z%a"ۄ\Ξ^* pnﭨ@" Q?*zWLCcoRS87"p6"0ARX roퟐ )΅V^Ў 7Hjy_*㪔JtN]vG^_.{X6LzC$7@}'V@^!4iu+H,FB9-rSB;ySLG> z%D@b 5_ rov3q(2t2 EpY9on?z2j A{x$ Zsi(32zWP{C,M܅(+( ϩ @T!PI[iom)R>@N"`WX]i_Xd54Xz9ERP:6[r g6aXIƅa}Ɂ@3X81S&H󇒘A SDuNe/-&lgƖg} 3}i@KˮBae+bdʹ&!EO}G: )pW0+\hZ"K: 2@A bSܓU&3 {$m鄩q2]Tzxfu\  *g}>] Nʩ_&elI\./UxfI L9ͷZ,7:=[*6[kcL 8Ͼ@ReJ[M@qgLMa2yK.[U02,ög 9bmΣQ nk?)'gbI]ȥe|C];=  @@cلˮn'Gwn>ƼW'zg^8/сڬFxKrT^{a1.\<@ shxa "UCy{]8>=[A2 @A6#yS5Qz b܂a.P=Z:,Kι.:+IT^b?@2-;5RAr[-e|үѭEC<{C}uӧ<6p@ C 5J$Q]RUSv{@i~jatܾoQ @DR#:F,'Wu4a^6@WۇRho Ξ }Pb%!  @ ̡KJ$62:V'#/{0z0;=!V5c{5R#Qo793S.FO@ v,R_I{b-+2}rcM"u.ʲfB9:T,a!`nD( 0TeO}npD-K8DìJXĻnr qb8n/|ȻJX`\<@  "Ί"܎P7u\آyŶ]m>ُ-vrbli;K=i0Zg u{by SÞS1H}j`f7&AE[f.*v"'*@ bmdZ>ڄ@|W#9mģo;CBIl2G̦l'o ϿG ~ܞ!!@_ `wwY"^TNI#,o3c*O6 {l~XdO@"ċ?lXEGF%= mjѻo&Εl/T9B5j گZ)n !5ۭB)7 @#Pd`rR\:פw{@ Ίj4F6J3*S ~ʇ}yD 2DI} ]>#X ̈́"@w#{NY&_cUcP_2nd\VL@(lb`1ۻ{)@UP[#0qA jXj^[{2%( t"Dd Vq +Mz @G _ͧ,ݺz&A 9ΛXako.lލ伱ąqYawV> (&=\ܾ y>5ڵu\T  =XmM|b6pYbzj͔oہA9CXW6SpG)$i X$Ql̉2wX;B%rex;`byź2GN^e;3u\P, 8I@S~I\EIUMV))2#kW){e;u[?Z@@P,u`eWhFȁkzŻV٠^H5.}{(3qݙSn $@>j2[:*XocߩgG J+Pq*.ҤnuVG)T컌1Y,s7,+')U9gDXۢ:@ sfRi֙SɁNխކtkN-%{|H՞"3LN=o6{NC@ Ɉ*+68.Q,$؀,spBgDKodf:%JSdTꬹKkXX;l1f}kN}u@Nqy͵Mi+vMb7[faᲲd ł2&[.r)G;n[ʄ QJ+bܿeDt+7a\fo 5 f{͟&c7^u [jTgNh&Ě:%Sp&MGE Zl̸s}轚o|FC?t 0/~7v gEXtR%jPOTE̝_%2u[V5Ve?6uv.X&j8AYjH$+h1M!_v2- v<~nv=) jAГl N "*έ:%sSnk- D-b턅.KX8-jVLoY*@Uj9c0%;Hha{(L\p( _H-Vp׶_H]Bnȳ=?l jݎ@Ԥ`RoJ#힦(=I eSuE@In7* DðmH7â}+6`^dǷ\1$`AnWxNs`9Xr!#Iơ! y0NV編M=zd py@?n?ŀit *쳤p3w#.'։F8I N6'p2D!"ꁲ8WJT-Prt0$@Er:Xh#'މ]Қ|ʛ>uh iX>M-6wȹl[<,LNH-j"ub=N%1L65<ŔBbywKT JNfv2XL]8[2KRQL9%a 9Xlm'9*Ix/kP2@B"{uzs_׻Kx38cEu~^ z_RvO9ŵ tHU uYq{FA  jAWTwgꩧoV!2@<ȝ/c\y{/i`ܞh zhॗ^R~^|U!)@0^s5Ї{!C(U`ȃ1֫^o@Rq{XA ={vQF֠j{ @D@o2u]7,tt  z3j3:CqGۛ[ @C`u֙4i7u (G .Wb ( j @C @EG@5@>@sjMkuw2@`- ws{a66p}dԇ.fs1њ@d4FԶfFM ԵQ ۧFX CDA|nM[0_WIk<.r8_msd3ӷl/% 9Q5e,]z<6\+WE[%.ה:!mJBO\1*5~ˏ(zO>//_V"zeua3|عXNiCLi*4G5Gr"IHQT4d(gV@Ӯ>Gu1J,ʫYRd|EIPeNT3?OknĔHSTbp'7„\P081,?v} aڀP(U0ڷ{;;[nNe:!3+ΔZzZwH`UY:1[',sӭgޭŖQd4zVa47Ó W81>VIibXo_o:]'H{y 0< ^q ESԩ1vT9VFhMHJC:Td$֩Cj*҅M:$-'\G#ى9 ٫pXaKʐvDZspI+kA!T#9?k1#=2+LǾ4$ɒGW^G=@G2?tx.;XxC1pSk363q%5jQ0 CX\J;5e0ZK.SG"zQWb֘Ԉ T2ɴTf+rIR` t 5iUv BGE,E Suɏ2'9X*)\[Q.nA%l7 ^;k_} ɨ;jb}T9$ $r8з;u /<㎻ l&N'?f[Ǔz#lC鮁ttm(~wILNZfʣ'\)ݰו2%Gh=˪+k1, iEM:lLU׳ھfY8؈nYC[%dm+WꫫV\g=i @^N8A.IENDB`filius-2.9.4+ds/src/info/img/webserver.png000066400000000000000000000106141506574241500204460ustar00rootroot00000000000000PNG  IHDR@@iqSIDATxZ \Y7{߱kbB!KY"m*VE"dKB"16$iJ+i'wb0"|W?D#339Neee(//Gk@x)F9%obbb# B³gpO+mƦ <~X`kkjsURR!-|TKD-`oMQO8Q>))=zLx&ɓ';99yhQ`6m!}t"=0U/^ />($.D ֨W|MdFCg$'%h%@vvH-|rP@;BN=ھ~w. ##>'<>Kmah۷ˈ***Xx1/PH Zjjj!f RxF^QPP1nnn-@L~ 0P9:#6pޓ`̙rss[,N{?&lzN9>x2&!8|V O?aٲepwwGPP{is̬x<~7BP5 c΂{2dgAB|> @;N~zү9%c4cmz7+М+m1\-b#mB(//ƭ"< U(o?ēgGemګh^HU0T+)[#(k(B -{-e?ӈB!G(F>G -v|޴EFFb[8'va_Btd0{E ^AAf>DH"*E2C vR3Y;]av%HH-;j9sO>|o pR%(EV}[Hʻ٬Ocn ,_"&%qi9V ,]&":UIȬg /QepNuJjS?**g P UZ7p2Յ!< iaU>WFȹaوθHPƖ"Uh9ȚF@y 9yF旛/hFϒ%KKS'/acͰ&vDm~iHȒx뮛A| Y[,Q}2hϺ47HhzEpxx8d%(1` 5N V_IC0^'C1?t@7u-Zc(cE-f{nǶiؒM #048cO qpW]} *F1j-B!BR[L?$Lҋi4a3mՆܫh>W:۱hc7 X E=[l{BhoǪllѴpe]X|-- ;*B( amY|4/:]1VNG^Y\5QVZ$=lݺyWHu&fD  A;giȽ1}+y t*k4fF 6;f, ;&YpMn=IHggglܸ6lѣGT lqa|B!pnΉQxy.TL AB\RXTD\7Fz= y|ی'bvoZwn߹Eqr$#.] 333̘1{FVV'LO{oH(ꧤl1n.7q0-)+.rbK4 9'H ksjL4J& ilFS9qcbi C)ؔ)u5{{_oΨX0tl+WĚ5k' rrr6l'SSS^5c…פ}b04YCb:Cz H%)'LFn Ø3a(%;v,0Gy!z)e0AUrƪ`Uz_3fw~atl%_v-y"DdhWzCCCϟ!""_An"8fέFR0;GOCq."_'{i(in5Y>6'kwIhD#y5fwq7:N:&&6doFg} DPTTHiii01p@O.0vX1Q\ )i#F=L6C_q9Q1/>@D\ [C<yB~~-P¯~m@rccpљ,^W|+V ]"K W'7'ƦCLLN>M=z@C8:`?@^q'&3F.v{Qlp>``'t"2>rt(5sl˚{ @1HnOv@XLYw;ڵD O]edl(0Rm@c T SY"R IENDB`filius-2.9.4+ds/src/info/img/workbench.png000066400000000000000000002334221506574241500204300ustar00rootroot00000000000000PNG  IHDR- pHYs  ~ IDATxw]e6ֻgJ B"`WXO~6wʽ-*lX@E$@B $@zO&$gN{nn3}é?>k-|:::>|?Ҋ YdEYdEYd-=555UUUÇ8oͷ|#z4:6F7oBL%nfK0%^aPj K7z/V8cʬh}] }7߽[ywߧoi/?gt]'?}K;m4UUÇwvv~oUXѧlvs*@{(e "!}>U¸DD jWP| pDX|<Ë5OmeZdYό'Gş#tk"Ou^ɺ0| WdaL{˾h=}'J/2dfԹ{-]tS Akw˜q0=wi 6.X  heWz$(Ml6grp5P)%g,GTM%"yWaSmsMƷ$I8 ^jpFzLr0hkymeW`o?|rxۿ͋zOG޷)\]؎p )ڼ" ѡ ً;dMZ̽+Q4^Q;yr|;i{"׆ڍn4H/mD$a{4S]7wSݟoGW+ #}Ȝ$DGR1;xĂ^)2Z%W3=ؿtvi dj9ˢȮ?)5HEW8/r:>46"` ̍0\yQyJ|uzz bW1Ãt?)y"xL4 ԊoWv4 EB6Iz>Nz[E,XEH]Y/D߹q?}34S ict 9C&^,ِ訝M4 W*|?K+ܹSfp>hv |C@լGF a6'"@ sBtcRJ\;)s!Av_zݯo/|V's+UG눮{;q zحژ@gih:7'hs.=%0 jk# f@W76P .{Ceq܎UBmf2L,i;M4:7)bwJ|1|aA;Ȧpq`h0wIA@@uNA`/Ddr` hMу |!I8!zUs:hS$qt>`tȝ<7\Dhx.URڱc/|-OR)$Qhnb=-8|X&V,a][EСpg2bÄsn))]D]NNy~I DF$RxCDcO~b!*uփޯ^8sOxxϼhT# Gc-ȾHtD`n+QRx-Sd7 J@Up!|!yLϹi]'en-=5:@{7Xݸ{8}ừ]&w8vk& _$ȅ[a; 3~>19qN  _ִƑs4M4Wu߮yy9g^=<3UڙyuH3ywv?dFGW,fƠ9']*)wsZWo'-*,]Vђٔɠ; ]ːLM1@hIcH<~7B!p'Pê_ڛHUW_nvXݠTHU _~uw~.1̭{x'ю݂hxdrae u5UddȢ (cn _'b#t"yR0^ hrqCyK 2,{^W%F/BmbP8{TСsw+˃#X#c䰻B]谢 p)ng&Z45^VR!ЃO>j[ɜEiu,񴄄:Xy\p P]ǥ;wX-TSc" {RBZTs o E ^PՄ(_+Kk\F5SJO@EOu]@ cUϙڛt_g=Q93j0ns H Z v7H$KKmn充 4v'I@v1EZ01ߝn%A?T%&Zhس;>6щVq.6ιŎt 4眓 5׺EZxZU<=4ʋEnF! 823,?8uiJ-oz`b8jhܙ5͐x53缷k\o/$j'56xrF@pFਯK Rߋmȍ% '~vP<^eB|h[':\:\,8~Gϫc07s8V&6k)ќSM$F@F{W*'XS֙CG.ޖ&:a8]s=:8҂'~X;9v 0W˅)E e68us*PބhNDKV2uH@lrݶ_ tqѿ;?[gKt-aәLߍ ڟ<_c m ~چnObyT`'U%l17o<ژݝ|%墽oPUC*sCbk*@g$"A7dۯ%5EC;aknK9PE8 n%cf3DC> DF?U^~Da^=(8@?bwhQNFFϫt'p:uKHsÞϖۖ;$ϾC(W?%6F7Mx5e4:";g “Olk+QWMS{Rtsli;ӷmwF|mW_i͓z6ٽkך"HB$ \F@@VO (ױk$(dTh4@f) {nKnIk'ong?|oO=qrs:\۽m{~;u?s jg.{x{cϖsB|W%7|ұnNj>mzA3}C]\ Q$8djgPTZˣvQpurd8[Qɠ1 ;xªve!H;kx6Qл`Ps'3M,gMfGtmaS_F}^KQe"kjq轮6 UD0K"*<-P+Z5"]\.˾C݋)}W"C@`{6nknM 80YQa歭6xnQ>}/단38rFQ`KYk+!5M):q 6'۾p˿nw 80 4Bp9M3'ޭ}N'O}3/Q",`/1>Ta7aX_`+(/ݸڒ*PP3y{+b' }` ~gRF-ZgK] )Fx s.9DҶ>3ܠeE@ yW]WP "   bGfJ]"Z;__C;ꗿz \~ց0G6rTR?O8oox1kkD#ڹ E쫮 ս7aU 4]ݍca5 ׃iʏ!\%Pl;녡 M[Weu~\=j g\sP 𦯊 > JDClŚrq0zM+O:`]mX~looPKDn0<µ;ֽ,jB{o.qM[?8Zˋn]梮StbBgc^v/.Wz Ef\ɛ|.@Anʬ·[YwZ8Y!ea߄.1vmD"C`J1lJ22!%L$Y*K!T\J'X*$&SHa{xOtM}OP^\d@v$w~H@b!)(yv]@\f-ɘ*T4zq!+X&R(7% I LXǔ$MAYJe*ڃHq_tqbry6GFag:sH]\bԮbeUd.c ڱ"@_ X9jw삛tOΚeO.-8? !k3G.-cqGw"aY ITdNA ㈚hBZ7 08K46 eZD2 $` !V@A τS^/yu@{pA qj6dV ZjP`LP;آS ;TNRՁK,aƑȞh4s`W(-QS,5.9n\6.%Ix,9-Ds/~_Q{plI Hi~s7KT9c,Y<.We= t/I@2p@? (^e#*1@%%r`Y&-}AĤ3޳䩗7?ؾww+)\U>"+W]O~6H.C ;C?;QDKne'ܹꪟH`gHΉo ];9p7M 3v~#XA>S KI /h 1 2A.6hPOP4!{kмg"v&=0=lզgᠰ{ȳ9}h*C[G"`ĘB(uS]E>t>`ǝAK'^.< 衯I/Kp8dC:1t up 5"|/ODvcA88!3Y^\q 2U՚dRɂ tڍ)K/B9w/$c F%M3Dq<\:{ m}whOꗆ)Lu)LVQޅ{B,] KEvr1}HE ULȌn:1/6WwyPT=)=݁cXL$q/^q.Dl5 01U52bL >lh34=*2"9$Ktc9uig˸L6{d#Olٶ^yxg3^s5V[;` Vp[xƟۗc|CK?1o?h^ŭݱFjey`= &9hwZJ#|+YSqC$\3D?䷇(+a.p@ CV.< Bda(4% Q٠XqP{~3Y``0ڳ aEr3O^2e)Rût,p95`wvy# vS& ̃Jƿ+ԟ,\LLFv,DNrLHؑ0n!D!6y #bbtVFWLHDz _|?.m:E4SL'ULol Dezsd̓d? IDATW L5v'!ht(hŷOS g'}Hlb>߶wWc]۷Mkm4I ifM՝umz,Aq"BtgY+nU'^EԊ_ݦ+ 8q,dwX/` F55osxQ.u""cc93ƿYmbqΉ܋xӊ ;[DŭQƱ&@EBtݰtۡ!ذ|d7o`5͊ ~7K/ާb'&F8i '4n"._T Yeq,c7`2V2mc<:ݓ ^ʎcqV$y#yLŠ1+?8ض7XfQa]@_K&|1 kB b1̩2V9YtI8)O:U.9 9|3‚E'EK]ၛNr(+4~,375nh7D G PXhw$Q, #t]F}'$(uHDx2iJܐ/H;CUR>uO`KX|s3;U:EpJspgc~]ܠ%b,3~ H @w]yW_|O90Cpز Cؘwe~PoIe:D.9Fo9ϧَ -a7(׈ 1;ns&7* J1N]rζXp~q)WIJ.`٧Бp=%x 'N3VGcخ(pAr\mJPݲ!e\̴-_F1he!?Pp`2|$-D HBV%B""KY ;!ɔˍdp<*fKw MW[B6&˺Fd;8wpL`p_u>xDr B/~4;S'-_;A1mp fħͣpHvLGr\GNc&QƐ׳4:ry:d/CE~B̭xeĆsD+fDO)H Iڧbd]2BeAĹr;'^&h (elu;:W$fz.Uyg܍[7I-SM_Z#+ k`Šݍ`i@3BRA/.̋GRr1% Kۍ>b*!Dpg?_4 vgNf@w)ib /dh r9-]UEjJck6!+tLl$z /$t,-' TLKЈra $l-kw)[⟼;^|uD5fR@NUXڇ'NFO[Zè t{yבR0FGSn/"u!}.hN$k *֋,- ŲW{JP.8ޏ7GDcd-U81ʢDGMl,Hv ~F}adIҼu1Q{E kj IK͔9?1ȏAt캾O4kw&w݀bfMh@˸b9 `SxT7ԑ)6@+dHVY9w3hޛ{!1E2@h'PES8,#qC6n\KDݒ8=nĵT}s(!yJ\$/~e)/U(Vhg.~&! ?HvD;+:X%zZ4u|}`& !{M^;}'1s"QUbx0r}C8aO,/]DM)($>LF[=#YO|L-njK@Iʽv2[#2ӥ? \LODQnr/d@ɐb)PV1sa8oE;. 0'~D1c4>qgAMŇa ,@F֕7oTX~hv,",",Q 9˯hʻMsA 1xDYdEYdEYdl߾ p QTWEYdEYdE6DEC*#*",",{,3",","l<`w7yujCd ㉦xfҤΎ7S3Ƈ=k4XwK.a,5lVb1dƉ1L&@{{]Ed2x\ȑ K"pBB\euwwX,ͿEVo~ښJx≟/^|yr"쨵|uoz#S\Q Ym8@ ?g۰%g& T+6y#]z)7;[~1%Nԥc5X6J3d-cVËsvvUU5[WuNÔIrqL$N 9ظk׮7VUUUUU}9.;W}$̙3gﭏ< fGy+_JMMMMMMgg//ZhҥgF.Ȏ*#;T&jUd76*9<@~ƌe_~-1Nwc! p ,&غy>!!WA 1)t?A >cvic*QG__a͚뮿>}ᅬzDZ#$r@v$px}֬Y/~7o޼cǎsehEQED 9Un~g/OONrksܾO-'=LQ_{{œܙӧ6[#"$A L18\!v]s,#(ʞ=~ƍW^;0?kD@0 'O|g\X,N2%JъS{_qNqPY3glkkkooonneyb q>y]{վɓ'pGkkjz{OVT և*}[ovwwoڴ_=#m?,˒$IӳaÆ[bX,$iǎw}w]]ҥKqߍko:|\G.E6c0⤴6M6}Æ S&ɑ;)PHW"Ξ={Æ {9cF%7ސӡ,ɲey=&f:x`$;S֯_cf:::~Ẻ?_r%獓qv.QSFȏA`:i?auM]Ɵyb 3]|Ճ}7~9{G*.\~e{˓/Or6d[` r 0d@y$75U)uΛt[1D 6=cԧ>2e KipAYO;[7oC,Xx^|Í]tO?H$RTSSH f(\ͱ9slڴͣzL:DNm;vX$ɱ,h`*k>ܟUM{mL?싶YM:볭8Je$"Ơbdn&/5xqG>t=+WjkeD"|^ۺf͚ .|9s$ɓ'755M4)NKjoo盛V]Yƿ/x?v1Ԕfˀ KR7naPx饗>[s@ %A#4iƌ-TW_ojnLgR1IbO20ZYɲ}\r=SBL?Ý/'?e˖;K.7oŪۇ_ϾIӕAF6 {`Q˩ں6uo\0I<6ӿQ:%=dz.<7X5> %0G+SP<*ݤv+w?Kw|g=H>|eYjii)|zuuulVӮiÇ׿ڹ]<}߷o̙3FT1q4:Z-3rug^yGy5(t\MXd7UI!lՈ18$dI֭Ć}odYdƺoHs7rϬ͟P C W\ Î+]U\ZRM6+!F~>< PqR%&@%͎bwqʂoX0?Qk=f8qرsko:kLŪT>B% M⼀CҫH]ij'Uf9'dYnX$;{H=s̺:y&N:jGD"RM yBfq7,?eCoظAnoB Q ?$o/27"ۉr"sxrSCCSUU$Uy|(c e9dYmy=cuXj WGYsH?|e޽R1W]l9twޜyňzlãm-hqIXˇ+Uϩg}cuhxo",1(򂦨: I S1ŔUҭ)=\Jq9/OcjӍ=^ ڞzJxkX*e0ebK$jCu^.IRWWW___P w5ҿh]oۡo}oqs}I{M# <`  bk* # $FH, R6Ӳ* #Da"d %IښcϘJgCeICI˜iCb1Y!"Mdק/h~na '3-د߾iR'ύqz(4u˨0~WŖLD"#J#b qj4ufUg6i*ߑ㟶s.s3ξIUHQX!DΩy.pM"^ ]|rW-\㡇DM͘H?/IlNҝ 8V\e˖{ifϞ}yՍ>1v9}Ƕ9)U,c_^%4P8I5R5@H眝r IDAT# #Yy͙TK1Y{ &OJ9V댻$1I҅2psƘ=^+3~3^DH _KK-x=-[|P/Eh*MG.Ү[a¤]=bЇ߈1)c(4^q8iD1$Eg,oZןfz=4Mg-PŖ} 4=IZ?iy @@ e]af~lҢEͧ:p,$Zi*iZEaF׿>裹ݻw޽W>-[6s.b8&p/+ڵ6O;Цt4}3 8rX =@*9D?TM~vS}M,f#TcF Q4p/Q޷$cCF;͏Io$I]~9_b /YP5rRh"p7kw?㫧뿢5h}%lZI@Mե P^D^qcppYvs[SIE`~K0)˕Cپ?1|c>ig6:8gKqȵ^}E1)ʕ+WXի>^qC̩c A_Y3{lU,#q@ 'ӵj.QA#\@pHpJSb_A{k`'Zt&1dQ*3{s+ϼh2fmN؞ݮw)R/Ǝ㫫۞~ڵk;::O }1Mm[ՏC䋊3"X6}_e_17MF@LLJ$yU\AR|z`wbo"nI`ͺVrW]y2iN"yBl&$zP0WzCj7Ĥp^"K4KrOo72]89bE4@٥7Jy]W^yeڴiK,c$ -f%*#sݻbZ)E]m=/~¦jӀT h&r;@T@EE k P^oٲv#Tcb czgNՁlDmX0yt{$vok?3?DcO8B"{'v+-^\D6Lv /_?^r~G u2ka݃z&1/UU3-*Lf?sbΎs̸Gj8'V0֏O5lFZ9vinNE9\R8cI)yHiװ̔QpuKiUfcbtZE3~J裏؊+U 7o۾w۶nZ/sDD;)BV,Q+W>͈WYkҩ]tij_*E΋#FD 45]>s!=ԯgH ;"uWSΜ9uҤIe_Jk׮7xC|EQݻw߿Ϯ~Y] 6lذaٲe'|(n23:<} XOvz9WbamAI%@ٔє1ȹ{?I$Ib"ʲ\ZV64vHpQ?n$Wo<4`h n\)״ /<31B[aܶh kxO 3њ;nx`E?|[˳x_QelԎ456=8E`G1L*jB3*jb8I8߷.p~"Kg[ pg,'q{v}Rod2ͭDHB0)_=ACiSdԇ/N3v{ի|r*et͡۶<36MQx*ӴDmmz`79f];z{{DmmmX|nSgyƁ8cNq #{ :VQ]SHєCLgsH%)D J\% iab~V9W9zwz}uu̙3.\x'ϙ3>\n]EQ*xjmŊwόHv]F>E2 䇈*\IHU"J @ڍl;0y3NʠX*AF;X&a ([[#;50`G|L4QsI@PYj˦w"?%1I$b:7)_߯JΙ3׮===D"H455=O;Bu"P*N$1D>k*ꐻ_ƾwDKZғN E΋\!K3%)@ QM^jl+F*'#W׬ny8N{SN3g… ,X0mڴL&#I]Vۻx^z<;6wݻKmʔ)#݅.78>4s>np8Œ ib}CdM#Rc,QHZNPN:ejpD8L:=yBܟc$XL&c)grl^旐/BH 6418`K+#a@~h_}sz_s;h;wxr s>yؽ{ƧZ(T(0*խ[v455Ob}7UB1;0PSߛh*r6[ӭm[[0CƀQbIkJS4[S:6],IռT^x&e˹N+P}4cDJֿnSLimm={٬e4Kǐ)m\qoViN/_e/S`=+l4CcG #8jKV ZFնzV$4pT:(ʽ+4ηoV( 0dvAT2#jB,mg\%@Nre&ǭJH3dmmUCCu__w?0o;}:pO$UUU<|?%L!ݸ^?ŕtkJg<3;nZSsEE&W)\ R XL> LL9θ0pYff͚:ujcccCCrԸvDb&-r]w7pC]]] [Μj#Hc?œ뛬pC!w@A:OVO)1c- NGW; 3R</ ;vTuޞؠFx lnUG)\; Ay11øqe+_2᜕szj s=S9 j+wU kU; gRu#@%IVU0|>0xMA_ͤX#x\QY)OO~O3W06q J )0dfJrev0tYiIDñx'$TAP, c^ H$پb0{ٳg<}v>lUUӷlrypxDdAh>jDŽ@!Ikos̊j "vo[c[Jd$xM؀LA-*jgY8/|s^N `u;(%4k]Pu- ya-#a_!`#af͟Z}NHU|kn6䕤_Oq#?׍(H(\r2ime.vmP˖m[DWCSTg"e h4<̓Wu#?LN"*dE V Ȣ^)p-p-L`0 㞊./!JPL2ɲ,Xb/~\k"kvݺu7>{%K\rɒ%sϫ d2J|>Y KšmØFTD7%FJ")=`uz-fnVNu-B鬙'OC ~+L&\yyݶm/[F]^?yޅ@sN2L* gRtN/]Mc`dkG$C*DUQZ'&8 uDAUU4_ H#vϟL<#袋(Ju+;cuq'nT~$>.#JDL7 Ř MJ/Dt Z!DDt m dY} ˪̐G 5CR)P^>ݾsq8)Avp J;B0a;>uȀQ"PaH$,R{Gs:eJ:,(1鸽L$>QUUUݥ*px׮][n3g_A[[i88@ ¿G?3oFEf v7P\h{|%þ')BZ g܇=$9:ў]0ޙmjhL VCbS0 !Ŝ2RQ6q-Q"%HEQ` Q2$.5MEQI^zq\#@(0qw\˯KN OgKB)Q#ZZP|,+`4&-Zr(D"a;!g=իWsW@J\i뮻k&O<ܻ?

kzt#JS&>V*e> Pg6iJm9mM>EYBdH ˲,iu,mM4+l~6qm[aljnȵ?{20#Q$0"@`=웭mV؞R^)Yzk@2})Lܰaq6 è`ɒ%P[[; nݔ# oXjTQYDP_(f~]3TEqںi$I<SO=e8q\YrϸKgW+wwϜSPA̝**0 :BuBivj},;;8cGw6MD~Bj+WYf͚5-WycIJR79<$Vx;_Bsg; '^_߬ gp.0%k׵Lę#$0#\`+S@sP&VQ1{w2G N翐N/_Ō_%o]TwX^Nj{%St.%'gy BL*vB=N:gӥ4s *J놡z1UARJb\|7]!oGqÛ;vwKnpm۶V6j1z{s,ݵk;c^="|\`umb.c r~oM/6}u#1BSu4{zzM,t;h˓@\{ $J㯺!%\u2}2> kp!%26sgnnο>g ,}*T%IT뺡(}iڶ5mò,{^Eqxk I;}2"O=M26Dp}dw,e~WWImoưoyMNSStJHxp. :kbVWW566y?A_|Ç`ʔ)۶O 4,bL$RdY.sae PQ$U4Mu]UT*ɘHZZZo>EQ4M;th[ϟ5#GH{-]×;ի&o>=fpo0ގT*8(t k2!5N@;{O?4}ˎ;*#dDǦi2Mt'yS0b'ٻ)7.O;WgPAh}]~Es'y32q4M]x.G*\#RYeYu0|qӴlǞ3gon&5M{x 믥RSSsg^z}E%Jt:mYָiڒ%Kxɒ%d|.+zOH=Fb+׿ihh])i۶S)<떗to59uxs|cV0HJ? dR%Zm^}5csw֢ dYmp"Ȓi SSءPض⦦>ϷiӦ?kp*p8|饗._|>/%c/V|J(5T< #y 9* Z.y} {IB;>]5k~o9{+Vf#Gr 3 ?q)>B{TTTt 2ԐJ[lَ{iƌ#ur&OGBh_.s.Qas#2d63a` {[-[zaee|8`ZV0d"aIOqwxw8|L&cYւ x޽{M䘏#,W-.01ww4n{4fVm# wd H<*נ$*&]8o҅{ll3*PYtD*T\4oY.C0{O}S[[[ g،Wʰ\p?a{ Hx!TȯAI8`0 eqL8|mۼ{!dٲe>hqq1!D4UVMGĹ v"+>v,ϟ??>|kjjUu<ÐH${CիW^tǩhG x=3B!rEQt]P(Yd]UUkjjxoo9K %I***LE|`Pu~|=۷oDz@jc S?9wjTQQAh2>=~L3P;l09lʨdy[|cW7/~?B!e[:3^#Y1cނ3ɩTA!Bh1`y\zB!X!0!BBG jw:TjRB!s 0B!Bcylwo&/!҂B!Bc\P_>pa|B!잍Y<9ϼ3Wyߟq@!zצ`Λ˰!i޳c^d/ y%rc} Dm>>΅p̀~7^G-B!px{yu̖,B!4qtuuT̛Og^H8,A!48TvW`B!B0W3h&|*B!Bcs<ѽN^p1sq۶-2M4 z˲l۶mq ۰aC]QpT8* G:UVZuO%;2s7Ւ̆dR;evq]u]>D]׵mM^pRB RJ( R~|ƫOcB!Tx?95??3yߟ{p ̪0?3rO/f˲ݶ$I< $y!wJ)ߘg}<B!(0Ѿjժ=U0(~|x1MӶ, ]kM,Zu@Qj #I}Ih ($I<˲c=<ʋ7B!}P8\=s.N.WaCf90r]WWqCä7߄GudiKSSȲ'eYwY_ !BwO.f)9ܳ24x:/᧖i7][}H x)۔/0&j}}i}§BlAH|ӧϜ9u*eYQ>7?zB!XeEQ1w-+ٜg٭d2 ?tzw7͝! $)핤,Kw]uOcm}۷! U~ɒKii(EyoB!4J ٝGoa˜L&L&2۶Mw_uv\;c!O,/ł.^DYlST*hےܘ9^m=ڱfמx㭷>ӓH$2\r2lQpT8* G5Fuɩdy[r y.OqZSS(fWa2i?haEQEJ)D!B'bp;8wf @fy wrȕqt*R7n S>0\R|`x|@ڶ CVT$A~j,^(7|lrֲol۶^IuLfΊ={'O9l$ u]4mB!F*B,W.%pv^!> Cis8ٳߜ<4A jOjkǻ]P]ԩpX4M"SUk5yu3eYҽUU[?j۶nX)0ƃMMc v]ݰ᭫JՅB@ 8ifwB!t|G>u'q eȀNFאVw]⋒mc.!Q|b޼sROQM뗿jY{ $)ʞ1FQ뺮x+.Be/U;T*H$z̟?V?IR$ٲfFž}\vUL B1EQ0#BqŇٝ -=b.:mu;Ue /H ]ZŭUUi&IR㽏?^W_/8qR6UU=paoya>U+<{ u#!V6d<Íнz㨮{F<|(ti2Gp]Xݻ\y=y8~3z!B*T)i;M۵lr\v-ǵlwPέSG cgS\G~0$I 9rᣏ76 JF"Z|>iDhw&(?5,@ +*:򷿝ib"c{zz |_ Rciy%[nrqJi<B!pɞܼ;^*E2 /L{4 #O͚ΓˣhIII(*N&/X}=OQ|j_|q饥啕X8s'{I4G`P(FcXԩ;?>E ږ2;{k ѶkwZz]]NTqwPaMpT8* GQ&ҨN4EZBA>`*u7~AeYT*qo,>ts(}~ʔ=W+))** B Ĺ?IΝu@a։o^-([_zIm`E޼X,VRRvdY;D!B|{v2A:fŌv~8:L&p}=9i2QQQQQ4|0cvQ@\U\tόEpqq1>~)jxB$O} S!'56nYX+Vh^⋲e={Ӧ׷_xdž7w!B OX*3ru2LBҢK?G"`0iu?>d_|όEEEeee%%%pQ,ˆaB⒒M_$\;Eu= z+Wڂ@$:'몎ɢ!Bh"lN`e#S-\::9~Oiik,,Nv !N;eJ81:B!0n{(t$I@ DجY,ූqEQ~$:%ttD_{7cpG!B# #<gg& Rya Mx;0DQt)cxfBh$ue:ZyQVw_x!ǩmh  #kXD"N-STB!л ˻ri.ftmFCk0JUU0:}xonL+= ~UUGb}pv5379m-fƘ |JLS{WCXl>OuA,46μ%J^D4g|=)<.B!!@:v Bd=d^Ù,k ͚ǗS\W`c˶ϛwk6mg5{?>u*|]$17<^G^!:p`mKc9稪'DZϸCKHCI @ s!Bayr=?a#yZUU޶sڴ]s͜'X⋊eQLf]QRTmOfͺr׮ŝRTjMnmy Ѩa G)l eat2n2$=xqSII BLӔ;;ϸ;X+p(;B!:uL6m Ɇ;2M{rɒ G"EEEHSf x!BAX?K5F&Mjzg{ŻP ` 0ö}c=&cqBxc/.W]}7Xu]'_~}Ϟ斖Ço_}YZۂm3,JE1cK[Z~<IJM$6f$6/YRTRR\\\TTxMpT8* GQ&ҨN8@E2$Գ wIz/ G;_;c&?+4uwwwwwl@4=%ĥQ@K'e=˿C 4A .9㌲򊊊h4yϙwlB!Z;~kCg&aV̘7́+\WqN)eni]W^Iio `2t@ϻ'45l@Qwy)+m42fP:Nm5vϧi?#GB!4X%o:wrjn 2~/1UUu]QM:>W>Dinc@w2)\V6q%i=#K]q+Iʛ4/OڼbEeqqYYYii)/mԎB!FN3SY#9>pw~*?wxlYbC,_E pDߺ8lmO5~RL:;dR0Mq2*Tjac㴞eШ 2!7/`AeYY,+--lTB!4r UdG9 9<[qAi]{OwnKˣ+Wӌm^RL&m[e܆K9;Sw]k7*)I̝[U^ʢ(?!B!Ј式2ޝ7ѧO_{]-9?ڕѴuW^ f2EEh4ZZZZ^^***KJJ"Ȥ&[[|5{/4~?[% ׆(1wnEEQk#B!WNkoft;|&_TT$͟?TY9xEY_Rg55 {xAe{PRRR\\\\\D`d ve rLΟUA/"D GQpT8*j"M0gAmM\`/J_aA1&uyYKOOO[[[KK˔[K,q&pawwڵkLRQQvt:]VVD,ˮf2^ٳrg;s2_X,y|ޢN}v!BSlyǭM]g~ Xw4'cJ+(/ڵizyӧK w6.T*%\}J$24+++kkkSTooEDQt|>X !={…ձXyyyiiiII ?UE~6*|!BhKv(cNzwm+W!X7+'>* ðmO^RRe˖6ϧgaeY$-[̲,I6-ne.Hݶ˖eL)+EEEPH4I0#Bvl;ṙy4, cXhΜfˬY/X;=zccc=|y꺾lٲP(k"'Oي)1卲]r ;45F!B~| %+UUsm^9?2cg `3\+lݺȲP(4gbX&%~x<.gzyٲp$FH$:k+FуB!6{+^˙1_ό]e]{/uT*K$X(w^n]&J~_+Vj_xᅿ<ժm\8:?  PB!4xP,/SdwAc|%TRL&JݻU੧ڻw/cq>_z4moo{oiwUSS 1]uOu]FE!Bcs8{NJu]Hoݺutu]Axp8 dYnkkۻw/wkjj7TUUb)8* GQpTiT' 0Yv%2e漷-2 ׏AcEw]8뺎8ND___WW:tHeI@ ,[7hnn:uj"hiioxwǿweSJ'2`B!S~6ޜR w !/you]YUU4MD"icL0(:a@ J 0i$i0u]UU~+s**}D!Bc^Vn08y[egYc 8(~=0$IBv~uOtRB! 25m @ 6QQpT8* GH:'ݽ>v& ҵ}pAr{He!BSk;wExTϝPLwC^'ÍB!(+p^W.WZpB!F=;h+G!BMhĩuB! #3 ٮ2 ajG!B` c^LUʰǿ|7??}'W\_W\q#M|u%JAa&J8* GQpTiT'.שyL9o7vE>_*vnc_ѻn8~wntg66~G?}םM$B! '?^{eꨖm hYh{o_}k4=zp_׻ RZ*`\uk? E!BjpNmAt/0p߿O{7=zi{\K<| wX_no{}[Vw=.B!P'w- ^>΋@ 87>熺:Xc1W\zǟ=k֬}o9YԺn}}S+/IU]A_!B~?n(4?:c|nz衛[ᵪ:p(=/K/z8ЄB!*M篜J.̔.">n{>|ͯt>zy*P +`RU9nU*wr*!B SXN+hg^?XJ5^y/+5"8IW/]|8>jܱ GQpT8*DՉ'3dy۷ғ3.^mIM|䎍nGm_QX| OmC|2w_~z#םE$B!iw;sȵ .KE{[uǟ/ɥ@Bc #_엘|Ǯà !B\Fr;)B!л(N"fuB! 88cG!Bh x4 !BXv)@f 6QQpT8* GH:>䎷h5 _pAs2 3`lB!Nߩݷ$mNJ3R|4B!*|ԣ3!DEQMt]rB!T ɩ$?C~pH^EY)M\MӼ!BBMdp>VNhYVIAp3MӲ,!B@1^XxqB?QEk,˲Nmƣ!Bh͆TB$IhrKM|>q}!&J8* GQpTiT'kAnmS??A ٽhqS˲Rp_Hv!BĜZ;_ޔ ȳ.2<rq$I$4L&B!*$_Wf©6FlgIQUUd28 !Bhs|+ø(J4wH)mR)!BhbP\R*I-+gi2!BJgåt'ڇiZ eB!4H n7 a1lMpT8* GQ&ҨN-g2sη6w'-o#]Q` EQafC^i4d2D!B'AG C;HJ)c; EQbt?B!4˅ym̘Gv8maB!D!w )/4\MӘB!(;wK˄J)@";c,d2™G!B>8 hv 2M3`+wB!4 >ԫ)qt:!B\&1(V7@uD2t]ƃOqT8* GQ&ҨN4q>ί kV~ iZ5k'{>!BĜZ}[o>NHI@}B,m>+(Cᜁ eY1!Bv}{4lHkmdzD}##:8@ž1!B@$3vkO;Ar=957Dьm;!BWb{={rD"ccգ8xL BĀRப,\!B> Ð$i)>NB&J8* GQpTiTL9oӦ_]5\喯\;J,uQhɿ]~^DlB!NƩ7P pŋӱI"#;ƘeYL@TB!BF(DQD"#l*og"B EHQ[uM3PB!Є!!`0$ӟw]q^:.B!0l=H(J86 tmۦib9;B!>lnٝz$1 dOZu4͉WΎMpT8* GQ&ҨNАǬE]4uo}[a 9\4-2M})j!BSko|ckCF/]2 7|CUe0p PUU1۶G *!B!tjƸLkkwFUU @ h!BMdrwI&lr꺎B!x Ǻ"3"nڵ[n W^O*B!x\*{e;$ɧzjӦM/^O-B!HNrƝftvv>C~͛&J8* GQpTfTwx}bgc2v((I].̚5ض7,A"B򖟼r>q ׋Sx1+)--xv޽~%IK@!B)ԸsXf̂W}kLBB!q Ǭƽv~ B!B(EϹeWo?={FÇ%L&c۶ ,k KKKC0!Bhlr>YcR~=O<1 .J۷ݶmKۛ<}FU{>LuɶpIl cYܵȽp $&@j&$!ow S -@M;mh,c h=9m?F=a„aÆ5 -Ztw۶G={5kxP(H$ H$*M,0x< 6onABU U*TPթTshZ |k^x7|0tl犃ܳgϳ>4罟%yUW :qpzAz]jKͩGAWɶs@شi}wr݇xqOeYG {5DgϞ|'hoo?Yy98 D U*TPBUTD[ zF=q{ڴiw^nnQ>8X,rx<~{']/s=qp`~ٜ*zt؏9x/yf[TT9??VKJJ<!Ќ{_yt:@aq}>w\o~A=L3gwxzGٳgϞ=s|00FǚsG;nܸ_cƌommݸqcggPBU U*Tu*U٨v9O iӦg4Q"-ZTTTtmվ/!WWW/8qĉ#Ȟ={iGuOS$Amjꫯ.\3L2e"jmmuOikk{;;;-' ~8t:~JAAAQQ8꽆a[n۶8Yg>Yρ~qO֭ۿg>[bK/iE]'2MsժUv4srrf̘1sLɘGň;KnN$Xmf׶?Ǐ=zٸ_tE|T*dObyK/4??y/8qwιbg[\[[[]]mYְaF]QQQXXv"NObarj~k_0aXhXS*r !:;;^YC`p)B˲:::>D"qKmv;v޼y` П};ugָ LBqѨ(=gcֶgϞ[Z'ŋ3&N8Ha-}/3M3LjX0zvo뉓r7=z@}^QBU U*TP:\XS&r-{:e &0UUL*b9ND˲z.񭪪=Sۏ'GE)..^dIAAQ+?O}8_!"!H1CAf2eD>(J^^^{{ܢp8`"Di(ZeY@sΙ2eJo,ρ#&(۠uϸջlnnn X,\.iZ*Jӆa(㜟܉s!(FZdD; d.FqwDLTιF^Wb1ιq8jYeYsMNX~~iaj :JD &z{ѽv۶Drrr4Ms2HDUU9;UU3eҸm~K_ҹ+wڷ\t?LⰇš"WVD".p9d2)UUV ̴/^\^^v{v=Iq1tЂyt]ףѨeY1\.irj*u]|'4mƌ]w]EEPBU U*Tu*Ṳ{Qc'ر[c1bދd%^{-Lfse^ղ,0LӔF㏃BE+ !ؒ2e5ę|/zgu9'#(qcr`gqιY`%q2_k5"ӷw  %L}y<әH$i8Bg9|Ӊe.rjwAI#^7UȘr:r0Z;c3 9_*++k.3i !2hC.+_ʁvؑL&{ޥ(4Mviڶ}^}̫;1q=Wwfʲ{nݺSkTU-**Zdɐ!CmtG~(R~(NUq]q3f>o…) (*TPBU UJU}Nؾc41Rmޫqjq{{{ʩv25_ϼ8HU_,Οqv1{Lq[VJl!C 2mڴx<#Huuu"+Dnn… G+)EnNS2ywds}=3TUU1ƦNs[uv85{ÃmN%]uoD4,EODmNCa =Zͳ]; ABk {=ݯcb|@ABU U*TPթT݉Q&rHB҄ѿqHWd `qwpB^U8*w=gܻ"y%ʎtnj;hݏ޸lS9 0ptxWCK]z/WX used for deleting a single cable */ private void contextMenuCable(final GUIKabelItem cable, int posX, int posY) { if (!cable.getDasKabel().getWireless()) { final JMenuItem pmRemoveCable = new JMenuItem(messages.getString("guievents_msg5")); pmRemoveCable.setActionCommand("removecable"); JPopupMenu popmen = new JPopupMenu(); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand() == pmRemoveCable.getActionCommand()) { removeConnection(cable); } } }; pmRemoveCable.addActionListener(al); popmen.add(pmRemoveCable); GUIContainer.getGUIContainer().getDesignpanel().add(popmen); popmen.setVisible(true); popmen.show(GUIContainer.getGUIContainer().getDesignpanel(), posX, posY); } } /** * * Löscht das durch loeschlabel angegebene Item NOTE: made public for using del key to delete items without local * context menu action (cf. JMainFrame) */ public void itemLoeschen(JSidebarButton loeschlabel, GUIKnotenItem loeschitem) { loeschlabel.setVisible(false); GUIContainer.getGUIContainer().setProperty(null); ListIterator iteratorAlleKabel = GUIContainer.getGUIContainer().getCableItems().listIterator(); GUIKabelItem kabel = new GUIKabelItem(); LinkedList loeschKabel = new LinkedList(); // Zu löschende Elemente werden in eine temporäre Liste gepackt while (iteratorAlleKabel.hasNext()) { kabel = (GUIKabelItem) iteratorAlleKabel.next(); if (kabel.getKabelpanel().getZiel1().equals(loeschitem) || kabel.getKabelpanel().getZiel2().equals(loeschitem)) { loeschKabel.add(kabel); } } // Temporäre Liste der zu löschenden Kabel wird iteriert und dabei // werden die Kabel aus der globalen Kabelliste gelöscht // und vom Panel entfernt ListIterator iteratorLoeschKabel = loeschKabel.listIterator(); while (iteratorLoeschKabel.hasNext()) { kabel = iteratorLoeschKabel.next(); this.removeConnection(kabel); } GUIContainer.getGUIContainer().removeNodeItem(loeschitem); GUIContainer.getGUIContainer().getDesignpanel().remove(loeschlabel); GUIContainer.getGUIContainer().getDesignpanel().updateUI(); GUIContainer.getGUIContainer().updateViewport(); } /** remove the connection from model and view */ public void removeConnection(GUIKabelItem cable) { LOG.trace("INVOKED filius.gui.GUIEvents, removeConnection(" + cable + ")"); if (cable == null) return; cable.getDasKabel().anschluesseTrennen(); GUIContainer.getGUIContainer().getCableItems().remove(cable); GUIContainer.getGUIContainer().getDesignpanel().remove(cable.getKabelpanel()); JKonfiguration.getInstance(cable.getKabelpanel().getZiel1().getKnoten()).updateAttribute(); JKonfiguration.getInstance(cable.getKabelpanel().getZiel2().getKnoten()).updateAttribute(); GUIContainer.getGUIContainer().updateViewport(); } /** Entfernt alle Verbindungen, die am aktuell selektierten Knoten angeschlossen sind. */ private void removeAllConnectionsFromActiveItem() { // Zu löschende Elemente werden in eine temporäre Liste gepackt LinkedList loeschListe = new LinkedList(); for (GUIKabelItem tempKabel : GUIContainer.getGUIContainer().getCableItems()) { if (tempKabel.getKabelpanel().getZiel1().equals(aktivesItem)) { loeschListe.add(tempKabel); } if (tempKabel.getKabelpanel().getZiel2().equals(aktivesItem)) { loeschListe.add(tempKabel); } } // Temporäre Liste der zu löschenden Kabel wird iteriert und dabei // werden die Kabel aus der globalen Kabelliste gelöscht // und vom Panel entfernt boolean wireless = false; for (GUIKabelItem tempKabel : loeschListe) { this.removeConnection(tempKabel); if (tempKabel.getDasKabel().getWireless()) { wireless = true; } } if (wireless && aktivesItem.getKnoten() instanceof Host) { ((Betriebssystem) aktivesItem.getKnoten().getSystemSoftware()).setSsid(null); } JKonfiguration.getInstance(aktivesItem.getKnoten()).updateAttribute(); } public void mausPressedActionMode(MouseEvent e) { if (e.getButton() == 1) { hideAuxPanel(false); } } public void mausPressedDocuMode(MouseEvent e) { if (e.getButton() == 1) { hideAuxPanel(true); } } } filius-2.9.4+ds/src/main/java/filius/gui/GUIMainMenu.java000066400000000000000000000561551506574241500231040ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.Container; import java.awt.Dimension; import java.awt.FileDialog; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.Serializable; import java.nio.file.Path; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.tools.ToolProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.netzwerksicht.GUIKabelItem; import filius.gui.netzwerksicht.GUIKnotenItem; import filius.gui.netzwerksicht.JGatewayConfiguration; import filius.gui.netzwerksicht.JVermittlungsrechnerKonfiguration; import filius.gui.quelltextsicht.FrameSoftwareWizard; import filius.hardware.Verbindung; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.SzenarioVerwaltung; import filius.software.system.SystemSoftware; public class GUIMainMenu implements Serializable, I18n { private static Logger LOG = LoggerFactory.getLogger(GUIMainMenu.class); private static final long serialVersionUID = 1L; public static final int MODUS_ENTWURF = 1; public static final int MODUS_AKTION = 2; public static final int MODUS_DOKUMENTATION = 3; private JBackgroundPanel menupanel; private JSlider simulationSpeedInPercent; private JLabel geschwindigkeit; private int aktuellerModus; private JButton btAktionsmodus; private JButton btEntwurfsmodus; private JButton btDokumodus; private JButton btOeffnen; private JButton btSpeichern; private JButton btNeu; private JButton btWizard; private JButton btHilfe; private JButton btInfo; private JButton btSettings; public GUIMainMenu() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIMainMenu), constr: GUIMainMenu()"); Container c = JMainFrame.getJMainFrame().getContentPane(); menupanel = new JBackgroundPanel(); menupanel.setPreferredSize(new Dimension(100, 63)); menupanel.setBounds(0, 0, c.getWidth(), 65); menupanel.setEnabled(false); menupanel.setBackgroundImage("gfx/allgemein/menue_hg.png"); btNeu = new JButton(); btNeu.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/neu.png"))); btNeu.setBounds(10, 5, btNeu.getIcon().getIconWidth(), btNeu.getIcon().getIconHeight()); btNeu.setActionCommand("neu"); btNeu.setToolTipText(messages.getString("guimainmemu_msg5")); btOeffnen = new JButton(); btOeffnen.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/oeffnen.png"))); btOeffnen.setBounds(80, 5, btOeffnen.getIcon().getIconWidth(), btOeffnen.getIcon().getIconHeight()); btOeffnen.setActionCommand("oeffnen"); btOeffnen.setToolTipText(messages.getString("guimainmemu_msg1")); btSpeichern = new JButton(); btSpeichern.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/speichern.png"))); btSpeichern.setBounds(150, 5, btSpeichern.getIcon().getIconWidth(), btSpeichern.getIcon().getIconHeight()); btSpeichern.setActionCommand("speichern"); btSpeichern.setToolTipText(messages.getString("guimainmemu_msg2")); btDokumodus = new JButton(); btDokumodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/dokumodus.png"))); btDokumodus.setBounds(250, 5, btDokumodus.getIcon().getIconWidth(), btDokumodus.getIcon().getIconHeight()); btDokumodus.setActionCommand("dokumodus"); btDokumodus.setToolTipText(messages.getString("guimainmemu_msg14")); btEntwurfsmodus = new JButton(); btEntwurfsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/entwurfsmodus_aktiv.png"))); btEntwurfsmodus.setBounds(350, 5, btEntwurfsmodus.getIcon().getIconWidth(), btEntwurfsmodus.getIcon().getIconHeight()); btEntwurfsmodus.setActionCommand("entwurfsmodus"); btEntwurfsmodus.setToolTipText(messages.getString("guimainmemu_msg3")); btAktionsmodus = new JButton(); btAktionsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/aktionsmodus.png"))); btAktionsmodus.setBounds(420, 5, btAktionsmodus.getIcon().getIconWidth(), btAktionsmodus.getIcon().getIconHeight()); btAktionsmodus.setActionCommand("aktionsmodus"); btAktionsmodus.setToolTipText(messages.getString("guimainmemu_msg4")); if (isSoftwareWizardEnabled()) { btWizard = new JButton(); btWizard.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/button_wizard.png"))); btWizard.setBounds(700, 5, btWizard.getIcon().getIconWidth(), btWizard.getIcon().getIconHeight()); btWizard.setActionCommand("wizard"); btWizard.setToolTipText(messages.getString("guimainmemu_msg6")); } btHilfe = new JButton(); btHilfe.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/hilfe.png"))); btHilfe.setBounds(800, 5, btHilfe.getIcon().getIconWidth(), btHilfe.getIcon().getIconHeight()); btHilfe.setActionCommand("hilfe"); btHilfe.setToolTipText(messages.getString("guimainmemu_msg7")); btInfo = new JButton(); btInfo.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/info.png"))); btInfo.setBounds(870, 5, btInfo.getIcon().getIconWidth(), btInfo.getIcon().getIconHeight()); btInfo.setActionCommand("info"); btInfo.setToolTipText(messages.getString("guimainmemu_msg8")); btSettings = new JButton(); btSettings.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/settings.png"))); btSettings.setBounds(940, 5, btInfo.getIcon().getIconWidth(), btInfo.getIcon().getIconHeight()); btSettings.setActionCommand("settings"); btSettings.setToolTipText(messages.getString("guimainmemu_msg18")); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { if (isSoftwareWizardEnabled() && e.getActionCommand().equals(btWizard.getActionCommand())) { FrameSoftwareWizard gsw = new FrameSoftwareWizard(); gsw.setVisible(true); } if (e.getActionCommand().equals(btHilfe.getActionCommand())) { GUIContainer.getGUIContainer().showHelp(); } if (e.getActionCommand().equals(btNeu.getActionCommand())) { newScenario(e); } if (e.getActionCommand().equals(btSpeichern.getActionCommand())) { if (GUIContainer.getGUIContainer().getActiveSite() != MODUS_AKTION) { saveScenario(); } } if (e.getActionCommand().equals(btOeffnen.getActionCommand())) { loadScenario(); } if (e.getActionCommand().equals(btEntwurfsmodus.getActionCommand())) { selectMode(MODUS_ENTWURF); } else if (e.getActionCommand().equals(btAktionsmodus.getActionCommand())) { selectMode(MODUS_AKTION); } else if (e.getActionCommand().equals(btDokumodus.getActionCommand())) { selectMode(MODUS_DOKUMENTATION); } else if (e.getActionCommand().equals(btInfo.getActionCommand())) { (new InfoDialog(JMainFrame.getJMainFrame())).setVisible(true); } else if (e.getActionCommand().equals(btSettings.getActionCommand())) { LanguageSelectionDialog.selectLanguage(true); } } }; btNeu.addActionListener(al); btOeffnen.addActionListener(al); btSpeichern.addActionListener(al); btEntwurfsmodus.addActionListener(al); btAktionsmodus.addActionListener(al); btDokumodus.addActionListener(al); if (isSoftwareWizardEnabled()) { btWizard.addActionListener(al); } btInfo.addActionListener(al); btHilfe.addActionListener(al); btSettings.addActionListener(al); geschwindigkeit = new JLabel(); geschwindigkeit.setVisible(true); geschwindigkeit.setToolTipText(messages.getString("guimainmemu_msg15")); geschwindigkeit.setAlignmentX(JLabel.RIGHT_ALIGNMENT); geschwindigkeit.setBounds(612, 10, 50, 44); simulationSpeedInPercent = new JSlider(0, 100); simulationSpeedInPercent.setToolTipText(messages.getString("guimainmemu_msg16")); simulationSpeedInPercent.setMaximum(100); simulationSpeedInPercent.setMinimum(1); simulationSpeedInPercent.setValue(100 - Verbindung.holeVerzoegerungsFaktor()); simulationSpeedInPercent.setBounds(510, 10, 100, 44); simulationSpeedInPercent.setOpaque(false); simulationSpeedInPercent.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { updateLatency(); } }); updateLatency(); menupanel.setLayout(null); menupanel.add(btEntwurfsmodus); menupanel.add(btAktionsmodus); menupanel.add(btDokumodus); menupanel.add(btNeu); menupanel.add(btOeffnen); menupanel.add(btSpeichern); menupanel.add(simulationSpeedInPercent); menupanel.add(geschwindigkeit); if (isSoftwareWizardEnabled()) { menupanel.add(btWizard); } menupanel.add(btHilfe); menupanel.add(btInfo); menupanel.add(btSettings); } private void updateLatency() { Verbindung.setzeVerzoegerungsFaktor( simulationSpeedInPercent.getMaximum() - simulationSpeedInPercent.getValue() + 1); geschwindigkeit.setText("" + simulationSpeedInPercent.getValue() + "%"); } private void initCurrentFileOrDirSelection(FileDialog dialog) { String scenarioPath = SzenarioVerwaltung.getInstance().holePfad(); String lastOpenedDir = Information.getInformation().getLastOpenedDirectory(); File file = null; if (scenarioPath != null) { file = new File(scenarioPath); } if (null != file && file.exists()) { dialog.setFile(file.getAbsolutePath()); } else if (null != lastOpenedDir) { file = new File(lastOpenedDir); if (file.exists()) { dialog.setDirectory(file.getAbsolutePath()); } } } private boolean isSoftwareWizardEnabled() { return (null != ToolProvider.getSystemJavaCompiler() && Information.getInformation().getSoftwareWizardMode() != Information.FeatureMode.FORCE_DISABLE) || Information.getInformation().getSoftwareWizardMode() == Information.FeatureMode.FORCE_ENABLE; } public void changeSlider(int diff) { if (diff < 0 && simulationSpeedInPercent.getValue() + diff < 1) { simulationSpeedInPercent.setValue(1); } else if (diff > 0 && simulationSpeedInPercent.getValue() + diff > 10) { simulationSpeedInPercent.setValue(10); } else simulationSpeedInPercent.setValue(simulationSpeedInPercent.getValue() + diff); } public boolean doClick(String button) { // manually perform click event on a registered button if (button.equals("btAktionsmodus")) btAktionsmodus.doClick(); else if (button.equals("btEntwurfsmodus")) btEntwurfsmodus.doClick(); else if (button.equals("btDokumodus")) btDokumodus.doClick(); else if (button.equals("btOeffnen")) btOeffnen.doClick(); else if (button.equals("btSpeichern")) btSpeichern.doClick(); else if (button.equals("btNeu")) btNeu.doClick(); else if (button.equals("btWizard")) btWizard.doClick(); else if (button.equals("btHilfe")) btHilfe.doClick(); else if (button.equals("btInfo")) btInfo.doClick(); else if (button.equals("btSettings")) btSettings.doClick(); else return false; return true; } private void newScenario(ActionEvent e) { int entscheidung = JOptionPane.YES_OPTION; try { if (SzenarioVerwaltung.getInstance().istGeaendert()) { entscheidung = JOptionPane.showConfirmDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg9"), messages.getString("guimainmemu_msg10"), JOptionPane.YES_NO_OPTION); } else { entscheidung = JOptionPane.YES_OPTION; } } catch (Exception exc) { LOG.debug("", e); } if (entscheidung == JOptionPane.YES_OPTION) { GUIContainer.getGUIContainer().clearAllItems(); GUIContainer.getGUIContainer().setProperty(null); Information.getInformation().reset(); SzenarioVerwaltung.getInstance().reset(); } } private void loadScenario() { int entscheidung = JOptionPane.YES_OPTION; try { if (SzenarioVerwaltung.getInstance().istGeaendert()) { entscheidung = JOptionPane.showConfirmDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg9"), messages.getString("guimainmemu_msg10"), JOptionPane.YES_NO_OPTION); } else { entscheidung = JOptionPane.YES_OPTION; } } catch (Exception exc) { LOG.debug("", exc); } if (entscheidung == JOptionPane.YES_OPTION && GUIContainer.getGUIContainer().getActiveSite() != MODUS_AKTION) { FileDialog fileDialog = new FileDialog(JMainFrame.getJMainFrame(), messages.getString("main_msg3"), FileDialog.LOAD); FilenameFilter filiusFilenameFilter = (dir, name) -> name.endsWith(".fls"); fileDialog.setFilenameFilter(filiusFilenameFilter); initCurrentFileOrDirSelection(fileDialog); fileDialog.setVisible(true); String selectedFile = fileDialog.getFile(); if (selectedFile != null) { Information.getInformation().setLastOpenedDirectory(fileDialog.getDirectory()); try { Path file = Path.of(fileDialog.getDirectory(), selectedFile); Information.getInformation().reset(); SzenarioVerwaltung.getInstance().laden(file.toString(), GUIContainer.getGUIContainer().getKnotenItems(), GUIContainer.getGUIContainer().getCableItems(), GUIContainer.getGUIContainer().getDocuItems()); GUIContainer.getGUIContainer().setProperty(null); GUIContainer.getGUIContainer().updateViewport(); Thread.sleep(10); GUIContainer.getGUIContainer().updateCables(); } catch (FileNotFoundException e1) { LOG.debug("Selected File could not be found.", e1); } catch (Exception e2) { LOG.debug(e2.getMessage(), e2); } } } } private void saveScenario() { boolean erfolg; FileDialog fileDialog = new FileDialog(JMainFrame.getJMainFrame(), messages.getString("main_msg4"), FileDialog.SAVE); FilenameFilter filiusFilenameFilter = (dir, name) -> name.endsWith(".fls"); fileDialog.setFilenameFilter(filiusFilenameFilter); initCurrentFileOrDirSelection(fileDialog); fileDialog.setVisible(true); if (fileDialog.getFile() != null) { Information.getInformation().setLastOpenedDirectory(fileDialog.getDirectory()); String targetFilePath; boolean nameChanged = false; if (fileDialog.getFile().endsWith(".fls")) { targetFilePath = Path.of(fileDialog.getDirectory(), fileDialog.getFile()).toString(); } else { nameChanged = true; targetFilePath = Path.of(fileDialog.getDirectory(), fileDialog.getFile() + ".fls").toString(); } int entscheidung = JOptionPane.YES_OPTION; if (nameChanged && new File(targetFilePath).exists()) { entscheidung = JOptionPane.showConfirmDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg17"), messages.getString("guimainmemu_msg10"), JOptionPane.YES_NO_OPTION); } if (entscheidung == JOptionPane.YES_OPTION) { erfolg = SzenarioVerwaltung.getInstance().speichern(targetFilePath, GUIContainer.getGUIContainer().getKnotenItems(), GUIContainer.getGUIContainer().getCableItems(), GUIContainer.getGUIContainer().getDocuItems()); if (erfolg) { SzenarioVerwaltung.getInstance().setCurrentFile(targetFilePath, false); } else { JOptionPane.showMessageDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg11")); } } } } // set/reset cable highlight, i.e., make all cables normal coloured for // simulation // and possibly highlight in development view private void resetCableHighlighting(int mode) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIMainMenu), resetCableHL(" + mode + ")"); if (mode == MODUS_AKTION) { // change to simulation view: de-highlight // all cables for (GUIKabelItem cableItem : GUIContainer.getGUIContainer().getCableItems()) { cableItem.getDasKabel().setAktiv(false); } } else { // change to development view: possibly highlight a cable (only // for 'Vermittlungsrechner' configuration if (GUIContainer.getGUIContainer().getProperty() instanceof JVermittlungsrechnerKonfiguration) { ((JVermittlungsrechnerKonfiguration) GUIContainer.getGUIContainer().getProperty()).highlightConnCable(); } if (GUIContainer.getGUIContainer().getProperty() instanceof JGatewayConfiguration) { ((JGatewayConfiguration) GUIContainer.getGUIContainer().getProperty()).highlightConnCable(); } } } public synchronized void selectMode(int mode) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIMainMenu), selectMode(" + mode + ")"); if (mode == MODUS_ENTWURF) { resetCableHighlighting(mode); // de-highlight cables btEntwurfsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/entwurfsmodus_aktiv.png"))); btAktionsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/aktionsmodus.png"))); btDokumodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/dokumodus.png"))); GUIContainer.getGUIContainer().setActiveSite(MODUS_ENTWURF); stopSimulation(); btOeffnen.setEnabled(true); btNeu.setEnabled(true); btSpeichern.setEnabled(true); if (isSoftwareWizardEnabled()) { btWizard.setEnabled(true); } } else if (mode == MODUS_DOKUMENTATION) { resetCableHighlighting(mode); // de-highlight cables btEntwurfsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/entwurfsmodus.png"))); btAktionsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/aktionsmodus.png"))); btDokumodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/dokumodus_aktiv.png"))); GUIContainer.getGUIContainer().setActiveSite(MODUS_DOKUMENTATION); stopSimulation(); btOeffnen.setEnabled(true); btNeu.setEnabled(true); btSpeichern.setEnabled(true); if (isSoftwareWizardEnabled()) { btWizard.setEnabled(false); } } else if (mode == MODUS_AKTION && aktuellerModus != MODUS_AKTION) { // LOG.debug("\tMode: MODUS_AKTION"); resetCableHighlighting(mode); // de-highlight cables btEntwurfsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/entwurfsmodus.png"))); btAktionsmodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/aktionsmodus_aktiv.png"))); btDokumodus.setIcon(new ImageIcon(getClass().getResource("/gfx/allgemein/dokumodus.png"))); GUIContainer.getGUIContainer().setActiveSite(MODUS_AKTION); for (GUIKnotenItem knotenItem : GUIContainer.getGUIContainer().getKnotenItems()) { SystemSoftware system; system = knotenItem.getKnoten().getSystemSoftware(); system.starten(); } btOeffnen.setEnabled(false); btNeu.setEnabled(false); btSpeichern.setEnabled(false); if (isSoftwareWizardEnabled()) { btWizard.setEnabled(false); } geschwindigkeit.setEnabled(true); simulationSpeedInPercent.setEnabled(true); } aktuellerModus = mode; } private void stopSimulation() { for (GUIKnotenItem knotenItem : GUIContainer.getGUIContainer().getKnotenItems()) { SystemSoftware system; system = knotenItem.getKnoten().getSystemSoftware(); try { system.beenden(); } catch (Exception e) {} } } public JBackgroundPanel getMenupanel() { return menupanel; } public void setMenupanel(JBackgroundPanel menupanel) { this.menupanel = menupanel; } } filius-2.9.4+ds/src/main/java/filius/gui/HelpPanel.java000066400000000000000000000076241506574241500226730ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.Dimension; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.regex.Matcher; import javax.swing.JEditorPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.ResourceUtil; @SuppressWarnings("serial") public class HelpPanel extends ControlPanel implements I18n { private static Logger LOG = LoggerFactory.getLogger(HelpPanel.class); private JEditorPane epHtml; public HelpPanel() { super(ControlPanel.VERTICAL, 350); reInit(); minimieren(); } protected void initContents() { epHtml = new JEditorPane("text/html;charset=UTF-8", null); epHtml.setText(messages.getString("guihilfe_msg2")); epHtml.setEditable(false); epHtml.setMinimumSize(new Dimension(1, 1)); scrollPane.getViewport().removeAll(); scrollPane.getViewport().add(epHtml); } public void loadContext(int mode) { File file; switch (mode) { case GUIMainMenu.MODUS_AKTION: file = ResourceUtil.getResourceFile("hilfe/" + messages.getString("hilfedatei_simulation")); break; case GUIMainMenu.MODUS_ENTWURF: file = ResourceUtil.getResourceFile("hilfe/" + messages.getString("hilfedatei_entwurf")); break; case GUIMainMenu.MODUS_DOKUMENTATION: default: file = ResourceUtil.getResourceFile("hilfe/" + messages.getString("hilfedatei_documentation")); break; } LOG.debug("Help file: " + file.getAbsolutePath()); String gfxBaseUrl = "file:" + ResourceUtil.getResourceUrlEncodedPath("hilfe/gfx/"); try (BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { StringBuffer sb = new StringBuffer(); for (String line = reader.readLine(); line != null; line = reader.readLine()) { sb.append(line); } String newText = sb.toString(); String escapedGfxBaseUrl = Matcher.quoteReplacement(gfxBaseUrl); newText = newText.replaceAll("hilfe/gfx/", escapedGfxBaseUrl); epHtml.read(new java.io.StringReader(newText), null); epHtml.setCaretPosition(0); } catch (FileNotFoundException e) { LOG.warn("help contents could not be found", e); } catch (IOException e) { LOG.warn("error while reading help contents", e); } } } filius-2.9.4+ds/src/main/java/filius/gui/InfoDialog.java000066400000000000000000000152341506574241500230320ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.SystemColor; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; public class InfoDialog extends JDialog implements I18n { private static final long serialVersionUID = 1L; private JBackgroundPanel jContentPane = null; private JLabel version = null; private JLabel personen = null; private JLabel maintainer = null; private JLabel erlaeuterung = null; /** * @param owner */ public InfoDialog(Frame owner) { super(owner); this.setIconImage(owner.getIconImage()); initialize(); } /** * This method initializes this * * @return void */ private void initialize() { this.setSize(350, 580); setLocation((getToolkit().getScreenSize().width - getWidth()) / 2, (getToolkit().getScreenSize().height - getHeight()) / 2); this.setForeground(Color.blue); this.setTitle(messages.getString("infodialog_msg1")); this.setContentPane(getJContentPane()); } /** * This method initializes jContentPane * * @return javax.swing.JPanel */ private JPanel getJContentPane() { if (jContentPane == null) { GridBagConstraints gridBagConstraints3 = new GridBagConstraints(); gridBagConstraints3.insets = new Insets(10, 10, 10, 10); gridBagConstraints3.gridx = 0; gridBagConstraints3.gridy = 1; GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); gridBagConstraints2.insets = new Insets(10, 10, 10, 10); gridBagConstraints2.gridx = 0; gridBagConstraints2.gridy = 2; GridBagConstraints gridBagConstraints1 = new GridBagConstraints(); gridBagConstraints1.insets = new Insets(10, 10, 10, 10); gridBagConstraints1.gridx = 0; gridBagConstraints1.gridy = 3; GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.insets = new Insets(10, 10, 10, 10); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 4; erlaeuterung = new JLabel(); erlaeuterung.setText(messages.getString("infodialog_msg2")); erlaeuterung.setPreferredSize(new Dimension(270, 50)); erlaeuterung.setBackground(SystemColor.activeCaption); erlaeuterung.setForeground(Color.WHITE); erlaeuterung.setFont(new Font("Dialog", Font.PLAIN, 12)); erlaeuterung.setVerticalAlignment(SwingConstants.TOP); personen = new JLabel(); personen.setText("" + "" + messages.getString("infodialog_msg3") + "" + "

André Asschoff, " + "Johannes Bade, " + "Carsten Dittich, " + "Thomas Gerding, " + "Nadja Haßler, " + "Ernst Johannes Klebert, " + "Michell Weyer

" + "" + messages.getString("infodialog_msg4") + "" + "

Stefan Freischlad, " + "Peer Stechert

" + "" + messages.getString("infodialog_msg11") + "

Matthias Brauer, Christian Eibl, Christoph Irniger, Detlef Justen, Andreas B. Mundt, Patrice Treton, pyropeter

" + ""); personen.setVerticalAlignment(SwingConstants.TOP); personen.setForeground(Color.WHITE); personen.setFont(new Font("Dialog", Font.PLAIN, 12)); personen.setPreferredSize(new Dimension(270, 180)); maintainer = new JLabel(); maintainer.setText("" + "" + messages.getString("infodialog_msg5") + "" + "

" + "Stefan Freischlad
 

" + "

" + messages.getString("infodialog_msg8") + "
" + " https://www.lernsoftware-filius.de." + "

"); maintainer.setVerticalAlignment(SwingConstants.TOP); maintainer.setForeground(Color.WHITE); maintainer.setFont(new Font("Dialog", Font.PLAIN, 12)); maintainer.setPreferredSize(new Dimension(270, 130)); version = new JLabel(); version.setText("

" + messages.getString("infodialog_msg6") + " " + Information.getVersion() + "


" + messages.getString("infodialog_msg10") + "

"); version.setFont(new Font("Dialog", Font.PLAIN, 12)); version.setForeground(Color.WHITE); version.setVerticalAlignment(SwingConstants.TOP); version.setPreferredSize(new Dimension(270, 80)); jContentPane = new JBackgroundPanel(); jContentPane.setBackgroundImage("gfx/allgemein/info_hintergrund.png"); jContentPane.setLayout(new GridBagLayout()); jContentPane.setBackground(new Color(0.9f, 0.9f, 0.95f)); jContentPane.add(version, gridBagConstraints); jContentPane.add(maintainer, gridBagConstraints1); jContentPane.add(personen, gridBagConstraints2); jContentPane.add(erlaeuterung, gridBagConstraints3); } return jContentPane; } } filius-2.9.4+ds/src/main/java/filius/gui/JBackgroundPanel.java000066400000000000000000000056211506574241500241670ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ /* * JBackgroundPanel.java * * Created on 2. Mai 2006, 17:31 * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package filius.gui; import java.awt.Graphics; import java.awt.Insets; import javax.swing.ImageIcon; import javax.swing.JPanel; /** * * @author Johannes Bade */ public class JBackgroundPanel extends JPanel { private static final long serialVersionUID = 1L; private ImageIcon backgroundImage; private boolean repeatBG = true; /** * Setzt ein neues Hintergrundbild * * @author Johannes Bade * @param dateiname * String */ public void setBackgroundImage(String dateiname) { backgroundImage = new ImageIcon(getClass().getResource("/" + dateiname)); } /** * Immer wenn die Komponente gezeichnet wird, malt diese Methode auf der Komponentenflaeche ein vorher bestimmtes * Hintergrundbild * * @author Johannes Bade */ protected void paintComponent(Graphics g) { super.paintComponent(g); Insets ins = getInsets(); if (backgroundImage != null) { if (repeatBG) { for (int a = 0; a <= this.getHeight() / backgroundImage.getIconHeight(); a++) { for (int i = 0; i <= this.getWidth() / backgroundImage.getIconWidth(); i++) { backgroundImage.paintIcon(this, g, ins.left + i * backgroundImage.getIconWidth(), ins.top + a * backgroundImage.getIconHeight()); } } } else { backgroundImage.paintIcon(this, g, ins.left, ins.top); } } } } filius-2.9.4+ds/src/main/java/filius/gui/JExtendedTable.java000066400000000000000000000070471506574241500236440ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.Color; import java.awt.Component; import java.util.HashMap; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; public class JExtendedTable extends JTable { public class ColorTableCellRenderer implements TableCellRenderer { private HashMap cellColor = new HashMap(); @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel label = new JLabel(value != null ? value.toString() : ""); int key = ((row + 1) * 1000) + column; label.setOpaque(true); if (isSelected) { label.setBackground(Color.CYAN); } else { if (cellColor.containsKey(key)) { label.setBackground(cellColor.get(key)); } else { label.setBackground(Color.WHITE); } } return label; } public void setColor(int row, int column, Color color) { int key = ((row + 1) * 1000) + column; cellColor.put(key, color); } public void resetColor(int row, int column) { int key = ((row + 1) * 1000) + column; cellColor.remove(key); } public void resetColors() { cellColor.clear(); } } private static final long serialVersionUID = 1L; private boolean editable; protected Object parentGUI; public JExtendedTable(TableModel model, boolean editable) { super(model); setEditable(editable); for (int i = 0; i < getColumnCount(); i++) { this.getColumnModel().getColumn(i).setCellRenderer(new ColorTableCellRenderer()); } } public void setParentGUI(Object parent) { this.parentGUI = parent; } /** * @return the editable */ public boolean isEditable() { return editable; } /** * @param editable * the editable to set */ public void setEditable(boolean editable) { this.editable = editable; } public boolean isCellEditable(int row, int column) { return editable; } } filius-2.9.4+ds/src/main/java/filius/gui/JMainFrame.java000066400000000000000000000337361506574241500227770ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ /* * NewJFrame.java * * Created on 28. April 2006, 18:31 */ package filius.gui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.MouseInfo; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Observable; import java.util.Observer; import javax.swing.JFrame; import javax.swing.JTextField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.Main; import filius.gui.netzwerksicht.GUIKnotenItem; import filius.rahmenprogramm.SzenarioVerwaltung; public class JMainFrame extends javax.swing.JFrame implements WindowListener, Observer { private static Logger LOG = LoggerFactory.getLogger(JMainFrame.class); private static final long serialVersionUID = 1L; private static JMainFrame frame = null; /** Creates new form NewJFrame */ private JMainFrame() { this.addWindowListener(this); SzenarioVerwaltung.getInstance().addObserver(this); initComponents(); KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() { public boolean dispatchKeyEvent(KeyEvent e) { if (e.getID() == KeyEvent.KEY_PRESSED && !(e.getSource() instanceof JTextField)) { LOG.trace("KEY dispatcher:\n" + "\tkey:'" + e.getKeyCode() + "'\n" + "\tmodifier: '" + e.getModifiersEx() + "'\n" + "\tmodifierText: '" + KeyEvent.getModifiersExText(e.getModifiersEx()) + "'\n" + "\tkeyChar: '" + e.getKeyChar() + "'\n" + "\tsourceType: '" + e.getSource().getClass().getSimpleName() + "'\n"); /* ignore space bar pressing on buttons */ if ((e.getKeyChar() == KeyEvent.VK_SPACE) && (e.getSource().getClass().getSimpleName() == "JButton")) { return true; } /* delete item on deletion key press */ if ((e.getKeyChar() == KeyEvent.VK_DELETE) && (frame.isFocused()) && GUIContainer.getGUIContainer().getActiveSite() == GUIMainMenu.MODUS_ENTWURF) { if (GUIContainer.getGUIContainer().isMarkerVisible()) { // multiple items are selected List itemlist = GUIContainer.getGUIContainer().getKnotenItems(); JMarkerPanel auswahl = GUIContainer.getGUIContainer().getAuswahl(); GUIKnotenItem tempitem; int tx, ty, twidth, theight; LinkedList markedlist = new LinkedList(); ListIterator it = itemlist.listIterator(); while (it.hasNext()) { // Code adapted from GUIEvents.mausReleased tempitem = (GUIKnotenItem) it.next(); tx = tempitem.getImageLabel().getX(); twidth = tempitem.getImageLabel().getWidth(); ty = tempitem.getImageLabel().getY(); theight = tempitem.getImageLabel().getHeight(); int itemPosX = tx + twidth / 2; int itemPosY = ty + theight / 2; if (itemPosX >= auswahl.getX() && itemPosX <= auswahl.getX() + auswahl.getWidth() && itemPosY >= auswahl.getY() && itemPosY <= auswahl.getY() + auswahl.getHeight()) { markedlist.add(tempitem); } } // LOG.debug("selected elements for deletion (via key press):"); for (int i = 0; i < markedlist.size(); i++) { // LOG.debug("\t"+((GUIKnotenItem) // markedlist.get(i)).getKnoten().getName()); GUIEvents.getGUIEvents().itemLoeschen( ((GUIKnotenItem) markedlist.get(i)).getImageLabel(), ((GUIKnotenItem) markedlist.get(i))); } auswahl.setVisible(false); GUIContainer.getGUIContainer().hideMarker(); return true; } else if (GUIEvents.getGUIEvents().getActiveItem() != null) { // single item active GUIEvents.getGUIEvents().itemLoeschen( GUIEvents.getGUIEvents().getActiveItem().getImageLabel(), GUIEvents.getGUIEvents().getActiveItem()); return true; } } if ((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) == KeyEvent.CTRL_DOWN_MASK) { // CTRL key pressed // LOG.debug("KeyDispatcher: CTRL-Key pressed, waiting for additional key!"); switch (e.getKeyCode()) { case 78: // N (new) // LOG.debug("KeyDispatcher: CTRL+N recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btNeu"); return true; case 79: // O (open) // LOG.debug("KeyDispatcher: CTRL+O recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btOeffnen"); return true; case 83: // S (save file) // LOG.debug("KeyDispatcher: CTRL+S recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btSpeichern"); return true; case 68: // D (development mode) // LOG.debug("KeyDispatcher: CTRL+D recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btEntwurfsmodus"); return true; case 82: // R (run-time/simulation mode) // LOG.debug("KeyDispatcher: CTRL+R recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btAktionsmodus"); return true; case 37: // left arrow (slower simulation) // LOG.debug("KeyDispatcher: CTRL+left recognised"); GUIContainer.getGUIContainer().getMenu().changeSlider(-1); return true; case 39: // right arrow (faster simulation) // LOG.debug("KeyDispatcher: CTRL+right recognised"); GUIContainer.getGUIContainer().getMenu().changeSlider(1); return true; case 87: // W (wizard for new modules) // LOG.debug("KeyDispatcher: CTRL+W recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btWizard"); return true; case 72: // H (help) // LOG.debug("KeyDispatcher: CTRL+H recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btHilfe"); return true; case 73: // A (about dialog) // LOG.debug("KeyDispatcher: CTRL+I recognised"); GUIContainer.getGUIContainer().getMenu().doClick("btInfo"); return true; } } // ALT key pressed; only makes sense for cables! if ((e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) == KeyEvent.ALT_DOWN_MASK) { // key '1' (cable) if ((e.getKeyCode() == 49) && (GUIContainer.getGUIContainer().getActiveSite() == GUIMainMenu.MODUS_ENTWURF)) { // LOG.debug("KeyDispatcher: ALT+1 recognised"); switchCablePreview(); return true; } } if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { GUIEvents.getGUIEvents().resetAndHideCablePreview(); } } // KEY_PRESSED return false; } }); aktualisiere(); } public static JMainFrame getJMainFrame() { if (frame == null) { frame = new JMainFrame(); Image image = Toolkit.getDefaultToolkit().getImage(JMainFrame.class.getResource("/gfx/hardware/kabel.png")); frame.setIconImage(image); frame.setLayout(new BorderLayout(0, 0)); } return frame; } public void addToContentPane(Component comp, Object constraints) { if (comp != null) { getContentPane().add(comp, constraints); } } private void initComponents() { setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setDefaultBounds(); } private void setDefaultBounds() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int width = (int) Math.min(1020, 0.9 * screenSize.getWidth()); int height = (int) Math.min(700, 0.9 * screenSize.getHeight()); super.setBounds( new Rectangle(screenSize.width / 2 - (width / 2), screenSize.height / 2 - (height / 2), width, height)); } @Override public void setBounds(Rectangle newBounds) { boolean validBounds = false; for (GraphicsDevice screenDevice : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) { Rectangle bounds = screenDevice.getDefaultConfiguration().getBounds(); if ((newBounds.x >= bounds.x && newBounds.x + newBounds.width <= bounds.x + bounds.width) && (newBounds.y >= bounds.y && newBounds.y + newBounds.height <= bounds.y + bounds.height)) { validBounds = true; super.setBounds(newBounds); break; } } if (!validBounds) { setDefaultBounds(); } } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} /** * * Fragt ab, ob wirklich beendet werden soll, ausserdem wird der temp-Ordner geleert * */ public void windowClosing(WindowEvent e) { Main.beenden(); } public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} private void aktualisiere() { String dateipfad; int startIndex; dateipfad = SzenarioVerwaltung.getInstance().holePfad(); if (dateipfad != null) { startIndex = dateipfad.length() - 80; if (startIndex > 0) dateipfad = dateipfad.substring(startIndex); if (SzenarioVerwaltung.getInstance().istGeaendert()) dateipfad = dateipfad + "*"; setTitle("FILIUS - " + dateipfad); } else { setTitle("FILIUS"); } } public void update(Observable arg0, Object arg1) { aktualisiere(); } private void switchCablePreview() { if (GUIContainer.getGUIContainer().getKabelvorschau().isVisible()) { GUIEvents.getGUIEvents().resetAndHideCablePreview(); } else { int currentPosX = (int) (MouseInfo.getPointerInfo().getLocation().getX() - GUIContainer.getGUIContainer().getDesignpanel().getLocationOnScreen().getX()); int currentPosY = (int) (MouseInfo.getPointerInfo().getLocation().getY() - GUIContainer.getGUIContainer().getDesignpanel().getLocationOnScreen().getY()); GUIEvents.getGUIEvents().resetAndShowCablePreview(currentPosX, currentPosY); } } public void removeFromContentPane(Component comp) { if (comp != null) { this.getContentPane().remove(comp); } } } filius-2.9.4+ds/src/main/java/filius/gui/JMarkerPanel.java000066400000000000000000000040351506574241500233270ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; public class JMarkerPanel extends JBackgroundPanel { private static final long serialVersionUID = 1L; public boolean inBounds(int x, int y) { return x >= this.getX() && x <= this.getX() + this.getWidth() && y >= this.getY() && y <= this.getY() + this.getHeight(); } protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(new Color(0, 0, 0)); Graphics2D g2 = (Graphics2D) g; Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 1, new float[] { 2 }, 0); g2.setStroke(stroke); g2.drawRect(0, 0, this.getWidth() - 1, this.getHeight() - 1); } } filius-2.9.4+ds/src/main/java/filius/gui/LanguageSelectionDialog.java000066400000000000000000000300641506574241500255260ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.Dimension; import java.awt.Image; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Locale; import java.util.ResourceBundle; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import filius.Main; import filius.rahmenprogramm.IconMode; import filius.rahmenprogramm.Information; @SuppressWarnings("serial") public class LanguageSelectionDialog extends JDialog { private static final String FRANCAIS = "Français"; private static final String ENGLISH = "English"; private static final String DEUTSCH = "Deutsch"; private JLabel selectLanguageLabel = new JLabel(); private JComboBox languageSelection = new JComboBox<>(); private JLabel selectIconModeLabel = new JLabel(); private JComboBox iconModeSelection = new JComboBox<>(); private JCheckBox layerVisualization = new JCheckBox(); private JLabel restartHint = new JLabel(); private JButton confirm = new JButton(); private JButton confirmAndExit = new JButton(); private JButton cancel = new JButton(); private Box buttonBox = Box.createHorizontalBox(); private LanguageSelectionDialog(boolean restartMode) { super(restartMode ? JMainFrame.getJMainFrame() : null); this.setModal(true); Image image = Toolkit.getDefaultToolkit() .getImage(LanguageSelectionDialog.class.getResource("/gfx/hardware/kabel.png")); this.setIconImage(image); if (restartMode) { this.setTitle("Einstellungen / Settings / Paramètres"); } else { this.setTitle("Sprache / Language / Langue"); } this.setResizable(false); initialize(restartMode); initializeText(Information.getInformation().getLocaleOrDefault()); initializeSettings(); } private void initializeSettings() { languageSelection.setSelectedItem(localeToLanguage(Information.getInformation().getLocaleOrDefault())); if (Information.getInformation().getIconMode() == IconMode.DEFAULT) { iconModeSelection.setSelectedIndex(0); } else if (Information.getInformation().getIconMode() == IconMode.ENTERPRISE) { iconModeSelection.setSelectedIndex(1); } else { iconModeSelection.setSelectedIndex(2); } layerVisualization.setSelected(Information.getInformation().isLayerVisualization()); } private void initializeText(Locale locale) { ResourceBundle bundle = ResourceBundle.getBundle("filius.messages.MessagesBundle", locale); selectLanguageLabel.setText(bundle.getString("languageSelection_msg1")); int selectedIndex = languageSelection.getSelectedIndex(); languageSelection.removeAllItems(); languageSelection.addItem(DEUTSCH); languageSelection.addItem(ENGLISH); languageSelection.addItem(FRANCAIS); languageSelection.setSelectedIndex(selectedIndex); selectIconModeLabel.setText(bundle.getString("settings_msg1")); selectedIndex = iconModeSelection.getSelectedIndex(); iconModeSelection.removeAllItems(); ; iconModeSelection.addItem(bundle.getString("settings_msg2")); iconModeSelection.addItem(bundle.getString("settings_msg3")); iconModeSelection.addItem(bundle.getString("settings_msg4")); iconModeSelection.setSelectedIndex(selectedIndex); layerVisualization.setText(bundle.getString("settings_msg5")); restartHint.setText("" + bundle.getString("languageSelection_msg2") + ""); confirm.setText(bundle.getString("languageSelection_msg3")); confirmAndExit.setText(bundle.getString("languageSelection_msg4")); cancel.setText(bundle.getString("languageSelection_msg5")); buttonBox.updateUI(); } public static void selectLanguage(boolean restart) { LanguageSelectionDialog instance = new LanguageSelectionDialog(restart); instance.setVisible(true); } private void apply() { String selectedValue = (String) languageSelection.getSelectedItem(); Information.getInformation().setLocale(languageToLocale(selectedValue)); switch (iconModeSelection.getSelectedIndex()) { case 1: Information.getInformation().setIconMode(IconMode.ENTERPRISE); break; case 2: Information.getInformation().setIconMode(IconMode.SYMBOL); break; default: Information.getInformation().setIconMode(IconMode.DEFAULT); } Information.getInformation().setLayerVisualization(layerVisualization.isSelected()); } private Locale languageToLocale(String language) { Locale locale = Locale.UK; if (FRANCAIS.equals(language)) { locale = Locale.FRANCE; } else if (DEUTSCH.equals(language)) { locale = Locale.GERMANY; } return locale; } private String localeToLanguage(Locale locale) { String language; if (null == locale || Locale.UK.getCountry().equals(locale.getCountry())) { language = ENGLISH; } else if (Locale.FRANCE.getCountry().equals(locale.getCountry())) { language = FRANCAIS; } else { language = DEUTSCH; } return language; } private void initialize(boolean restartMode) { Box verticalBox = Box.createVerticalBox(); verticalBox.setPreferredSize(new Dimension(400, 320)); getContentPane().add(verticalBox); pack(); if (restartMode) { Rectangle mainFrameBounds = JMainFrame.getJMainFrame().getBounds(); setLocation(mainFrameBounds.x + mainFrameBounds.width / 2 - getWidth() / 2, mainFrameBounds.y + mainFrameBounds.height / 2 - getHeight() / 2); } else { setLocation((getToolkit().getScreenSize().width - getWidth()) / 2, (getToolkit().getScreenSize().height - getHeight()) / 2); } verticalBox.add(Box.createVerticalStrut(10)); Box languageLabelBox = Box.createHorizontalBox(); languageLabelBox.add(Box.createHorizontalStrut(10)); languageLabelBox.add(selectLanguageLabel); languageLabelBox.add(Box.createGlue()); verticalBox.add(languageLabelBox); verticalBox.add(Box.createVerticalStrut(10)); languageSelection.setMaximumSize(new Dimension(380, 30)); languageSelection.setPreferredSize(new Dimension(380, 30)); languageSelection.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { LanguageSelectionDialog.this .initializeText(languageToLocale((String) languageSelection.getSelectedItem())); } }); Box languageSelectBox = Box.createHorizontalBox(); languageSelectBox.add(Box.createHorizontalStrut(10)); languageSelectBox.add(languageSelection); languageSelectBox.add(Box.createGlue()); verticalBox.add(languageSelectBox); if (restartMode) { verticalBox.add(Box.createVerticalStrut(10)); Box iconModeLabelBox = Box.createHorizontalBox(); iconModeLabelBox.add(Box.createHorizontalStrut(10)); iconModeLabelBox.add(selectIconModeLabel); iconModeLabelBox.add(Box.createGlue()); verticalBox.add(iconModeLabelBox); verticalBox.add(Box.createVerticalStrut(10)); iconModeSelection.setMaximumSize(new Dimension(380, 30)); iconModeSelection.setPreferredSize(new Dimension(380, 30)); Box iconModeSelectBox = Box.createHorizontalBox(); iconModeSelectBox.add(Box.createHorizontalStrut(10)); iconModeSelectBox.add(iconModeSelection); iconModeSelectBox.add(Box.createGlue()); verticalBox.add(iconModeSelectBox); } if (restartMode) { verticalBox.add(Box.createVerticalStrut(10)); Box layerVisualizationBox = Box.createHorizontalBox(); layerVisualizationBox.add(Box.createHorizontalStrut(10)); layerVisualizationBox.add(layerVisualization); layerVisualizationBox.add(Box.createGlue()); verticalBox.add(layerVisualizationBox); verticalBox.add(Box.createVerticalStrut(10)); layerVisualization.setMaximumSize(new Dimension(380, 30)); layerVisualization.setPreferredSize(new Dimension(380, 30)); } if (restartMode) { verticalBox.add(Box.createVerticalStrut(10)); Box hintBox = Box.createHorizontalBox(); hintBox.add(Box.createHorizontalStrut(10)); // hintBox.setBorder(BorderFactory.createEtchedBorder()); restartHint.setPreferredSize(new Dimension(350, 60)); restartHint.setSize(new Dimension(350, 60)); restartHint.setMinimumSize(new Dimension(350, 60)); hintBox.add(restartHint); hintBox.add(Box.createGlue()); verticalBox.add(hintBox); verticalBox.add(Box.createVerticalStrut(10)); } else { verticalBox.add(Box.createVerticalStrut(90)); } // buttonBox.setAlignmentX(Box.RIGHT_ALIGNMENT); // buttonBox.setBorder(BorderFactory.createEtchedBorder()); buttonBox.setSize(new Dimension(390, 50)); buttonBox.setPreferredSize(new Dimension(390, 50)); buttonBox.setMinimumSize(new Dimension(390, 50)); if (restartMode) { cancel.setPreferredSize(new Dimension(120, 30)); cancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { LanguageSelectionDialog.this.setVisible(false); } }); buttonBox.add(cancel); buttonBox.add(Box.createHorizontalStrut(10)); confirmAndExit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { LanguageSelectionDialog.this.apply(); Main.beenden(); } }); buttonBox.add(confirmAndExit); } else { confirm.setPreferredSize(new Dimension(250, 30)); confirm.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { LanguageSelectionDialog.this.apply(); LanguageSelectionDialog.this.setVisible(false); } }); buttonBox.add(confirm); } verticalBox.add(Box.createVerticalGlue()); verticalBox.add(buttonBox); } } filius-2.9.4+ds/src/main/java/filius/gui/NatViewerControl.java000066400000000000000000000071711506574241500242650ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import filius.gui.anwendungssicht.NatViewer; import filius.hardware.knoten.Gateway; import filius.rahmenprogramm.I18n; import filius.software.system.SystemSoftware; /** Controller of viewer components for Network Address Translation Tables used by Routers. */ public class NatViewerControl implements I18n { private Map natViewer = new HashMap<>(); private Set visibleViewer = new HashSet<>(); private static NatViewerControl singleton; private NatViewerControl() {} public static NatViewerControl getInstance() { if (null == singleton) { singleton = new NatViewerControl(); } return singleton; } public void hideViewer() { visibleViewer.clear(); for (NatViewer viewer : natViewer.values()) { if (viewer.isVisible()) { visibleViewer.add(viewer.getGateway().getSystemSoftware()); } viewer.setVisible(false); } } public void showViewer(Gateway gw) { if (!natViewer.containsKey(gw.getSystemSoftware())) { NatViewer viewer = new NatViewer(gw); viewer.setLocation( JMainFrame.getJMainFrame().getX() + (JMainFrame.getJMainFrame().getWidth() - viewer.getWidth()) / 2, JMainFrame.getJMainFrame().getY() + (JMainFrame.getJMainFrame().getHeight() - viewer.getHeight()) / 2); gw.getSystemSoftware().addPropertyChangeListener(viewer); natViewer.put(gw.getSystemSoftware(), viewer); } natViewer.get(gw.getSystemSoftware()).setVisible(true); } public void removeViewer(SystemSoftware systemSoftware) { natViewer.remove(systemSoftware); } public void reshowViewer(Set activeSystemSoftware) { Set satToRemove = new HashSet<>(); for (SystemSoftware sysSoftawre : natViewer.keySet()) { if (!activeSystemSoftware.contains(sysSoftawre)) { satToRemove.add(sysSoftawre); } else if (visibleViewer.contains(sysSoftawre)) { natViewer.get(sysSoftawre).setVisible(true); } } for (SystemSoftware sysSoftware : satToRemove) { natViewer.remove(sysSoftware); } } } filius-2.9.4+ds/src/main/java/filius/gui/SatViewerControl.java000066400000000000000000000071551506574241500242740ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import filius.gui.anwendungssicht.SatViewer; import filius.hardware.knoten.Switch; import filius.rahmenprogramm.I18n; import filius.software.system.SystemSoftware; /** Controller of viewer components for Source Address Tables used by Switches. */ public class SatViewerControl implements I18n { private Map satViewer = new HashMap<>(); private Set visibleViewer = new HashSet<>(); private static SatViewerControl singleton; private SatViewerControl() {} public static SatViewerControl getInstance() { if (null == singleton) { singleton = new SatViewerControl(); } return singleton; } public void hideViewer() { visibleViewer.clear(); for (SatViewer viewer : satViewer.values()) { if (viewer.isVisible()) { visibleViewer.add(viewer.getSwitch().getSystemSoftware()); } viewer.setVisible(false); } } public void showViewer(Switch sw) { if (!satViewer.containsKey(sw.getSystemSoftware())) { SatViewer viewer = new SatViewer(sw); viewer.setLocation( JMainFrame.getJMainFrame().getX() + (JMainFrame.getJMainFrame().getWidth() - viewer.getWidth()) / 2, JMainFrame.getJMainFrame().getY() + (JMainFrame.getJMainFrame().getHeight() - viewer.getHeight()) / 2); sw.getSystemSoftware().addPropertyChangeListener(viewer); satViewer.put(sw.getSystemSoftware(), viewer); } satViewer.get(sw.getSystemSoftware()).setVisible(true); } public void removeViewer(SystemSoftware systemSoftware) { satViewer.remove(systemSoftware); } public void reshowViewer(Set activeSystemSoftware) { Set satToRemove = new HashSet<>(); for (SystemSoftware sysSoftawre : satViewer.keySet()) { if (!activeSystemSoftware.contains(sysSoftawre)) { satToRemove.add(sysSoftawre); } else if (visibleViewer.contains(sysSoftawre)) { satViewer.get(sysSoftawre).setVisible(true); } } for (SystemSoftware sysSoftware : satToRemove) { satViewer.remove(sysSoftware); } } } filius-2.9.4+ds/src/main/java/filius/gui/SplashScreen.java000066400000000000000000000047761506574241500234220ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Toolkit; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JWindow; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; /* Basiert auf SplashScreen von Tony Colston, JavaWorld.com, 11/17/00 */ public class SplashScreen extends JWindow implements I18n { /** * */ private static final long serialVersionUID = 1L; public SplashScreen(String filename, Frame f) { super(f); JLabel l = new JLabel(new ImageIcon(getClass().getResource("/" + filename))); getContentPane().add(l, BorderLayout.CENTER); JLabel info = new JLabel( " Version " + Information.getVersion() + ", " + messages.getString("splashscreen_msg1")); info.setForeground(Color.BLACK); info.setFont(new Font("Dialog", Font.PLAIN, 10)); getContentPane().add(info, BorderLayout.SOUTH); pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension labelSize = l.getPreferredSize(); setLocation(screenSize.width / 2 - (labelSize.width / 2), screenSize.height / 2 - (labelSize.height / 2)); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/000077500000000000000000000000001506574241500233535ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/DMTNFileChooser.java000066400000000000000000000253621506574241500271130ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Enumeration; import javax.swing.Box; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.tree.DefaultMutableTreeNode; import filius.gui.JMainFrame; import filius.rahmenprogramm.I18n; import filius.software.system.Betriebssystem; import filius.software.system.Datei; import filius.software.system.Dateisystem; public class DMTNFileChooser implements I18n { private JPanel pHaupt; private JList lVerzeichnisse; private DefaultMutableTreeNode aktuellerOrdner; private String aktuellerDateiname; private JButton btEbeneHoch; private JTextField tfDateiname; private JLabel lbDateiname, lbAktuellerOrdner; private JButton btAktion, btAbbrechen; private Betriebssystem betriebssystem; private int rueckgabe = 0; private JDialog dialog; public static final int OK = 1; public static final int CANCEL = 2; public DMTNFileChooser(Betriebssystem bs) { dialog = new JDialog(); dialog.setIconImage(JMainFrame.getJMainFrame().getIconImage()); this.betriebssystem = bs; this.aktuellerOrdner = betriebssystem.getDateisystem().getRoot(); pHaupt = new JPanel(new BorderLayout()); Box boxHaupt = Box.createVerticalBox(); Box tmpBox = Box.createHorizontalBox(); tmpBox.add(Box.createHorizontalStrut(5)); lbAktuellerOrdner = new JLabel(aktuellerOrdner.toString()); lbAktuellerOrdner.setHorizontalAlignment(JLabel.LEFT); boxHaupt.add(Box.createVerticalStrut(10)); tmpBox.add(lbAktuellerOrdner); tmpBox.add(Box.createHorizontalStrut(20)); btEbeneHoch = new JButton(messages.getString("dmtnfilechooser_msg1")); btEbeneHoch.setActionCommand("ebeneHoch"); btEbeneHoch.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(btEbeneHoch.getActionCommand())) { if (aktuellerOrdner.getParent() != null) { aktuellerOrdner = (DefaultMutableTreeNode) aktuellerOrdner.getParent(); ordnerInhaltAnzeigen(aktuellerOrdner); } } } }); tmpBox.add(Box.createHorizontalStrut(5)); tmpBox.add(btEbeneHoch); boxHaupt.add(Box.createVerticalStrut(10)); boxHaupt.add(tmpBox); DefaultListModel lmDateiListe = new DefaultListModel(); lVerzeichnisse = new JList(lmDateiListe); lVerzeichnisse.setFixedCellHeight(16); JScrollPane dateiListenScrollPane = new JScrollPane(lVerzeichnisse); boxHaupt.add(Box.createVerticalStrut(10)); boxHaupt.add(dateiListenScrollPane); lVerzeichnisse.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { /* Doppelklick (Linke Maustaste) */ if (e.getClickCount() == 2 && e.getButton() == 1) { int index = ((JList) e.getSource()).locationToIndex(e.getPoint()); DefaultListModel lm = (DefaultListModel) lVerzeichnisse.getModel(); int selektiert = selektierteZelle(index, e.getPoint()); if (selektiert > -1) { String[] teile = lm.getElementAt(index).toString().split(";"); if (teile.length > 0) { if (teile[0].equals("Ordner")) { DefaultMutableTreeNode ordnerNode = Dateisystem.verzeichnisKnoten(aktuellerOrdner, teile[1]); aktuellerOrdner = ordnerNode; ordnerInhaltAnzeigen(ordnerNode); } if (teile[0].equals("Datei")) { Datei datei = betriebssystem.getDateisystem().holeDatei(aktuellerOrdner, teile[1]); tfDateiname.setText(datei.getName()); } } } } } }); Box boxDateiname = Box.createHorizontalBox(); lbDateiname = new JLabel(messages.getString("dmtnfilechooser_msg2")); boxDateiname.add(lbDateiname); tfDateiname = new JTextField(); boxDateiname.add(Box.createHorizontalStrut(5)); boxDateiname.add(tfDateiname); boxHaupt.add(Box.createVerticalStrut(10)); boxHaupt.add(boxDateiname); Box boxButtons = Box.createHorizontalBox(); btAktion = new JButton(messages.getString("dmtnfilechooser_msg3")); btAbbrechen = new JButton(messages.getString("dmtnfilechooser_msg4")); btAbbrechen.setActionCommand("cancel"); boxButtons.add(Box.createHorizontalStrut(5)); boxButtons.add(btAktion); boxButtons.add(Box.createHorizontalStrut(5)); boxButtons.add(btAbbrechen); boxHaupt.add(Box.createVerticalStrut(10)); boxHaupt.add(boxButtons); boxHaupt.add(Box.createVerticalStrut(10)); btAbbrechen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(btAbbrechen.getActionCommand())) { rueckgabe = CANCEL; dialog.setVisible(false); dialog.dispose(); } } }); pHaupt.add(boxHaupt, BorderLayout.CENTER); dialog.setModal(true); dialog.setBounds(100, 100, 320, 240); dialog.add(pHaupt); } public int saveDialog() { dialog.setTitle(messages.getString("dmtnfilechooser_msg5")); btAktion.setText(messages.getString("dmtnfilechooser_msg6")); btAktion.setActionCommand("save"); btAktion.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(btAktion.getActionCommand())) { rueckgabe = OK; aktuellerDateiname = tfDateiname.getText(); dialog.setVisible(false); dialog.dispose(); } } }); ordnerInhaltAnzeigen(aktuellerOrdner); dialog.setVisible(true); return rueckgabe; } public int openDialog() { dialog.setTitle(messages.getString("dmtnfilechooser_msg7")); btAktion.setText(messages.getString("dmtnfilechooser_msg8")); btAktion.setActionCommand("open"); btAktion.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(btAktion.getActionCommand())) { rueckgabe = OK; aktuellerDateiname = tfDateiname.getText(); dialog.setVisible(false); dialog.dispose(); } } }); ordnerInhaltAnzeigen(aktuellerOrdner); dialog.setVisible(true); return rueckgabe; } public DefaultMutableTreeNode getAktuellerOrdner() { return aktuellerOrdner; } public void setAktuellerOrdner(DefaultMutableTreeNode aktuellerOrdner) { this.aktuellerOrdner = aktuellerOrdner; } /** * Fuegt den Inhalt einer DefaultMutableTreeNode in ListModel der dateiListe ein. Um im CellRenderer zwischen * Dateien und Ordnern unterscheiden zu koennen, wird der Typ (Datei/Ordner) gefolgt von einem Semicolon angegeben. * * @param node * Die DefaultMutableTreeNode deren Inhalt angezeigt werden soll. */ public void ordnerInhaltAnzeigen(DefaultMutableTreeNode node) { DefaultListModel lm = (DefaultListModel) lVerzeichnisse.getModel(); lm.clear(); lVerzeichnisse.setCellRenderer(new OrdnerInhaltListRenderer()); for (Enumeration e = node.children(); e.hasMoreElements();) { DefaultMutableTreeNode enode = (DefaultMutableTreeNode) e.nextElement(); if (enode.getUserObject().getClass().equals(Datei.class)) { Datei dat = (Datei) enode.getUserObject(); lm.addElement("Datei;" + dat.getName()); } else { lm.addElement("Ordner;" + enode.toString()); } } lbAktuellerOrdner.setText(messages.getString("dmtnfilechooser_msg11") + " " + aktuellerOrdner.toString()); } /** * Ueberprueft ob eine (per locationToIndex) ermittelte Zelle wirklich geklickt wurde Das ist noetig, weil im * leeren, unteren Teil der JList automatisch der unterste Index zurueckgegeben wird. */ public int selektierteZelle(int index, Point punkt) { int ergebnis = -1; if (lVerzeichnisse.indexToLocation(index) != null) { if (lVerzeichnisse.indexToLocation(index).getY() + lVerzeichnisse.getFixedCellHeight() > punkt.getY()) { ergebnis = index; } } return ergebnis; } public String getAktuellerDateiname() { return aktuellerDateiname; } public void setAktuellerDateiname(String aktuellerDateiname) { this.aktuellerDateiname = aktuellerDateiname; } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/DNSConfigTable.java000066400000000000000000000105371506574241500267460ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import static filius.software.dns.ResourceRecord.ADDRESS; import static filius.software.dns.ResourceRecord.MAIL_EXCHANGE; import static filius.software.dns.ResourceRecord.NAME_SERVER; import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; import javax.swing.table.TableModel; import filius.gui.JExtendedTable; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.dns.DNSServer; @SuppressWarnings("serial") public class DNSConfigTable extends JExtendedTable { private String typeID = null; public DNSConfigTable(TableModel model, boolean editable, String type) { super(model, editable); this.typeID = type; } public void editingStopped(ChangeEvent e) { TableCellEditor editor = getCellEditor(); if (editor != null) { String value = (String) editor.getCellEditorValue(); value = value.trim(); DNSServer dnsServer = (DNSServer) ((GUIApplicationDNSServerWindow) parentGUI).holeAnwendung(); boolean validChange; if (typeID != null && typeID.equals("A")) { if (editingColumn == 0) { validChange = EingabenUeberpruefung.isGueltig(value, EingabenUeberpruefung.musterDomain); if (validChange) { dnsServer.changeSingleEntry(editingRow, 0, ADDRESS, value); } } else { validChange = EingabenUeberpruefung.isGueltig(value, EingabenUeberpruefung.musterIpAdresse); if (validChange) { dnsServer.changeSingleEntry(editingRow, 3, ADDRESS, value); } } } else if (typeID != null && typeID.equals("MX")) { if (editingColumn == 0) { validChange = EingabenUeberpruefung.isGueltig(value, EingabenUeberpruefung.musterDomain); if (validChange) { dnsServer.changeSingleEntry(editingRow, 0, MAIL_EXCHANGE, value); } } else { validChange = EingabenUeberpruefung.isGueltig(value, EingabenUeberpruefung.musterDomain); if (validChange) { dnsServer.changeSingleEntry(editingRow, 3, MAIL_EXCHANGE, value); } } } else if (typeID != null && typeID.equals("NS")) { if (editingColumn == 0) { validChange = EingabenUeberpruefung.isGueltig(value, EingabenUeberpruefung.musterDomain); if (validChange) { dnsServer.changeSingleEntry(editingRow, 0, NAME_SERVER, value); } } else { validChange = EingabenUeberpruefung.isGueltig(value, EingabenUeberpruefung.musterDomain); if (validChange) { dnsServer.changeSingleEntry(editingRow, 3, NAME_SERVER, value); } } } removeEditor(); } } public String getType() { return this.typeID; } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationClientBausteinWindow.java000066400000000000000000000170461506574241500330600ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import filius.software.clientserver.ClientBaustein; /** *

* Diese Klasse stellt die Benutzungsoberflaeche fuer das Client-Programm einer einfachen Client-Server-Anwendung zur * Verfuegung. *

*

* Nachrichten von der Anwendung werden nach dem Beobachtermuster durch die Benachrichtigung der Beobachter angenommen * und verarbeitet. *

*/ public class GUIApplicationClientBausteinWindow extends GUIApplicationWindow { private static final long serialVersionUID = 1L; /** * Textfeld fuer die Ausgabe gesendeter und empfangener Nachrichten sowie fuer Fehlermeldungen */ private JTextArea taAusgabe; /** * Textfeld fuer die Adresse des Servers, zu dem die Verbindung hergestellt werden soll */ private JTextField tfServerAdresse; /** * Textfeld zur Angabe des TCP-Ports, auf dem der Server auf eingehende Nachrichten wartet */ private JTextField tfServerPort; /** Textbereich zur Eingabe der Nachrichten */ private JTextArea taSenden; /** Schaltflaeche zum initiieren des Verbindungsaufbaus */ private JButton btVerbinden; /** * Schaltflaeche zum Senden einer zuvor eingegebenen Nachricht */ private JButton btSenden; /** * Standard-Konstruktor, der automatisch zur Erzeugung der graphischen Benutzungsoberflaeche fuer diese Anwendung * aufgerufen wird. */ public GUIApplicationClientBausteinWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); initialisiereKomponenten(); } /** Methode zur Initialisierung der graphischen Komponenten */ private void initialisiereKomponenten() { JScrollPane scrollPane; Box hauptBox; Box hBox; JLabel label; hauptBox = Box.createVerticalBox(); hauptBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hauptBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); label = new JLabel(messages.getString("clientbaustein_msg1")); label.setPreferredSize(new Dimension(140, label.getHeight())); hBox.add(label); tfServerAdresse = new JTextField(); tfServerAdresse.setPreferredSize(new Dimension(100, 20)); hBox.add(tfServerAdresse); hauptBox.add(hBox); hauptBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); label = new JLabel(messages.getString("clientbaustein_msg2")); label.setPreferredSize(new Dimension(140, label.getHeight())); hBox.add(label); tfServerPort = new JTextField(); tfServerPort.setPreferredSize(new Dimension(100, 20)); hBox.add(tfServerPort); hauptBox.add(hBox); hauptBox.add(Box.createVerticalStrut(5)); btVerbinden = new JButton(messages.getString("clientbaustein_msg3")); btVerbinden.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("verbinden")) { ((ClientBaustein) holeAnwendung()).setZielIPAdresse(tfServerAdresse.getText()); ((ClientBaustein) holeAnwendung()).setZielPort(Integer.parseInt(tfServerPort.getText())); ((ClientBaustein) holeAnwendung()).verbinden(); } else { ((ClientBaustein) holeAnwendung()).trennen(); } aktualisieren(); } }); hauptBox.add(btVerbinden); hauptBox.add(Box.createVerticalStrut(5)); label = new JLabel(messages.getString("clientbaustein_msg4")); label.setPreferredSize(new Dimension(100, 20)); hauptBox.add(label); taSenden = new JTextArea(); scrollPane = new JScrollPane(taSenden); scrollPane.setPreferredSize(new Dimension(400, 50)); hauptBox.add(scrollPane); btSenden = new JButton(messages.getString("clientbaustein_msg5")); btSenden.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((ClientBaustein) holeAnwendung()).senden(taSenden.getText()); taSenden.setText(""); } }); hauptBox.add(btSenden); hauptBox.add(Box.createVerticalStrut(5)); taAusgabe = new JTextArea(); taAusgabe.setEditable(false); scrollPane = new JScrollPane(taAusgabe); scrollPane.setPreferredSize(new Dimension(400, 200)); hauptBox.add(scrollPane); hauptBox.add(Box.createVerticalStrut(5)); add(hauptBox, BorderLayout.CENTER); aktualisieren(); } /** * Methode zum aktualisieren der Komponenten der graphischen Benutzungsoberflaeche in Abhaengigkeit vom Zustand der * Anwendung */ private void aktualisieren() { ClientBaustein client; client = (ClientBaustein) holeAnwendung(); tfServerAdresse.setText(client.getZielIPAdresse()); tfServerPort.setText("" + client.getZielPort()); if (client.istVerbunden()) { btVerbinden.setText(messages.getString("clientbaustein_msg6")); btVerbinden.setActionCommand("trennen"); btSenden.setEnabled(true); tfServerAdresse.setEditable(false); tfServerPort.setEditable(false); } else { btVerbinden.setText(messages.getString("clientbaustein_msg3")); btVerbinden.setActionCommand("verbinden"); btSenden.setEnabled(false); tfServerAdresse.setEditable(true); tfServerPort.setEditable(true); } } /** * Diese Methode wird automatisch ausgefuehrt, wenn eine Nachricht an den Beobachter der Anwendung gesendet wird. * Der Parameter arg enthaelt die Nachricht, die von der Anwendung verschickt wurde. */ public void update(Observable o, Object arg) { if (arg != null) { this.taAusgabe.append(arg.toString() + "\n"); } aktualisieren(); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationDNSServerWindow.java000066400000000000000000000522051506574241500317560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.dns.DNSServer; import filius.software.dns.ResourceRecord; import filius.software.vermittlungsschicht.IP; @SuppressWarnings({ "serial", "deprecation" }) public class GUIApplicationDNSServerWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationDNSServerWindow.class); private JTextField aDomainField; private JTextField mxURLField; private JTextField aIpField; private JTextField mxMaildomainField; private JTextField nsDomainField; private JTextField nsDomainServerField; private JCheckBox recResolution; private JButton buttonStart; private DNSConfigTable aRecordsTable; private DNSConfigTable mxRecordsTable; private DNSConfigTable nsRecordsTable; public GUIApplicationDNSServerWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); initialisiereKomponenten(); aktualisieren(); } private void initialisiereKomponenten() { JPanel contentPane = new JPanel(new BorderLayout()); contentPane.add(createServerControlBox(), BorderLayout.NORTH); JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.addTab(messages.getString("dnsserver_msg2"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_netzwerk_klein.png")), createAPanel()); tabbedPane.addTab(messages.getString("dnsserver_msg3"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_netzwerk_klein.png")), createMXPanel()); tabbedPane.addTab(messages.getString("dnsserver_msg15"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_netzwerk_klein.png")), createNSPanel()); Box hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); hBox.add(tabbedPane); contentPane.add(hBox, BorderLayout.CENTER); add(contentPane, BorderLayout.CENTER); } private Box createServerControlBox() { Box hBox; buttonStart = new JButton(messages.getString("dnsserver_msg1")); buttonStart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (((DNSServer) holeAnwendung()).isAktiv()) { ((DNSServer) holeAnwendung()).setAktiv(false); } else { ((DNSServer) holeAnwendung()).setAktiv(true); } } }); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); hBox.add(buttonStart); recResolution = new JCheckBox(); recResolution.setText(messages.getString("dnsserver_msg20")); DNSServer dnsServer = (DNSServer) this.holeAnwendung(); recResolution.setSelected(dnsServer.isRecursiveResolutionEnabled()); recResolution.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JCheckBox checkBox = GUIApplicationDNSServerWindow.this.recResolution; boolean activated = checkBox.isSelected(); DNSServer dnsServer = (DNSServer) GUIApplicationDNSServerWindow.this.holeAnwendung(); dnsServer.setRecursiveResolutionEnabled(activated); checkBox.setSelected(dnsServer.isRecursiveResolutionEnabled()); } }); hBox.add(Box.createHorizontalStrut(20)); hBox.add(recResolution); return hBox; } private JPanel createAPanel() { Box vBox, hBox; DefaultTableModel tabellenModell; TableColumnModel tcm; JScrollPane scrollPane; JPanel aPanel = new JPanel(new BorderLayout()); vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel aDomainLabel = new JLabel(messages.getString("dnsserver_msg4")); aDomainLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(aDomainLabel); hBox.add(Box.createHorizontalStrut(5)); aDomainField = new JTextField(); aDomainField.setPreferredSize(new Dimension(275, 25)); aDomainField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkFQDN(aDomainField); } }); hBox.add(aDomainField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel aIpLabel = new JLabel(messages.getString("dnsserver_msg5")); aIpLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(aIpLabel); hBox.add(Box.createHorizontalStrut(5)); aIpField = new JTextField(this.holeAnwendung().getSystemSoftware().primaryIPAdresse()); aIpField.setPreferredSize(new Dimension(275, 25)); aIpField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkIP(aIpField); } }); hBox.add(aIpField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JButton aAddButton = new JButton(messages.getString("dnsserver_msg6")); aAddButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (checkFQDN(aDomainField) && checkIP(aIpField) && IP.ipCheck(aIpField.getText()) != null) { ((DNSServer) holeAnwendung()).hinzuRecord(aDomainField.getText(), ResourceRecord.ADDRESS, aIpField.getText()); aDomainField.setText(""); aIpField.setText(""); updateARecordsTable(); } else { GUIApplicationDNSServerWindow.this.showMessageDialog(messages.getString("dnsserver_msg21")); } } }); hBox.add(aAddButton); hBox.add(Box.createHorizontalStrut(5)); JButton buttonEntfernen = new JButton(messages.getString("dnsserver_msg7")); buttonEntfernen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int zeilenNummer = aRecordsTable.getSelectedRow(); if (zeilenNummer != -1) { String domainname = aRecordsTable.getValueAt(zeilenNummer, 0).toString(); ((DNSServer) holeAnwendung()).loescheResourceRecord(domainname, ResourceRecord.ADDRESS); LOG.debug("GUIApplicationDNSServerWindow: A-Eintrag " + domainname + " geloescht"); updateARecordsTable(); } } }); hBox.add(buttonEntfernen); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); tabellenModell = new DefaultTableModel(0, 2); aRecordsTable = new DNSConfigTable(tabellenModell, true, "A"); aRecordsTable.setParentGUI(this); aRecordsTable.setIntercellSpacing(new Dimension(5, 5)); aRecordsTable.setRowHeight(30); aRecordsTable.setShowGrid(false); aRecordsTable.setFillsViewportHeight(true); aRecordsTable.setBackground(Color.WHITE); aRecordsTable.setShowHorizontalLines(true); tcm = aRecordsTable.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("dnsserver_msg8")); tcm.getColumn(1).setHeaderValue(messages.getString("dnsserver_msg9")); scrollPane = new JScrollPane(aRecordsTable); vBox.add(scrollPane); aPanel.add(vBox, BorderLayout.CENTER); return aPanel; } private JPanel createMXPanel() { Box vBox, hBox; DefaultTableModel tabellenModell; TableColumnModel tcm; JScrollPane scrollPane; JPanel mxPanel = new JPanel(new BorderLayout()); vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel mxMaildomainLabel = new JLabel(messages.getString("dnsserver_msg10")); mxMaildomainLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(mxMaildomainLabel); hBox.add(Box.createHorizontalStrut(5)); mxMaildomainField = new JTextField(); mxMaildomainField.setPreferredSize(new Dimension(275, 25)); mxMaildomainField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkFQDN(mxMaildomainField); } }); hBox.add(mxMaildomainField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel mxURLLabel = new JLabel(messages.getString("dnsserver_msg11")); mxURLLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(mxURLLabel); hBox.add(Box.createHorizontalStrut(5)); mxURLField = new JTextField(); mxURLField.setPreferredSize(new Dimension(275, 25)); mxURLField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkFQDN(mxURLField); } }); hBox.add(mxURLField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JButton mxAddButton = new JButton(messages.getString("dnsserver_msg6")); mxAddButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (checkFQDN(mxMaildomainField) && checkFQDN(mxURLField)) { ((DNSServer) holeAnwendung()).hinzuRecord(mxMaildomainField.getText(), ResourceRecord.MAIL_EXCHANGE, mxURLField.getText()); mxMaildomainField.setText(""); mxURLField.setText(""); updateMXRecordsTable(); } else { GUIApplicationDNSServerWindow.this.showMessageDialog(messages.getString("dnsserver_msg23")); } } }); hBox.add(mxAddButton); JButton buttonMXEntfernen = new JButton(messages.getString("dnsserver_msg7")); buttonMXEntfernen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int zeilenNummer = mxRecordsTable.getSelectedRow(); if (zeilenNummer != -1) { String domainname = mxRecordsTable.getValueAt(zeilenNummer, 0).toString(); ((DNSServer) holeAnwendung()).loescheResourceRecord(domainname, ResourceRecord.MAIL_EXCHANGE); LOG.debug("GUIApplicationDNSServerWindow: MX-Eintrag " + domainname + " geloescht"); updateMXRecordsTable(); } } }); hBox.add(buttonMXEntfernen); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); tabellenModell = new DefaultTableModel(0, 2); mxRecordsTable = new DNSConfigTable(tabellenModell, true, "MX"); mxRecordsTable.setParentGUI(this); // tell the table who presents its // values, such that the back-end DNS // server can be found for adapting // resource entries mxRecordsTable.setIntercellSpacing(new Dimension(5, 5)); mxRecordsTable.setRowHeight(30); mxRecordsTable.setShowGrid(false); mxRecordsTable.setFillsViewportHeight(true); mxRecordsTable.setBackground(Color.WHITE); mxRecordsTable.setShowHorizontalLines(true); tcm = mxRecordsTable.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("dnsserver_msg12")); tcm.getColumn(1).setHeaderValue(messages.getString("dnsserver_msg13")); scrollPane = new JScrollPane(mxRecordsTable); vBox.add(scrollPane); mxPanel.add(vBox, BorderLayout.CENTER); return mxPanel; } private JPanel createNSPanel() { Box vBox, hBox; DefaultTableModel tabellenModell; TableColumnModel tcm; JScrollPane scrollPane; JPanel nsPanel = new JPanel(new BorderLayout()); vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel nsDomainLabel = new JLabel(messages.getString("dnsserver_msg16")); nsDomainLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(nsDomainLabel); hBox.add(Box.createHorizontalStrut(5)); nsDomainField = new JTextField(); nsDomainField.setPreferredSize(new Dimension(275, 25)); nsDomainField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkFQDN(nsDomainField); } }); hBox.add(nsDomainField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel nsDomainServerLabel = new JLabel(messages.getString("dnsserver_msg17")); nsDomainServerLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(nsDomainServerLabel); hBox.add(Box.createHorizontalStrut(5)); nsDomainServerField = new JTextField(); nsDomainServerField.setPreferredSize(new Dimension(275, 25)); nsDomainServerField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkFQDN(nsDomainServerField); } }); hBox.add(nsDomainServerField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JButton nsAddButton = new JButton(messages.getString("dnsserver_msg6")); nsAddButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (checkFQDN(nsDomainField) && checkFQDN(nsDomainServerField)) { ((DNSServer) holeAnwendung()).hinzuRecord(nsDomainField.getText(), ResourceRecord.NAME_SERVER, nsDomainServerField.getText()); nsDomainField.setText(""); nsDomainServerField.setText(""); updateNSRecordsTable(); } else { GUIApplicationDNSServerWindow.this.showMessageDialog(messages.getString("dnsserver_msg22")); } } }); hBox.add(nsAddButton); JButton nsRemoveButton = new JButton(messages.getString("dnsserver_msg7")); nsRemoveButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int zeilenNummer = nsRecordsTable.getSelectedRow(); if (zeilenNummer != -1) { String domainname = nsRecordsTable.getValueAt(zeilenNummer, 0).toString(); ((DNSServer) holeAnwendung()).loescheResourceRecord(domainname, ResourceRecord.NAME_SERVER); LOG.debug("GUIApplicationDNSServerWindow: NS-Eintrag " + domainname + " geloescht"); updateNSRecordsTable(); } } }); hBox.add(nsRemoveButton); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); tabellenModell = new DefaultTableModel(0, 2); nsRecordsTable = new DNSConfigTable(tabellenModell, true, "NS"); nsRecordsTable.setParentGUI(this); nsRecordsTable.setIntercellSpacing(new Dimension(5, 5)); nsRecordsTable.setRowHeight(30); nsRecordsTable.setShowGrid(false); nsRecordsTable.setFillsViewportHeight(true); nsRecordsTable.setBackground(Color.WHITE); nsRecordsTable.setShowHorizontalLines(true); tcm = nsRecordsTable.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("dnsserver_msg18")); tcm.getColumn(1).setHeaderValue(messages.getString("dnsserver_msg19")); scrollPane = new JScrollPane(nsRecordsTable); vBox.add(scrollPane); nsPanel.add(vBox, BorderLayout.CENTER); return nsPanel; } public void updateARecordsTable() { updateRecordsTable(aRecordsTable); } public void updateNSRecordsTable() { updateRecordsTable(nsRecordsTable); } public void updateMXRecordsTable() { updateRecordsTable(mxRecordsTable); } private synchronized void updateRecordsTable(DNSConfigTable table) { String type = table.getType(); List tempListe = ((DNSServer) holeAnwendung()).holeResourceRecords(); List recordsOfSelectedType = new ArrayList(); for (ResourceRecord rr : tempListe) { if (rr.getType().equals(type)) { recordsOfSelectedType.add(rr); } } ((DefaultTableModel) table.getModel()).setRowCount(recordsOfSelectedType.size()); int row = 0; for (ResourceRecord rr : recordsOfSelectedType) { table.setValueAt(rr.getDomainname(), row, 0); table.setValueAt(rr.getRdata(), row, 1); row++; } } /** * Funktion die während der Eingabe ueberprueft ob die bisherige Eingabe einen korrekten Wert darstellt. */ private boolean checkIP(JTextField feld) { feld.setText(feld.getText().trim()); boolean validAddress = EingabenUeberpruefung.isGueltig(feld.getText(), EingabenUeberpruefung.musterIpAdresse); if (validAddress) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); } return validAddress; } private boolean checkFQDN(JTextField feld) { feld.setText(feld.getText().trim()); boolean validFqdn = EingabenUeberpruefung.isGueltig(feld.getText(), EingabenUeberpruefung.musterDomain); if (validFqdn) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); } return validFqdn; } private void aktualisieren() { if (((DNSServer) holeAnwendung()).isAktiv()) { buttonStart.setText(messages.getString("dnsserver_msg14")); } else { buttonStart.setText(messages.getString("dnsserver_msg1")); } updateARecordsTable(); updateMXRecordsTable(); updateNSRecordsTable(); } public void update(Observable arg0, Object arg1) { if ("A".equals(arg1)) { updateARecordsTable(); } else if ("NS".equals(arg1)) { updateNSRecordsTable(); } else if ("MX".equals(arg1)) { updateMXRecordsTable(); } else if (arg1 != null) { aktualisieren(); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationEmailAnwendungWindow.java000066400000000000000000001057731506574241500330520ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Vector; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.JExtendedTable; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.ResourceUtil; import filius.software.email.Email; import filius.software.email.EmailAnwendung; import filius.software.email.EmailKonto; import filius.software.email.EmailUtils; /** * Applikationsfenster fr den Email-Client * * @author Thomas Gerding & Johannes Bade * */ public class GUIApplicationEmailAnwendungWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationEmailAnwendungWindow.class); private enum ListMode { INBOX, OUTBOX, UNKNOWN }; private static final long serialVersionUID = 1L; private JTabbedPane tabbedPane; private JPanel gesendetPanel, eingangPanel; private JScrollPane gesendetScroll, eingangScroll; private JEditorPane emailVorschau; private JButton buttonMailsAbholen, buttonMailVerfassen, buttonMailAntworten, buttonKonten, buttonEmailLoeschen; private JProgressBar progressBar; private Box middleBox; private DefaultTableModel posteingangModell = new DefaultTableModel(0, 2); private DefaultTableModel gesendeteModell = new DefaultTableModel(0, 2); private JTable posteingangTable, gesendeteTable; private JTextField tfName, tfEmailAdresse, tfPOP3Server, tfPOP3Port, tfSMTPServer, tfSMTPPort, tfBenutzername; private JPasswordField tfPasswort; private Email aktuelleMail = null; private int zeilenNummer; private int auswahlfuerloeschen; ListMode paa = ListMode.UNKNOWN; private JPanel progressPanel; private JPanel verfassenPanel; public GUIApplicationEmailAnwendungWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); ((EmailAnwendung) holeAnwendung()).holePOP3Client().hinzuBeobachter(this); initialisiereKomponenten(); laden(); posteingangAktualisieren(); gesendeteAktualisieren(); } private void initialisiereKomponenten() { tabbedPane = new JTabbedPane(); gesendetPanel = new JPanel(new BorderLayout()); eingangPanel = new JPanel(new BorderLayout()); Box gesendetBox = Box.createHorizontalBox(); gesendetBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); Box eingangBox = Box.createHorizontalBox(); eingangBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); Box vorschauBox = Box.createHorizontalBox(); vorschauBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); posteingangTable = new JExtendedTable(posteingangModell, false); TableColumnModel tcm = posteingangTable.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("emailanwendung_msg1")); tcm.getColumn(1).setHeaderValue(messages.getString("emailanwendung_msg2")); eingangScroll = new JScrollPane(posteingangTable); posteingangTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent lse) { zeilenNummer = posteingangTable.getSelectedRow(); auswahlfuerloeschen = zeilenNummer; paa = ListMode.INBOX; if (zeilenNummer != -1) { Email tmpEmail = (Email) ((EmailAnwendung) holeAnwendung()).getEmpfangeneNachrichten() .get(zeilenNummer); emailVorschau.setContentType("text/plain"); emailVorschau.setText(tmpEmail.getText()); aktuelleMail = tmpEmail; emailVorschau.updateUI(); } } }); eingangBox.add(eingangScroll); eingangPanel.add(eingangBox, BorderLayout.CENTER); gesendeteTable = new JExtendedTable(gesendeteModell, false); TableColumnModel tcmGesendet = gesendeteTable.getColumnModel(); tcmGesendet.getColumn(0).setHeaderValue(messages.getString("emailanwendung_msg3")); tcmGesendet.getColumn(1).setHeaderValue(messages.getString("emailanwendung_msg2")); gesendeteTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent lse) { zeilenNummer = gesendeteTable.getSelectedRow(); auswahlfuerloeschen = zeilenNummer; paa = ListMode.OUTBOX; if (zeilenNummer != -1) { Email tmpEmail = (Email) ((EmailAnwendung) holeAnwendung()).getGesendeteNachrichten() .get(zeilenNummer); emailVorschau.setContentType("text/plain"); emailVorschau.setText(tmpEmail.getText()); aktuelleMail = tmpEmail; emailVorschau.updateUI(); } } }); gesendetScroll = new JScrollPane(gesendeteTable); gesendetBox.add(gesendetScroll); gesendetPanel.add(gesendetBox, BorderLayout.CENTER); emailVorschau = new JEditorPane(); emailVorschau.setBackground(new Color(255, 255, 255)); emailVorschau.setContentType("text/html"); emailVorschau.setEditable(false); emailVorschau.setText("" + "" + "" + messages.getString("emailanwendung_msg4") + "!
" + ""); JScrollPane vorschauScrollPane = new JScrollPane(emailVorschau); vorschauScrollPane.setPreferredSize(new Dimension(300, 200)); vorschauBox.add(vorschauScrollPane); eingangPanel.add(vorschauBox, BorderLayout.SOUTH); tabbedPane.addTab(messages.getString("emailanwendung_msg5"), new ImageIcon(getClass().getResource("/gfx/desktop/email_ordner_posteingang.png")), eingangPanel); tabbedPane.addTab(messages.getString("emailanwendung_msg6"), new ImageIcon(getClass().getResource("/gfx/desktop/email_ordner_gesendet.png")), gesendetPanel); tabbedPane.setTabPlacement(JTabbedPane.LEFT); Box topBox = Box.createHorizontalBox(); ImageIcon image = new ImageIcon(getClass().getResource("/gfx/desktop/email_emails_abholen.png")); buttonMailsAbholen = new JButton(image); image = new ImageIcon(getClass().getResource("/gfx/desktop/email_emails_abholen.gif")); buttonMailsAbholen.setRolloverIcon(image); buttonMailsAbholen.setFocusPainted(false); buttonMailsAbholen.setActionCommand("Abholen"); buttonMailsAbholen.setToolTipText(messages.getString("emailanwendung_msg7")); topBox.add(buttonMailsAbholen); topBox.add(Box.createHorizontalStrut(5)); // Platz zw. urlFeld und // senden image = new ImageIcon(getClass().getResource("/gfx/desktop/email_email_verfassen.png")); buttonMailVerfassen = new JButton(image); /* Gif Animation fuer Hover Effekt */ image = new ImageIcon(getClass().getResource("/gfx/desktop/email_email_verfassen.gif")); buttonMailVerfassen.setRolloverIcon(image); buttonMailVerfassen.setFocusPainted(false); buttonMailVerfassen.setActionCommand("Verfassen"); buttonMailVerfassen.setToolTipText(messages.getString("emailanwendung_msg8")); /* ActionListener */ ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (arg0.getActionCommand() == buttonMailVerfassen.getActionCommand()) { emailVerfassen(null); } if (arg0.getActionCommand() == buttonMailAntworten.getActionCommand()) { emailVerfassen(aktuelleMail); } if (arg0.getActionCommand() == buttonMailsAbholen.getActionCommand()) { emailsAbholen(); } if (arg0.getActionCommand() == buttonKonten.getActionCommand()) { kontoVerwalten(); kontoAktualisieren(); } if (arg0.getActionCommand() == buttonEmailLoeschen.getActionCommand()) { emailLoeschen(auswahlfuerloeschen, paa); } } }; buttonMailVerfassen.addActionListener(al); buttonMailsAbholen.addActionListener(al); topBox.add(buttonMailVerfassen); topBox.add(Box.createHorizontalStrut(5)); image = new ImageIcon(getClass().getResource("/gfx/desktop/email_email_antworten.png")); buttonMailAntworten = new JButton(image); image = new ImageIcon(getClass().getResource("/gfx/desktop/email_email_antworten.gif")); buttonMailAntworten.setRolloverIcon(image); buttonMailAntworten.setFocusPainted(false); buttonMailAntworten.addActionListener(al); buttonMailAntworten.setActionCommand("antworten"); buttonMailAntworten.setToolTipText(messages.getString("emailanwendung_msg9")); topBox.add(buttonMailAntworten); topBox.add(Box.createHorizontalStrut(5)); image = new ImageIcon(getClass().getResource("/gfx/desktop/icon_emailloeschen.png")); buttonEmailLoeschen = new JButton(messages.getString("emailanwendung_msg43")); buttonEmailLoeschen.addActionListener(al); buttonEmailLoeschen.setActionCommand("loeschen"); buttonEmailLoeschen.setToolTipText(messages.getString("emailanwendung_msg10")); topBox.add(buttonEmailLoeschen); topBox.add(Box.createHorizontalStrut(5)); buttonKonten = new JButton(messages.getString("emailanwendung_msg44")); buttonKonten.addActionListener(al); buttonKonten.setActionCommand("konten"); buttonKonten.setToolTipText(messages.getString("emailanwendung_msg11")); topBox.add(buttonKonten); topBox.add(Box.createHorizontalStrut(5)); topBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JPanel contentPane = new JPanel(new BorderLayout()); contentPane.add(tabbedPane, BorderLayout.CENTER); contentPane.add(vorschauBox, BorderLayout.SOUTH); contentPane.add(topBox, BorderLayout.NORTH); add(contentPane, BorderLayout.CENTER); } /** * loescht eine ausgewaehlte und als Parameter uebergebene Email aus dem Posteingang oder dem Postausgang des * Client. Dazu dient der zweite Parameter als Abfrage. * * @return boolean */ public void emailLoeschen(int rowIndex, ListMode list) { if (ListMode.INBOX.equals(list)) { // dann loeschen aus dem Posteingang ((EmailAnwendung) holeAnwendung()).removeReceivedMail(posteingangTable.getSelectedRow()); zeilenNummer = zeilenNummer - 1; posteingangAktualisieren(); emailVorschau.setText(" "); emailVorschau.updateUI(); } else if (ListMode.OUTBOX.equals(list)) { // dann loeschen aus dem Postausgang ((EmailAnwendung) holeAnwendung()).removeSentMail(gesendeteTable.getSelectedRow()); zeilenNummer = zeilenNummer - 1; gesendeteAktualisieren(); emailVorschau.setText(" "); emailVorschau.updateUI(); } else { LOG.debug( "============================================GuiAppl. Emailloeschen: Email konnte nicht geloescht werden======================================="); } } // provide more sophisticated and 'real' layout for quoted text private String replyLayout(String text) { return "> " + text.replaceAll("\\n", "\n> "); } private void emailVerfassen(Email antwortAuf) { verfassenPanel = new JPanel(new BorderLayout()); /* Obere Box (Sende Button usw.) */ Box topBox = Box.createHorizontalBox(); JButton buttonSenden = new JButton(messages.getString("emailanwendung_msg13")); buttonSenden.setActionCommand("senden"); topBox.add(buttonSenden); topBox.add(Box.createHorizontalStrut(5)); JButton cancelButton = new JButton(messages.getString("emailanwendung_msg37")); cancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { GUIApplicationEmailAnwendungWindow.this.desktop.closeModularWindow(); } }); topBox.add(cancelButton); topBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); verfassenPanel.add(topBox, BorderLayout.NORTH); /* * Mittlere Box (enthält das Betreffs- & Nachrichten Feld sowie An,CC und BCC */ middleBox = Box.createVerticalBox(); Box absenderBox = Box.createHorizontalBox(); Vector kontenVector = new Vector(); for (EmailKonto konto : ((EmailAnwendung) holeAnwendung()).holeKontoListe().values()) { kontenVector.addElement(konto.getBenutzername()); } final JComboBox cbAbsender = new JComboBox(kontenVector); absenderBox.add(cbAbsender); absenderBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); middleBox.add(absenderBox); /* Box mit An Feld und dazugehörigem Label */ Box kleineBox = Box.createHorizontalBox(); JLabel anLabel = new JLabel(messages.getString("emailanwendung_msg14")); anLabel.setPreferredSize(new Dimension(120, 20)); kleineBox.add(anLabel); kleineBox.add(Box.createHorizontalStrut(5)); final JTextField anField = new JTextField(); anField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { mailPruefen(anField); } }); anField.setBorder(null); kleineBox.add(anField); middleBox.add(kleineBox); middleBox.add(Box.createVerticalStrut(3)); /* Box mit CC Feld und dazugehörigem Label */ kleineBox = Box.createHorizontalBox(); JLabel ccLabel = new JLabel(messages.getString("emailanwendung_msg15")); ccLabel.setPreferredSize(new Dimension(120, 20)); kleineBox.add(ccLabel); kleineBox.add(Box.createHorizontalStrut(5)); final JTextField ccField = new JTextField(); ccField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { mailPruefen(ccField); } }); ccField.setBorder(null); kleineBox.add(ccField); middleBox.add(kleineBox); middleBox.add(Box.createVerticalStrut(3)); /* Box mit CC Feld und dazugehörigem Label */ kleineBox = Box.createHorizontalBox(); JLabel bccLabel = new JLabel(messages.getString("emailanwendung_msg16")); bccLabel.setPreferredSize(new Dimension(120, 20)); kleineBox.add(bccLabel); kleineBox.add(Box.createHorizontalStrut(5)); final JTextField bccField = new JTextField(); bccField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { mailPruefen(bccField); } }); bccField.setBorder(null); kleineBox.add(bccField); middleBox.add(kleineBox); middleBox.add(Box.createVerticalStrut(10)); /* Box mit Betreffszeile und dazugehörigem Label */ kleineBox = Box.createHorizontalBox(); JLabel betreffLabel = new JLabel(messages.getString("emailanwendung_msg17")); betreffLabel.setPreferredSize(new Dimension(120, 20)); kleineBox.add(betreffLabel); kleineBox.add(Box.createHorizontalStrut(5)); final JTextField betreffszeile = new JTextField(); betreffszeile.setBorder(null); kleineBox.add(betreffszeile); middleBox.add(kleineBox); middleBox.add(Box.createVerticalStrut(5)); final JTextArea inhaltField = new JTextArea(); inhaltField.setPreferredSize(new Dimension(100, 300)); middleBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JScrollPane inhaltScrollPane = new JScrollPane(inhaltField); inhaltScrollPane.setPreferredSize(new Dimension(500, 150)); middleBox.add(inhaltScrollPane); verfassenPanel.add(middleBox); if (antwortAuf != null) { betreffszeile.setText(messages.getString("emailanwendung_msg18") + " " + antwortAuf.getBetreff()); inhaltField.setText("\n\n" + antwortAuf.getAbsender().toString() + " " + messages.getString("emailanwendung_msg19") + "\n" + replyLayout(antwortAuf.getText())); anField.setText(antwortAuf.getAbsender().toString()); } inhaltField.requestFocus(); inhaltField.grabFocus(); inhaltField.setCaretPosition(0); /* ActionListener fuer Senden Button */ ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent arg0) { Email mail; String kontoString; EmailKonto versendeKonto; boolean eingabeFehler = false; mail = new Email(); if (cbAbsender.getSelectedItem() == null) { String msgNoAccountAvailable = GUIApplicationEmailAnwendungWindow.messages .getString("emailanwendung_msg47"); GUIApplicationEmailAnwendungWindow.this.showMessageDialog(msgNoAccountAvailable); } else { kontoString = cbAbsender.getSelectedItem().toString(); versendeKonto = (EmailKonto) ((EmailAnwendung) holeAnwendung()).holeKontoListe().get(kontoString); mail.setAbsender(versendeKonto.getVorname() + (!versendeKonto.getNachname().isEmpty() ? (" " + versendeKonto.getNachname()) : "") + " <" + versendeKonto.getEmailAdresse() + ">"); if (!mailPruefen(anField)) { eingabeFehler = true; } else { mail.setEmpfaenger(EmailUtils.stringToAddressEntryList(anField.getText())); } if (!mailPruefen(ccField)) { eingabeFehler = true; } else { mail.setCc(EmailUtils.stringToAddressEntryList(ccField.getText())); } if (!mailPruefen(bccField)) { eingabeFehler = true; } else { mail.setBcc(EmailUtils.stringToAddressEntryList(bccField.getText())); } if (eingabeFehler) { showMessageDialog(messages.getString("emailanwendung_msg20")); } else if (mail.getEmpfaenger().size() == 0 && mail.getCc().size() == 0 && mail.getBcc().size() == 0) { showMessageDialog(messages.getString("emailanwendung_msg21")); } else { mail.setBetreff(betreffszeile.getText()); mail.setText(inhaltField.getText()); progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setIndeterminate(true); progressBar.setStringPainted(true); middleBox.add(progressBar); middleBox.invalidate(); middleBox.validate(); progressBar.setString(messages.getString("emailanwendung_msg22")); ((EmailAnwendung) holeAnwendung()).versendeEmail(versendeKonto.getSmtpserver(), mail, versendeKonto.getEmailAdresse()); tabbedPane.setSelectedIndex(1); } } } }; buttonSenden.addActionListener(al); desktop.showModularWindow(messages.getString("emailanwendung_msg12"), verfassenPanel); } public void emailsAbholen() { if (!((EmailAnwendung) holeAnwendung()).holeKontoListe().isEmpty()) { progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setIndeterminate(true); progressBar.setStringPainted(true); progressPanel = new JPanel(new BorderLayout()); progressPanel.add(progressBar, BorderLayout.CENTER); progressPanel.setPreferredSize(new Dimension(500, 40)); desktop.showModularWindow(messages.getString("emailanwendung_msg23"), progressPanel); for (EmailKonto aktuellesKonto : ((EmailAnwendung) holeAnwendung()).holeKontoListe().values()) { progressBar .setString(messages.getString("emailanwendung_msg24") + aktuellesKonto.getEmailAdresse() + ")"); ((EmailAnwendung) holeAnwendung()).emailsAbholenEmails(aktuellesKonto.getBenutzername(), aktuellesKonto.getPasswort(), aktuellesKonto.getPop3port(), aktuellesKonto.getPop3server()); tabbedPane.setSelectedIndex(0); } } } private void posteingangAktualisieren() { posteingangModell.setRowCount(0); List empfangeneNachrichten = ((EmailAnwendung) holeAnwendung()).getEmpfangeneNachrichten(); for (Email neueMail : empfangeneNachrichten) { Vector v = new Vector(); String absender; if (neueMail.getAbsender() == null) { absender = ""; } else if (neueMail.getAbsender().getName() == null) { absender = neueMail.getAbsender().getMailAddress(); } else { absender = neueMail.getAbsender().getName(); } v.add(absender); v.add(neueMail.getBetreff()); posteingangModell.addRow(v); } } public void gesendeteAktualisieren() { gesendeteModell.setRowCount(0); List gesendeteNachrichten = ((EmailAnwendung) holeAnwendung()).getGesendeteNachrichten(); for (Email neueMail : gesendeteNachrichten) { Vector v = new Vector(); v.add(neueMail.holeEmpfaengerListe()); v.add(neueMail.getBetreff()); gesendeteModell.addRow(v); } } private void kontoAktualisieren() { Map kontoListe = ((EmailAnwendung) holeAnwendung()).holeKontoListe(); for (EmailKonto konto : kontoListe.values()) { tfName.setText(konto.getVorname() + " " + konto.getNachname()); tfEmailAdresse.setText(konto.getEmailAdresse()); tfPOP3Server.setText(konto.getPop3server()); tfPOP3Port.setText(konto.getPop3port()); tfSMTPServer.setText(konto.getSmtpserver()); tfSMTPPort.setText(konto.getSmtpport()); tfBenutzername.setText(konto.getBenutzername()); tfPasswort.setText(konto.getPasswort()); } } private void kontoVerwalten() { JLabel label; JPanel panel; Box vBox, hBox; JButton button; JScrollPane scroller; vBox = Box.createVerticalBox(); /* Name */ label = new JLabel(messages.getString("emailanwendung_msg45")); label.setPreferredSize(new Dimension(150, 25)); tfName = new JTextField(); tfName.setPreferredSize(new Dimension(150, 25)); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfName); vBox.add(hBox); /* Email-Adresse */ label = new JLabel(messages.getString("emailanwendung_msg26")); label.setPreferredSize(new Dimension(150, 25)); tfEmailAdresse = new JTextField(); tfEmailAdresse.setPreferredSize(new Dimension(150, 25)); tfEmailAdresse.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { gueltigkeitPruefen(tfEmailAdresse, EingabenUeberpruefung.musterEmailAdresse); } }); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfEmailAdresse); vBox.add(hBox); /* POP3 Server */ label = new JLabel(messages.getString("emailanwendung_msg27")); label.setPreferredSize(new Dimension(150, 25)); tfPOP3Server = new JTextField(); tfPOP3Server.setPreferredSize(new Dimension(150, 25)); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfPOP3Server); vBox.add(hBox); /* POP3 Port */ label = new JLabel(messages.getString("emailanwendung_msg28")); label.setPreferredSize(new Dimension(150, 25)); tfPOP3Port = new JTextField(); tfPOP3Port.setPreferredSize(new Dimension(150, 25)); tfPOP3Port.setText("110"); tfPOP3Port.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { gueltigkeitPruefen(tfPOP3Port, EingabenUeberpruefung.musterPort); } }); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfPOP3Port); vBox.add(hBox); /* SMTP Server */ label = new JLabel(messages.getString("emailanwendung_msg29")); label.setPreferredSize(new Dimension(150, 25)); tfSMTPServer = new JTextField(); tfSMTPServer.setPreferredSize(new Dimension(150, 25)); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfSMTPServer); vBox.add(hBox); /* SMTP Port */ label = new JLabel(messages.getString("emailanwendung_msg30")); label.setPreferredSize(new Dimension(150, 25)); tfSMTPPort = new JTextField(); tfSMTPPort.setPreferredSize(new Dimension(150, 25)); tfSMTPPort.setText("25"); tfSMTPPort.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { gueltigkeitPruefen(tfSMTPPort, EingabenUeberpruefung.musterPort); } }); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfSMTPPort); vBox.add(hBox); /* Benutzername */ label = new JLabel(messages.getString("emailanwendung_msg31")); label.setPreferredSize(new Dimension(150, 25)); tfBenutzername = new JTextField(); tfBenutzername.setPreferredSize(new Dimension(150, 25)); tfBenutzername.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { gueltigkeitPruefen(tfBenutzername, EingabenUeberpruefung.musterEmailBenutzername); } }); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfBenutzername); vBox.add(hBox); /* Passwort */ label = new JLabel(messages.getString("emailanwendung_msg32")); label.setPreferredSize(new Dimension(150, 25)); tfPasswort = new JPasswordField(); tfPasswort.setPreferredSize(new Dimension(150, 25)); hBox = Box.createHorizontalBox(); hBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hBox.add(label); hBox.add(tfPasswort); vBox.add(hBox); hBox = Box.createHorizontalBox(); /* Erstellen-Button */ button = new JButton(messages.getString("emailanwendung_msg33")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (kontoSpeichern()) { GUIApplicationEmailAnwendungWindow.this.desktop.closeModularWindow(); } else { showMessageDialog(messages.getString("emailanwendung_msg46")); } } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(5)); button = new JButton(messages.getString("emailanwendung_msg37")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { GUIApplicationEmailAnwendungWindow.this.desktop.closeModularWindow(); } }); hBox.add(button); vBox.add(Box.createVerticalStrut(10)); vBox.add(hBox); panel = new JPanel(); panel.add(vBox); scroller = new JScrollPane(panel); scroller.setPreferredSize(new Dimension(340, 300)); desktop.showModularWindow(messages.getString("emailanwendung_msg25"), scroller); } private boolean kontoSpeichern() { String[] teilStrings; if (EingabenUeberpruefung.isGueltig(tfPOP3Port.getText(), EingabenUeberpruefung.musterPort) && EingabenUeberpruefung.isGueltig(tfSMTPPort.getText(), EingabenUeberpruefung.musterPort) && EingabenUeberpruefung.isGueltig(tfBenutzername.getText(), EingabenUeberpruefung.musterEmailBenutzername)) { EmailKonto konto = new EmailKonto(); if (tfName.getText().trim().equals("")) { konto.setVorname(""); konto.setNachname(""); } else { teilStrings = tfName.getText().split(" "); if (teilStrings.length == 1) { konto.setVorname(tfName.getText().trim()); } else if (teilStrings.length >= 2) { konto.setNachname(teilStrings[teilStrings.length - 1]); String tmp = ""; for (int i = 0; i < teilStrings.length - 1; i++) tmp += teilStrings[i] + " "; konto.setVorname(tmp.trim()); } } konto.setBenutzername(tfBenutzername.getText()); konto.setPasswort(new String(tfPasswort.getPassword())); konto.setPop3port(tfPOP3Port.getText()); konto.setPop3server(tfPOP3Server.getText()); konto.setSmtpport(tfSMTPPort.getText()); konto.setSmtpserver(tfSMTPServer.getText()); konto.setEmailAdresse(tfEmailAdresse.getText()); ((EmailAnwendung) holeAnwendung()).setzeKonto(konto); speichern(); return true; } else { return false; } } private void speichern() { ((EmailAnwendung) holeAnwendung()).speichern(); } private void laden() { ((EmailAnwendung) holeAnwendung()).laden(); } /** * Ueberprueft Eingabefelder auf Richtigkeit * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ public void gueltigkeitPruefen(JTextField feld, Pattern pruefRegel) { if (EingabenUeberpruefung.isGueltig(feld.getText(), pruefRegel)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); JTextField temp = new JTextField(); feld.setBorder(temp.getBorder()); } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setBorder(BorderFactory.createLineBorder(EingabenUeberpruefung.farbeFalsch, 1)); } } /** * Funktion die während der Eingabe überprüft ob die bisherige Eingabe einen korrekten Wert darstellt. * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ private boolean mailPruefen(JTextField feld) { String[] adressen; boolean fehler = false; if (!feld.getText().trim().equals("")) { adressen = feld.getText().split(","); for (int i = 0; i < adressen.length; i++) { if (!EingabenUeberpruefung.isGueltig(adressen[i].trim(), EingabenUeberpruefung.musterEmailAdresse)) { fehler = true; } } } if (!fehler) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); feld.setBorder(null); } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setBorder(BorderFactory.createLineBorder(EingabenUeberpruefung.farbeFalsch, 1)); } return !fehler; } public void update(Observable arg0, Object arg1) { posteingangAktualisieren(); gesendeteAktualisieren(); if (arg1 instanceof Exception) { showMessageDialog(((Exception) arg1).getMessage()); } if (arg1 == null || arg1.equals("") || arg1 instanceof Exception) { desktop.closeModularWindow(progressPanel); desktop.closeModularWindow(verfassenPanel); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationEmailServerWindow.java000066400000000000000000000361251506574241500323640ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.Observable; import java.util.Vector; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.CreateAccountException; import filius.gui.JExtendedTable; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.email.EmailKonto; import filius.software.email.EmailServer; /** * Diese Klasse stellt das Konfigurationsfenster fr den E-Mail Server dar. Damit knnen Konten auf dem Server angelegt * und entfernt werden. * * @author Thomas Gerding & Johannes Bade * */ public class GUIApplicationEmailServerWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationEmailServerWindow.class); /** * */ private static final long serialVersionUID = 1L; private JPanel listPanel, formPanel, logPanel; private JScrollPane scrolly, sPane; private JTabbedPane tabby; private JButton addButton, deleteButton, startStopButton; private JTextField benutzernameField, domainField; private JLabel benutzernameLabel, nameLabel, passwortLabel, domainLabel; private JTable kontenListenTabelle; private int markierteZeile; private JTextArea logArea; private JPasswordField passwortField; public GUIApplicationEmailServerWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); ((EmailServer) holeAnwendung()).kontenLaden(); initialisiereKomponenten(); aktualisiere(); } private void initialisiereKomponenten() { startStopButton = new JButton(messages.getString("emailserver_msg1")); startStopButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (startStopButton.getText().equals(messages.getString("emailserver_msg1"))) { ((EmailServer) holeAnwendung()).setAktiv(true); updateLog(messages.getString("emailserver_msg2")); } else { ((EmailServer) holeAnwendung()).setAktiv(false); updateLog(messages.getString("emailserver_msg3")); } aktualisiere(); } }); domainLabel = new JLabel(messages.getString("emailserver_msg4")); domainField = new JTextField(); domainField.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { ((EmailServer) holeAnwendung()).setMailDomain(domainField.getText()); aktualisiere(); } }); domainField.addFocusListener(new FocusListener() { public void focusLost(FocusEvent arg0) { ((EmailServer) holeAnwendung()).setMailDomain(domainField.getText()); aktualisiere(); } public void focusGained(FocusEvent arg0) {} }); domainField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { gueltigkeitPruefen(domainField, EingabenUeberpruefung.musterDomain); } }); Box obenBox = Box.createHorizontalBox(); obenBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); obenBox.add(startStopButton); obenBox.add(Box.createHorizontalStrut(10)); obenBox.add(domainLabel); obenBox.add(Box.createHorizontalStrut(3)); obenBox.add(domainField); obenBox.add(Box.createHorizontalStrut(3)); tabby = new JTabbedPane(); tabby.setTabPlacement(JTabbedPane.LEFT); /* * Dieser Button entfernt ein bestehendes Konto, wenn in der Sicherheitsabfrage YES ausgewaehlt wurde Danach * wird die Tabelle neu erstellt, damit die Aenderung auch sichtbar wird. */ deleteButton = new JButton(messages.getString("emailserver_msg8")); deleteButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { EmailKonto mailAccount = (EmailKonto) ((EmailServer) holeAnwendung()).getListeBenutzerkonten() .get(markierteZeile); int Auswahl = showOptionDialog( messages.getString("emailserver_msg11") + mailAccount.getBenutzername().toString() + messages.getString("emailserver_msg12"), messages.getString("emailserver_msg13"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); if (Auswahl == JOptionPane.YES_OPTION) { ((EmailServer) holeAnwendung()).kontoLoeschen(mailAccount); updatekontenListenTabelle(); } } }); DefaultTableModel kontenListenTabelleModell = new DefaultTableModel(0, 2); kontenListenTabelle = new JExtendedTable(kontenListenTabelleModell, false); kontenListenTabelle.setDragEnabled(false); kontenListenTabelle.setIntercellSpacing(new Dimension(5, 5)); kontenListenTabelle.setRowHeight(30); kontenListenTabelle.setShowGrid(false); kontenListenTabelle.setFillsViewportHeight(true); kontenListenTabelle.setBackground(Color.WHITE); kontenListenTabelle.setShowHorizontalLines(true); TableColumnModel tcm = kontenListenTabelle.getColumnModel(); tcm = kontenListenTabelle.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("emailserver_msg14")); tcm.getColumn(1).setHeaderValue(messages.getString("emailserver_msg15")); scrolly = new JScrollPane(kontenListenTabelle); scrolly.setPreferredSize(new Dimension(200, 300)); Box listenBox = Box.createHorizontalBox(); listenBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); listenBox.add(scrolly); Box buttonBox = Box.createHorizontalBox(); buttonBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); buttonBox.add(deleteButton); listPanel = new JPanel(new BorderLayout()); listPanel.add(listenBox, BorderLayout.CENTER); listPanel.add(buttonBox, BorderLayout.SOUTH); benutzernameLabel = new JLabel(messages.getString("emailserver_msg16")); benutzernameLabel.setPreferredSize(new Dimension(120, 25)); nameLabel = new JLabel(messages.getString("emailserver_msg17")); nameLabel.setPreferredSize(new Dimension(120, 25)); passwortLabel = new JLabel(messages.getString("emailserver_msg18")); passwortLabel.setPreferredSize(new Dimension(120, 25)); benutzernameField = new JTextField(); benutzernameField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { gueltigkeitPruefen(benutzernameField, EingabenUeberpruefung.musterEmailBenutzername); } }); passwortField = new JPasswordField(); Box benutzerBox = Box.createHorizontalBox(); benutzerBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); benutzerBox.add(benutzernameLabel); benutzerBox.add(Box.createHorizontalStrut(2)); benutzerBox.add(benutzernameField); benutzerBox.setPreferredSize(new Dimension(250, 30)); Box passwortBox = Box.createHorizontalBox(); passwortBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); passwortBox.add(passwortLabel); passwortBox.add(Box.createHorizontalStrut(2)); passwortBox.add(passwortField); passwortBox.setPreferredSize(new Dimension(250, 30)); Box addButtonBox = Box.createHorizontalBox(); addButton = new JButton(messages.getString("emailserver_msg19")); addButton.setPreferredSize(new Dimension(120, 25)); addButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { if (!benutzernameField.getText().equals("") && !(new String(passwortField.getPassword())).equals("")) { if (EingabenUeberpruefung.isGueltig(benutzernameField.getText(), EingabenUeberpruefung.musterKeineLeerzeichen)) { try { ((EmailServer) holeAnwendung()).benutzerHinzufuegen(benutzernameField.getText(), (new String(passwortField.getPassword())), messages.getString("emailserver_msg20"), messages.getString("emailserver_msg21")); showMessageDialog(messages.getString("emailserver_msg22") + " " + benutzernameField.getText() + " " + messages.getString("emailserver_msg23")); benutzernameField.setText(""); passwortField.setText(""); } catch (CreateAccountException e1) { LOG.debug("", e1); } } else { showMessageDialog(messages.getString("emailserver_msg24")); } } else { showMessageDialog(messages.getString("emailserver_msg25")); } } }); addButtonBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); addButtonBox.add(addButton); Box formBox = Box.createVerticalBox(); formBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); formBox.add(benutzerBox); formBox.add(passwortBox); formBox.add(addButtonBox); Box dummyBox = Box.createHorizontalBox(); dummyBox.setPreferredSize(new Dimension(1, 100)); formBox.add(dummyBox); formPanel = new JPanel(new BorderLayout()); formPanel.add(formBox, BorderLayout.NORTH); logArea = new JTextArea(); sPane = new JScrollPane(logArea); Box logBox = Box.createHorizontalBox(); logBox.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); logBox.add(sPane); logPanel = new JPanel(new BorderLayout()); logPanel.add(logBox, BorderLayout.CENTER); tabby.addTab(messages.getString("emailserver_msg26"), formPanel); tabby.addTab(messages.getString("emailserver_msg27"), listPanel); tabby.addTab(messages.getString("emailserver_msg28"), logPanel); JPanel contentPane = new JPanel(new BorderLayout()); contentPane.add(obenBox, BorderLayout.NORTH); contentPane.add(tabby, BorderLayout.CENTER); add(contentPane, BorderLayout.CENTER); } private void aktualisiere() { EmailServer server; server = (EmailServer) holeAnwendung(); if (server.isAktiv()) { startStopButton.setText(messages.getString("emailserver_msg29")); domainField.setEnabled(false); } else { startStopButton.setText(messages.getString("emailserver_msg1")); domainField.setEnabled(true); } domainField.setText(server.getMailDomain()); updatekontenListenTabelle(); } /** * Durch Beobachterprinzip ausgeloeste Funktion, die die Tabelle mit den Email Konten des Servers aktualisiert. * * @author Thomas Gerding & Johannes Bade * */ private void updatekontenListenTabelle() { DefaultTableModel tabellenModell = (DefaultTableModel) kontenListenTabelle.getModel(); tabellenModell.setRowCount(0); for (EmailKonto tmpKonto : ((EmailServer) holeAnwendung()).getListeBenutzerkonten()) { Vector v = new Vector(); v.add(tmpKonto.getBenutzername() + "@" + ((EmailServer) holeAnwendung()).getMailDomain()); v.add(Integer.toString(tmpKonto.getNachrichten().size())); tabellenModell.addRow(v); } kontenListenTabelle.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent lse) { markierteZeile = kontenListenTabelle.getSelectedRow(); } }); } /** * Ueberprueft Eingabefelder auf Richtigkeit * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ private void gueltigkeitPruefen(JTextField feld, Pattern pruefRegel) { if (EingabenUeberpruefung.isGueltig(feld.getText(), pruefRegel)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); JTextField temp = new JTextField(); feld.setBorder(temp.getBorder()); } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setBorder(BorderFactory.createLineBorder(EingabenUeberpruefung.farbeFalsch, 1)); } } public void update(Observable arg0, Object arg1) { updatekontenListenTabelle(); if (arg1 instanceof String) updateLog(arg1); } private void updateLog(Object arg1) { this.logArea.append(arg1.toString() + "\n"); LOG.debug("GUIApplicationWebServerWindow: update() aufgerufen."); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationFileExplorerWindow.java000066400000000000000000000545031506574241500325460ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FileDialog; import java.awt.Font; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Enumeration; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.ListModel; import javax.swing.event.MouseInputAdapter; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.JMainFrame; import filius.software.lokal.FileExplorer; import filius.software.system.Datei; import filius.software.system.Dateisystem; public class GUIApplicationFileExplorerWindow extends GUIApplicationWindow { private enum OpType { CUT_AND_PASTE, COPY_AND_PASTE } private static Logger LOG = LoggerFactory.getLogger(GUIApplicationFileExplorerWindow.class); private static final long serialVersionUID = 1L; private final ImageIcon dateiIcon = new ImageIcon(getClass().getResource("/gfx/desktop/datei.png")); private final ImageIcon ordnerIcon = new ImageIcon(getClass().getResource("/gfx/desktop/ordner.png")); private JPanel backPanel; private JTree tv; private DefaultMutableTreeNode aktuellerOrdner; private JList dateiListe; private DefaultMutableTreeNode selektierteNode; private DefaultMutableTreeNode zwischenAblageNode; private OpType lastSelectedPasteOperation; private JButton btImportieren; private String datei; private String pfad; public GUIApplicationFileExplorerWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); aktuellerOrdner = holeAnwendung().getSystemSoftware().getDateisystem().getRoot(); initialisiereKomponenten(); } private void initialisiereKomponenten() { backPanel = new JPanel(new BorderLayout()); tv = new JTree(aktuellerOrdner); tv.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tv.getLastSelectedPathComponent(); if (node != null) { aktuellerOrdner = node; ordnerInhaltAnzeigen(node); } } }); tv.setBounds(0, 0, 150, 100); tv.setCellRenderer(new GUITreeRenderer(dateiIcon, ordnerIcon)); Box buttonBox = Box.createHorizontalBox(); buttonBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); final JButton aktualisieren = new JButton(messages.getString("fileexplorer_msg1")); aktualisieren.setActionCommand("aktualisieren"); aktualisieren.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { aktualisieren(); } }); buttonBox.add(aktualisieren); buttonBox.add(Box.createHorizontalStrut(5)); btImportieren = new JButton(messages.getString("fileexplorer_msg2")); btImportieren.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fileImport(); } }); buttonBox.add(btImportieren); Box box = Box.createVerticalBox(); box.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); box.add(Box.createVerticalStrut(5)); box.add(buttonBox); JScrollPane scrollpane = new JScrollPane(tv); scrollpane.setPreferredSize(new Dimension(10, 350)); Box horBox = Box.createHorizontalBox(); horBox.add(scrollpane); horBox.setPreferredSize(new Dimension(180, 350)); ListModel lmDateiListe = new DefaultListModel<>(); dateiListe = new JList(lmDateiListe); dateiListe.setFixedCellHeight(16); JScrollPane dateiListenScrollPane = new JScrollPane(dateiListe); dateiListenScrollPane.setPreferredSize(new Dimension(10, 240)); horBox.add(dateiListenScrollPane); dateiListe.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == 3) { if (aktuellerOrdner != null) { int index = ((JList) e.getSource()).locationToIndex(e.getPoint()); ListModel lm = dateiListe.getModel(); int selektiert = selektierteZelle(index, e.getPoint()); JPopupMenu popmen = new JPopupMenu(); final JMenuItem miNeuerOrdner = new JMenuItem(messages.getString("fileexplorer_msg3")); miNeuerOrdner.setActionCommand("neuerordner"); final JMenuItem miLoeschen = new JMenuItem(messages.getString("fileexplorer_msg4")); miLoeschen.setActionCommand("loeschen"); final JMenuItem miAusschneiden = new JMenuItem(messages.getString("fileexplorer_msg5")); miAusschneiden.setActionCommand("ausschneiden"); final JMenuItem miKopieren = new JMenuItem(messages.getString("fileexplorer_msg6")); miKopieren.setActionCommand("kopieren"); final JMenuItem miEinfuegen = new JMenuItem(messages.getString("fileexplorer_msg7")); miEinfuegen.setActionCommand("einfuegen"); final JMenuItem miUmbenennen = new JMenuItem(messages.getString("fileexplorer_msg8")); miUmbenennen.setActionCommand("umbenennen"); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { /* Neuer Ordner */ if (e.getActionCommand().equals(miNeuerOrdner.getActionCommand())) { String ordnerName = JOptionPane .showInputDialog(GUIApplicationFileExplorerWindow.this, ""); if (!ordnerName.equals("")) { holeAnwendung().getSystemSoftware().getDateisystem() .erstelleVerzeichnis(aktuellerOrdner, ordnerName); aktualisieren(); } } /* Loeschen */ if (e.getActionCommand().equals(miLoeschen.getActionCommand())) { int loeschAbfrage = JOptionPane.showConfirmDialog( GUIApplicationFileExplorerWindow.this, messages.getString("fileexplorer_msg18"), messages.getString("fileexplorer_msg18"), JOptionPane.YES_NO_OPTION); if (loeschAbfrage == JOptionPane.YES_OPTION) { aktuellerOrdner.remove(selektierteNode); aktualisieren(); } } /* Ausschneiden */ if (e.getActionCommand().equals(miAusschneiden.getActionCommand())) { zwischenAblageNode = selektierteNode; lastSelectedPasteOperation = OpType.CUT_AND_PASTE; aktualisieren(); } /* Kopieren */ if (e.getActionCommand().equals(miKopieren.getActionCommand())) { zwischenAblageNode = selektierteNode; lastSelectedPasteOperation = OpType.COPY_AND_PASTE; aktualisieren(); } /* Einfuegen */ if (e.getActionCommand().equals(miEinfuegen.getActionCommand())) { if (zwischenAblageNode.isNodeDescendant(aktuellerOrdner)) { JOptionPane.showMessageDialog(GUIApplicationFileExplorerWindow.this, messages.getString("fileexplorer_msg19"), "", JOptionPane.ERROR_MESSAGE); } else { try { aktuellerOrdner.add(tiefesKopieren(zwischenAblageNode)); if (lastSelectedPasteOperation == OpType.CUT_AND_PASTE) { ((DefaultMutableTreeNode) zwischenAblageNode.getParent()) .remove(zwischenAblageNode); zwischenAblageNode = null; } } catch (ClassNotFoundException | IOException e1) { LOG.debug("Error inserting file/dir", e); } aktualisieren(); } } /* Umbenennen */ if (e.getActionCommand().equals(miUmbenennen.getActionCommand())) { String neuerName = JOptionPane.showInputDialog( GUIApplicationFileExplorerWindow.this, messages.getString("fileexplorer_msg9")); if (neuerName != null && !neuerName.trim().isEmpty()) { if (!holeAnwendung().getSystemSoftware().getDateisystem() .dateiVorhanden(aktuellerOrdner, neuerName)) { if (selektierteNode.getUserObject().getClass().equals(Datei.class)) { /* Datei umbenennen */ Datei dat = (Datei) selektierteNode.getUserObject(); dat.setName(neuerName); } else { /* Ordner umbenennen */ selektierteNode.setUserObject(neuerName); } aktualisieren(); } } } } }; miNeuerOrdner.addActionListener(al); miLoeschen.addActionListener(al); miAusschneiden.addActionListener(al); miKopieren.addActionListener(al); miEinfuegen.addActionListener(al); miUmbenennen.addActionListener(al); if (selektiert == -1) { popmen.add(miNeuerOrdner); if (zwischenAblageNode != null) { popmen.add(miEinfuegen); } } else { String[] teile = lm.getElementAt(index).toString().split(";"); if (teile.length > 0) { selektierteNode = Dateisystem.verzeichnisKnoten(aktuellerOrdner, teile[1]); dateiListe.setSelectedIndex(index); } popmen.add(miLoeschen); popmen.add(miAusschneiden); popmen.add(miKopieren); popmen.add(miUmbenennen); } GUIApplicationFileExplorerWindow.this.add(popmen); popmen.show(GUIApplicationFileExplorerWindow.this.dateiListe, e.getX(), e.getY()); } } } }); box.add(horBox); backPanel.add(box, BorderLayout.CENTER); ordnerInhaltAnzeigen(aktuellerOrdner); add(backPanel, BorderLayout.CENTER); } /** * Fuegt den Inhalt einer DefaultMutableTreeNode in ListModel der dateiListe ein. Um im CellRenderer zwischen * Dateien und Ordnern unterscheiden zu koennen, wird der Typ (Datei/Ordner) gefolgt von einem Semicolon angegeben. * * @param node * Die DefaultMutableTreeNode deren Inhalt angezeigt werden soll. */ public void ordnerInhaltAnzeigen(DefaultMutableTreeNode node) { DefaultListModel lm = (DefaultListModel) dateiListe.getModel(); lm.clear(); dateiListe.setCellRenderer(new OrdnerInhaltListRenderer()); for (Enumeration e = node.children(); e.hasMoreElements();) { DefaultMutableTreeNode enode = (DefaultMutableTreeNode) e.nextElement(); if (enode.getUserObject().getClass().equals(Datei.class)) { Datei dat = (Datei) enode.getUserObject(); lm.addElement(messages.getString("fileexplorer_msg10") + ";" + dat.getName()); } else { lm.addElement(messages.getString("fileexplorer_msg11") + ";" + enode.toString()); } } } /** * Ueberprueft ob eine (per locationToIndex) ermittelte Zelle wirklich geklickt wurde Das ist noetig, weil im * leeren, unteren Teil der JList automatisch der unterste Index zurueckgegeben wird. */ public int selektierteZelle(int index, Point punkt) { int ergebnis = -1; if (dateiListe.indexToLocation(index) != null) { if (dateiListe.indexToLocation(index).getY() + dateiListe.getFixedCellHeight() > punkt.getY()) { ergebnis = index; } } return ergebnis; } public void aktualisieren() { tv.updateUI(); ordnerInhaltAnzeigen(aktuellerOrdner); } /** * Da bei clone() nur das Objekt und nicht seine Referenzen kopiert werden, wird fuer DefaultMutableTreeNode Tiefes * Kopieren gebraucht, um z.B. bei einem Ordner die komplette eingeschlossene Struktur zu erhalten */ public DefaultMutableTreeNode tiefesKopieren(DefaultMutableTreeNode original) throws IOException, ClassNotFoundException { DefaultMutableTreeNode ergebnis = null; // ObjectOutputStream erzeugen ByteArrayOutputStream bufOutStream = new ByteArrayOutputStream(); ObjectOutputStream outStream = new ObjectOutputStream(bufOutStream); // Objekt im byte-Array speichern outStream.writeObject(original); outStream.close(); // Pufferinhalt abrufen byte[] buffer = bufOutStream.toByteArray(); // ObjectInputStream erzeugen ByteArrayInputStream bufInStream = new ByteArrayInputStream(buffer); ObjectInputStream inStream = new ObjectInputStream(bufInStream); // Objekt wieder auslesen ergebnis = (DefaultMutableTreeNode) inStream.readObject(); return ergebnis; } public void fileImport() { backPanel = new JPanel(new BorderLayout()); final JTextArea outputField = new JTextArea(""); outputField.setEditable(false); outputField.setBackground(backPanel.getBackground()); outputField.setFont(outputField.getFont().deriveFont(Font.BOLD)); outputField.setBorder(null); outputField.setPreferredSize(new Dimension(500, 30)); JLabel fileLabel = new JLabel(messages.getString("fileexplorer_msg13")); fileLabel.setPreferredSize(new Dimension(100, 30)); final JTextField inputField = new JTextField(""); inputField.setPreferredSize(new Dimension(200, 30)); inputField.setEditable(false); final JTextField renameField = new JTextField(""); renameField.setPreferredSize(new Dimension(200, 30)); JLabel renameLabel = new JLabel(messages.getString("fileexplorer_msg9")); renameLabel.setPreferredSize(new Dimension(100, 30)); JButton fileButton = new JButton(messages.getString("fileexplorer_msg14")); fileButton.setPreferredSize(new Dimension(150, 30)); fileButton.addMouseListener(new MouseInputAdapter() { @Override public void mousePressed(MouseEvent e) { FileDialog fileDialog = new FileDialog(JMainFrame.getJMainFrame(), messages.getString("main_msg3"), FileDialog.LOAD); fileDialog.setVisible(true); if (null != fileDialog.getFile()) { datei = fileDialog.getFile(); pfad = fileDialog.getDirectory(); if (!pfad.endsWith(System.getProperty("file.separator"))) pfad += System.getProperty("file.separator"); } if (datei != null) { inputField.setText(pfad + datei); renameField.setText(datei); } } }); JButton importButton = new JButton(messages.getString("fileexplorer_msg15")); importButton.setPreferredSize(new Dimension(150, 30)); importButton.addMouseListener(new MouseInputAdapter() { @Override public void mousePressed(MouseEvent z) { if (inputField.getText().equals("") || renameField.getText().equals("")) { outputField.setText(messages.getString("fileexplorer_msg16")); } else { if (aktuellerOrdner == null) { outputField.setText(messages.getString("fileexplorer_msg17")); } else { outputField.setText(((FileExplorer) holeAnwendung()).addFile(pfad, datei, aktuellerOrdner, renameField.getText())); aktualisieren(); } } } }); JButton closeButton = new JButton(messages.getString("sw_fileimporter_msg3")); closeButton.setPreferredSize(new Dimension(150, 30)); closeButton.addMouseListener(new MouseInputAdapter() { @Override public void mousePressed(MouseEvent z) { GUIApplicationFileExplorerWindow.this.desktop.closeModularWindow(); } }); Box fileSelectionBox = Box.createHorizontalBox(); fileSelectionBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); fileSelectionBox.add(fileLabel); fileSelectionBox.add(Box.createHorizontalStrut(5)); fileSelectionBox.add(inputField); fileSelectionBox.add(Box.createHorizontalStrut(5)); fileSelectionBox.add(fileButton); Box fileImportBox = Box.createHorizontalBox(); fileImportBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); fileImportBox.add(renameLabel); fileImportBox.add(Box.createHorizontalStrut(5)); fileImportBox.add(renameField); fileImportBox.add(Box.createHorizontalStrut(5)); fileImportBox.add(importButton); Box importResultBox = Box.createHorizontalBox(); importResultBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); importResultBox.add(outputField); Box upperBox = Box.createVerticalBox(); upperBox.add(fileSelectionBox); upperBox.add(fileImportBox); upperBox.add(importResultBox); backPanel.add(upperBox, BorderLayout.NORTH); Box lowerBox = Box.createVerticalBox(); lowerBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); lowerBox.add(Box.createVerticalStrut(5)); lowerBox.add(closeButton); lowerBox.add(Box.createVerticalStrut(5)); backPanel.add(lowerBox, BorderLayout.SOUTH); desktop.showModularWindow(messages.getString("fileexplorer_msg12"), backPanel); } @Override public void update(Observable arg0, Object arg1) { aktualisieren(); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationFirewallWindow.java000066400000000000000000000262371506574241500317160ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import java.util.Vector; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import filius.gui.JExtendedTable; import filius.software.firewall.Firewall; import filius.software.firewall.FirewallRule; /** * Applikationsfenster für die Firewall * * @author Johannes Bade & Thomas Gerding * */ @SuppressWarnings("serial") public class GUIApplicationFirewallWindow extends GUIApplicationWindow { private static final String ALL_PROT_STRING = "*"; private static final String UDP_STRING = "UDP"; private static final String TCP_STRING = "TCP"; private JTable tTabellePort; private JTextField tfPort; private JComboBox cbProtocol; private JComboBox cbAlleAbsender; private JTextArea log; // Log-Fenster private JCheckBox cbEinAus = new JCheckBox(); private JCheckBox cbIcmp = new JCheckBox(); private JCheckBox cbUdp = new JCheckBox(); private void initKomponenten() { JEditorPane text; JLabel label; JButton button; TableColumnModel columnModel; JPanel panel; JScrollPane scrollPane; Box boxFirewall; JTabbedPane tp; boxFirewall = Box.createVerticalBox(); boxFirewall.add(Box.createVerticalStrut(10)); JPanel globalBox = new JPanel(new GridBagLayout()); cbEinAus = new JCheckBox(messages.getString("firewall_msg1")); cbEinAus.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { ((Firewall) holeAnwendung()).setActivated(cbEinAus.isSelected()); updateAttribute(); } }); GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 0; constraints.fill = GridBagConstraints.HORIZONTAL; globalBox.add(cbEinAus, constraints); cbIcmp = new JCheckBox(messages.getString("firewall_msg13")); cbIcmp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { ((Firewall) holeAnwendung()).setDropICMP(!cbIcmp.isSelected()); updateAttribute(); } }); globalBox.add(cbIcmp, constraints); cbUdp = new JCheckBox(messages.getString("firewall_msg14")); cbUdp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { ((Firewall) holeAnwendung()).setFilterUdp(cbUdp.isSelected()); updateAttribute(); } }); globalBox.add(cbUdp, constraints); boxFirewall.add(globalBox); boxFirewall.add(Box.createVerticalStrut(10)); text = new JEditorPane(); text.setEditable(false); text.setText(messages.getString("firewall_msg2")); text.setBackground(boxFirewall.getBackground()); Box box = Box.createHorizontalBox(); box.add(Box.createHorizontalStrut(10)); label = new JLabel(messages.getString("firewall_msg3")); box.add(label); box.add(Box.createHorizontalStrut(10)); tfPort = new JTextField(); tfPort.setPreferredSize(new Dimension(30, 15)); box.add(tfPort); box.add(Box.createHorizontalStrut(10)); String[] protValues = { TCP_STRING, UDP_STRING, ALL_PROT_STRING }; cbProtocol = new JComboBox(protValues); box.add(cbProtocol); box.add(Box.createHorizontalStrut(10)); cbAlleAbsender = new JComboBox(); cbAlleAbsender.addItem(messages.getString("firewall_msg4")); cbAlleAbsender.addItem(messages.getString("firewall_msg5")); cbAlleAbsender.setSelectedIndex(0); box.add(cbAlleAbsender); box.add(Box.createHorizontalStrut(10)); boxFirewall.add(box); boxFirewall.add(Box.createVerticalStrut(10)); box = Box.createHorizontalBox(); box.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("firewall_msg6")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { hinzuRegel(); updateAttribute(); } }); box.add(button); box.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("firewall_msg7")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { loescheRegel(); updateAttribute(); } }); box.add(button); box.add(Box.createHorizontalStrut(10)); boxFirewall.add(box); boxFirewall.add(Box.createVerticalStrut(10)); DefaultTableModel tabellenModell = new DefaultTableModel(0, 3); tTabellePort = new JExtendedTable(tabellenModell, false); tTabellePort.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tTabellePort.setIntercellSpacing(new Dimension(10, 5)); tTabellePort.setRowHeight(30); tTabellePort.setShowGrid(false); tTabellePort.setFillsViewportHeight(true); tTabellePort.setBackground(Color.WHITE); tTabellePort.setShowHorizontalLines(true); columnModel = tTabellePort.getColumnModel(); columnModel.getColumn(0).setHeaderValue(messages.getString("jfirewalldialog_msg31")); columnModel.getColumn(0).setWidth(20); columnModel.getColumn(1).setHeaderValue(messages.getString("firewall_msg8")); columnModel.getColumn(1).setWidth(20); columnModel.getColumn(2).setHeaderValue(messages.getString("firewall_msg9")); columnModel.getColumn(2).setWidth(300); scrollPane = new JScrollPane(tTabellePort); scrollPane.setPreferredSize(new Dimension(90, 250)); boxFirewall.add(scrollPane); boxFirewall.add(Box.createVerticalStrut(10)); tp = new JTabbedPane(); tp.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { updateAttribute(); } }); panel = new JPanel(new BorderLayout()); panel.add(boxFirewall, BorderLayout.CENTER); tp.add(messages.getString("firewall_msg10"), panel); log = new JTextArea(); scrollPane = new JScrollPane(log); tp.add(messages.getString("firewall_msg11"), scrollPane); add(tp, BorderLayout.CENTER); } private void hinzuRegel() { try { int port = Integer.parseInt(tfPort.getText()); boolean unterscheideNetzwerk = (cbAlleAbsender.getSelectedIndex() == 1); short protocol = cbProtocol.getSelectedIndex() == 0 ? FirewallRule.TCP : cbProtocol.getSelectedIndex() == 1 ? FirewallRule.UDP : FirewallRule.ALL_PROTOCOLS; FirewallRule newRule = new FirewallRule("", "", "", "", port, protocol, FirewallRule.ACCEPT); if (unterscheideNetzwerk) { newRule.srcIP = FirewallRule.SAME_NETWORK; } ((Firewall) holeAnwendung()).addRule(newRule); tfPort.setText(""); log.append(messages.getString("sw_firewall_msg5") + " " + port + "/" + cbProtocol.getSelectedItem() + "\n"); } catch (Exception e) {} updateAttribute(); } private void loescheRegel() { if (tTabellePort.getSelectedRow() != -1) { ((Firewall) holeAnwendung()).deleteRule(tTabellePort.getSelectedRow()); log.append(messages.getString("sw_firewall_msg7") + "\n"); } updateAttribute(); } public GUIApplicationFirewallWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); initKomponenten(); updateAttribute(); } public void updateAttribute() { DefaultTableModel model; model = (DefaultTableModel) tTabellePort.getModel(); model.setRowCount(0); Vector ruleset = ((Firewall) holeAnwendung()).getRuleset(); for (int i = 0; i < ruleset.size(); i++) { model.addRow(ruleToVector(ruleset.get(i))); } cbEinAus.setSelected(((Firewall) holeAnwendung()).isActivated()); cbIcmp.setSelected(!((Firewall) holeAnwendung()).getDropICMP()); cbIcmp.setEnabled(cbEinAus.isSelected()); cbUdp.setSelected(((Firewall) holeAnwendung()).getFilterUdp()); cbUdp.setEnabled(cbEinAus.isSelected()); } private Vector ruleToVector(FirewallRule rule) { Vector resultVec = new Vector(); if (rule.protocol == FirewallRule.TCP) { resultVec.addElement(TCP_STRING); } else if (rule.protocol == FirewallRule.UDP) { resultVec.addElement(UDP_STRING); } else { resultVec.addElement(ALL_PROT_STRING); } if (rule.port >= 0) { resultVec.addElement(Integer.toString(rule.port)); } else { resultVec.addElement(""); } if (rule.srcIP.equals(FirewallRule.SAME_NETWORK)) { resultVec.addElement(messages.getString("firewall_msg12")); } else { resultVec.addElement(messages.getString("firewall_msg4")); } return resultVec; } public void update(Observable arg0, Object nachricht) { log.append(nachricht + "\n"); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationImageViewerWindow.java000066400000000000000000000074101506574241500323450ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.Information; import filius.software.system.Betriebssystem; import filius.software.system.Datei; @SuppressWarnings("serial") public class GUIApplicationImageViewerWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationImageViewerWindow.class); private JPanel backPanel; public GUIApplicationImageViewerWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); backPanel = new JPanel(new BorderLayout()); JButton open = new JButton(messages.getString("imageviewer_msg2")); open.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { oeffnen(); } }); backPanel.add(open, BorderLayout.NORTH); add(backPanel, BorderLayout.CENTER); } public void oeffnen() { DMTNFileChooser fc; int rueckgabe; Datei aktuelleDatei; String path; ImageIcon image; fc = new DMTNFileChooser((Betriebssystem) holeAnwendung().getSystemSoftware()); rueckgabe = fc.openDialog(); if (rueckgabe == DMTNFileChooser.OK) { aktuelleDatei = holeAnwendung().getSystemSoftware().getDateisystem().holeDatei(fc.getAktuellerOrdner(), fc.getAktuellerDateiname()); if (aktuelleDatei != null) { this.setTitle(aktuelleDatei.getName()); Base64.decodeToFile(aktuelleDatei.getDateiInhalt(), Information.getInformation().getTempPfad() + aktuelleDatei.getName()); path = Information.getInformation().getTempPfad() + aktuelleDatei.getName(); image = new ImageIcon(path); JLabel titelgrafik = new JLabel(image); backPanel.add(titelgrafik, BorderLayout.CENTER); backPanel.updateUI(); } else { LOG.debug("Fehler beim oeffnen einer Datei: keine Datei ausgewaehlt"); } } else { LOG.debug("Fehler beim oeffnen einer Datei"); } } public void update(Observable arg0, Object arg1) { } } GUIApplicationPeerToPeerAnwendungWindow.java000066400000000000000000000463371506574241500337560ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.List; import java.util.Observable; import java.util.StringTokenizer; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.InternalFrameEvent; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.dateiaustausch.PeerToPeerAnwendung; import filius.software.system.Datei; /** * Diese Klasse ist die grafische Benutzungsoberflaeche fuer die Peer2Peer Anwendung. * * @author Johannes Bade & Thomas Gerding */ @SuppressWarnings("serial") public class GUIApplicationPeerToPeerAnwendungWindow extends GUIApplicationWindow { private boolean pruefungOK = true; private boolean zahlOK = true; private JPanel mainPanel; private JPanel networkPanel; private JPanel searchPanel; private JLabel networkIpLabel; private JButton connectNetworkButton; private JButton quitNetworkButton; private JTextField networkIpField; private JLabel searchLabel; private JButton searchButton, downloadButton, stopSearchButton, emptyListButton; private JTextField searchField; private JTable netzwerkTabelle, ergebnisTabelle, dateiTabelle; private JPanel filePanel; private JLabel filePanelLabel; private JLabel maxClientsLabel; private JTextField maxClientsField; private JPanel configPanel; private JLabel tabHead; public GUIApplicationPeerToPeerAnwendungWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); JTabbedPane tabbedPane = new JTabbedPane(); mainPanel = new JPanel(new BorderLayout()); networkPanel = new JPanel(new BorderLayout()); networkIpLabel = new JLabel(messages.getString("peertopeeranwendung_msg1")); connectNetworkButton = new JButton(messages.getString("peertopeeranwendung_msg2")); connectNetworkButton.setToolTipText(messages.getString("peertopeeranwendung_msg3")); quitNetworkButton = new JButton(messages.getString("peertopeeranwendung_msg30")); quitNetworkButton.setToolTipText(messages.getString("peertopeeranwendung_msg31")); tabHead = new JLabel(messages.getString("peertopeeranwendung_msg4")); networkIpField = new JTextField("192.168.0.1"); networkIpField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ipPruefen(networkIpField); } }); connectNetworkButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { if (pruefungOK == true) { ((PeerToPeerAnwendung) holeAnwendung()).beitretenNetzwerk(networkIpField.getText()); } else { JOptionPane.showMessageDialog(desktop, messages.getString("peertopeeranwendung_msg5")); } } }); quitNetworkButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { ((PeerToPeerAnwendung) holeAnwendung()).resetNetwork(); } }); DefaultTableModel neuesTabellenModell = new DefaultTableModel(0, 2); netzwerkTabelle = new JTable(neuesTabellenModell); // Add a mouse listener to the table netzwerkTabelle.setEnabled(false); netzwerkTabelle.setIntercellSpacing(new Dimension(10, 10)); netzwerkTabelle.setRowHeight(30); netzwerkTabelle.setShowGrid(false); netzwerkTabelle.setFillsViewportHeight(true); netzwerkTabelle.setBackground(Color.WHITE); netzwerkTabelle.setShowHorizontalLines(true); TableColumnModel tcm = netzwerkTabelle.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("peertopeeranwendung_msg6")); tcm.getColumn(0).setPreferredWidth(20); tcm.getColumn(1).setHeaderValue(messages.getString("peertopeeranwendung_msg7")); tcm.getColumn(1).setPreferredWidth(300); JScrollPane tabellenScrollPane = new JScrollPane(netzwerkTabelle); neuesTabellenModell = new DefaultTableModel(0, 3); ergebnisTabelle = new JTable(neuesTabellenModell); ergebnisTabelle.setDragEnabled(false); ergebnisTabelle.setIntercellSpacing(new Dimension(10, 10)); ergebnisTabelle.setRowHeight(30); ergebnisTabelle.setShowGrid(false); ergebnisTabelle.setFillsViewportHeight(true); ergebnisTabelle.setBackground(Color.WHITE); ergebnisTabelle.setShowHorizontalLines(true); ergebnisTabelle.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tcm = ergebnisTabelle.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("peertopeeranwendung_msg8")); tcm.getColumn(0).setPreferredWidth(200); tcm.getColumn(1).setHeaderValue(messages.getString("peertopeeranwendung_msg9")); tcm.getColumn(1).setPreferredWidth(200); tcm.getColumn(2).setHeaderValue(messages.getString("peertopeeranwendung_msg10")); tcm.getColumn(2).setPreferredWidth(50); JScrollPane tabellenScrollPaneErgebnis = new JScrollPane(ergebnisTabelle); Box verticalTopBox = Box.createVerticalBox(); Box topBox = Box.createHorizontalBox(); topBox.add(networkIpLabel); topBox.add(Box.createHorizontalStrut(5)); topBox.add(networkIpField); topBox.add(Box.createHorizontalStrut(15)); topBox.add(connectNetworkButton); topBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); Box labelBox = Box.createHorizontalBox(); labelBox.add(tabHead); verticalTopBox.add(topBox); verticalTopBox.add(Box.createVerticalStrut(5)); verticalTopBox.add(tabHead); networkPanel.add(verticalTopBox, BorderLayout.NORTH); networkPanel.add(tabellenScrollPane, BorderLayout.CENTER); networkPanel.add(quitNetworkButton, BorderLayout.SOUTH); tabbedPane.addTab(messages.getString("peertopeeranwendung_msg29"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_netzwerk_klein.png")), networkPanel); searchPanel = new JPanel(new BorderLayout()); searchLabel = new JLabel(messages.getString("peertopeeranwendung_msg11")); searchField = new JTextField(); searchField.setToolTipText(messages.getString("peertopeeranwendung_msg12")); searchButton = new JButton(messages.getString("peertopeeranwendung_msg13")); searchButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { ((PeerToPeerAnwendung) holeAnwendung()).sucheDatei(searchField.getText()); } }); stopSearchButton = new JButton(messages.getString("peertopeeranwendung_msg14")); stopSearchButton.setToolTipText(messages.getString("peertopeeranwendung_msg15")); stopSearchButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { { ((PeerToPeerAnwendung) holeAnwendung()).abbrechenSuche(); } } }); emptyListButton = new JButton(messages.getString("peertopeeranwendung_msg16")); emptyListButton.setToolTipText(messages.getString("peertopeeranwendung_msg17")); emptyListButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { { ((PeerToPeerAnwendung) holeAnwendung()).loescheSuchergebnisse(); updateErgebnisTabelle(); } } }); downloadButton = new JButton(messages.getString("peertopeeranwendung_msg18")); downloadButton.setToolTipText(messages.getString("peertopeeranwendung_msg19")); downloadButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { { int zeilenNummer = ergebnisTabelle.getSelectedRow(); if (zeilenNummer > -1) { ((PeerToPeerAnwendung) holeAnwendung()).herunterladenDatei(zeilenNummer); } } } }); Box searchBox = Box.createHorizontalBox(); searchBox.add(searchLabel); searchBox.add(Box.createHorizontalStrut(5)); searchBox.add(searchField); searchBox.add(Box.createHorizontalStrut(5)); searchBox.add(searchButton); searchBox.add(Box.createHorizontalStrut(2)); searchBox.add(stopSearchButton); searchBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); Box downloadBox = Box.createHorizontalBox(); downloadBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); downloadBox.add(downloadButton); downloadBox.add(Box.createHorizontalStrut(5)); downloadBox.add(emptyListButton); searchPanel.add(searchBox, BorderLayout.NORTH); searchPanel.add(tabellenScrollPaneErgebnis, BorderLayout.CENTER); searchPanel.add(downloadBox, BorderLayout.SOUTH); tabbedPane.addTab(messages.getString("peertopeeranwendung_msg20"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_suchen_klein.png")), searchPanel); filePanel = new JPanel(new BorderLayout()); filePanelLabel = new JLabel(messages.getString("peertopeeranwendung_msg21")); DefaultTableModel dateiTabellenModell = new DefaultTableModel(0, 2); dateiTabelle = new JTable(dateiTabellenModell); dateiTabelle.setDragEnabled(false); dateiTabelle.setEnabled(false); dateiTabelle.setIntercellSpacing(new Dimension(10, 10)); dateiTabelle.setRowHeight(30); dateiTabelle.setShowGrid(false); dateiTabelle.setFillsViewportHeight(true); dateiTabelle.setBackground(Color.WHITE); dateiTabelle.setShowHorizontalLines(true); tcm = dateiTabelle.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("peertopeeranwendung_msg22")); tcm.getColumn(0).setPreferredWidth(300); tcm.getColumn(1).setHeaderValue(messages.getString("peertopeeranwendung_msg23")); tcm.getColumn(1).setPreferredWidth(50); JScrollPane dateiScrollPane = new JScrollPane(dateiTabelle); this.updateDateiTabelle(); Box dateiBox = Box.createHorizontalBox(); dateiBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); dateiBox.add(filePanelLabel); filePanel.add(dateiBox, BorderLayout.NORTH); filePanel.add(dateiScrollPane, BorderLayout.CENTER); tabbedPane.addTab(messages.getString("peertopeeranwendung_msg24"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_dateien_klein.png")), filePanel); configPanel = new JPanel(new BorderLayout()); maxClientsLabel = new JLabel(messages.getString("peertopeeranwendung_msg25")); maxClientsField = new JTextField("" + ((PeerToPeerAnwendung) holeAnwendung()).getMaxTeilnehmerZahl()); maxClientsField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { zahlPruefen(maxClientsField); } }); JButton maxClientButton = new JButton(messages.getString("peertopeeranwendung_msg26")); maxClientButton.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { { if (zahlOK == true) { ((PeerToPeerAnwendung) holeAnwendung()) .setMaxTeilnehmerZahl(Integer.parseInt(maxClientsField.getText())); } else { JOptionPane.showMessageDialog(desktop, messages.getString("peertopeeranwendung_msg27")); } } } }); Box configBox = Box.createHorizontalBox(); configBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); configBox.add(maxClientsLabel); configBox.add(Box.createHorizontalStrut(10)); configBox.add(maxClientsField); configBox.add(Box.createHorizontalStrut(10)); configBox.add(maxClientButton); configPanel.add(configBox, BorderLayout.NORTH); tabbedPane.addTab(messages.getString("peertopeeranwendung_msg28"), new ImageIcon(getClass().getResource("/gfx/desktop/peertopeer_einstellungen.png")), configPanel); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { updateDateiTabelle(); updateErgebnisTabelle(); updateNetzwerkTabelle(); } }); mainPanel.add(tabbedPane, BorderLayout.CENTER); add(mainPanel, BorderLayout.CENTER); updateNetzwerkTabelle(); updateErgebnisTabelle(); updateDateiTabelle(); } /** * Diese Funktion erstellt die Tabelle mit den von der P2P Anwendung "verwalteten" Dateien zur Verfuegung. Sie wird * �ber das Beobachtermuster ausgeloest. * * @author Thomas Gerding & Johannes Bade * */ public void updateDateiTabelle() { DefaultTableModel tabellenModell = (DefaultTableModel) dateiTabelle.getModel(); tabellenModell.setRowCount(0); List tempListe = holeAnwendung().getSystemSoftware().getDateisystem() .holeDateien(((PeerToPeerAnwendung) holeAnwendung()).holeVerzeichnis()); for (Datei tmpDatei : tempListe) { Vector v = new Vector<>(); v.add(tmpDatei.getName()); v.add(tmpDatei.getDateiTyp()); tabellenModell.addRow(v); } } /** * Diese Funktion erstellt die Tabelle mit den bekannten Peer2Peer Teilnehmern. Sie wird ueber das Beobachtermuster * ausgeloest. * * @author Thomas Gerding & Johannes Bade * */ public void updateNetzwerkTabelle() { DefaultTableModel tabellenModell = (DefaultTableModel) netzwerkTabelle.getModel(); tabellenModell.setRowCount(0); List tempListe = ((PeerToPeerAnwendung) holeAnwendung()).holeBekanntePeerToPeerTeilnehmer(); int zaehler = 0; for (String ip : tempListe) { zaehler++; Vector v = new Vector<>(); v.add(zaehler); v.add(ip); tabellenModell.addRow(v); } } /** * Diese Funktion erstellt die Tabelle mit den empfangenen Suchergebnissen. Sie wird ueber das Beobachtermuster * ausgeloest. * * @author Thomas Gerding & Johannes Bade * */ public void updateErgebnisTabelle() { String tmp; DefaultTableModel tabellenModell = (DefaultTableModel) ergebnisTabelle.getModel(); tabellenModell.setRowCount(0); List tempListe = ((PeerToPeerAnwendung) holeAnwendung()).holeErgebnisse(); for (String ergebnis : tempListe) { Vector v = new Vector<>(); StringTokenizer tempTokenizer = new StringTokenizer(ergebnis, "/"); v.add(tempTokenizer.nextToken()); tmp = tempTokenizer.nextToken(); v.add(tmp.substring(0, tmp.lastIndexOf(":"))); v.add(tmp.substring(tmp.lastIndexOf(":") + 1)); tabellenModell.addRow(v); } } /** * Funktion die w�hrend der Eingabe �berpr�ft ob die bisherige Eingabe einen korrekten Wert darstellt. * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ public void ipPruefen(JTextField feld) { if (EingabenUeberpruefung.isGueltig(feld.getText(), EingabenUeberpruefung.musterIpAdresse)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); pruefungOK = true; } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); pruefungOK = false; } } /** * Funktion die w�hrend der Eingabe �berpr�ft ob die bisherige Eingabe einen korrekten Wert darstellt. * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ public void zahlPruefen(JTextField feld) { if (EingabenUeberpruefung.isGueltig(feld.getText(), EingabenUeberpruefung.musterNurZahl)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); zahlOK = true; } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); zahlOK = false; } } /** * Sorgt dafuer, dass die DateiTabelle bei Reaktivierung des Fensters aktualisiert wird */ public void internalFrameActivated(InternalFrameEvent e) { this.updateDateiTabelle(); } public void internalFrameClosed(InternalFrameEvent e) { this.setVisible(false); } public void internalFrameClosing(InternalFrameEvent e) { this.setVisible(false); } public void internalFrameDeactivated(InternalFrameEvent e) { } public void internalFrameDeiconified(InternalFrameEvent e) { this.updateDateiTabelle(); } public void internalFrameIconified(InternalFrameEvent e) { this.updateDateiTabelle(); } public void internalFrameOpened(InternalFrameEvent e) { this.updateDateiTabelle(); } public void update(Observable arg0, Object arg1) { updateNetzwerkTabelle(); updateErgebnisTabelle(); updateDateiTabelle(); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationServerBausteinWindow.java000066400000000000000000000136661506574241500331140ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import filius.software.clientserver.ServerAnwendung; import filius.software.clientserver.ServerBaustein; /** *

* Diese Klasse stellt die Benutzungsoberflaeche fuer das Server-Programm einer einfachen Client-Server-Anwendung zur * Verfuegung. *

*

* Nachrichten von der Anwendung werden nach dem Beobachtermuster durch die Benachrichtigung der Beobachter angenommen * und verarbeitet. *

*/ public class GUIApplicationServerBausteinWindow extends GUIApplicationWindow { private static final long serialVersionUID = 1L; /** * Textbereich zur Ausgabe von Log-Daten, die vom Server-Programm erzeugt und durch Benachrichtigung der Beobachter * an die graphische Benutzungsoberflaeche weiter gegeben werden */ private JTextArea taLog; /** * Schaltflaeche zum Starten bzw. Stoppen des Server-Programms, d. h. zum Starten bzw. Beenden der * Verbindungsannahme durch den Server */ private JButton bStartStop; /** * Textfeld zur Eingabe des Ports, an dem der Server eingehende Verbindungsanfragen entgegen nimmt. */ private JTextField tfPort; /** * Standard-Konstruktor, der automatisch zur Erzeugung der graphischen Benutzungsoberflaeche fuer diese Anwendung * aufgerufen wird. */ public GUIApplicationServerBausteinWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); initialisiereKomponenten(); } /** Methode zur Initialisierung der graphischen Komponenten */ private void initialisiereKomponenten() { JPanel hauptPanel; JScrollPane spLogScroller; JLabel lbPort; Box hBox, vBox; hauptPanel = new JPanel(new BorderLayout()); vBox = Box.createVerticalBox(); vBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); bStartStop = new JButton(); bStartStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("start")) { ((ServerAnwendung) holeAnwendung()).setPort(Integer.parseInt(tfPort.getText())); ((ServerAnwendung) holeAnwendung()).setAktiv(true); ((ServerAnwendung) holeAnwendung()).setPort(Integer.parseInt(tfPort.getText())); } else { ((ServerAnwendung) holeAnwendung()).setAktiv(false); } aktualisieren(); } }); hBox.add(bStartStop); hBox.add(Box.createHorizontalStrut(5)); lbPort = new JLabel(messages.getString("serverbaustein_msg1")); hBox.add(lbPort); tfPort = new JTextField("" + ((ServerAnwendung) holeAnwendung()).getPort()); hBox.add(tfPort); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); taLog = new JTextArea(); taLog.setEditable(false); spLogScroller = new JScrollPane(taLog); spLogScroller.setPreferredSize(new Dimension(400, 400)); vBox.add(spLogScroller); vBox.add(Box.createVerticalStrut(5)); hauptPanel.add(vBox, BorderLayout.CENTER); add(hauptPanel, BorderLayout.CENTER); aktualisieren(); } /** * Methode zum aktualisieren der Komponenten der graphischen Benutzungsoberflaeche in Abhaengigkeit vom Zustand der * Anwendung */ public void aktualisieren() { ServerBaustein server; server = (ServerBaustein) holeAnwendung(); if (server.isAktiv()) { tfPort.setEditable(false); bStartStop.setText(messages.getString("serverbaustein_msg2")); bStartStop.setActionCommand("stop"); } else { bStartStop.setText(messages.getString("serverbaustein_msg3")); bStartStop.setActionCommand("start"); tfPort.setEditable(true); } } /** * Diese Methode wird automatisch ausgefuehrt, wenn der eine Nachricht an den Beobachter der Anwendung gesendet * wird. Der Parameter arg enthaelt die Nachricht, die von der Anwendung verschickt wurde. */ public void update(Observable o, Object arg) { if (null != arg) { taLog.append(arg.toString() + "\n"); } aktualisieren(); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationTerminalWindow.java000066400000000000000000000277331506574241500317260ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Observable; import java.util.StringTokenizer; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.lokal.Terminal; import filius.software.system.Dateisystem; /** * Applikationsfenster fuer ein Terminal * * @author Johannes Bade & Thomas Gerding * */ @SuppressWarnings({ "serial", "deprecation" }) public class GUIApplicationTerminalWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationTerminalWindow.class); private static final Color BACKGROUND = new Color(0, 0, 0); private static final Color FOREGROUND = new Color(222, 222, 222); private static final String MENU_LINE = "==========================================================================\n"; private JTextArea terminalField; private JPanel backPanel; private JLabel inputLabel; private JTextField inputField; private JScrollPane tpPane; private boolean jobRunning; private String enteredCommand; private String[] enteredParameters; private int lastScrollPaneSize; private boolean multipleObserverEvents; private ArrayList commandHistory = new ArrayList(); private int commandHistoryPointer = -1; public GUIApplicationTerminalWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); jobRunning = false; multipleObserverEvents = false; terminalField = new JTextArea(""); terminalField.setEditable(false); terminalField.setCaretColor(FOREGROUND); terminalField.setForeground(FOREGROUND); terminalField.setBackground(BACKGROUND); terminalField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11)); terminalField.setFocusable(false); terminalField.setBorder(null); terminalField.setLineWrap(true); initInput(); inputLabel = new JLabel(">"); inputLabel.setBackground(BACKGROUND); inputLabel.setForeground(FOREGROUND); inputLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11)); Box terminalBox = Box.createHorizontalBox(); terminalBox.setBackground(BACKGROUND); terminalBox.add(terminalField); terminalBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 1, 5)); Box inputBox = Box.createHorizontalBox(); inputBox.setBackground(BACKGROUND); inputBox.add(inputLabel); inputBox.add(Box.createHorizontalStrut(1)); inputBox.add(inputField); inputBox.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); backPanel = new JPanel(new BorderLayout()); backPanel.setBackground(BACKGROUND); backPanel.setFocusCycleRoot(true); backPanel.add(terminalBox, BorderLayout.CENTER); backPanel.add(inputBox, BorderLayout.SOUTH); tpPane = new JScrollPane(backPanel); // make textfield scrollable tpPane.setBorder(null); tpPane.setBackground(BACKGROUND); tpPane.setFocusable(false); tpPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); tpPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); tpPane.getViewport().addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { GUIApplicationTerminalWindow.this.scrollToBottomAndFocusInput(); } }); add(tpPane, BorderLayout.CENTER); terminalField.setText(""); showStartScreen(); inputLabel.setText(Dateisystem.absoluterPfad(((Terminal) holeAnwendung()).getAktuellerOrdner()) + "> "); scrollToBottomAndFocusInput(); } private void showStartScreen() { appendText(messages.getString("sw_terminal_msg57") + "\n"); appendText(MENU_LINE); appendText(messages.getString("sw_terminal_msg26") + "\n"); appendText(MENU_LINE); } private void initInput() { inputField = new JTextField(""); inputField.setEditable(true); inputField.setBackground(BACKGROUND); inputField.setForeground(FOREGROUND); inputField.setCaretColor(FOREGROUND); inputField.setBorder(null); inputField.setFont(new Font("Courier New", Font.PLAIN, 11)); inputField.setOpaque(false); inputField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "doNothing"); inputField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "doNothing"); inputField.getActionMap().put("doNothing", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) {} }); inputField.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { commandHistoryPointer = -1; if (!StringUtils.isBlank(inputField.getText())) { appendText("\n" + inputLabel.getText() + inputField.getText() + "\n"); StringTokenizer tk = new StringTokenizer(inputField.getText(), " "); /* Erstes Token enthaelt den Befehl */ enteredCommand = tk.nextToken(); /* * restliche Tokens werden in String Array geschrieben. Array wird sicherheitshalber mit * mindestens 3 leeren Strings gefüllt! */ enteredParameters = new String[3 + tk.countTokens()]; for (int i = 0; i < 3 + tk.countTokens(); i++) { enteredParameters[i] = new String(); } int iti = 0; while (tk.hasMoreTokens()) { enteredParameters[iti] = tk.nextToken(); iti++; } commandHistory.add(inputField.getText()); if (enteredCommand.equals("exit")) { GUIApplicationTerminalWindow.this.close(); } else if (enteredCommand.equals("reset") || enteredCommand.equals("cls")) { terminalField.setText(""); showStartScreen(); scrollToBottomAndFocusInput(); } else { GUIApplicationTerminalWindow.this.prepareJobStart(); ((Terminal) holeAnwendung()).terminalEingabeAuswerten(enteredCommand, enteredParameters); } } else { appendText(" \n"); } inputField.setText(""); } else if (e.getKeyCode() == KeyEvent.VK_C // [strg] + [c] && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) == KeyEvent.CTRL_DOWN_MASK) { ((Terminal) holeAnwendung()).setInterrupt(true); LOG.debug("execution aborted with ctrl+c"); } else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) { // 38 arrow-up / 40 arrow-down if (e.getKeyCode() == KeyEvent.VK_UP) { commandHistoryPointer++; } if (e.getKeyCode() == KeyEvent.VK_DOWN) { commandHistoryPointer--; } if (commandHistoryPointer < -1) { commandHistoryPointer = -1; } if (commandHistoryPointer >= commandHistory.size()) { commandHistoryPointer = commandHistory.size() - 1; } try { if (commandHistoryPointer != -1) { inputField.setText(commandHistory.get(commandHistory.size() - 1 - commandHistoryPointer)); } else if (commandHistoryPointer == -1) { inputField.setText(""); } } catch (IndexOutOfBoundsException eis) {} } scrollToBottom(true); } public void keyReleased(KeyEvent arg0) {} public void keyTyped(KeyEvent arg0) {} }); } public void update(Observable observable, Object notificationObject) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIApplicationTerminalWindow), update(" + observable + "," + notificationObject + ")"); if (notificationObject != null && jobRunning) { if (notificationObject instanceof Boolean) { multipleObserverEvents = ((Boolean) notificationObject).booleanValue(); } else { // expect String appendText(notificationObject.toString()); } if (!multipleObserverEvents) { inputLabel.setText(Dateisystem.absoluterPfad(((Terminal) holeAnwendung()).getAktuellerOrdner()) + "> "); prepareJobFinished(); } } scrollToBottomAndFocusInput(); } private void prepareJobFinished() { inputLabel.setVisible(true); jobRunning = false; } private void prepareJobStart() { inputLabel.setVisible(false); jobRunning = true; } private void appendText(String text) { terminalField.append(text); } private void scrollToBottomAndFocusInput() { scrollToBottom(false); focusInput(); } private void focusInput() { if (null != inputField) { inputField.requestFocusInWindow(); inputField.grabFocus(); } } private void scrollToBottom(boolean force) { if (force || lastScrollPaneSize < tpPane.getVerticalScrollBar().getMaximum()) { lastScrollPaneSize = tpPane.getVerticalScrollBar().getMaximum(); tpPane.getVerticalScrollBar().setValue(tpPane.getVerticalScrollBar().getMaximum()); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationTextEditorWindow.java000066400000000000000000000176111506574241500322400ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.system.Betriebssystem; import filius.software.system.Datei; /** * Applikationsfenster fuer TextEditor * * @author Johannes Bade & Thomas Gerding * */ public class GUIApplicationTextEditorWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationTextEditorWindow.class); private static final long serialVersionUID = 1L; private JTextArea editorField; private JPanel backPanel; private Datei aktuelleDatei = null; private String original = ""; private DefaultMutableTreeNode arbeitsVerzeichnis; public GUIApplicationTextEditorWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); editorField = new JTextArea(""); editorField.setEditable(true); editorField.setFont(new Font("Courier New", Font.PLAIN, 11)); this.arbeitsVerzeichnis = holeAnwendung().getSystemSoftware().getDateisystem().getRoot(); String dateiName = holeParameter()[0]; if (!dateiName.equals("")) { if (this.arbeitsVerzeichnis == null) { this.arbeitsVerzeichnis = holeAnwendung().getSystemSoftware().getDateisystem().getRoot(); } Datei datei = holeAnwendung().getSystemSoftware().getDateisystem().holeDatei(arbeitsVerzeichnis, dateiName); if (datei != null) { aktuelleDatei = datei; } updateFromFile(); } JScrollPane tpPane = new JScrollPane(editorField); tpPane.setBorder(null); Box editorBox = Box.createHorizontalBox(); // editorBox.add(editorField); editorBox.add(tpPane); editorBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); backPanel = new JPanel(new BorderLayout()); backPanel.add(editorBox, BorderLayout.CENTER); add(backPanel, BorderLayout.CENTER); JPanel menubar = new JPanel(new FlowLayout()); JButton newFile = new JButton(messages.getString("texteditor_msg3")); newFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { neu(); } }); menubar.add(newFile); JButton openFile = new JButton(messages.getString("texteditor_msg4")); openFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { oeffnen(); } }); menubar.add(openFile); JButton saveFile = new JButton(messages.getString("texteditor_msg5")); saveFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { speichern(); } }); menubar.add(saveFile); JButton saveAsFile = new JButton(messages.getString("texteditor_msg6")); saveAsFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { speichernUnter(); } }); menubar.add(saveAsFile); backPanel.add(menubar, BorderLayout.NORTH); } public void speichern() { if (aktuelleDatei != null) { original = editorField.getText(); aktuelleDatei.setDateiInhalt(original); } else { speichernUnter(); } } public void speichernUnter() { DMTNFileChooser fc = new DMTNFileChooser((Betriebssystem) holeAnwendung().getSystemSoftware()); int rueckgabe = fc.saveDialog(); if (rueckgabe == DMTNFileChooser.OK) { String dateiNameNeu = fc.getAktuellerDateiname(); Datei tmpFile = new Datei(dateiNameNeu, messages.getString("texteditor_msg8"), editorField.getText()); this.holeAnwendung().getSystemSoftware().getDateisystem().speicherDatei(fc.getAktuellerOrdner(), tmpFile); changeCurrentFile(tmpFile); } } public void changeCurrentFile(Datei tmpFile) { if (aktuelleDatei != null) { aktuelleDatei.deleteObserver(this); } aktuelleDatei = tmpFile; updateFromFile(); if (aktuelleDatei != null) { aktuelleDatei.addObserver(this); } } public void oeffnen() { DMTNFileChooser fc = new DMTNFileChooser((Betriebssystem) holeAnwendung().getSystemSoftware()); int rueckgabe = fc.openDialog(); if (rueckgabe == DMTNFileChooser.OK) { String aktuellerDateiname = fc.getAktuellerDateiname(); Datei tmpFile = holeAnwendung().getSystemSoftware().getDateisystem().holeDatei(fc.getAktuellerOrdner(), aktuellerDateiname); changeCurrentFile(tmpFile); } else { LOG.debug("Fehler beim oeffnen einer Datei"); } } private void updateFromFile() { if (aktuelleDatei != null) { setTitle(anwendung, aktuelleDatei.getName()); original = aktuelleDatei.getDateiInhalt(); editorField.setText(original); } else { setTitle(anwendung); LOG.debug("Fehler beim oeffnen einer Datei: keine Datei ausgewaehlt"); } } public void starten(String[] param) { String dateiName = holeParameter()[0]; if (!dateiName.equals("")) { Datei datei = this.holeAnwendung().getSystemSoftware().getDateisystem().holeDatei(arbeitsVerzeichnis, dateiName); if (datei != null) { aktuelleDatei = datei; } updateFromFile(); } } public void neu() { editorField.setText(""); setTitle(anwendung); changeCurrentFile(null); } public void updateUnchangedTextFromFile() { if (original != null && editorField != null && aktuelleDatei != null && original.equals(editorField.getText())) { original = aktuelleDatei.getDateiInhalt(); editorField.setText(original); } } @Override public void update(Observable observable, Object arg1) { if (observable == aktuelleDatei) { updateUnchangedTextFromFile(); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationWebBrowserWindow.java000066400000000000000000000262101506574241500322210ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.net.MalformedURLException; import java.net.URL; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.html.FormSubmitEvent; import javax.swing.text.html.FormSubmitEvent.MethodType; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import org.htmlparser.Parser; import org.htmlparser.Tag; import org.htmlparser.filters.TagNameFilter; import org.htmlparser.util.NodeList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.Information; import filius.software.www.HTTPNachricht; import filius.software.www.WebBrowser; public class GUIApplicationWebBrowserWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationWebBrowserWindow.class); private static final long serialVersionUID = 1L; private JPanel browserPanel; private JTextField urlFeld; private JEditorPane anzeigeFeld; private JButton goButton; public GUIApplicationWebBrowserWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); HTMLEditorKit ek; browserPanel = new JPanel(new BorderLayout()); add(browserPanel, BorderLayout.CENTER); Box topBox = Box.createHorizontalBox(); urlFeld = new JTextField("http://"); urlFeld.setVisible(true); topBox.add(urlFeld); topBox.add(Box.createHorizontalStrut(5)); // Platz zw. urlFeld und // senden goButton = new JButton(messages.getString("webbrowser_msg2")); topBox.add(goButton); topBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); browserPanel.add(topBox, BorderLayout.NORTH); Box middleBox = Box.createHorizontalBox(); middleBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); /* ActionListener */ ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent arg0) { URL url; url = erzeugeURL(urlFeld.getText()); abrufenWebseite(url, null); } }; goButton.addActionListener(al); /* KeyListener */ urlFeld.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { URL url; if (e.getKeyCode() == KeyEvent.VK_ENTER) { // LOG.debug("\t"+getClass()+": enter pressed"); url = erzeugeURL(urlFeld.getText()); abrufenWebseite(url, null); } } public void keyReleased(KeyEvent arg0) {} public void keyTyped(KeyEvent arg0) {} }); ek = new HTMLEditorKit(); // Braucht er für SubmitEvent! ek.setAutoFormSubmission(false); // muss! anzeigeFeld = new JEditorPane(); anzeigeFeld.setEditorKit(ek); anzeigeFeld.setContentType("text/html"); // text/html muss bleiben // wegen folgendem Quelltext: anzeigeFeld.setText("" + "
" + "" + ""); // filius.rahmenprogramm.SzenarioVerwaltung.kopiereDatei(Information // .getInformation().getProgrammPfad() // + "gfx/desktop/browser_waterwolf_logo.png", Information // .getInformation().getTempPfad() // + "browser_waterwolf_logo.png"); filius.rahmenprogramm.SzenarioVerwaltung.saveStream( getClass().getResourceAsStream("/gfx/desktop/browser_waterwolf_logo.png"), Information.getInformation().getTempPfad() + "browser_waterwolf_logo.png"); try { ((HTMLDocument) anzeigeFeld.getDocument()) .setBase(new URL("file:" + Information.getInformation().getTempPfad())); } catch (MalformedURLException e1) { LOG.debug("", e1); } anzeigeFeld.setEditable(false); anzeigeFeld.setBorder(null); hyperLinkListener(anzeigeFeld); anzeigeFeld.setVisible(true); JScrollPane spAnzeige = new JScrollPane(anzeigeFeld); middleBox.add(spAnzeige); browserPanel.add(middleBox, BorderLayout.CENTER); } private URL erzeugeURL(String ressource) { URL url = null; String[] teilstrings; String host = null, pfad = ""; teilstrings = ressource.split("/"); // Fuer den Fall, dass URL-Eingabe mit Hostadresse beginnt if (teilstrings.length > 0 && !teilstrings[0].equalsIgnoreCase("http:")) { if (EingabenUeberpruefung.isGueltig(teilstrings[0], EingabenUeberpruefung.musterDomain) || EingabenUeberpruefung.isGueltig(teilstrings[0], EingabenUeberpruefung.musterIpAdresse)) { host = teilstrings[0]; for (int i = 1; i < teilstrings.length; i++) { pfad = pfad + "/" + teilstrings[i]; } } } // Fuer den Fall, dass URL-Eingabe mit http:// beginnt if (teilstrings.length > 2 && teilstrings[0].equalsIgnoreCase("http:")) { if (EingabenUeberpruefung.isGueltig(teilstrings[2], EingabenUeberpruefung.musterDomain) || EingabenUeberpruefung.isGueltig(teilstrings[2], EingabenUeberpruefung.musterIpAdresse)) { host = teilstrings[2]; } for (int i = 3; i < teilstrings.length; i++) { pfad = pfad + "/" + teilstrings[i]; } } if (host != null) { if (pfad.equals("")) pfad = "/"; try { url = new URL("http", host, pfad); } catch (MalformedURLException e) { LOG.debug("", e); } } return url; } private void abrufenWebseite(URL url, String postDaten) { String host; if (url != null) { if (postDaten == null) { ((WebBrowser) holeAnwendung()).holeWebseite(url); } else { ((WebBrowser) holeAnwendung()).holeWebseite(url, postDaten); } if (url.getHost() == null || url.getHost().equals("")) { host = ((WebBrowser) holeAnwendung()).holeHost(); } else { host = url.getHost(); } urlFeld.setText(url.getProtocol() + "://" + host + url.getPath()); setTitle(url.getProtocol() + "://" + host + url.getPath()); } else { urlFeld.setText("http://"); } } private void initialisiereWebseite(String quelltext) { Parser parser; NodeList liste; Tag tag; anzeigeFeld.setContentType("text/html"); anzeigeFeld.setText(quelltext); parser = Parser.createParser(quelltext, null); try { liste = parser.parse(new TagNameFilter("title")); if (liste.size() > 0) { tag = (Tag) liste.elementAt(0); if (tag.getChildren() != null && tag.getChildren().size() > 0) setTitle(tag.getChildren().elementAt(0).toHtml()); } } catch (Exception e) { LOG.debug("", e); } } private void hyperLinkListener(JEditorPane editorPane) { editorPane.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { URL url = null, tmp; String pfad; // Hier wird auf einen Klick reagiert if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { tmp = ((HTMLDocument) anzeigeFeld.getDocument()).getBase(); if (e.getURL().getProtocol().equals(tmp.getProtocol())) { try { pfad = e.getURL().getFile().replace(tmp.getFile(), "/"); url = new URL("http", "", pfad); } catch (MalformedURLException e1) { LOG.debug("", e); } } else { url = e.getURL(); } // in diesem Fall kam das Event vom Submit-Button: if (e instanceof FormSubmitEvent) { // Zerlegen erfolgt erst im Server String postDatenteil = ""; FormSubmitEvent formSubmitEvent = (FormSubmitEvent) e; if (formSubmitEvent.getMethod() == MethodType.POST) { postDatenteil = formSubmitEvent.getData(); } abrufenWebseite(url, postDatenteil); } else { abrufenWebseite(url, null); } } } }); } public void update(Observable arg0, Object arg1) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIApplicationWebBrowserWindow), update(" + arg0 + "," + arg1 + ")"); if (arg1 == null) { anzeigeFeld.updateUI(); } else if (arg1 instanceof HTTPNachricht) { if (((HTTPNachricht) arg1).getDaten() == null) { anzeigeFeld.updateUI(); } else { initialisiereWebseite(((HTTPNachricht) arg1).getDaten()); } } else { // LOG.debug(arg1); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationWebServerWindow.java000066400000000000000000000202711506574241500320450ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.table.DefaultTableModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.www.WebServer; public class GUIApplicationWebServerWindow extends GUIApplicationWindow { private static Logger LOG = LoggerFactory.getLogger(GUIApplicationWebServerWindow.class); private static final int VHOST_NUMBER = 5; private static final long serialVersionUID = 1L; private JPanel backPanel; private JTextArea logArea; private JButton buttonStart; private JCheckBox showVHosts; private VHostConfigTable vHostTable; private Box vHostBox = null, logBox = null; public GUIApplicationWebServerWindow(final GUIDesktopPanel desktop, String appName) { super(desktop, appName); initialisiereKomponenten(); aktualisieren(); } private void initialisiereKomponenten() { backPanel = new JPanel(new BorderLayout()); buttonStart = new JButton(messages.getString("webserver_msg1")); buttonStart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (buttonStart.getText().equals(messages.getString("webserver_msg1"))) { ((WebServer) holeAnwendung()).setAktiv(true); } else { ((WebServer) holeAnwendung()).setAktiv(false); } } }); Box upperBox = Box.createHorizontalBox(); upperBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); upperBox.add(buttonStart); upperBox.add(Box.createHorizontalStrut(100)); showVHosts = new JCheckBox(); showVHosts.setText(messages.getString("webserver_msg6")); showVHosts.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { JCheckBox checkBox = (JCheckBox) evt.getSource(); ((WebServer) holeAnwendung()).setUseVHost(checkBox.isSelected()); GUIApplicationWebServerWindow.this.aktualisieren(); LOG.debug("vhost status changed: " + (checkBox.isSelected() ? "enabled" : "disabled")); } }); upperBox.add(showVHosts); backPanel.add(upperBox, BorderLayout.NORTH); vHostBox = Box.createVerticalBox(); vHostBox.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); JLabel label = new JLabel(messages.getString("webserver_msg3")); Box vHostLabel = Box.createHorizontalBox(); vHostLabel.add(label); DefaultTableModel tablemodel = new DefaultTableModel(VHOST_NUMBER, 2); vHostTable = new VHostConfigTable(tablemodel, true); vHostTable.setParentGUI(this); JScrollPane tableScrollPane = new JScrollPane(vHostTable); tableScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); tableScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); LOG.debug("message webserver_msg4='" + messages.getString("webserver_msg4") + "'"); vHostTable.getColumnModel().getColumn(0).setHeaderValue(messages.getString("webserver_msg4")); vHostTable.getColumnModel().getColumn(1).setHeaderValue(messages.getString("webserver_msg5")); LOG.debug("header value: '" + vHostTable.getColumnModel().getColumn(0).getHeaderValue() + "'"); vHostTable.setIntercellSpacing(new java.awt.Dimension(5, 5)); vHostTable.setRowHeight(26); vHostTable.setShowGrid(true); vHostTable.setFillsViewportHeight(true); vHostTable.setBackground(java.awt.Color.WHITE); vHostTable.setShowHorizontalLines(true); vHostBox.add(vHostLabel); vHostBox.add(Box.createVerticalStrut(5)); vHostBox.add(tableScrollPane); logArea = new JTextArea(); logArea.setEditable(false); JScrollPane sPane = new JScrollPane(logArea); logBox = Box.createHorizontalBox(); logBox.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); logBox.add(sPane); backPanel.add(logBox, BorderLayout.SOUTH); add(backPanel, BorderLayout.CENTER); } private void showVHostTable(boolean tableVisible) { if (tableVisible) { backPanel.add(vHostBox, BorderLayout.CENTER); logBox.setPreferredSize(new Dimension(300, 150)); } else { backPanel.remove(vHostBox); logBox.setPreferredSize(new Dimension(300, 350)); } showVHosts.setSelected(tableVisible); Dimension size = getSize(); setSize(size); updateUI(); } public void updateVHostTable() { LOG.trace("GUIApplicationWebServerWindow, updateTable; vHostArray:\n" + ((WebServer) holeAnwendung()).printVHostTable()); String[][] vhosts = ((WebServer) holeAnwendung()).getVHostArray(); DefaultTableModel tablemodel = (DefaultTableModel) vHostTable.getModel(); tablemodel.setRowCount(0); for (int i = 0; i < vhosts.length; i++) { Vector> v = new Vector>(); if (vhosts[i][0] != null) v.add(vhosts[i][0]); else v.add(""); if (vhosts[i][1] != null) v.add(vhosts[i][1]); else v.add(""); tablemodel.addRow(v); } for (int i = vhosts.length; i < VHOST_NUMBER; i++) { Vector> v = new Vector>(); v.add(""); v.add(""); tablemodel.addRow(v); } } private void aktualisieren() { WebServer webserver = (WebServer) holeAnwendung(); if (webserver.isAktiv()) { buttonStart.setText(messages.getString("webserver_msg2")); } else { buttonStart.setText(messages.getString("webserver_msg1")); } buttonStart.setEnabled(true); updateVHostTable(); showVHostTable(webserver.isUseVHost()); } public void update(Observable arg0, Object arg1) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIApplicationWebServerWindow), update(" + arg0 + "," + arg1 + ")"); if (logArea != null && arg1 != null) { logArea.append(arg1.toString() + "\n"); } try { aktualisieren(); } catch (Exception e) { LOG.debug("GUIApplicationWebServerWindow: update() Exception: " + e.getMessage()); } LOG.debug("GUIApplicationWebServerWindow: update() aufgerufen."); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIApplicationWindow.java000066400000000000000000000144551506574241500302270ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import static filius.gui.anwendungssicht.GUIDesktopPanel.HEIGHT_APP_TITLEBAR; import static filius.gui.anwendungssicht.GUIDesktopPanel.PANEL_WIDTH; import static filius.gui.anwendungssicht.GUIDesktopPanel.HEIGHT_CONTENT; import static filius.gui.anwendungssicht.GUIDesktopPanel.SIZE_TITLEBAR_ICON; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Image; import java.util.Map; import java.util.Observer; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingConstants; import org.apache.commons.lang3.StringUtils; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.Anwendung; /** * Diese Klasse dient als Oberklasse für alle Anwendungsfenster * */ @SuppressWarnings({ "serial", "deprecation" }) public abstract class GUIApplicationWindow extends JPanel implements I18n, Observer { protected GUIDesktopPanel desktop; protected Anwendung anwendung; protected JLabel titleLabel; protected JLabel icon; public GUIApplicationWindow(GUIDesktopPanel desktop) { super(new BorderLayout()); this.desktop = desktop; initComponents(); } public GUIApplicationWindow(GUIDesktopPanel desktop, String appKlasse) { super(new BorderLayout()); this.desktop = desktop; initComponents(); this.anwendung = desktop.getBetriebssystem().holeSoftware(appKlasse); this.anwendung.hinzuBeobachter(this); setIcon(anwendung); setTitle(anwendung); } private void initComponents() { setSize(PANEL_WIDTH, HEIGHT_CONTENT); setPreferredSize(new Dimension(PANEL_WIDTH, HEIGHT_CONTENT)); BorderLayout appWindowLayout = (BorderLayout) getLayout(); appWindowLayout.setHgap(0); appWindowLayout.setVgap(0); setLayout(appWindowLayout); JPanel titleBar = new JPanel(new BorderLayout()); titleBar.setPreferredSize(new Dimension(PANEL_WIDTH, HEIGHT_APP_TITLEBAR)); titleBar.setBackground(Color.DARK_GRAY); add(titleBar, BorderLayout.NORTH); icon = new JLabel(); icon.setPreferredSize(new Dimension(SIZE_TITLEBAR_ICON, SIZE_TITLEBAR_ICON)); icon.setHorizontalAlignment(SwingConstants.CENTER); titleBar.add(icon, BorderLayout.WEST); titleLabel = new JLabel(); titleLabel.setForeground(new Color(225, 225, 225)); titleLabel.setHorizontalAlignment(SwingConstants.CENTER); titleBar.add(titleLabel, BorderLayout.CENTER); } public void close() { desktop.showApplications(); } private void setIcon(Anwendung app) { for (Map tmpMap : Information.getInformation().ladeProgrammListe()) { if (app.holeAnwendungsName().equals(tmpMap.get("Anwendung"))) { String path = "/" + tmpMap.get("gfxFile"); setIcon(path); break; } } } protected void setIcon(String path) { ImageIcon image = new ImageIcon(getClass().getResource(path)); image.setImage(image.getImage().getScaledInstance(16, 16, Image.SCALE_AREA_AVERAGING)); icon.setIcon(image); } protected void setTitle(Anwendung app) { setTitle(app, StringUtils.EMPTY); } protected void setTitle(Anwendung app, String titleExtension) { String title; if (StringUtils.isNoneBlank(titleExtension) && null != app) { title = app.holeAnwendungsName() + " - " + titleExtension; } else if (null != app) { title = app.holeAnwendungsName(); } else { title = titleExtension; } setTitle(title); } public void setTitle(String title) { titleLabel.setText(title); } public Anwendung holeAnwendung() { return anwendung; } public void showMessageDialog(String msg) { JOptionPane.showMessageDialog(desktop, msg); } public int showOptionDialog(Object message, String title, int optionType, int messageType, Icon icon, Object[] options, Object initialValue) { return JOptionPane.showOptionDialog(desktop, message, title, optionType, messageType, icon, options, initialValue); } public int showConfirmDialog(String msg) { return JOptionPane.showConfirmDialog(desktop, msg); } public void addFrame(JInternalFrame frame) { desktop.getDesktopPane().add(frame); } public void starteExterneAnwendung(String softwareName) { desktop.starteAnwendung(softwareName); } public void starteExterneAnwendung(String softwareName, String[] param) { desktop.starteAnwendung(softwareName, param); } public String[] holeParameter() { return desktop.getParameter(); } public void zeigePopupMenu(JPopupMenu menu, int x, int y) { menu.show(desktop, x, y); } public void starten(String[] param) {} } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIDesktopIcon.java000066400000000000000000000045601506574241500270120ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.Icon; import javax.swing.JLabel; @SuppressWarnings("serial") public class GUIDesktopIcon extends JLabel implements MouseListener { private String anwendungsName; private String invokeName; private GUIDesktopPanel desktop; public GUIDesktopIcon(GUIDesktopPanel desktop, Icon icon) { super(icon); this.desktop = desktop; addMouseListener(this); } public String getAnwendungsName() { return anwendungsName; } public void setAnwendungsName(String anwendungsName) { this.anwendungsName = anwendungsName; } public String getInvokeName() { return invokeName; } public void setInvokeName(String invokeName) { this.invokeName = invokeName; } public void mouseClicked(MouseEvent arg0) {} public void mouseEntered(MouseEvent arg0) {} public void mouseExited(MouseEvent arg0) {} public void mousePressed(MouseEvent arg0) { desktop.starteAnwendung(invokeName); } public void mouseReleased(MouseEvent arg0) {} } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIDesktopPanel.java000066400000000000000000000354101506574241500271570ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.Map; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JLabel; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.event.MouseInputAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.FiliusClassLoader; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.system.Betriebssystem; @SuppressWarnings({ "deprecation", "serial" }) public class GUIDesktopPanel extends JPanel implements I18n, Observer { private static Logger LOG = LoggerFactory.getLogger(GUIDesktopPanel.class); static final int HEIGHT_TASKBAR = 30; static final int HEIGHT_OVERALL = 460; static final int PANEL_WIDTH = 640; static final int HEIGHT_CONTENT = HEIGHT_OVERALL - HEIGHT_TASKBAR; static final int HEIGHT_APP_TITLEBAR = 30; static final int SIZE_TITLEBAR_ICON = 30; private static final String SOFTWARE_INSTALLATION = "Software-Installation"; private static final String DESKTOP_WITH_APPS = "desktop-with-apps"; private Betriebssystem betriebssystem; private HashMap laufendeAnwendung = new HashMap(); private CardLayout workingAreaCardLayout = new CardLayout(); private JBackgroundDesktopPane desktopCard; private JPanel workingLayer; private JPanel appPanel; private JPanel taskbar; private JLayeredPane mainPaneWithOverlay; private JPanel infoLayer; private NetworkInfoPanel networkInfo; private JLabel networkIcon; private JPanel dialogLayer; private String[] parameter = { "", "", "" }; public GUIDesktopPanel(Betriebssystem betriebssystem) { super(); this.betriebssystem = betriebssystem; betriebssystem.addObserver(this); initOverallArea(); initWorkingArea(); initTaskbar(); updateAppPane(); } private void initOverallArea() { setBounds(0, 0, PANEL_WIDTH, HEIGHT_OVERALL); BorderLayout layout = new BorderLayout(); layout.setHgap(0); layout.setVgap(0); setLayout(layout); } private void initTaskbar() { taskbar = new JPanel(new BorderLayout()); taskbar.setPreferredSize(new Dimension(PANEL_WIDTH, HEIGHT_TASKBAR)); taskbar.setBackground(new Color(180, 180, 180)); add(taskbar, BorderLayout.SOUTH); networkIcon = new JLabel(new ImageIcon(getClass().getResource("/gfx/desktop/netzwek_aus.png"))); networkIcon.setPreferredSize(new Dimension(50, 30)); networkIcon.setAlignmentX(0.5f); networkIcon.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { GUIDesktopPanel.this.setInfoVisible(true); } }); initNetworkIcon(); taskbar.add(networkIcon, BorderLayout.EAST); JButton homeButton = new JButton(" " + messages.getString("desktoppanel_application") + " "); homeButton.setBackground(taskbar.getBackground()); homeButton.setContentAreaFilled(false); homeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { GUIDesktopPanel.this.showApplications(); } }); taskbar.add(homeButton, BorderLayout.CENTER); JLabel leftPlaceholder = new JLabel(); leftPlaceholder.setPreferredSize(networkIcon.getPreferredSize()); taskbar.add(leftPlaceholder, BorderLayout.WEST); } private void setInfoVisible(boolean visible) { if (visible) { mainPaneWithOverlay.moveToFront(infoLayer); if (null != networkInfo) { networkInfo.setVisible(true); } } else { mainPaneWithOverlay.moveToBack(infoLayer); } mainPaneWithOverlay.updateUI(); } private void initWorkingArea() { mainPaneWithOverlay = new JLayeredPane(); mainPaneWithOverlay.setPreferredSize(new Dimension(PANEL_WIDTH, HEIGHT_OVERALL - HEIGHT_TASKBAR)); add(mainPaneWithOverlay, BorderLayout.CENTER); workingLayer = new JPanel(workingAreaCardLayout); workingLayer.setSize(new Dimension(PANEL_WIDTH, HEIGHT_OVERALL - HEIGHT_TASKBAR)); mainPaneWithOverlay.add(workingLayer, JLayeredPane.DEFAULT_LAYER); desktopCard = new JBackgroundDesktopPane(); desktopCard.setBackgroundImage("gfx/desktop/hintergrundbild.png"); appPanel = new JPanel(); appPanel.setBounds(0, 0, PANEL_WIDTH, HEIGHT_OVERALL); appPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 10)); appPanel.setOpaque(false); desktopCard.add(appPanel, BorderLayout.CENTER); workingLayer.add(desktopCard, DESKTOP_WITH_APPS); infoLayer = new JPanel(); infoLayer.setLayout(null); infoLayer.setOpaque(false); infoLayer.setBounds(0, 0, PANEL_WIDTH, HEIGHT_OVERALL); infoLayer.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { GUIDesktopPanel.this.setInfoVisible(false); } }); networkInfo = new NetworkInfoPanel(this); int infoWidth = (int) networkInfo.getPreferredSize().getWidth(); int infoHeight = (int) networkInfo.getPreferredSize().getHeight(); networkInfo.setBounds(PANEL_WIDTH - infoWidth, HEIGHT_OVERALL - HEIGHT_TASKBAR - infoHeight, infoWidth, infoHeight); infoLayer.add(networkInfo); mainPaneWithOverlay.add(infoLayer, JLayeredPane.DEFAULT_LAYER); dialogLayer = new JPanel(); dialogLayer.setLayout(null); dialogLayer.setBounds(0, HEIGHT_APP_TITLEBAR, PANEL_WIDTH, HEIGHT_CONTENT); dialogLayer.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) {} }); dialogLayer.setVisible(false); mainPaneWithOverlay.add(dialogLayer, JLayeredPane.DRAG_LAYER); } public void showApplications() { workingAreaCardLayout.show(workingLayer, DESKTOP_WITH_APPS); dialogLayer.setVisible(false); } public void showModularWindow(String title, JComponent panel) { dialogLayer.removeAll(); dialogLayer.setVisible(true); int windowWidth = (int) panel.getPreferredSize().getWidth(); int windowHeight = (int) panel.getPreferredSize().getHeight(); panel.setBounds((PANEL_WIDTH - windowWidth) / 2, HEIGHT_APP_TITLEBAR, windowWidth, windowHeight); panel.setBorder(BorderFactory.createEtchedBorder()); dialogLayer.add(panel); JLabel titlebar = new JLabel(title); titlebar.setOpaque(true); titlebar.setBackground(new Color(100, 100, 100)); titlebar.setForeground(new Color(250, 250, 250)); titlebar.setHorizontalAlignment(SwingConstants.CENTER); titlebar.setBounds(0, 0, PANEL_WIDTH, HEIGHT_APP_TITLEBAR); dialogLayer.add(titlebar); dialogLayer.updateUI(); mainPaneWithOverlay.moveToFront(dialogLayer); mainPaneWithOverlay.updateUI(); } public void closeModularWindow() { dialogLayer.removeAll(); dialogLayer.setVisible(false); mainPaneWithOverlay.moveToBack(dialogLayer); mainPaneWithOverlay.updateUI(); } public void closeModularWindow(JPanel progressPanel) { for (Component comp : dialogLayer.getComponents()) { if (comp.equals(progressPanel)) { closeModularWindow(); break; } } } public void updateAppPane() { appPanel.removeAll(); appPanel.add(initInstaller()); for (Map appInfo : Information.getInformation().ladeProgrammListe()) { String appClass = appInfo.get("Klasse"); if ((betriebssystem.holeSoftware(appClass) != null)) { appPanel.add(initApp(appInfo)); } } appPanel.updateUI(); } private GUIDesktopIcon initInstaller() { GUIApplicationWindow tempWindow = new GUIInstallationsDialog(this); tempWindow.setBounds(0, 0, PANEL_WIDTH, HEIGHT_OVERALL); addLaufendeAnwendung(SOFTWARE_INSTALLATION, tempWindow); return createIcon(messages.getString("desktoppanel_msg1"), SOFTWARE_INSTALLATION, "/gfx/desktop/icon_softwareinstallation.png"); } private GUIDesktopIcon initApp(Map appInfo) { GUIDesktopIcon tmpLabel = null; try { Class cl = Class.forName(appInfo.get("GUI-Klasse"), true, FiliusClassLoader.getInstance(Thread.currentThread().getContextClassLoader())); GUIApplicationWindow tempWindow = (GUIApplicationWindow) cl .getConstructor(GUIDesktopPanel.class, String.class).newInstance(this, appInfo.get("Klasse")); tempWindow.setBounds(0, 0, PANEL_WIDTH, HEIGHT_OVERALL); addLaufendeAnwendung(appInfo.get("Klasse"), tempWindow); tmpLabel = createIcon((String) appInfo.get("Anwendung"), (String) appInfo.get("Klasse"), "/" + ((String) appInfo.get("gfxFile"))); } catch (Exception e) { LOG.debug("app {} could not be initiated", appInfo, e); } return tmpLabel; } private GUIDesktopIcon createIcon(String appName, String invokeName, String imagePath) { GUIDesktopIcon tmpLabel = new GUIDesktopIcon(this, new ImageIcon(getClass().getResource(imagePath))); tmpLabel.setAnwendungsName(appName); tmpLabel.setToolTipText(appName); tmpLabel.setText(appName); tmpLabel.setInvokeName(invokeName); tmpLabel.setVerticalTextPosition(SwingConstants.BOTTOM); tmpLabel.setHorizontalTextPosition(SwingConstants.CENTER); tmpLabel.setForeground(new Color(255, 255, 255)); tmpLabel.setPreferredSize(new Dimension(120, 96)); return tmpLabel; } private void initNetworkIcon() { NetzwerkInterface nic = ((InternetKnoten) betriebssystem.getKnoten()).getNetzwerkInterfaces().get(0); if (nic != null && nic.getPort() != null && nic.getPort().getVerbindung() != null) { nic.getPort().getVerbindung().addObserver(this); networkIcon.setToolTipText(nic.getIp()); } } public GUIApplicationWindow starteAnwendung(String softwareKlasse, String[] param) { setParameter(param); return starteAnwendung(softwareKlasse); } public GUIApplicationWindow starteAnwendung(String softwareKlasse) { GUIApplicationWindow tempWindow = null; if (getLaufendeAnwendungByName(softwareKlasse) != null) { tempWindow = getLaufendeAnwendungByName(softwareKlasse); tempWindow.updateUI(); tempWindow.starten(parameter); workingAreaCardLayout.show(workingLayer, softwareKlasse); workingLayer.transferFocus(); } return tempWindow; } /** * Fuegt der Hashmap laufendeAnwendung das Fenster der laufenden Anwendung hinzu, damit Fenster geschlossen und * wieder geoeffnet werden koennen, ohne die Anwendung dafuer neu starten zu muessen. * * @author Thomas Gerding & Johannes Bade * @param fenster * Das GUIApplicationWindow der Anwendung * @param appClass * Name der Anwendung */ private void addLaufendeAnwendung(String appClass, GUIApplicationWindow fenster) { laufendeAnwendung.put(appClass, fenster); workingLayer.add(fenster, appClass); } /** * Gibt das GUIApplicationWindow einer Anwendung aus der HashMap laufendeAnwendung zurueck. * * @param anwendungsName * @return Das GUIApplicationWindow der angeforderten Anwendung */ private GUIApplicationWindow getLaufendeAnwendungByName(String anwendungsName) { GUIApplicationWindow tmpFenster = null; tmpFenster = (GUIApplicationWindow) laufendeAnwendung.get(anwendungsName); return tmpFenster; } public filius.software.system.Betriebssystem getBetriebssystem() { return betriebssystem; } public JDesktopPane getDesktopPane() { return desktopCard; } public String[] getParameter() { return parameter; } public void setParameter(String[] parameter) { this.parameter = parameter; } public void update(Observable o, Object arg) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GUIDesktopPanel), update(" + o + "," + arg + ")"); if (arg == null) { updateAppPane(); initNetworkIcon(); } else if (arg.equals(Boolean.TRUE)) { networkIcon.setIcon(new ImageIcon(getClass().getResource("/gfx/desktop/netzwek_c.png"))); } else { networkIcon.setIcon(new ImageIcon(getClass().getResource("/gfx/desktop/netzwek_aus.png"))); } } public void updateInfo() { if (null != networkInfo) { networkInfo.updateInfo(); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIDesktopWindow.java000066400000000000000000000105741506574241500273730ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import static filius.gui.anwendungssicht.GUIDesktopPanel.HEIGHT_OVERALL; import static filius.gui.anwendungssicht.GUIDesktopPanel.PANEL_WIDTH; import java.util.Observable; import java.util.Observer; import javax.swing.ImageIcon; import javax.swing.JFrame; import filius.gui.JMainFrame; import filius.gui.netzwerksicht.GUIDesignSidebar; import filius.hardware.Hardware; import filius.hardware.knoten.Host; import filius.hardware.knoten.Notebook; import filius.hardware.knoten.Rechner; import filius.rahmenprogramm.IconMode; import filius.rahmenprogramm.Information; import filius.software.system.Betriebssystem; @SuppressWarnings({ "serial", "deprecation" }) public class GUIDesktopWindow extends JFrame implements Observer { private GUIDesktopPanel desktopPanel; public enum Mode { ROW(0), COLUMN(1), STACK(2); private final int value; Mode(int mode) { this.value = mode; } public static Mode getMode(int value) { if (value == ROW.value) { return ROW; } else if (value == COLUMN.value) { return COLUMN; } else { return STACK; } } } public GUIDesktopWindow(Betriebssystem bs) { bs.addObserver(this); Hardware hardware; String imageFile = null; IconMode mode = Information.getInformation().getIconMode(); hardware = bs.getKnoten(); if (hardware instanceof Rechner) { imageFile = GUIDesignSidebar.RECHNER + mode.suffix(); } else if (hardware instanceof Notebook) { imageFile = GUIDesignSidebar.NOTEBOOK + mode.suffix(); } ImageIcon icon = new ImageIcon(getClass().getResource("/" + imageFile)); setIconImage(icon.getImage()); int titleBarHeight = JMainFrame.getJMainFrame().getHeight() - JMainFrame.getJMainFrame().getContentPane().getHeight(); int borderWidth = JMainFrame.getJMainFrame().getWidth() - JMainFrame.getJMainFrame().getContentPane().getWidth(); setSize(PANEL_WIDTH + borderWidth, HEIGHT_OVERALL + titleBarHeight); setResizable(false); desktopPanel = new GUIDesktopPanel(bs); getContentPane().add(desktopPanel); } public void setVisible(boolean flag) { super.setVisible(flag); updateTitle(); desktopPanel.updateInfo(); if (flag) { toFront(); } } private void updateTitle() { String title; Host host = (Host) desktopPanel.getBetriebssystem().getKnoten(); if (host.isUseIPAsName()) { title = desktopPanel.getBetriebssystem().getKnoten().holeAnzeigeName(); } else { title = desktopPanel.getBetriebssystem().getKnoten().holeAnzeigeName() + " - " + desktopPanel.getBetriebssystem().primaryIPAdresse(); } setTitle(title); } public Betriebssystem getBetriebssystem() { return desktopPanel.getBetriebssystem(); } @Override public void update(Observable o, Object arg) { desktopPanel.updateInfo(); updateTitle(); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUIInstallationsDialog.java000066400000000000000000000232251506574241500305330ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Vector; import javax.swing.Box; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JScrollPane; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.Anwendung; import filius.software.system.InternetKnotenBetriebssystem; @SuppressWarnings("serial") public class GUIInstallationsDialog extends GUIApplicationWindow implements I18n { private JList softwareInstalliert; private JList softwareVerfuegbar; private DefaultListModel lmVerfuegbar; private DefaultListModel lmInstalliert; private List> programme = null; public GUIInstallationsDialog(GUIDesktopPanel dp) { super(dp); programme = Information.getInformation().ladeProgrammListe(); setTitle(messages.getString("installationsdialog_msg1")); setIcon("/gfx/desktop/icon_softwareinstallation.png"); /* Komponenten dem Panel hinzufügen */ Box gesamtBox = Box.createVerticalBox(); Box listenBox = Box.createHorizontalBox(); listenBox.add(Box.createHorizontalStrut(10)); listenBox.add(createInstalledAppsBox()); listenBox.add(Box.createHorizontalGlue()); listenBox.add(createSelectionButtonBox()); listenBox.add(createAvailableAppsBox()); listenBox.add(Box.createHorizontalStrut(10)); gesamtBox.add(Box.createVerticalStrut(10)); gesamtBox.add(listenBox); gesamtBox.add(Box.createVerticalStrut(10)); gesamtBox.add(createLowerButtonBox()); gesamtBox.add(Box.createVerticalStrut(10)); add(gesamtBox, BorderLayout.CENTER); } private Box createInstalledAppsBox() { Box wrapperInstBox = Box.createVerticalBox(); wrapperInstBox.add(new JLabel(messages.getString("installationsdialog_msg3"))); wrapperInstBox.add(Box.createVerticalStrut(10)); lmInstalliert = new DefaultListModel<>(); Anwendung[] anwendungen = desktop.getBetriebssystem().holeArrayInstallierteSoftware(); for (int i = 0; i < anwendungen.length; i++) { if (anwendungen[i] != null) { lmInstalliert.addElement(anwendungen[i].holeAnwendungsName()); } } softwareInstalliert = new JList<>(); softwareInstalliert.setModel(lmInstalliert); softwareInstalliert.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { GUIInstallationsDialog.this.entfernen(); } } }); JScrollPane scrollAnwendungInstallieren = new JScrollPane(softwareInstalliert); scrollAnwendungInstallieren.setPreferredSize(new Dimension(170, 200)); wrapperInstBox.add(scrollAnwendungInstallieren); return wrapperInstBox; } private Box createAvailableAppsBox() { Box wrapperAvailBox = Box.createVerticalBox(); wrapperAvailBox.add(new JLabel(messages.getString("installationsdialog_msg4"))); wrapperAvailBox.add(Box.createVerticalStrut(10)); lmVerfuegbar = new DefaultListModel<>(); if (programme != null) { for (Map programmInfo : programme) { String awKlasse = (String) programmInfo.get("Klasse"); if (desktop.getBetriebssystem().holeSoftware(awKlasse) == null) { lmVerfuegbar.addElement(programmInfo.get("Anwendung")); } } } softwareVerfuegbar = new JList<>(); softwareVerfuegbar.setModel(lmVerfuegbar); softwareVerfuegbar.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { GUIInstallationsDialog.this.hinzufuegen(); } } }); JScrollPane scrollAnwendungVerfuegbar = new JScrollPane(softwareVerfuegbar); scrollAnwendungVerfuegbar.setPreferredSize(new Dimension(170, 200)); wrapperAvailBox.add(scrollAnwendungVerfuegbar); return wrapperAvailBox; } private Box createSelectionButtonBox() { Box topButtonBox = Box.createVerticalBox(); JButton addButton = new JButton(new ImageIcon(getClass().getResource("/gfx/allgemein/pfeil_links.png"))); addButton.setMargin(new Insets(2, 2, 2, 2)); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { hinzufuegen(); } }); topButtonBox.add(addButton); topButtonBox.add(Box.createVerticalStrut(10)); JButton removeButton = new JButton(new ImageIcon(getClass().getResource("/gfx/allgemein/pfeil_rechts.png"))); removeButton.setMargin(new Insets(2, 2, 2, 2)); removeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { entfernen(); } }); topButtonBox.add(removeButton); return topButtonBox; } private Box createLowerButtonBox() { Box bottomButtonBox = Box.createVerticalBox(); JButton confirmButton = new JButton(messages.getString("installationsdialog_msg2")); confirmButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { aenderungenSpeichern(); setVisible(false); } }); confirmButton.setAlignmentX(JComponent.CENTER_ALIGNMENT); bottomButtonBox.add(confirmButton); return bottomButtonBox; } private void hinzufuegen() { Vector vLoeschen = new Vector(); int[] selektiertIndices = softwareVerfuegbar.getSelectedIndices(); for (int i : selektiertIndices) { lmInstalliert.addElement(lmVerfuegbar.get(i)); vLoeschen.add((String) lmVerfuegbar.get(i)); } for (Enumeration e = vLoeschen.elements(); e.hasMoreElements();) { String oZuLoeschen = e.nextElement(); lmVerfuegbar.removeElement(oZuLoeschen); } } private void entfernen() { int[] selektiertIndices = softwareInstalliert.getSelectedIndices(); Vector hinzu = new Vector(); for (int i : selektiertIndices) { lmVerfuegbar.addElement(lmInstalliert.getElementAt(i)); hinzu.add((String) lmInstalliert.getElementAt(i)); } for (Enumeration e = hinzu.elements(); e.hasMoreElements();) { String hinzuObjekt = e.nextElement(); lmInstalliert.removeElement(hinzuObjekt); } } private void aenderungenSpeichern() { InternetKnotenBetriebssystem bs = desktop.getBetriebssystem(); Anwendung anwendung; for (Map appInfo : programme) { for (int i = 0; i < lmInstalliert.getSize(); i++) { if (lmInstalliert.getElementAt(i).equals(appInfo.get("Anwendung")) && bs.holeSoftware(appInfo.get("Klasse").toString()) == null) { bs.installAppIfAvailable(appInfo.get("Klasse").toString()); anwendung = bs.holeSoftware(appInfo.get("Klasse").toString()); anwendung.starten(); } } for (int i = 0; i < lmVerfuegbar.getSize(); i++) { if (lmVerfuegbar.getElementAt(i).equals(appInfo.get("Anwendung"))) { anwendung = bs.holeSoftware(appInfo.get("Klasse").toString()); if (anwendung != null) { anwendung.beenden(); bs.entferneSoftware(appInfo.get("Klasse").toString()); } } } } desktop.updateAppPane(); } @Override public void update(Observable arg0, Object arg1) {} } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/GUITreeRenderer.java000066400000000000000000000061001506574241500271460ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.Component; import javax.swing.Icon; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import filius.software.system.Datei; class GUITreeRenderer extends DefaultTreeCellRenderer { private Icon dateiIcon, ordnerIcon; private boolean dateienAnzeigen; public GUITreeRenderer(Icon dateiIcon, Icon ordnerIcon) { this.dateiIcon = dateiIcon; this.ordnerIcon = ordnerIcon; } public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (leaf && isDatei(value)) { setIcon(dateiIcon); setText(this.getDateiName(value)); } else { setIcon(ordnerIcon); } if (isDatei(value) && !isDateienAnzeigen()) { setVisible(false); setText(""); setIcon(null); } return this; } protected boolean isDatei(Object value) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; if (node.getUserObject().getClass().equals(Datei.class)) { return true; } return false; } protected String getDateiName(Object value) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; if (node.getUserObject().getClass().equals(Datei.class)) { Datei datei = (Datei) node.getUserObject(); return datei.getName(); } return ""; } public boolean isDateienAnzeigen() { return dateienAnzeigen; } public void setDateienAnzeigen(boolean dateienAnzeigen) { this.dateienAnzeigen = dateienAnzeigen; } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/JBackgroundDesktopPane.java000066400000000000000000000053711506574241500305530ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.Graphics; import java.awt.Insets; import javax.swing.ImageIcon; import javax.swing.JDesktopPane; public class JBackgroundDesktopPane extends JDesktopPane { private static final long serialVersionUID = 1L; private ImageIcon backgroundImage = new ImageIcon(getClass().getResource("/gfx/allgemein/menue_hg.png")); private boolean repeatBG = true; /** * Setzt ein neues Hintergrundbild * * @author Johannes Bade * @param dateiname * String */ public void setBackgroundImage(String dateiname) { backgroundImage = new ImageIcon(getClass().getResource("/" + dateiname)); } /** * Immer wenn die Komponente gezeichnet wird, malt diese Methode auf der Komponentenfläche ein vorher bestimmtes * Hintergrundbild * * @author Johannes Bade * @param g * Graphics */ protected void paintComponent(Graphics g) { super.paintComponent(g); Insets ins = getInsets(); if (repeatBG) { for (int a = 0; a <= this.getHeight() / backgroundImage.getIconHeight(); a++) { for (int i = 0; i <= this.getWidth() / backgroundImage.getIconWidth(); i++) { backgroundImage.paintIcon(this, g, ins.left + i * backgroundImage.getIconWidth(), ins.top + a * backgroundImage.getIconHeight()); } } } else { backgroundImage.paintIcon(this, g, ins.left, ins.top); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/NatViewer.java000066400000000000000000000103301506574241500261170ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; import filius.gui.JMainFrame; import filius.hardware.knoten.Gateway; import filius.rahmenprogramm.I18n; import filius.software.system.GatewayFirmware; @SuppressWarnings("serial") public class NatViewer extends JDialog implements I18n, PropertyChangeListener { private Gateway gw; private DefaultTableModel dtm; public NatViewer(Gateway gw) { super(JMainFrame.getJMainFrame(), messages.getString("guievents_msg29") + " " + gw.holeAnzeigeName()); this.gw = gw; init(); updateNat(); } private void init() { getContentPane().removeAll(); setBounds(100, 100, 840, 240); ImageIcon icon = new ImageIcon(getClass().getResource("/gfx/hardware/router.png")); setIconImage(icon.getImage()); dtm = new DefaultTableModel(0, 6); JTable tableNATNachrichten = new JTable(dtm); DefaultTableColumnModel dtcm = (DefaultTableColumnModel) tableNATNachrichten.getColumnModel(); dtcm.getColumn(0).setHeaderValue(messages.getString("guievents_msg30")); dtcm.getColumn(1).setHeaderValue(messages.getString("guievents_msg32")); dtcm.getColumn(2).setHeaderValue(messages.getString("guievents_msg31")); dtcm.getColumn(3).setHeaderValue(messages.getString("guievents_msg33")); dtcm.getColumn(4).setHeaderValue(messages.getString("guievents_msg34")); dtcm.getColumn(5).setHeaderValue(messages.getString("guievents_msg27")); JPopupMenu menu = new JPopupMenu(); JMenuItem resetMenuItem = new JMenuItem(messages.getString("guievents_msg36")); resetMenuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { menu.setVisible(false); ((GatewayFirmware) gw.getSystemSoftware()).loescheNAT(); } }); menu.add(resetMenuItem); tableNATNachrichten.setComponentPopupMenu(menu); JScrollPane spNAT = new JScrollPane(tableNATNachrichten); getContentPane().add(spNAT); } public Gateway getGateway() { return gw; } private void updateNat() { dtm.setRowCount(0); for (Vector zeile : ((GatewayFirmware) gw.getSystemSoftware()).holeNAT()) { dtm.addRow(zeile); } } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName() == "nat_entry") { updateNat(); } else if (evt.getPropertyName() == "nat_method") { init(); updateNat(); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/NetworkInfoPanel.java000066400000000000000000000244011506574241500274440ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.Host; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.system.Betriebssystem; /** * Die Klasse stellt einen Dialog dar, in dem der Nutzer die Netzwerkeinstellungen des aktuellen Rechners betrachten und * editieren kann, waehrend er im Anwendungsmodus ist. * * @author Thomas Gerding * */ @SuppressWarnings("serial") public class NetworkInfoPanel extends JPanel implements I18n { private GUIDesktopPanel dp; private JLabel ipLabel, dnsLabel, gatewayLabel, netmaskLabel, macLabel; private JTextField ipField, dnsField, gatewayField, netmaskField, macField; private JButton changeButton; private Betriebssystem bs; private boolean istGueltig = true; public NetworkInfoPanel(final GUIDesktopPanel dp) { super(new BorderLayout()); NetzwerkInterface nic; this.dp = dp; ipLabel = new JLabel(messages.getString("network_msg1")); ipLabel.setSize(new Dimension(150, 15)); ipLabel.setPreferredSize(new Dimension(150, 15)); dnsLabel = new JLabel(messages.getString("network_msg2")); dnsLabel.setSize(new Dimension(150, 15)); dnsLabel.setPreferredSize(new Dimension(150, 15)); gatewayLabel = new JLabel(messages.getString("network_msg3")); gatewayLabel.setSize(new Dimension(150, 15)); gatewayLabel.setPreferredSize(new Dimension(150, 15)); netmaskLabel = new JLabel(messages.getString("network_msg4")); netmaskLabel.setSize(new Dimension(150, 15)); netmaskLabel.setPreferredSize(new Dimension(150, 15)); macLabel = new JLabel(messages.getString("network_msg9")); macLabel.setSize(new Dimension(150, 15)); macLabel.setPreferredSize(new Dimension(150, 15)); bs = this.dp.getBetriebssystem(); nic = (NetzwerkInterface) ((Host) bs.getKnoten()).getNetzwerkInterfaces().get(0); ipField = new JTextField(nic.getIp()); ipField.setEditable(false); ipField.setSize(new Dimension(150, 15)); ipField.setPreferredSize(new Dimension(150, 15)); ipField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, ipField); } }); dnsField = new JTextField(bs.getDNSServer()); dnsField.setEditable(false); dnsField.setSize(new Dimension(150, 15)); dnsField.setPreferredSize(new Dimension(150, 15)); dnsField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, dnsField); } }); gatewayField = new JTextField(bs.getStandardGateway()); gatewayField.setEditable(false); gatewayField.setSize(new Dimension(150, 15)); gatewayField.setPreferredSize(new Dimension(150, 15)); gatewayField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, gatewayField); } }); netmaskField = new JTextField(nic.getSubnetzMaske()); netmaskField.setEditable(false); netmaskField.setSize(new Dimension(150, 15)); netmaskField.setPreferredSize(new Dimension(150, 15)); netmaskField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, netmaskField); } }); macField = new JTextField(nic.getMac()); macField.setEditable(false); macField.setSize(new Dimension(150, 15)); macField.setPreferredSize(new Dimension(150, 15)); changeButton = new JButton(messages.getString("network_msg5")); changeButton.setToolTipText(messages.getString("network_msg6")); changeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { istGueltig = true; // Um den nächsten Testdurchlauf zu // ermöglichen if (!EingabenUeberpruefung.isGueltig(ipField.getText(), EingabenUeberpruefung.musterIpAdresse) || istGueltig == false) istGueltig = false; if (!EingabenUeberpruefung.isGueltig(netmaskField.getText(), EingabenUeberpruefung.musterIpAdresse) || istGueltig == false) istGueltig = false; if (!EingabenUeberpruefung.isGueltig(dnsField.getText(), EingabenUeberpruefung.musterIpAdresse) || istGueltig == false) istGueltig = false; if (!EingabenUeberpruefung.isGueltig(gatewayField.getText(), EingabenUeberpruefung.musterIpAdresse) || istGueltig == false) istGueltig = false; if (istGueltig == true) { bs.setzeIPAdresse(ipField.getText()); bs.setzeNetzmaske(netmaskField.getText()); bs.setDNSServer(dnsField.getText()); bs.setStandardGateway(gatewayField.getText()); } else { JOptionPane.showMessageDialog(dp, messages.getString("network_msg7")); } } }); Box backBox = Box.createVerticalBox(); Box ipBox = Box.createHorizontalBox(); ipBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); ipBox.add(ipLabel); ipBox.add(Box.createHorizontalStrut(5)); ipBox.add(ipField); Box maskBox = Box.createHorizontalBox(); maskBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); maskBox.add(netmaskLabel); maskBox.add(Box.createHorizontalStrut(5)); maskBox.add(netmaskField); Box dnsBox = Box.createHorizontalBox(); dnsBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); dnsBox.add(dnsLabel); dnsBox.add(Box.createHorizontalStrut(5)); dnsBox.add(dnsField); Box gateBox = Box.createHorizontalBox(); gateBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); gateBox.add(gatewayLabel); gateBox.add(Box.createHorizontalStrut(5)); gateBox.add(gatewayField); Box macBox = Box.createHorizontalBox(); macBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); macBox.add(macLabel); macBox.add(Box.createHorizontalStrut(5)); macBox.add(macField); Box buttonBox = Box.createHorizontalBox(); buttonBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); buttonBox.add(changeButton); backBox.setBorder(BorderFactory.createBevelBorder(2)); backBox.add(ipBox); backBox.add(Box.createHorizontalStrut(5)); backBox.add(maskBox); backBox.add(Box.createHorizontalStrut(5)); backBox.add(gateBox); backBox.add(Box.createHorizontalStrut(5)); backBox.add(dnsBox); backBox.add(Box.createHorizontalStrut(10)); backBox.add(macBox); backBox.add(Box.createHorizontalStrut(5)); // backBox.add(buttonBox); add(backBox, BorderLayout.CENTER); } /** * Funktion die waehrend der Eingabe ueberprueft ob die bisherige Eingabe einen korrekten Wert darstellt. * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ public void ueberpruefen(Pattern pruefRegel, JTextField feld) { if (EingabenUeberpruefung.isGueltig(feld.getText(), pruefRegel)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); JTextField test = new JTextField(); feld.setBorder(test.getBorder()); } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setBorder(BorderFactory.createLineBorder(EingabenUeberpruefung.farbeFalsch, 1)); } } public void setVisible(boolean b) { if (b) { updateInfo(); } super.setVisible(b); } public void updateInfo() { // bring data up-to-date: bs = this.dp.getBetriebssystem(); NetzwerkInterface nic = (NetzwerkInterface) ((Host) bs.getKnoten()).getNetzwerkInterfaces().get(0); ipField.setText(nic.getIp()); dnsField.setText(bs.getDNSServer()); gatewayField.setText(bs.getStandardGateway()); netmaskField.setText(nic.getSubnetzMaske()); } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/OrdnerInhaltListRenderer.java000066400000000000000000000060031506574241500311310ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.Color; import java.awt.Component; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import filius.rahmenprogramm.I18n; // import sun.misc.JavaLangAccess; /** * * Dient dazu in einer JList Ordner und Dateien mit Symbolen darzustellen * * @author Hannes * */ @SuppressWarnings("serial") public class OrdnerInhaltListRenderer extends JLabel implements ListCellRenderer, I18n { private ImageIcon dateiIcon, ordnerIcon; public OrdnerInhaltListRenderer() { super(); this.dateiIcon = new ImageIcon(getClass().getResource("/gfx/desktop/datei.png")); this.ordnerIcon = new ImageIcon(getClass().getResource("/gfx/desktop/ordner.png")); } @Override public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) { String[] teile = value.split(";", 2); if (teile.length > 0) { if (teile[0].equals(messages.getString("fileexplorer_msg10"))) { setIcon(dateiIcon); } else { setIcon(ordnerIcon); } setText(teile[1]); } /* Wenn Selektiert: Farbe ändern */ if (isSelected) { setBackground(new Color(128, 200, 255)); setOpaque(true); } else { setOpaque(false); } /* Wenn Fokus: Border setzen */ if (cellHasFocus) { setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0))); } else { setBorder(null); } /* experimentell: Feste ZellenHoehe basierend auf JLabel Hoehe */ return this; } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/SatViewer.java000066400000000000000000000077421506574241500261410ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; import filius.gui.JMainFrame; import filius.hardware.knoten.Switch; import filius.rahmenprogramm.I18n; import filius.software.system.SwitchFirmware; @SuppressWarnings("serial") public class SatViewer extends JDialog implements I18n, PropertyChangeListener { private Switch sw; private DefaultTableModel dtm; public SatViewer(Switch sw) { super(JMainFrame.getJMainFrame(), messages.getString("guievents_msg8") + " " + sw.holeAnzeigeName()); this.sw = sw; init(); updateSat(); } private void init() { setBounds(100, 100, 400, 280); ImageIcon icon = new ImageIcon(getClass().getResource("/gfx/hardware/switch.png")); setIconImage(icon.getImage()); dtm = new DefaultTableModel(0, 3); JTable tableSATNachrichten = new JTable(dtm); DefaultTableColumnModel dtcm = (DefaultTableColumnModel) tableSATNachrichten.getColumnModel(); dtcm.getColumn(0).setHeaderValue(messages.getString("guievents_msg9")); dtcm.getColumn(0).setPreferredWidth(150); dtcm.getColumn(1).setHeaderValue(messages.getString("guievents_msg10")); dtcm.getColumn(1).setPreferredWidth(100); dtcm.getColumn(2).setHeaderValue(messages.getString("guievents_msg27")); dtcm.getColumn(2).setPreferredWidth(150); JPopupMenu menu = new JPopupMenu(); JMenuItem resetMenuItem = new JMenuItem(messages.getString("guievents_msg28")); resetMenuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { menu.setVisible(false); ((SwitchFirmware) sw.getSystemSoftware()).loescheSAT(); } }); menu.add(resetMenuItem); tableSATNachrichten.setComponentPopupMenu(menu); JScrollPane spSAT = new JScrollPane(tableSATNachrichten); getContentPane().add(spSAT); } public Switch getSwitch() { return sw; } private void updateSat() { dtm.setRowCount(0); for (Vector zeile : ((SwitchFirmware) sw.getSystemSoftware()).holeSAT()) { dtm.addRow(zeile); } } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName() == "sat_entry") { updateSat(); } } } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/SimulationControl.java000066400000000000000000000075641506574241500277170ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.io.IOException; import java.io.OutputStream; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import filius.gui.ControlPanel; import filius.hardware.Verbindung; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.rahmenprogramm.nachrichten.LauscherBeobachter; @SuppressWarnings("serial") public class SimulationControl extends ControlPanel implements I18n, LauscherBeobachter { private JTextPane textPane; public SimulationControl() { super(); reInit(); } protected void initContents() { JButton dropButton = new JButton(messages.getString("simulation_control_drop_packets")); dropButton.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent evt) { ButtonModel model = ((JButton) evt.getSource()).getModel(); if (model.isPressed() && !Verbindung.isDrop()) { Verbindung.setDrop(true); } else if (!model.isPressed() && Verbindung.isDrop()) { Verbindung.setDrop(false); } } }); Box tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(dropButton); rightBox.add(tempBox, BorderLayout.NORTH); textPane = new JTextPane(); JScrollPane scrollPane = new JScrollPane(); scrollPane.setViewportView(textPane); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); box.add(scrollPane); Lauscher.getLauscher().resetDroppedDataUnits(); Lauscher.getLauscher().addBeobachter(Lauscher.DROPPED, this); } @Override public void update() { StringBuffer buffer = new StringBuffer(); for (String dataUnit : Lauscher.getLauscher().getDroppedDataUnits()) { buffer.append(dataUnit).append("\n"); } textPane.setText(buffer.toString()); } @Override public void writeToStream(OutputStream outputStream) throws IOException {} } filius-2.9.4+ds/src/main/java/filius/gui/anwendungssicht/VHostConfigTable.java000066400000000000000000000040751506574241500273650ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; import javax.swing.table.TableModel; import filius.gui.JExtendedTable; import filius.software.www.WebServer; @SuppressWarnings("serial") public class VHostConfigTable extends JExtendedTable { public VHostConfigTable(TableModel model, boolean editable) { super(model, editable); } public void editingStopped(ChangeEvent e) { TableCellEditor editor = getCellEditor(); if (editor != null) { String value = (String) editor.getCellEditorValue(); ((WebServer) ((GUIApplicationWebServerWindow) parentGUI).holeAnwendung()).changeVHostTable(editingRow, editingColumn, value); setValueAt(value, editingRow, editingColumn); removeEditor(); } } } filius-2.9.4+ds/src/main/java/filius/gui/documentation/000077500000000000000000000000001506574241500230205ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/documentation/ReportGenerator.java000066400000000000000000000433501506574241500270120ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.documentation; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.lang3.StringUtils; import com.itextpdf.text.BadElementException; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Chunk; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.FontFactory; import com.itextpdf.text.Image; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.Rectangle; import com.itextpdf.text.Section; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPHeaderCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; import filius.gui.GUIContainer; import filius.gui.netzwerksicht.GUIKnotenItem; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.SzenarioVerwaltung; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.Anwendung; import filius.software.dns.DNSServer; import filius.software.dns.ResourceRecord; import filius.software.system.Betriebssystem; import filius.software.system.GatewayFirmware; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; public class ReportGenerator { private static final Font SMALL_FONT = FontFactory.getFont(FontFactory.HELVETICA, 8, BaseColor.BLACK); private static final Font SMALL_BOLD_FONT = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 8, BaseColor.BLACK); private static final Font BOLD_FONT = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 10, BaseColor.BLACK); private static final Font DEFAULT_FONT = FontFactory.getFont(FontFactory.HELVETICA, 10, BaseColor.BLACK); private Lauscher lauscher; private static ReportGenerator singleton; private int sectionNoLevel1; private int sectionNoLevel2; ReportGenerator() {} public static ReportGenerator getInstance() { if (null == singleton) { singleton = new ReportGenerator(); singleton.lauscher = Lauscher.getLauscher(); } return singleton; } private void resetSections() { sectionNoLevel1 = 0; sectionNoLevel2 = 0; } public void generateReport(String pdfFilepath) throws DocumentException, IOException { Document document = initDocument(pdfFilepath); resetSections(); addOverviewSection(document); document.add(Chunk.NEXTPAGE); addComponentConfigSection(document); document.add(Chunk.NEXTPAGE); addNetworkTrafficSection(document); closeDocument(document); } void addOverviewSection(Document document) throws BadElementException, IOException, DocumentException { createSection(document, I18n.messages.getString("report_overview"), 1); Image img = Image.getInstance(GUIContainer.getGUIContainer().createNetworkImage(), null); float percent = (document.right() - document.left()) / img.getWidth() * 100; img.scalePercent(percent); document.add(img); } void addComponentConfigSection(Document document) throws BadElementException, IOException, DocumentException { createSection(document, I18n.messages.getString("report_component_config"), 1); List components = GUIContainer.getGUIContainer().getKnotenItems(); for (GUIKnotenItem item : components) { if (item.getKnoten() instanceof InternetKnoten) { InternetKnotenBetriebssystem systemSoftware = (InternetKnotenBetriebssystem) item.getKnoten() .getSystemSoftware(); createSection(document, item.getKnoten().holeAnzeigeName(), 2); createSection(document, I18n.messages.getString("report_base_config"), 3); PdfPTable configTable = new PdfPTable(2); configTable.setHorizontalAlignment(Element.ALIGN_LEFT); configTable.setTotalWidth(new float[] { 180, 360 }); configTable.setLockedWidth(false); addConfigParam(I18n.messages.getString("jhostkonfiguration_msg5"), systemSoftware.getStandardGateway(), configTable); addConfigParam(I18n.messages.getString("jhostkonfiguration_msg6"), systemSoftware.getDNSServer(), configTable); if (systemSoftware instanceof Betriebssystem) { addConfigParam(I18n.messages.getString("jhostkonfiguration_msg7"), String.valueOf(((Betriebssystem) systemSoftware).isDHCPKonfiguration()), configTable); } document.add(configTable); document.add(Chunk.NEWLINE); InternetKnoten node = (InternetKnoten) item.getKnoten(); PdfPTable interfaceTable = new PdfPTable(3); interfaceTable.setHorizontalAlignment(Element.ALIGN_LEFT); interfaceTable.setTotalWidth(new float[] { 180, 180, 180 }); interfaceTable.setLockedWidth(false); addHeaderCell(I18n.messages.getString("jhostkonfiguration_msg9"), interfaceTable); addHeaderCell(I18n.messages.getString("jhostkonfiguration_msg3"), interfaceTable); addHeaderCell(I18n.messages.getString("jhostkonfiguration_msg4"), interfaceTable); createSection(document, I18n.messages.getString("report_nics"), 3); for (NetzwerkInterface networkInterface : node.getNetzwerkInterfaces()) { addCell(networkInterface.getMac(), interfaceTable); addCell(networkInterface.getIp(), interfaceTable); addCell(networkInterface.getSubnetzMaske(), interfaceTable); } document.add(interfaceTable); document.add(Chunk.NEWLINE); if (systemSoftware instanceof Betriebssystem) { createSection(document, I18n.messages.getString("report_apps"), 3); Chunk chunk = new Chunk(I18n.messages.getString("installationsdialog_msg3") + " ", BOLD_FONT); document.add(chunk); Anwendung[] apps = ((Betriebssystem) systemSoftware).holeArrayInstallierteSoftware(); String[] appNames = new String[apps.length]; for (int i = 0; i < apps.length; i++) { appNames[i] = apps[i].holeAnwendungsName(); } String appList = StringUtils.join(appNames, ", "); chunk = new Chunk(StringUtils.isBlank(appList) ? "-" : appList, DEFAULT_FONT); document.add(chunk); document.add(Chunk.NEWLINE); document.add(Chunk.NEWLINE); } DNSServer dnsServer = (DNSServer) systemSoftware.holeSoftware("filius.software.dns.DNSServer"); if (dnsServer != null) { createSection(document, I18n.messages.getString("report_dns_rr"), 3); PdfPTable dnsTable = new PdfPTable(3); dnsTable.setHorizontalAlignment(Element.ALIGN_LEFT); dnsTable.setTotalWidth(new float[] { 180, 60, 260 }); dnsTable.setLockedWidth(false); addHeaderCell(I18n.messages.getString("report_domain"), dnsTable); addHeaderCell(I18n.messages.getString("report_type"), dnsTable); addHeaderCell(I18n.messages.getString("report_data"), dnsTable); for (ResourceRecord resourceRecord : dnsServer.holeResourceRecords()) { addCell(resourceRecord.getDomainname(), dnsTable); addCell(resourceRecord.getType(), dnsTable); addCell(resourceRecord.getRdata(), dnsTable); } document.add(dnsTable); document.add(Chunk.NEWLINE); } createSection(document, I18n.messages.getString("report_routing_table"), 3); PdfPTable forwardingTable = new PdfPTable(4); forwardingTable.setHorizontalAlignment(Element.ALIGN_LEFT); forwardingTable.setTotalWidth(new float[] { 180, 180, 180, 180 }); forwardingTable.setLockedWidth(false); addHeaderCell(I18n.messages.getString("jweiterleitungstabelle_msg3"), forwardingTable); addHeaderCell(I18n.messages.getString("jweiterleitungstabelle_msg4"), forwardingTable); addHeaderCell(I18n.messages.getString("jweiterleitungstabelle_msg5"), forwardingTable); addHeaderCell(I18n.messages.getString("jweiterleitungstabelle_msg6"), forwardingTable); for (String[] routeEntry : systemSoftware.getWeiterleitungstabelle().holeTabelle()) { if(EingabenUeberpruefung.isGueltig(routeEntry[0],EingabenUeberpruefung.musterIpAdresse)) { for (int i = 0; i < routeEntry.length; i++) { addCell(routeEntry[i], forwardingTable); } } } document.add(forwardingTable); document.add(Chunk.NEWLINE); if (systemSoftware instanceof GatewayFirmware) { PdfPTable portForwardingTable = new PdfPTable(4); portForwardingTable.setHorizontalAlignment(Element.ALIGN_LEFT); portForwardingTable.setTotalWidth(new float[] { 180, 180, 180, 180 }); portForwardingTable.setLockedWidth(false); addHeaderCell(I18n.messages.getString("jportforwarding_msg4"), portForwardingTable); addHeaderCell(I18n.messages.getString("jportforwarding_msg5"), portForwardingTable); addHeaderCell(I18n.messages.getString("jportforwarding_msg6"), portForwardingTable); addHeaderCell(I18n.messages.getString("jportforwarding_msg7"), portForwardingTable); boolean notEmpty = false; for (String[] routeEntry : ((GatewayFirmware) systemSoftware).getStaticNAT()) { System.out.println(routeEntry[0]+" "+EingabenUeberpruefung.musterProtocol); if(EingabenUeberpruefung.isGueltig(routeEntry[0],EingabenUeberpruefung.musterProtocol)) { notEmpty = true; switch(routeEntry[0]) { case (""+IpPaket.TCP): case "TCP": routeEntry[0] = "TCP"; break; case (""+IpPaket.UDP): case "UDP": routeEntry[0] = "UDP"; break; } for (int i = 0; i < routeEntry.length; i++) { addCell(routeEntry[i], portForwardingTable); } } } if(notEmpty) { createSection(document, I18n.messages.getString("jgatewayconfiguration_msg19"), 3); document.add(portForwardingTable); document.add(Chunk.NEWLINE); } } } } } private void addConfigParam(String key, String value, PdfPTable table) { addHeaderCell(key, table); addCell(value, table); } private void addCell(String value, PdfPTable table) { PdfPCell cell = new PdfPCell(new Phrase(value, DEFAULT_FONT)); cell.setBorder(Rectangle.NO_BORDER); table.addCell(cell); } private void addHeaderCell(String key, PdfPTable table) { PdfPCell header = new PdfPHeaderCell(); header.setBorder(Rectangle.NO_BORDER); header.setPhrase(new Phrase(key, BOLD_FONT)); table.addCell(header); } void addNetworkTrafficSection(Document document) throws BadElementException, IOException, DocumentException { String[] columnHeader = lauscher.getHeader(); Collection interfaceIDs = lauscher.getInterfaceIDs(); if (interfaceIDs.size() > 0) { createSection(document, I18n.messages.getString("report_network_traffic"), 1); } for (String interfaceId : interfaceIDs) { String hostname = "Unknown"; String ipAddress = "0.0.0.0"; for (GUIKnotenItem item : GUIContainer.getGUIContainer().getKnotenItems()) { if (item.getKnoten() instanceof InternetKnoten) { InternetKnoten node = (InternetKnoten) item.getKnoten(); NetzwerkInterface nic = node.getNetzwerkInterfaceByMac(interfaceId); if (nic != null) { hostname = item.getKnoten().holeAnzeigeName(); ipAddress = nic.getIp(); break; } } } createSection(document, hostname + " - " + ipAddress, 2); PdfPTable table = new PdfPTable(columnHeader.length); table.setTotalWidth(new float[] { 20, 60, 80, 80, 40, 50, 180 }); table.setLockedWidth(true); for (int i = 0; i < columnHeader.length; i++) { PdfPCell header = new PdfPHeaderCell(); header.setBackgroundColor(new BaseColor(230, 240, 255)); header.setBorder(Rectangle.NO_BORDER); header.setPhrase(new Phrase(columnHeader[i], SMALL_BOLD_FONT)); table.addCell(header); } Object[][] data = lauscher.getDaten(interfaceId, true, 0); for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { PdfPCell cell = new PdfPCell(new Phrase((String) data[i][j], SMALL_FONT)); cell.setBorder(Rectangle.NO_BORDER); if (i % 2 == 1) { cell.setBackgroundColor(new BaseColor(240, 240, 240)); } table.addCell(cell); } } document.add(table); document.add(Chunk.NEWLINE); } } private void createSection(Document document, String title, int level) throws DocumentException { ArrayList numbers = new ArrayList(); if (level == 1) { numbers.add(++sectionNoLevel1); sectionNoLevel2 = 0; } else if (level == 2) { numbers.add(++sectionNoLevel2); numbers.add(sectionNoLevel1); } Font font = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 16 - 2 * level, BaseColor.DARK_GRAY); Paragraph sectionHeader = Section.constructTitle(new Paragraph(title, font), numbers, level, Section.NUMBERSTYLE_DOTTED); document.add(sectionHeader); } void closeDocument(Document document) { document.close(); GUIContainer.getGUIContainer().updateViewport(); } Document initDocument(String pdfFilepath) throws DocumentException, FileNotFoundException { Document document = new Document(); PdfWriter.getInstance(document, new FileOutputStream(pdfFilepath)); document.open(); String storagePath = SzenarioVerwaltung.getInstance().holePfad(); String title; if (storagePath != null) { String filename = new File(storagePath).getName(); if (filename.contains(".")) { filename = filename.substring(0, filename.lastIndexOf('.')); } title = I18n.messages.getString("report_title") + ": " + filename; } else { title = I18n.messages.getString("report_title"); } document.addTitle(title); document.addCreator("Filius (www.lernsoftware-filius.de)"); Font font = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 18, new BaseColor(30, 50, 120)); Paragraph paragraph = new Paragraph(title, font); paragraph.setAlignment(Paragraph.ALIGN_CENTER); document.add(paragraph); if (null != storagePath) { document.add(new Chunk(storagePath, SMALL_FONT)); } document.add(Chunk.NEWLINE); return document; } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/000077500000000000000000000000001506574241500234705ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/AggregatedExchangeComponent.java000066400000000000000000000026651506574241500317240ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import javax.swing.JRootPane; public interface AggregatedExchangeComponent extends ExchangeComponent { String getTabTitle(String interfaceId); JRootPane getRootPane(); } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/AggregatedExchangeDialog.java000066400000000000000000000076101506574241500311540ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.BorderLayout; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLayeredPane; import javax.swing.JPanel; import filius.rahmenprogramm.I18n; import filius.software.system.SystemSoftware; /** * This class is used to show exchanged messages between components. Its functionality shall be akin to that of * wireshark. * * @author stefan */ @SuppressWarnings("serial") public class AggregatedExchangeDialog extends JDialog implements ExchangeComponent, I18n { private static AggregatedExchangeDialog instance = null; private AggregatedExchangePanel exchangePanel; public static AggregatedExchangeDialog getInstance(JFrame owner) { if (instance == null) { instance = new AggregatedExchangeDialog(owner); } return instance; } private AggregatedExchangeDialog(JFrame owner) { super(owner); ((JFrame) owner).getLayeredPane().setLayer(this, JLayeredPane.PALETTE_LAYER); Image image; setTitle(messages.getString("lauscherdialog_msg1")); int screenHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight(); int screenWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth(); setBounds(1 * screenWidth / 8, screenHeight / 10, 3 * screenWidth / 4, 4 * screenHeight / 5); image = Toolkit.getDefaultToolkit() .getImage(getClass().getResource("/gfx/allgemein/nachrichtenfenster_icon.png")); setIconImage(image); this.setModal(false); setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); exchangePanel = new AggregatedExchangePanel(); getContentPane().add(exchangePanel, BorderLayout.CENTER); this.setVisible(false); this.addWindowListener(new WindowAdapter() { @Override public void windowActivated(WindowEvent e) { AggregatedExchangeDialog.this.exchangePanel.updateTabTitle(); AggregatedExchangeDialog.this.exchangePanel.clearUnavailableComponents(); } }); } @Override public void reset() { if (instance != null) { instance.setVisible(false); } instance = null; } /** * Diese Methode fuegt eine Tabelle hinzu */ @Override public void addTable(SystemSoftware system, String identifier) { exchangePanel.addTable(system, identifier); } @Override public void removeTable(String mac, JPanel panel) { exchangePanel.removeTable(mac, panel); } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/AggregatedExchangePanel.java000066400000000000000000000206011506574241500310070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Hashtable; import java.util.Map.Entry; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.plaf.basic.BasicButtonUI; import filius.hardware.knoten.Host; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.system.SystemSoftware; /** * This class is used to show exchanged messages between components. Its functionality shall be akin to that of * wireshark. * * @author stefan */ @SuppressWarnings({ "serial", "deprecation" }) public class AggregatedExchangePanel extends JTabbedPane implements AggregatedExchangeComponent, I18n, Observer { private Hashtable openedTabs = new Hashtable(); private Hashtable systems = new Hashtable(); private Hashtable tabellen = new Hashtable(); /** * Diese Methode fuegt eine Tabelle hinzu */ @Override public void addTable(SystemSoftware system, String identifier) { final AggregatedMessageTable tabelle; system.addObserver(this); final MessageDetails messageDetails; if (Information.getInformation().isLayerVisualization()) { messageDetails = new MessageDetailsTable(identifier, 0, Color.WHITE); } else { messageDetails = new MessageDetailsPanel(identifier); } if (openedTabs.get(identifier) == null) { tabelle = new AggregatedMessageTable(this, identifier, system); tabelle.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (tabelle.getSelectedRow() >= 0) { messageDetails.update(identifier, tabelle.getSelectedRow() + 1); } else { messageDetails.clear(); } } }); JPanel panel = new JPanel(new BorderLayout()); JScrollPane scrollPane; scrollPane = new JScrollPane(tabelle); tabelle.setScrollPane(scrollPane); JSplitPane splitPane = new JSplitPane(); // prevent the SplitPane to get user input (mainly to lock the divider) splitPane.setEnabled(false); splitPane.setDividerLocation(500); splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); splitPane.setTopComponent(scrollPane); splitPane.setBottomComponent(new JScrollPane(messageDetails)); splitPane.setOneTouchExpandable(true); panel.add(splitPane, BorderLayout.CENTER); add(panel); setSelectedComponent(panel); TabTitle title = new TabTitle(this, identifier); setTabComponentAt(getSelectedIndex(), title); openedTabs.put(identifier, panel); systems.put(identifier, (InternetKnotenBetriebssystem) system); tabellen.put(identifier, tabelle); if (openedTabs.size() > 0) { setVisible(true); } updateTabTitle(); } else { // if there is already a tab opened for this system set it to selected setSelectedComponent(openedTabs.get(identifier)); tabellen.get(identifier).update(); } } void updateTabTitle() { for (int i = 0; i < getTabCount(); i++) { for (String identifier : openedTabs.keySet()) { if (getComponentAt(i).equals(openedTabs.get(identifier))) { SystemSoftware system = systems.get(identifier); String ipAddress = ((InternetKnoten) system.getKnoten()).getNetzwerkInterfaceByMac(identifier) .getIp(); String tabTitle; if (system.getKnoten() instanceof Host && ((Host) system.getKnoten()).isUseIPAsName()) { tabTitle = ipAddress; } else { tabTitle = system.getKnoten().holeAnzeigeName() + " - " + ipAddress; } TabTitle titlePanel = (TabTitle) getTabComponentAt(i); titlePanel.setTitle(tabTitle); break; } } } } void clearUnavailableComponents() { for (Entry system : systems.entrySet()) { if (!system.getValue().isStarted()) { removeTable(system.getKey()); } } } private void removeTable(String mac) { removeTable(mac, openedTabs.get(mac)); } @Override public void removeTable(String mac, JPanel panel) { if (mac != null) { openedTabs.remove(mac); tabellen.remove(mac); remove(panel); if (openedTabs.size() == 0) { setVisible(false); } } } public String getTabTitle(String interfaceId) { String title = interfaceId.replaceAll(":", "-"); for (int i = 0; i < getTabCount(); i++) { Component tab = getComponentAt(i); if (tab == openedTabs.get(interfaceId)) { title = ((TabTitle) getTabComponentAt(i)).getTitle(); break; } } return title; } @Override public void reset() {} private class TabTitle extends JPanel { private JLabel label; TabTitle(AggregatedExchangePanel parent, String identifier) { setOpaque(false); label = new JLabel(); add(label, BorderLayout.WEST); JButton btnClose = new JButton("X"); btnClose.setUI(new BasicButtonUI()); btnClose.setForeground(Color.GRAY); btnClose.setBorder(BorderFactory.createEmptyBorder()); btnClose.setPreferredSize(new Dimension(18, 18)); btnClose.setToolTipText(messages.getString("buttontabcomponent_msg1")); add(btnClose, BorderLayout.EAST); btnClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { parent.removeTable(identifier); } }); } void setTitle(String title) { label.setText(title); } String getTitle() { return label.getText(); } } @Override public void update(Observable o, Object arg) { updateTabTitle(); } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/AggregatedMessageTable.java000066400000000000000000000607601506574241500306530ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.Color; import java.awt.Dimension; import java.awt.FileDialog; import java.awt.Point; import java.awt.event.MouseEvent; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Vector; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.JMainFrame; import filius.gui.schichtensicht.GlobalLayerPath; import filius.gui.schichtensicht.LayerPathDialog; import filius.gui.schichtensicht.LocalLayerPathPanel; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.SzenarioVerwaltung; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.rahmenprogramm.nachrichten.LauscherBeobachter; import filius.software.netzzugangsschicht.Ethernet; import filius.software.system.SystemSoftware; public class AggregatedMessageTable extends JTable implements LauscherBeobachter, I18n { private static Logger LOG = LoggerFactory.getLogger(AggregatedMessageTable.class); private static final int EXPORT_MAX_LINES_PER_MESSAGE = 5; private static final long serialVersionUID = 1L; /** Index der Spalte, in der die Schicht des Protokollschichtenmodells steht */ public static final int SCHICHT_SPALTE = 5; public static final int BEMERKUNG_SPALTE = 6; public static final int LFD_NR_SPALTE = 0; private SystemSoftware systemSoftware; private String interfaceId; private int selectedRowByRightclick; private JCheckBoxMenuItem checkbox; private AggregatedExchangeComponent exchangeComponent; private JScrollPane scrollPane = null; private boolean autoscroll = true; private JPopupMenu menu; public AggregatedMessageTable(AggregatedExchangeComponent component, String macAddress, SystemSoftware systemSoftware) { super(); TableColumn col; DefaultTableColumnModel columnModel; this.systemSoftware = systemSoftware; this.exchangeComponent = component; setinterfaceId(macAddress); this.initTableModel(); this.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); columnModel = new DefaultTableColumnModel(); String[] spalten = Lauscher.SPALTEN; for (int i = 0; i < spalten.length; i++) { col = new TableColumn(); col.setHeaderValue(spalten[i]); col.setIdentifier(spalten[i]); col.setModelIndex(i); columnModel.addColumn(col); } this.setColumnModel(columnModel); this.setIntercellSpacing(new Dimension(0, 5)); this.setRowHeight(25); this.setEnabled(true); this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.setShowGrid(false); this.setColumnSelectionAllowed(false); this.setBackground(Color.DARK_GRAY); this.getTableHeader().setReorderingAllowed(false); this.setFillsViewportHeight(true); initTableColumnWidth(); this.setDefaultRenderer(Object.class, new LauscherTableCellRenderer()); initKontextMenue(); this.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { if (e.getButton() == 3) { AggregatedMessageTable target = (AggregatedMessageTable) e.getSource(); target.selectedRowByRightclick = target.rowAtPoint(e.getPoint()); // Falls der Rechtsklick im leeren Bereich gemacht wurde if (target.selectedRowByRightclick == -1) { disablePathOptions(); } else { target.setRowSelectionInterval(target.selectedRowByRightclick, target.selectedRowByRightclick); enablePathOptions(); } menu.setVisible(true); menu.show(getTabelle(), e.getX(), e.getY()); } } }); update(); } private void initTableModel() { LauscherTableModel tableModel = new LauscherTableModel(); tableModel.addTableModelListener(this); tableModel.setColumnIdentifiers(Lauscher.SPALTEN); this.setModel(tableModel); } public void setScrollPane(JScrollPane scrollPane) { this.scrollPane = scrollPane; } private AggregatedMessageTable getTabelle() { return this; } private void initTableColumnWidth() { this.getColumnModel().getColumn(0).setMaxWidth(40); this.getColumnModel().getColumn(0).setPreferredWidth(30); this.getColumnModel().getColumn(1).setMaxWidth(120); this.getColumnModel().getColumn(1).setPreferredWidth(90); this.getColumnModel().getColumn(2).setMaxWidth(180); this.getColumnModel().getColumn(2).setPreferredWidth(160); this.getColumnModel().getColumn(3).setMaxWidth(180); this.getColumnModel().getColumn(3).setPreferredWidth(160); this.getColumnModel().getColumn(4).setMaxWidth(100); this.getColumnModel().getColumn(4).setPreferredWidth(70); this.getColumnModel().getColumn(5).setMaxWidth(140); this.getColumnModel().getColumn(5).setPreferredWidth(100); this.getColumnModel().getColumn(6).setMaxWidth(Integer.MAX_VALUE); this.getColumnModel().getColumn(6).setPreferredWidth(460); this.getColumnModel().getColumn(6).setResizable(true); } private int initKontextMenue() { menu = new JPopupMenu(); AggregatedMessageTable target = this; if (Information.getInformation().isLayerVisualization()) { /* * JMenuItem localPathThroughLayersMenuItem = new JMenuItem(messages.getString("nachrichtentabelle_msg10")); * localPathThroughLayersMenuItem.addMouseListener(new MouseInputAdapter() { public void * mousePressed(MouseEvent e) { if (isPathOptionsEnabled()) { menu.setVisible(false); * showLocalPathThroughLayers(systemSoftware, target.selectedRowByRightclick + 1); } } }); * menu.add(localPathThroughLayersMenuItem); */ JMenuItem globalPathThroughLayersMenuItem = new JMenuItem(messages.getString("nachrichtentabelle_msg11")); globalPathThroughLayersMenuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { if (isPathOptionsEnabled()) { menu.setVisible(false); showGlobalPathThroughLayers(systemSoftware, target.selectedRowByRightclick + 1); } } }); menu.add(globalPathThroughLayersMenuItem); } JMenuItem resetMenuItem = new JMenuItem(messages.getString("nachrichtentabelle_msg7")); resetMenuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { menu.setVisible(false); Lauscher.getLauscher().reset(); } }); menu.add(resetMenuItem); JMenuItem exportMenuItem = new JMenuItem(messages.getString("nachrichtentabelle_msg9")); exportMenuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { menu.setVisible(false); FileDialog fileDialog = new FileDialog(JMainFrame.getJMainFrame(), messages.getString("main_msg4"), FileDialog.SAVE); FilenameFilter filiusFilenameFilter = (dir, name) -> name.endsWith(".txt"); fileDialog.setFilenameFilter(filiusFilenameFilter); String path = SzenarioVerwaltung.getInstance().holePfad(); if (path != null) { File preselectedFile = new File( exchangeComponent.getTabTitle(AggregatedMessageTable.this.interfaceId) + ".txt"); fileDialog.setFile(preselectedFile.toString()); } fileDialog.setVisible(true); if (fileDialog.getFile() != null) { Path outFile; boolean nameChanged = false; if (StringUtils.endsWith(fileDialog.getFile(), ".txt")) { outFile = Path.of(fileDialog.getDirectory(), fileDialog.getFile()); } else { nameChanged = true; outFile = Path.of(fileDialog.getDirectory(), fileDialog.getFile() + ".txt"); } int entscheidung = JOptionPane.YES_OPTION; if (nameChanged && outFile.toFile().exists()) { entscheidung = JOptionPane.showConfirmDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg17"), messages.getString("guimainmemu_msg10"), JOptionPane.YES_NO_OPTION); } if (entscheidung == JOptionPane.YES_OPTION) { try (FileOutputStream outputStream = new FileOutputStream(outFile.toString())) { writeToStream(outputStream); } catch (IOException e1) {} } } } }); menu.add(exportMenuItem); checkbox = new JCheckBoxMenuItem(messages.getString("nachrichtentabelle_msg8"), autoscroll); checkbox.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (autoscroll != checkbox.getState()) { menu.setVisible(false); autoscroll = checkbox.getState(); update(); } } }); menu.add(checkbox); menu.setVisible(false); exchangeComponent.getRootPane().getLayeredPane().add(menu); return menu.getComponentCount(); } // activate context menu entries "lokaler Weg ..." and "globaler Weg ..." private void enablePathOptions() { if (!menu.getComponent(0).isEnabled() && !menu.getComponent(1).isEnabled()) { menu.getComponent(0).setEnabled(true); menu.getComponent(1).setEnabled(true); } } // deactivate context menu entries "lokaler Weg ..." and "globaler Weg ..." private void disablePathOptions() { if (menu.getComponent(0).isEnabled() && menu.getComponent(1).isEnabled()) { menu.getComponent(0).setEnabled(false); menu.getComponent(1).setEnabled(false); } } private boolean isPathOptionsEnabled() { return menu.getComponent(0).isEnabled() && menu.getComponent(1).isEnabled(); } private void setinterfaceId(String interfaceId) { this.interfaceId = interfaceId; Lauscher.getLauscher().addBeobachter(interfaceId, this); } @Override public synchronized void update() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (NachrichtenTabelle), update()"); int selectedRow = getSelectedRow(); Object[][] daten = Lauscher.getLauscher().getDaten(interfaceId, true, 0); if (daten.length == 0) { initTableModel(); initTableColumnWidth(); } int lastNo = -1; if (getModel().getRowCount() > 0) { lastNo = Integer.parseInt(this.getModel().getValueAt(this.getModel().getRowCount() - 1, 0).toString()); } int currentNo = 1; int previousNo = 1; for (int i = 0; i < daten.length; i++) { currentNo = Integer.parseInt(daten[i][0].toString()); if (currentNo <= lastNo || currentNo == previousNo) { continue; } if (currentNo > previousNo && previousNo > lastNo && i > 0) { Object[] row = daten[i - 1]; addRowData(row); previousNo = currentNo; } } if (currentNo > lastNo && daten.length > 0) { addRowData(daten[daten.length - 1]); } ((DefaultTableModel) this.getModel()).fireTableDataChanged(); if (getModel().getRowCount() > 0 && scrollPane != null && scrollPane.getViewport() != null && autoscroll) { scrollPane.getViewport().setViewPosition(new Point(0, this.getHeight())); } if (selectedRow >= 0 && selectedRow < getModel().getRowCount()) { setRowSelectionInterval(selectedRow, selectedRow); } } private void addRowData(Object[] row) { Vector rowData = new Vector(row.length); for (int col = 0; col < row.length; col++) { rowData.add(col, row[col]); } ((DefaultTableModel) this.getModel()).addRow(rowData); } @Override public synchronized void writeToStream(OutputStream outputStream) throws IOException { String rowTemplate; String lineSeparator; Object[] columnNames = getColumnHeader(); StringBuilder tmplBuilder = new StringBuilder("| "); StringBuilder lineSeparatorBuilder = new StringBuilder("+"); for (int colIdx = 0; colIdx < getColumnCount(); colIdx++) { if (colIdx == BEMERKUNG_SPALTE) { tmplBuilder.append("%-40s | "); lineSeparatorBuilder.append(StringUtils.repeat('-', 42) + "+"); } else if (colIdx == LFD_NR_SPALTE) { tmplBuilder.append("%-10s | "); lineSeparatorBuilder.append(StringUtils.repeat('-', 12) + "+"); } else { tmplBuilder.append("%-20s | "); lineSeparatorBuilder.append(StringUtils.repeat('-', 22) + "+"); } } tmplBuilder.append("\r\n"); rowTemplate = tmplBuilder.toString(); lineSeparatorBuilder.append("\r\n"); lineSeparator = lineSeparatorBuilder.toString(); outputStream.write(lineSeparator.replace('-', '=').getBytes("UTF8")); outputStream.write(String.format(rowTemplate, columnNames).getBytes("UTF8")); outputStream.write(lineSeparator.replace('-', '=').getBytes("UTF8")); for (int rowIndex = 0; rowIndex < getRowCount(); rowIndex++) { String[] values = new String[getColumnCount()]; for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) { values[columnIndex] = getModel().getValueAt(rowIndex, columnIndex).toString(); } List lineData = prepareDataArrays(values, 40); for (String[] data : lineData) { outputStream.write(String.format(rowTemplate, (Object[]) data).getBytes("UTF8")); } outputStream.write(lineSeparator.getBytes("UTF8")); } } public Object[] getColumnHeader() { Object[] columnNames = new Object[getColumnCount()]; for (int colIdx = 0; colIdx < getColumnCount(); colIdx++) { columnNames[colIdx] = getColumnName(colIdx); } return columnNames; } static List prepareDataArrays(String[] values, int maxRemarkLength) { List remarks = Collections. emptyList(); String[] data = new String[values.length]; for (int columnIndex = 0; columnIndex < values.length; columnIndex++) { if (columnIndex == BEMERKUNG_SPALTE) { if (StringUtils.isNoneBlank(values[columnIndex])) { remarks = splitString(values[columnIndex].toString(), maxRemarkLength); data[columnIndex] = remarks.get(0); } } else { data[columnIndex] = values[columnIndex]; } } List lineData = new ArrayList(); lineData.add(data); for (int i = 1; i < remarks.size() && i < EXPORT_MAX_LINES_PER_MESSAGE; i++) { data = new String[values.length]; Arrays.fill(data, StringUtils.EMPTY); data[BEMERKUNG_SPALTE] = remarks.get(i); lineData.add(data); } if (remarks.size() > EXPORT_MAX_LINES_PER_MESSAGE) { data = new String[values.length]; Arrays.fill(data, StringUtils.EMPTY); data[BEMERKUNG_SPALTE] = "..."; lineData.add(data); } return lineData; } /** * Original Filius colors for the different layers. */ public enum LayerColorOrig { DEFAULT(Color.WHITE, Color.GRAY), LINK(Color.BLACK, new Color(0.9f, 0.9f, 0.9f)), INTERNET(Color.BLACK, new Color(0.3f, 1f, 0.3f)), TRANSPORT(Color.BLACK, Color.CYAN), APPLICATION(Color.WHITE, Color.BLUE); public final Color foregroundColor; public final Color backgroundColor; private LayerColorOrig(Color foregroundColor, Color backgroundColor) { this.foregroundColor = foregroundColor; this.backgroundColor = backgroundColor; } } /** * Color variants for the different layers. * * @author Christoph Irniger */ public enum LayerColorVar { DEFAULT(Color.BLACK, Color.decode("#A6A6A6"), Color.WHITE, Color.WHITE, Color.WHITE), LINK(Color.BLACK, Color.decode("#A6A6A6"), Color.decode("#C9C9C9"), Color.decode("#DBDBDB"), Color.decode("#F2F2F2")), INTERNET(Color.BLACK, Color.decode("#A6A6A6"), Color.decode("#8FAADC"), Color.decode("#B4C6E7"), Color.decode("#D9E2F3")), TRANSPORT(Color.BLACK, Color.decode("#A6A6A6"), Color.decode("#F4B183"), Color.decode("#F7CAAC"), Color.decode("#FBE4D5")), APPLICATION(Color.BLACK, Color.decode("#A6A6A6"), Color.decode("#FFD966"), Color.decode("#FFE599"), Color.decode("#FFF2CC")); public final Color foregroundColor; public final Color foregroundColorGreyedOut; public final Color backgroundColor; public final Color backgroundColorFine; public final Color backgroundColorUltraFine; private LayerColorVar(Color foregroundColor, Color foregroundColorGreyedOut, Color backgroundColor, Color backgroundColorFine, Color backgroundColorUltraFine) { this.foregroundColor = foregroundColor; this.foregroundColorGreyedOut = foregroundColorGreyedOut; this.backgroundColor = backgroundColor; this.backgroundColorFine = backgroundColorFine; this.backgroundColorUltraFine = backgroundColorUltraFine; } public static Color getLayerColorVar(String typeOfColor, int layerNumber) { String layerName = null; if (layerNumber == 1) { layerName = "LINK"; } else if (layerNumber == 2) { layerName = "INTERNET"; } else if (layerNumber == 3) { layerName = "TRANSPORT"; } else if (layerNumber == 4) { layerName = "APPLICATION"; } Color color = null; if (typeOfColor == "foregroundColor") { color = valueOf(layerName).foregroundColor; } else if (typeOfColor == "foregroundColorGreyedOut") { color = valueOf(layerName).foregroundColorGreyedOut; } else if (typeOfColor == "backgroundColor") { color = valueOf(layerName).backgroundColor; } else if (typeOfColor == "backgroundColorFine") { color = valueOf(layerName).backgroundColorFine; } else if (typeOfColor == "backgroundColorUltraFine") { color = valueOf(layerName).backgroundColorUltraFine; } return color; } } static List splitString(String text, int maxLength) { String normalizedText = StringUtils.normalizeSpace(text); String[] tokens = normalizedText.split(" "); List lines = new ArrayList(); int currentLength = 0; StringBuilder lineBuilder = new StringBuilder(); for (String token : tokens) { if (currentLength + token.length() <= maxLength) { lineBuilder.append(token).append(" "); currentLength += token.length() + 1; } else if (token.length() > maxLength) { if (currentLength > 0) { lines.add(lineBuilder.toString().trim()); } String[] parts = token.split("(?<=\\G.{" + maxLength + "})"); for (int i = 0; i < parts.length - 1; i++) { lines.add(parts[i]); } lineBuilder = new StringBuilder(parts[parts.length - 1]).append(" "); currentLength = parts[parts.length - 1].length() + 1; } else { lines.add(lineBuilder.toString().trim()); lineBuilder = new StringBuilder(token).append(" "); currentLength = token.length() + 1; } } lines.add(lineBuilder.toString().trim()); return lines; } /** * Opens local view of the path through layers. * * @param systemSoftware * system software of the node * @param frameNumber * number of the selected frame in the data exchange (starts with 1) * * @author Christoph Irniger */ private void showLocalPathThroughLayers(SystemSoftware systemSoftware, int frameNumber) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (AggregatedMessageTable), showLocalPathThroughLayers()"); new LayerPathDialog(filius.gui.JMainFrame.getJMainFrame(), interfaceId, systemSoftware, frameNumber, null, true); } /** * Opens global view of the path through layers. * * @param systemSoftware * system software of the node * @param frameNumber * number of the selected frame in the data exchange (starts with 1) * * @author Christoph Irniger */ private void showGlobalPathThroughLayers(SystemSoftware systemSoftware, int frameNumber) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (AggregatedMessageTable), showGlobalPathThroughLayers()"); // if the frame was sent and destination MAC of the frame is broadcast, global // view is not possible if (LocalLayerPathPanel.isFrameSent(interfaceId, frameNumber) && Lauscher.getLauscher() .getFrame(interfaceId, frameNumber).getZielMacAdresse().equals(Ethernet.ETHERNET_BROADCAST)) { JOptionPane.showMessageDialog(JMainFrame.getJMainFrame(), messages.getString("nachrichtentabelle_msg12")); } else { new GlobalLayerPath(filius.gui.JMainFrame.getJMainFrame(), interfaceId, systemSoftware, frameNumber); } } }filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/ExchangeComponent.java000066400000000000000000000032401506574241500277370ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import javax.swing.JPanel; import filius.software.system.SystemSoftware; /** * @author stefan * */ public interface ExchangeComponent { /** * @param system * @param identifier */ void addTable(SystemSoftware system, String identifier); /** * @param system * @param panel */ void removeTable(String mac, JPanel panel); /** * */ void reset(); } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/LauscherTableCellRenderer.java000066400000000000000000000115211506574241500313400ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.Color; import java.awt.Component; import java.awt.Font; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; import filius.gui.nachrichtensicht.AggregatedMessageTable.LayerColorVar; import filius.rahmenprogramm.nachrichten.Lauscher; public class LauscherTableCellRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { String schichtString; int schicht = 5; int lfdNr = 0; int tblCols = table.getColumnCount(); int tblRows = table.getRowCount(); if (tblRows > row && tblCols > LayeredMessageTable.SCHICHT_SPALTE && table.getValueAt(row, LayeredMessageTable.SCHICHT_SPALTE) != null) schichtString = table.getValueAt(row, LayeredMessageTable.SCHICHT_SPALTE).toString(); else schichtString = ""; if (table.getValueAt(row, 0) != null) { try { lfdNr = Integer.parseInt((String) table.getValueAt(row, 0)); } catch (Exception e) {} } for (int i = 0; i < Lauscher.PROTOKOLL_SCHICHTEN.length; i++) { if (schichtString.equals(Lauscher.PROTOKOLL_SCHICHTEN[i])) schicht = i; } Color color1; Color color2; switch (schicht) { case 0: color1 = LayerColorVar.LINK.foregroundColor; // if (lfdNr%2==0) color2 = LayerColorVar.LINK.backgroundColor; // else setBackground(new Color(0.8f, 0.8f, 0.8f)); break; case 1: color1 = LayerColorVar.INTERNET.foregroundColor; // if (lfdNr % 2 == 0) color2 = LayerColorVar.INTERNET.backgroundColor; // else // setBackground(new Color(0.2f, 1f, 0.2f)); break; case 2: color1 = LayerColorVar.TRANSPORT.foregroundColor; color2 = LayerColorVar.TRANSPORT.backgroundColor; // setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10)); break; case 3: color1 = LayerColorVar.APPLICATION.foregroundColor; color2 = LayerColorVar.APPLICATION.backgroundColor; // setFont(new Font(Font.DIALOG, Font.BOLD, 12)); break; default: color1 = LayerColorVar.DEFAULT.foregroundColor; color2 = LayerColorVar.DEFAULT.backgroundColor; } if (!isSelected) { setForeground(color1); setBackground(color2); } else { setForeground(Color.WHITE); setBackground(Color.BLACK); } switch (column) { case 0: setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); break; case 1: setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); break; case 2: case 3: setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); break; case 4: setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12)); break; case 5: setFont(new Font(Font.DIALOG, Font.BOLD + Font.ITALIC, 12)); break; case 6: setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); break; default: setFont(new Font(Font.DIALOG, Font.ITALIC, 12)); } if (value != null) setText(value.toString().replace('\n', ' ')); else setText(""); return this; } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/LauscherTableModel.java000066400000000000000000000030051506574241500300300ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import javax.swing.table.DefaultTableModel; /** * @author stefan * */ @SuppressWarnings("serial") public class LauscherTableModel extends DefaultTableModel { @Override public boolean isCellEditable(int row, int col) { return false; } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/LayeredExchangeDialog.java000066400000000000000000000117451506574241500305130ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.Image; import java.awt.Toolkit; import java.util.Hashtable; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import filius.hardware.knoten.Host; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.software.system.SystemSoftware; public class LayeredExchangeDialog extends JDialog implements ExchangeComponent, I18n { private static final long serialVersionUID = 1L; private JTabbedPane tabbedPane; private static LayeredExchangeDialog lauscherDialog = null; private Hashtable openedTabs = new Hashtable(); private Hashtable tabellen = new Hashtable(); public static synchronized LayeredExchangeDialog getInstance(Frame owner) { if (lauscherDialog == null) { lauscherDialog = new LayeredExchangeDialog(owner); } return lauscherDialog; } @Override public void reset() { if (lauscherDialog != null) { lauscherDialog.setVisible(false); } lauscherDialog = null; } private LayeredExchangeDialog(Frame owner) { super(owner); ((JFrame) owner).getLayeredPane().setLayer(this, JLayeredPane.PALETTE_LAYER); Image image; setTitle(messages.getString("lauscherdialog_msg1")); setBounds(20, (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight() - 350, ((int) Toolkit.getDefaultToolkit().getScreenSize().getWidth()) - 40, 300); image = Toolkit.getDefaultToolkit() .getImage(getClass().getResource("/gfx/allgemein/nachrichtenfenster_icon.png")); setIconImage(image); this.setModal(false); setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); openedTabs.clear(); tabbedPane = new JTabbedPane(); getContentPane().add(tabbedPane, BorderLayout.CENTER); this.setVisible(false); } /** * Diese Methode fuegt eine Tabelle hinzu */ @Override public void addTable(SystemSoftware system, String identifier) { LayeredMessageTable tabelle; JPanel panel; JScrollPane scrollPane; if (openedTabs.get(identifier) == null) { tabelle = new LayeredMessageTable(this, identifier); tabelle.update(); panel = new JPanel(new BorderLayout()); scrollPane = new JScrollPane(tabelle); tabelle.setScrollPane(scrollPane); panel.add(scrollPane, BorderLayout.CENTER); String ipAddress = ((InternetKnoten) system.getKnoten()).getNetzwerkInterfaceByMac(identifier).getIp(); String tabTitle; if (system.getKnoten() instanceof Host && ((Host) system.getKnoten()).isUseIPAsName()) { tabTitle = ipAddress; } else { tabTitle = system.getKnoten().holeAnzeigeName() + " - " + ipAddress; } tabbedPane.add(tabTitle, panel); tabbedPane.setSelectedComponent(panel); openedTabs.put(identifier, panel); tabellen.put(identifier, tabelle); } // if there is already a tab opened for this system set it to selected else { tabbedPane.setSelectedComponent(openedTabs.get(identifier)); tabellen.get(identifier).update(); } } @Override public void removeTable(String mac, JPanel panel) { if (mac != null) { openedTabs.remove(mac); tabellen.remove(mac); tabbedPane.remove(panel); } } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/LayeredMessageTable.java000066400000000000000000000273741506574241500302120ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Point; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Vector; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JDialog; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.rahmenprogramm.nachrichten.LauscherBeobachter; public class LayeredMessageTable extends JTable implements LauscherBeobachter, I18n { private static Logger LOG = LoggerFactory.getLogger(LayeredMessageTable.class); private static final long serialVersionUID = 1L; /** Index der Spalte, in der die Schicht des Protokollschichtenmodells steht */ public static final int SCHICHT_SPALTE = 5; private String interfaceId; private boolean netzzugangVisible = true; private boolean vermittlungVisible = true; private boolean transportVisible = true; private boolean anwendungVisible = true; private JCheckBoxMenuItem checkbox; private JDialog schichtenKonfigDialog; private JDialog dialog; private JScrollPane scrollPane = null; private boolean autoscroll = true; private JPopupMenu menu; public LayeredMessageTable(JDialog dialog, String macAddress) { super(); TableColumn col; DefaultTableColumnModel columnModel; this.dialog = dialog; setinterfaceId(macAddress); initTableModel(); this.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); columnModel = new DefaultTableColumnModel(); String[] spalten = Lauscher.SPALTEN; for (int i = 0; i < spalten.length; i++) { col = new TableColumn(); col.setHeaderValue(spalten[i]); col.setIdentifier(spalten[i]); col.setModelIndex(i); columnModel.addColumn(col); } this.setColumnModel(columnModel); this.setIntercellSpacing(new Dimension(0, 5)); this.setRowHeight(25); this.setEnabled(false); this.setShowGrid(false); this.setColumnSelectionAllowed(false); this.setBackground(Color.DARK_GRAY); this.getTableHeader().setReorderingAllowed(false); this.setFillsViewportHeight(true); initTableColumnWidth(); this.setDefaultRenderer(Object.class, new LauscherTableCellRenderer()); initKontextMenue(); this.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { if (e.getButton() == 3) { menu.setVisible(true); menu.show(getTabelle(), e.getX(), e.getY()); } } }); update(); } private void initTableModel() { DefaultTableModel tableModel; tableModel = new DefaultTableModel(); this.setModel(tableModel); tableModel.addTableModelListener(this); tableModel.setColumnIdentifiers(Lauscher.SPALTEN); } public void setScrollPane(JScrollPane scrollPane) { this.scrollPane = scrollPane; } private JDialog getDialog() { return dialog; } private LayeredMessageTable getTabelle() { return this; } private void initTableColumnWidth() { this.getColumnModel().getColumn(0).setMaxWidth(40); this.getColumnModel().getColumn(0).setPreferredWidth(30); this.getColumnModel().getColumn(1).setMaxWidth(120); this.getColumnModel().getColumn(1).setPreferredWidth(90); this.getColumnModel().getColumn(2).setMaxWidth(180); this.getColumnModel().getColumn(2).setPreferredWidth(140); this.getColumnModel().getColumn(3).setMaxWidth(180); this.getColumnModel().getColumn(3).setPreferredWidth(140); this.getColumnModel().getColumn(4).setMaxWidth(100); this.getColumnModel().getColumn(4).setPreferredWidth(70); this.getColumnModel().getColumn(5).setMaxWidth(140); this.getColumnModel().getColumn(5).setPreferredWidth(100); this.getColumnModel().getColumn(6).setMaxWidth(Integer.MAX_VALUE); this.getColumnModel().getColumn(6).setPreferredWidth(500); this.getColumnModel().getColumn(6).setResizable(true); } private void initKontextMenue() { JMenuItem menuItem; schichtenKonfigDialog = new JDialog(dialog); schichtenKonfigDialog.setTitle(messages.getString("nachrichtentabelle_msg1")); schichtenKonfigDialog.setModal(true); schichtenKonfigDialog.setBounds(50, 50, 200, 150); schichtenKonfigDialog.setResizable(false); schichtenKonfigDialog.getContentPane().setLayout(new FlowLayout()); ((FlowLayout) schichtenKonfigDialog.getContentPane().getLayout()).setAlignment(FlowLayout.LEFT); final JCheckBox netzzugangCheckBox = new JCheckBox(messages.getString("nachrichtentabelle_msg2"), netzzugangVisible); final JCheckBox vermittlungCheckBox = new JCheckBox(messages.getString("nachrichtentabelle_msg3"), vermittlungVisible); final JCheckBox transportCheckBox = new JCheckBox(messages.getString("nachrichtentabelle_msg4"), transportVisible); final JCheckBox anwendungCheckBox = new JCheckBox(messages.getString("nachrichtentabelle_msg5"), anwendungVisible); schichtenKonfigDialog.getContentPane().add(netzzugangCheckBox); schichtenKonfigDialog.getContentPane().add(vermittlungCheckBox); schichtenKonfigDialog.getContentPane().add(transportCheckBox); schichtenKonfigDialog.getContentPane().add(anwendungCheckBox); menu = new JPopupMenu(); menuItem = new JMenuItem(messages.getString("nachrichtentabelle_msg6")); menuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { int x, y; menu.setVisible(false); x = getDialog().getX() + getDialog().getWidth() / 2 - schichtenKonfigDialog.getWidth() / 2; y = getDialog().getY() + getDialog().getHeight() / 2 - schichtenKonfigDialog.getHeight() / 2; schichtenKonfigDialog.setBounds(x, y, schichtenKonfigDialog.getWidth(), schichtenKonfigDialog.getHeight()); schichtenKonfigDialog.setVisible(true); netzzugangVisible = netzzugangCheckBox.isSelected(); vermittlungVisible = vermittlungCheckBox.isSelected(); transportVisible = transportCheckBox.isSelected(); anwendungVisible = anwendungCheckBox.isSelected(); initTableModel(); update(); } }); menu.add(menuItem); menuItem = new JMenuItem(messages.getString("nachrichtentabelle_msg7")); menuItem.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { menu.setVisible(false); Lauscher.getLauscher().reset(); } }); menu.add(menuItem); checkbox = new JCheckBoxMenuItem(messages.getString("nachrichtentabelle_msg8"), autoscroll); checkbox.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (autoscroll != checkbox.getState()) { menu.setVisible(false); autoscroll = checkbox.getState(); update(); } } }); menu.add(checkbox); menu.setVisible(false); dialog.getRootPane().getLayeredPane().add(menu); } private void setinterfaceId(String interfaceId) { this.interfaceId = interfaceId; Lauscher.getLauscher().addBeobachter(interfaceId, this); } public synchronized void update() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (NachrichtenTabelle), update()"); Object[][] daten; daten = Lauscher.getLauscher().getDaten(interfaceId, false, 1); if (daten.length == 0) { initTableModel(); } int lastNo = -1; if (getModel().getRowCount() > 0) { lastNo = Integer.parseInt(this.getModel().getValueAt(this.getModel().getRowCount() - 1, 0).toString()); } for (int i = 0; i < daten.length; i++) { int currentNo = Integer.parseInt(daten[i][0].toString()); if (currentNo <= lastNo) { continue; } boolean isValid = false; if (daten[i][SCHICHT_SPALTE].equals(Lauscher.PROTOKOLL_SCHICHTEN[0])) { if (netzzugangVisible) { isValid = true; } } else if (daten[i][SCHICHT_SPALTE].equals(Lauscher.PROTOKOLL_SCHICHTEN[1])) { if (vermittlungVisible) { isValid = true; } } else if (daten[i][SCHICHT_SPALTE].equals(Lauscher.PROTOKOLL_SCHICHTEN[2])) { if (transportVisible) { isValid = true; } } else if (daten[i][SCHICHT_SPALTE].equals(Lauscher.PROTOKOLL_SCHICHTEN[3])) { if (anwendungVisible) { isValid = true; } } else { isValid = true; } if (isValid) { Vector rowData = new Vector(daten[i].length); for (int col = 0; col < daten[i].length; col++) { rowData.add(col, daten[i][col]); } ((DefaultTableModel) this.getModel()).addRow(rowData); } } ((DefaultTableModel) this.getModel()).fireTableDataChanged(); if (this.getRowCount() > 0 && scrollPane != null && scrollPane.getViewport() != null && autoscroll) { scrollPane.getViewport().setViewPosition(new Point(0, this.getHeight())); } } @Override public void writeToStream(OutputStream outputStream) throws UnsupportedEncodingException, IOException { outputStream.write("not implemented".getBytes("UTF8")); } } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/MessageDetails.java000066400000000000000000000026701506574241500272320ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import javax.swing.JPanel; public abstract class MessageDetails extends JPanel { public abstract void clear(); public abstract void update(String interfaceId, int frameNumber); } filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/MessageDetailsPanel.java000066400000000000000000000237771506574241500302250ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.nachrichtensicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ColorUIResource; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeCellRenderer; import filius.rahmenprogramm.nachrichten.Lauscher; /** * Not used anymore, since this presentation of the message details was replaced with class MessageDetailsTable. */ public class MessageDetailsPanel extends MessageDetails { private String macAddress; public MessageDetailsPanel(String macAddress) { this.macAddress = macAddress; this.setLayout(new BorderLayout()); this.setBackground(Color.WHITE); } public void clear() { removeAll(); updateUI(); } public void update(String interfaceId, int frameNumber) { Object[][] daten = Lauscher.getLauscher().getDaten(macAddress, false, 0); int dataSetNo = 0; int currNo = 0; for (; dataSetNo < daten.length; dataSetNo++) { currNo = Integer.parseInt(daten[dataSetNo][0].toString()); if (currNo == frameNumber) break; } Object[] dataSet = daten[dataSetNo]; DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( AggregatedExchangePanel.messages.getString("rp_lauscher_msg1") + ": " + dataSet[0] + " / " + AggregatedExchangePanel.messages.getString("rp_lauscher_msg2") + ": " + dataSet[1]); for (; dataSetNo < daten.length && Integer.parseInt(daten[dataSetNo][0].toString()) == frameNumber; dataSetNo++) { dataSet = daten[dataSetNo]; DefaultMutableTreeNode layerNode = new DefaultMutableTreeNode(dataSet[5], true); DefaultMutableTreeNode dateNode; DefaultMutableTreeNode labelNode; if (dataSet[2] != null && !dataSet[2].toString().isEmpty()) { String srcLabel = String.format("%-15s", AggregatedExchangePanel.messages.getString("rp_lauscher_msg3") + ": "); dateNode = new DefaultMutableTreeNode(srcLabel + dataSet[2]); layerNode.add(dateNode); } if (dataSet[3] != null && !dataSet[3].toString().isEmpty()) { String destLabel = String.format("%-15s", AggregatedExchangePanel.messages.getString("rp_lauscher_msg4") + ": "); dateNode = new DefaultMutableTreeNode(destLabel + dataSet[3]); layerNode.add(dateNode); } if (dataSet[4] != null && !dataSet[4].toString().isEmpty()) { String protocolLabel = String.format("%-15s", AggregatedExchangePanel.messages.getString("rp_lauscher_msg5") + ": "); dateNode = new DefaultMutableTreeNode(protocolLabel + dataSet[4]); layerNode.add(dateNode); } if (dataSet[6] != null && !dataSet[6].toString().isEmpty()) { String contentLabel = AggregatedExchangePanel.messages.getString("rp_lauscher_msg7"); if (dataSet[5] == Lauscher.PROTOKOLL_SCHICHTEN[3]) { contentLabel += " (" + dataSet[6].toString().length() + " Bytes)"; } contentLabel += ": "; if (dataSet[6].toString().contains("\n") || dataSet[6].toString().length() > 60) { labelNode = new DefaultMutableTreeNode(contentLabel); dateNode = new DefaultMutableTreeNode(dataSet[6]); labelNode.add(dateNode); layerNode.add(labelNode); } else { dateNode = new DefaultMutableTreeNode(String.format("%-15s", contentLabel) + dataSet[6]); layerNode.add(dateNode); } } rootNode.add(layerNode); } JTree detailsTree = new JTree(rootNode); for (int i = 0; i < detailsTree.getRowCount(); i++) { detailsTree.expandRow(i); } detailsTree.setCellRenderer(new MultiLineCellRenderer()); this.removeAll(); this.add(detailsTree, BorderLayout.WEST); this.updateUI(); } // This code is based on an example published at // http://www.java2s.com/Code/Java/Swing-Components/MultiLineTreeExample.htm class MultiLineCellRenderer extends JPanel implements TreeCellRenderer { protected JLabel icon; protected TreeTextArea text; public MultiLineCellRenderer() { setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); icon = new JLabel() { public void setBackground(Color color) { if (color instanceof ColorUIResource) color = null; super.setBackground(color); } }; add(icon); add(Box.createHorizontalStrut(4)); add(text = new TreeTextArea()); } public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) { String stringValue = tree.convertValueToText(value, isSelected, expanded, leaf, row, hasFocus); setEnabled(tree.isEnabled()); text.setText(stringValue); text.setSelect(isSelected); text.setFocus(hasFocus); return this; } public Dimension getPreferredSize() { Dimension iconD = icon.getPreferredSize(); Dimension textD = text.getPreferredSize(); int height = iconD.height < textD.height ? textD.height : iconD.height; return new Dimension(iconD.width + textD.width, height); } public void setBackground(Color color) { if (color instanceof ColorUIResource) color = null; super.setBackground(color); } class TreeTextArea extends JTextArea { Dimension preferredSize; TreeTextArea() { setLineWrap(true); setWrapStyleWord(true); setOpaque(true); Font font = getFont(); setFont(new Font(Font.MONOSPACED, Font.BOLD, font.getSize())); } public void setBackground(Color color) { if (color instanceof ColorUIResource) color = null; super.setBackground(color); } public void setPreferredSize(Dimension d) { if (d != null) { preferredSize = d; } } public Dimension getPreferredSize() { return preferredSize; } public void setText(String str) { Font font = getFont(); FontMetrics fm = getToolkit().getFontMetrics(font); BufferedReader br = new BufferedReader(new StringReader(str)); String line; int maxWidth = 0, lines = 0; try { while ((line = br.readLine()) != null) { int width = SwingUtilities.computeStringWidth(fm, line); if (maxWidth < width) { maxWidth = width; } lines++; } } catch (IOException ex) { ex.printStackTrace(); } lines = (lines < 1) ? 1 : lines; int height = fm.getHeight() * lines; setPreferredSize(new Dimension(maxWidth + 12, height)); super.setText(str); } void setSelect(boolean isSelected) { Color bColor; if (isSelected) { bColor = UIManager.getColor("Tree.selectionBackground"); } else { bColor = UIManager.getColor("Tree.textBackground"); } super.setBackground(bColor); } void setFocus(boolean hasFocus) { if (hasFocus) { Color lineColor = UIManager.getColor("Tree.selectionBorderColor"); setBorder(BorderFactory.createLineBorder(lineColor)); } else { setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); } } } } }filius-2.9.4+ds/src/main/java/filius/gui/nachrichtensicht/MessageDetailsTable.java000066400000000000000000000521751506574241500302070ustar00rootroot00000000000000package filius.gui.nachrichtensicht; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.HashSet; import java.util.Set; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.EtchedBorder; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; /** * Class for the colored message details table at the bottom of the data exchange and the layer views. * * @author Christoph Irniger */ public class MessageDetailsTable extends MessageDetails implements I18n { private final GridBagLayout GRID_BAG_LAYOUT; private GridBagConstraints c; private static final int MSG_DETAILS_CELL_WIDTH = 150; private static final int MSG_DETAILS_CELL_HEIGHT = 30; private String interfaceId; /** starts with 1 */ private int frameNumber; /** data for the graphical table */ private String[][] messageDetailsMatrix; private int maxLayerInsideFrame; private Color backgroundColor; public MessageDetailsTable(String interfaceId, int frameNumber, Color backgroundColor) { GRID_BAG_LAYOUT = new GridBagLayout(); c = new GridBagConstraints(); this.interfaceId = interfaceId; this.frameNumber = frameNumber; this.backgroundColor = backgroundColor; setLayout(GRID_BAG_LAYOUT); setBackground(Color.WHITE); if (frameNumber >= 1) { update(interfaceId, frameNumber); } } public GridBagLayout getGridBagLayout() { return this.GRID_BAG_LAYOUT; } public int getMaxLayerInsideFrame() { return this.maxLayerInsideFrame; } @Override public Dimension getPreferredSize() { return getSizeOfMessageDetailsTable(); } @Override public Dimension getMinimumSize() { return getSizeOfMessageDetailsTable(); } @Override public Dimension getMaximumSize() { return getSizeOfMessageDetailsTable(); } /** * Returns the size of the whole table based on the corresponding constants. * * @return Dimension of the whole MessageDetailsTable */ public static Dimension getSizeOfMessageDetailsTable() { return new Dimension(9 * MSG_DETAILS_CELL_WIDTH, 8 * MSG_DETAILS_CELL_HEIGHT); } /** * Fills the message details matrix with the values of the frame associated with the interface id and frame number. * The matrix will have an empty second line (e.g. messageDetails[1][...] is Null), such that it will match row and * column numbers of the MessageDetailsTable, which will be the graphical variant. */ private void setMessageDetails() { Object[][] daten = Lauscher.getLauscher().getDaten(interfaceId, false, 0); String frameNumberAsString = Integer.toString(frameNumber); for (int i = 0; i < daten.length; i++) { if (daten[i][0].equals(frameNumberAsString)) { // -----Netzzugangs-Daten----- // Quelle messageDetailsMatrix[3][1] = daten[i][2].toString(); // Ziel messageDetailsMatrix[4][1] = daten[i][3].toString(); // Bemerkungen / Details messageDetailsMatrix[5][1] = daten[i][6].toString(); // -----Vermittlungs-Daten (falls existent)----- if (i + 1 < daten.length && daten[i + 1][0].equals(frameNumberAsString)) { // Protokoll messageDetailsMatrix[2][2] = daten[i + 1][4].toString(); // Quelle messageDetailsMatrix[3][3] = daten[i + 1][2].toString(); // Ziel messageDetailsMatrix[4][3] = daten[i + 1][3].toString(); // Bemerkungen / Details messageDetailsMatrix[5][3] = daten[i + 1][6].toString(); // -----Transport-Daten (falls existent)----- if (i + 2 < daten.length && daten[i + 2][0].equals(frameNumberAsString)) { // Protokoll messageDetailsMatrix[2][4] = daten[i + 2][4].toString(); // Quelle messageDetailsMatrix[3][5] = daten[i + 2][2].toString(); // Ziel messageDetailsMatrix[4][5] = daten[i + 2][3].toString(); // Bemerkungen / Details messageDetailsMatrix[5][5] = daten[i + 2][6].toString(); // -----Anwendungs-Daten (falls existent)----- if (i + 3 < daten.length && daten[i + 3][0].equals(frameNumberAsString)) { // Protokoll messageDetailsMatrix[2][6] = daten[i + 3][4].toString(); // Daten messageDetailsMatrix[3][6] = daten[i + 3][6].toString(); } break; } else { break; } } else { break; } } } } /** * Computes the maximum layer inside which appears inside the frame. * * @return maximum layer that appears inside the frame */ private int computeMaxLayerInsideFrame() { int maxLayerInsideFrame = 1; // -----Vermittlung----- if (messageDetailsMatrix[2][2] != null) { maxLayerInsideFrame = 2; // -----Transport----- if (messageDetailsMatrix[2][4] != null) { maxLayerInsideFrame = 3; // -----Anwendung----- if (messageDetailsMatrix[2][6] != null) { maxLayerInsideFrame = 4; } } } return maxLayerInsideFrame; } /** * Fills the message details matrix with the titles (column and cell labels) that are the same for both tables. * * @param messageDetails * the matrix which should be filled with the titles */ private void setTitles() { // Netzzugang messageDetailsMatrix[0][0] = messages.getString("rp_lauscher_msg8"); // Vermittlung messageDetailsMatrix[0][2] = messages.getString("rp_lauscher_msg9"); // Transport messageDetailsMatrix[0][4] = messages.getString("rp_lauscher_msg10"); // Anwendung messageDetailsMatrix[0][6] = messages.getString("rp_lauscher_msg11"); // Ethernet messageDetailsMatrix[2][0] = messages.getString("schichten_lokaler_weg_msg5"); // Quelle (Netzzugang) messageDetailsMatrix[3][0] = messages.getString("rp_lauscher_msg3") + ":"; // Ziel (Netzzugang) messageDetailsMatrix[4][0] = messages.getString("rp_lauscher_msg4") + ":"; // Bemerkungen / Details (Netzzugang) messageDetailsMatrix[5][0] = messages.getString("rp_lauscher_msg7") + ":"; // Quelle (Vermittlung) messageDetailsMatrix[3][2] = messages.getString("rp_lauscher_msg3") + ":"; // Ziel (Vermittlung) messageDetailsMatrix[4][2] = messages.getString("rp_lauscher_msg4") + ":"; // Bemerkungen / Details (Vermittlung) messageDetailsMatrix[5][2] = messages.getString("rp_lauscher_msg7") + ":"; if (maxLayerInsideFrame >= 3) { // Quelle (Transport) messageDetailsMatrix[3][4] = messages.getString("rp_lauscher_msg3") + ":"; // Ziel (Transport) messageDetailsMatrix[4][4] = messages.getString("rp_lauscher_msg4") + ":"; // Bemerkungen / Details (Transport) messageDetailsMatrix[5][4] = messages.getString("rp_lauscher_msg7") + ":"; } // Schicht messageDetailsMatrix[0][7] = messages.getString("schichten_lokaler_weg_msg2"); // Protokoll messageDetailsMatrix[2][7] = messages.getString("schichten_lokaler_weg_msg3"); // Daten messageDetailsMatrix[3][7] = messages.getString("schichten_lokaler_weg_msg4"); } /** * Initializes the graphical table without any text. */ private void initializeTable() { // Storage for creating the Labels (cells with little content) and // TextAreas (cells with potential large content) JLabel label; JTextArea textArea; // first row (from left to right) for (int column = 0; column < 6; column = column + 2) { label = setLabel("", column, 0, 2, 1, 1, 1, false); label.setHorizontalAlignment(SwingConstants.CENTER); label.setBackground((getColorByColumn("backgroundColorFine", column))); label.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); setBoldAndItalic(label); } label = setLabel("", 6, 0, 1, 1, 2, 1, false); label.setHorizontalAlignment(SwingConstants.CENTER); label.setBackground(getLayerColorVar("backgroundColorFine", 4)); label.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); setBoldAndItalic(label); label = setLabel("", 7, 0, 1, 1, 0.5, 1, false); label.setHorizontalAlignment(JLabel.CENTER); label.setVerticalAlignment(JLabel.CENTER); label.setBackground(backgroundColor); label.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); setBoldAndItalic(label); // second empty row, which is transparent (from left to right) for (int column = 0; column < 6; column = column + 2) { label = setEmptyCell(column, 1, 2, 1, 1, 0.8); } label = setEmptyCell(6, 1, 1, 1, 2, 0.8); label = setEmptyCell(7, 1, 1, 1, 0.5, 0.8); // third row (from left to right) for (int column = 0; column < 6; column = column + 2) { label = setLabel("", column, 2, 2, 1, 1, 1, true); label.setHorizontalAlignment(SwingConstants.CENTER); label.setBackground((getColorByColumn("backgroundColor", column))); } label = setLabel("", 6, 2, 1, 1, 2, 1, true); label.setHorizontalAlignment(SwingConstants.CENTER); label.setBackground(getLayerColorVar("backgroundColor", 4)); label = setLabel("", 7, 2, 1, 1, 0.5, 1, false); label.setHorizontalAlignment(JLabel.CENTER); label.setVerticalAlignment(JLabel.CENTER); label.setBackground(backgroundColor); label.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); setBoldAndItalic(label); // fourth and fifth row, first to sixth columns (from left to right) for (int row = 3; row < 5; row++) { for (int column = 0; column < 6; column++) { label = setLabel("", column, row, 1, 1, 1, 1, true); if (column % 2 == 0) { label.setHorizontalAlignment(SwingConstants.RIGHT); } label.setBackground((getColorByColumn("backgroundColor", column))); } } // sixth row, first to sixth columns (from left to right) for (int column = 0; column < 6; column++) { if (column % 2 == 0) { label = setLabel("", column, 5, 1, 1, 1, 3, true); label.setHorizontalAlignment(SwingConstants.RIGHT); label.setVerticalAlignment(SwingConstants.TOP); label.setBackground((getColorByColumn("backgroundColor", column))); } else { textArea = setTextArea("", column, 5, 1, 1, 1, 3); textArea.setBackground(getColorByColumn("backgroundColor", column)); } } // last two cells on the bottom right with height 3 textArea = setTextArea("", 6, 3, 1, 3, 2, 5 / 3.0); textArea.setBackground(getLayerColorVar("backgroundColor", 4)); label = setLabel("", 7, 3, 1, 3, 0.5, 5 / 3.0, false); label.setHorizontalAlignment(JLabel.CENTER); label.setVerticalAlignment(JLabel.CENTER); label.setBackground(backgroundColor); label.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); setBoldAndItalic(label); } /** * Updates this table with the values in the message details matrix. * * @param interfaceId * the interface id where the frame should be looked up * @param frameNumber * the number of the frame in the data exchange of the interface (starts with 1) */ public void update(String interfaceId, int frameNumber) { this.interfaceId = interfaceId; this.frameNumber = frameNumber; messageDetailsMatrix = new String[6][8]; setMessageDetails(); maxLayerInsideFrame = computeMaxLayerInsideFrame(); setTitles(); initializeTable(); for (Component component : getComponents()) { int gridx = GRID_BAG_LAYOUT.getConstraints(component).gridx; int gridy = GRID_BAG_LAYOUT.getConstraints(component).gridy; String text = messageDetailsMatrix[gridy][gridx] != null ? messageDetailsMatrix[gridy][gridx] : ""; if (component instanceof JLabel) { ((JLabel) component).setText(text); } else if (component instanceof JTextArea) { ((JTextArea) component).setText(text); } } setLayersInvisible(); } /** * Sets the layers which are not needed invisible if they do not appear inside the frame. */ public void setLayersInvisible() { // determine columns to deactivate Set columns = new HashSet<>(); if (maxLayerInsideFrame == 4) { return; } else if (maxLayerInsideFrame == 3) { columns.add(6); // application data } else if (maxLayerInsideFrame == 2) { columns.add(4); // transport data left side columns.add(5); // transport data right side columns.add(6); // application data } // deactivate columns for (Component component : getComponents()) { if (columns.contains(getGridBagLayout().getConstraints(component).gridx)) { component.setVisible(false); } } } /** * Remove all components from this container. */ public void clear() { removeAll(); updateUI(); } /** * This method returns the color defined in the enum class LayerColorVar in class AggregatedMessageTable * corresponding to the type of color and the layer number. * * @param typeOfColor * All types of colors defined in the enum class in class AggregatedMessageTable: "foregroundColor", * "backgroundColor", "backgroundColorFine", "backgroundColorUltraFine" * @param layerNumber * number of the layer (1 = LINK, 2 = INTERNET, 3 = TRANSPORT, 4 = APPLICATION) * @return the desired color */ public static Color getLayerColorVar(String typeOfColor, int layerNumber) { return AggregatedMessageTable.LayerColorVar.getLayerColorVar(typeOfColor, layerNumber); } /** * This method returns the color corresponding to the type of color and the layer which belongs to the column of the * MessageDetailsTable. * * @param typeOfColor * All types of colors defined in the enum class in class AggregatedMessageTable: "foregroundColor", * "backgroundColor", "backgroundColorFine", "backgroundColorUltraFine" * @param column * column of the MessageDetailsTable * @return the desired color */ private Color getColorByColumn(String typeOfColor, int column) { Color color = null; if (column == 0 || column == 1) { color = getLayerColorVar(typeOfColor, 1); } else if (column == 2 || column == 3) { color = getLayerColorVar(typeOfColor, 2); } else if (column == 4 || column == 5) { color = getLayerColorVar(typeOfColor, 3); } else if (column == 6) { color = getLayerColorVar(typeOfColor, 4); } return color; } /** * Helper method to create a new JLabel in the GridBagLayout. JLabels are needed for one line content. * * @return The JLabel */ private JLabel setLabel(String text, int x_pos, int y_pos, int width, int height, double width_multiplier, double height_multiplier, boolean needsBorder) { JLabel label = new JLabel(text); setStandards(label, x_pos, y_pos, width, height, width_multiplier, height_multiplier); label.setOpaque(true); setPlain(label); if (needsBorder) { setBorderVisible(label, true); } add(label, c); return label; } /** * Helper method to create a new JTextArea in the GridBagLayout. JTextAreas are needed for multiline content. * * @return The JTextArea */ private JTextArea setTextArea(String text, int x_pos, int y_pos, int width, int height, double width_multiplier, double height_multiplier) { JTextArea textArea = new JTextArea(text); textArea.setOpaque(true); textArea.setWrapStyleWord(true); textArea.setLineWrap(true); textArea.setEditable(false); textArea.setFocusable(false); setBorderVisible(textArea, true); setStandards(textArea, x_pos, y_pos, width, height, width_multiplier, height_multiplier); add(textArea, c); return textArea; } /** * Helper method to create a visually empty cell, which is a JLabel, into the GridBagLayout. Empty cells form the * gap between the first and the third row. * * @param x_pos * @param y_pos * @param width * @param height * @param width_multiplier * @param height_multiplier * @return The JLabel */ private JLabel setEmptyCell(int x_pos, int y_pos, int width, int height, double width_multiplier, double height_multiplier) { JLabel label = new JLabel(); label.setOpaque(false); label.setBorder(javax.swing.BorderFactory.createEmptyBorder()); setStandards(label, x_pos, y_pos, width, height, width_multiplier, height_multiplier); add(label, c); return label; } /** * Helper method to set standard GridBagLayout constraints. * * @param component * @param x_pos * @param y_pos * @param width * @param height * @param width_multiplier * @param height_multiplier */ private void setStandards(Component component, int x_pos, int y_pos, int width, int height, double width_multiplier, double height_multiplier) { Dimension dimensionOfComponent = new Dimension((int) (width_multiplier * width * MSG_DETAILS_CELL_WIDTH), (int) (height_multiplier * height * MSG_DETAILS_CELL_HEIGHT)); component.setPreferredSize(dimensionOfComponent); component.setMinimumSize(dimensionOfComponent); component.setMaximumSize(dimensionOfComponent); c.gridx = x_pos; c.gridy = y_pos; c.gridwidth = width; c.gridheight = height; } /** * Set the font to bold and italic for a component. * * @param comp */ private void setBoldAndItalic(Component comp) { Font boldFont = new Font(comp.getFont().getName(), Font.BOLD + Font.ITALIC, comp.getFont().getSize()); comp.setFont(boldFont); } /** * Set the font to plain (normal) for a component. * * @param comp */ private void setPlain(Component comp) { Font boldFont = new Font(comp.getFont().getName(), Font.PLAIN, comp.getFont().getSize()); comp.setFont(boldFont); } /** * If the boolean value is true, creates the border of the component. Otherwise sets an empty border. * * @param component * the component whose border will be affected * @param isVisible * the border is visible (true) or not (false) */ public void setBorderVisible(Component component, boolean isVisible) { if (isVisible) { // create border Border loweredEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); Border marginBorder = new EmptyBorder(5, 5, 5, 5); if (component instanceof JTextArea) { ((JTextArea) component).setBorder(new CompoundBorder(loweredEtchedBorder, marginBorder)); ; } else if (component instanceof JLabel) { ((JLabel) component).setBorder(new CompoundBorder(loweredEtchedBorder, marginBorder)); } } else { // remove border (set empty border) if (component instanceof JTextArea) { ((JTextArea) component).setBorder(javax.swing.BorderFactory.createEmptyBorder()); } else if (component instanceof JLabel) { ((JLabel) component).setBorder(javax.swing.BorderFactory.createEmptyBorder()); } } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/000077500000000000000000000000001506574241500230535ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIDesignSidebar.java000066400000000000000000000121541506574241500267710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.event.MouseEvent; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.SwingConstants; import javax.swing.event.MouseInputAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.gui.GUIEvents; import filius.hardware.Kabel; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.Modem; import filius.hardware.knoten.Notebook; import filius.hardware.knoten.Rechner; import filius.hardware.knoten.Switch; import filius.hardware.knoten.Vermittlungsrechner; import filius.rahmenprogramm.IconMode; import filius.rahmenprogramm.Information; /** * Klasse für das linke Panel in der Entwurfsansicht. Darin werden alle nutzbaren Elemente für den Netzwerkentwurf * angezeigt und können per Drag&Drop in den Entwurfsbildschirm gezogen werden. * * @author Johannes Bade & Thomas Gerding */ public class GUIDesignSidebar extends GUISidebar { private static Logger LOG = LoggerFactory.getLogger(GUIDesignSidebar.class); public static final String KABEL = "gfx/hardware/kabel.png"; public static final String RECHNER = "gfx/hardware/server"; public static final String SWITCH = "gfx/hardware/switch"; public static final String SWITCH_CLOUD = "gfx/hardware/cloud.png"; public static final String VERMITTLUNGSRECHNER = "gfx/hardware/router"; public static final String NOTEBOOK = "gfx/hardware/laptop"; public static final String MODEM = "gfx/hardware/modem"; public static final String GATEWAY = "gfx/hardware/gateway"; private JLabel newCableCursor; private static GUIDesignSidebar sidebar; public static GUIDesignSidebar getGUIDesignSidebar() { if (sidebar == null) { sidebar = new GUIDesignSidebar(); } return sidebar; } private void addCableItemToSidebar() { newCableCursor = new JLabel(new ImageIcon(getClass().getResource("/" + KABEL))); newCableCursor.setText(Kabel.TYPE); newCableCursor.setVerticalTextPosition(SwingConstants.BOTTOM); newCableCursor.setHorizontalTextPosition(SwingConstants.CENTER); newCableCursor.setAlignmentX(0.5f); newCableCursor.setVerticalTextPosition(SwingConstants.BOTTOM); newCableCursor.setHorizontalTextPosition(SwingConstants.CENTER); newCableCursor.setToolTipText("+1"); leistenpanel.add(newCableCursor); newCableCursor.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { GUIEvents.getGUIEvents().resetAndShowCablePreview( e.getX() - GUIContainer.getGUIContainer().getSidebarScrollpane().getWidth(), e.getY()); } }); } @Override protected void addItemsToSidebar() { addCableItemToSidebar(); addComponentItemsToSidebar(); } private void addComponentItemsToSidebar() { IconMode mode = Information.getInformation().getIconMode(); addHardwareComponent(RECHNER + mode.suffix(), Rechner.TYPE); addHardwareComponent(NOTEBOOK + mode.suffix(), Notebook.TYPE); addHardwareComponent(SWITCH + mode.suffix(), Switch.TYPE); if (Information.getInformation().isGatewayAvailable()) { addHardwareComponent(GATEWAY + mode.suffix(), Gateway.TYPE); } addHardwareComponent(VERMITTLUNGSRECHNER + mode.suffix(), Vermittlungsrechner.TYPE); addHardwareComponent(MODEM + mode.suffix(), Modem.TYPE); } private void addHardwareComponent(String imageResourcePath, String hardwareType) { JSidebarButton newLabel; ImageIcon icon; icon = new ImageIcon(getClass().getResource("/" + imageResourcePath)); newLabel = new JSidebarButton(hardwareType, icon, hardwareType); /* Label wird liste und Leiste hinzugefuegt */ buttonList.add(newLabel); leistenpanel.add(newLabel); } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIDocuItem.java000066400000000000000000000106311506574241500257750ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Color; import java.awt.Font; public class GUIDocuItem { public static final int RECT = 1, TEXT = 2; private int type; private String text; private int x; private int y; private int width; private int height; private Color color; private Font font; private JDocuElement elem; public static GUIDocuItem createDocuItem(JDocuElement elem) { GUIDocuItem item = new GUIDocuItem(); item.text = elem.getText(); item.type = item.text != null ? TEXT : RECT; item.x = elem.getX(); item.y = elem.getY(); item.width = elem.getWidth(); item.height = elem.getHeight(); item.color = elem.getColor(); item.font = elem.getFont(); item.elem = elem; return item; } public JDocuElement asDocuElement() { if (elem == null) { elem = new JDocuElement(type == TEXT); elem.setText(text); elem.setBounds(x, y, width, height); elem.setColor(color); elem.setFont(font); elem.setEnabled(false); } return elem; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getText() { if (elem != null) { this.text = elem.getText(); } return text; } public void setText(String text) { this.text = text; if (elem != null) { elem.setText(this.text); } } public int getX() { if (elem != null) { this.x = elem.getX(); } return x; } public void setX(int x) { this.x = x; if (elem != null) { elem.setBounds(x, y, width, height); } } public int getY() { if (elem != null) { this.y = elem.getY(); } return y; } public void setY(int y) { this.y = y; if (elem != null) { elem.setBounds(x, y, width, height); } } public int getWidth() { if (elem != null) { this.width = elem.getWidth(); } return width; } public void setWidth(int width) { this.width = width; if (elem != null) { elem.setBounds(x, y, width, height); } } public int getHeight() { if (elem != null) { this.height = elem.getHeight(); } return height; } public void setHeight(int height) { this.height = height; if (elem != null) { elem.setBounds(x, y, width, height); } } public Color getColor() { if (elem != null) { color = elem.getColor(); } return color; } public void setColor(Color color) { this.color = color; if (elem != null) { elem.setColor(color); } } public Font getFont() { if (elem != null) { font = elem.getFont(); } return font; } public void setFont(Font font) { this.font = font; if (elem != null) { elem.setFont(font); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIDocumentationPanel.java000066400000000000000000000056411506574241500300620ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Component; import java.awt.Dimension; import java.awt.event.MouseEvent; import java.util.List; import javax.swing.event.MouseInputAdapter; public class GUIDocumentationPanel extends GUIMainArea { private static final long serialVersionUID = 1L; public GUIDocumentationPanel(int width, int height) { setLayout(null); setPreferredSize(new Dimension(width, height)); setBounds(0, 0, width, height); setOpaque(false); // A click on the background removes the focus from any JDocuElement addMouseListener(new MouseInputAdapter() { @Override public void mousePressed(MouseEvent e) { removeElementsFocus(); } }); } public void removeElementsFocus() { getRootPane().requestFocusInWindow(); Component[] c = getComponents(); for (int i = 0; i < c.length; i++) { if (c[i] instanceof JDocuElement && c[i] != this) { ((JDocuElement) c[i]).setLocalFocus(false); } } } public void updateViewport(List docuItems, boolean elementsEnabled) { removeAll(); for (GUIDocuItem item : docuItems) { if (item.getType() == GUIDocuItem.TEXT) { addItem(elementsEnabled, item); } } for (GUIDocuItem item : docuItems) { if (item.getType() == GUIDocuItem.RECT) { addItem(elementsEnabled, item); } } } private void addItem(boolean elementsEnabled, GUIDocuItem item) { add(item.asDocuElement()); item.asDocuElement().setEnabled(elementsEnabled); } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIDocumentationSidebar.java000066400000000000000000000143061506574241500303720ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.FileDialog; import java.awt.event.MouseEvent; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.Path; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import javax.swing.event.MouseInputAdapter; import com.itextpdf.text.DocumentException; import filius.gui.GUIContainer; import filius.gui.JMainFrame; import filius.gui.documentation.ReportGenerator; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.SzenarioVerwaltung; public class GUIDocumentationSidebar extends GUISidebar implements I18n { public static final String TYPE_TEXTFIELD = "textfield"; public static final String TYPE_RECTANGLE = "rectangle"; public static final String TYPE_EXPORT = "export"; public static final String TYPE_REPORT = "report"; public static final String ADD_TEXT = "gfx/dokumentation/add_text_small.png"; public static final String ADD_RECTANGLE = "gfx/dokumentation/add_small.png"; public static final String EXPORT = "gfx/dokumentation/download_small.png"; public static final String REPORT = "gfx/dokumentation/pdf_small.png"; private static GUIDocumentationSidebar sidebar; public static GUIDocumentationSidebar getGUIDocumentationSidebar() { if (sidebar == null) { sidebar = new GUIDocumentationSidebar(); } return sidebar; } @Override protected void addItemsToSidebar() { ImageIcon icon = new ImageIcon(getClass().getResource("/" + ADD_TEXT)); JSidebarButton newLabel = new JSidebarButton(messages.getString("docusidebar_msg1"), icon, TYPE_TEXTFIELD); buttonList.add(newLabel); leistenpanel.add(newLabel); icon = new ImageIcon(getClass().getResource("/" + ADD_RECTANGLE)); newLabel = new JSidebarButton(messages.getString("docusidebar_msg3"), icon, TYPE_RECTANGLE); buttonList.add(newLabel); leistenpanel.add(newLabel); icon = new ImageIcon(getClass().getResource("/" + EXPORT)); newLabel = new JSidebarButton(messages.getString("docusidebar_msg5"), icon, TYPE_EXPORT); newLabel.setToolTipText(messages.getString("docusidebar_msg6")); newLabel.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { GUIContainer.getGUIContainer().exportAsImage(); } }); buttonList.add(newLabel); leistenpanel.add(newLabel); icon = new ImageIcon(getClass().getResource("/" + REPORT)); newLabel = new JSidebarButton(messages.getString("docusidebar_msg7"), icon, TYPE_REPORT); newLabel.setToolTipText(messages.getString("docusidebar_msg8")); newLabel.addMouseListener(new MouseInputAdapter() { public void mousePressed(MouseEvent e) { GUIDocumentationSidebar.this.generateReport(); } }); buttonList.add(newLabel); leistenpanel.add(newLabel); } private void generateReport() { FileDialog fileDialog = new FileDialog(JMainFrame.getJMainFrame(), messages.getString("main_msg4"), FileDialog.SAVE); FilenameFilter pdfFilenameFilter = (dir, name) -> name.endsWith(".pdf") || name.endsWith(".PDF"); fileDialog.setFilenameFilter(pdfFilenameFilter); String path = SzenarioVerwaltung.getInstance().holePfad(); if (path != null) { String szenarioFile = new File(path).getAbsolutePath(); File preselectedFile = new File(szenarioFile.substring(0, szenarioFile.lastIndexOf(".")) + ".pdf"); fileDialog.setDirectory(preselectedFile.getParent()); fileDialog.setFile(preselectedFile.getName()); } fileDialog.setVisible(true); if (null != fileDialog.getFile()) { String reportFilename; boolean nameChanged = false; if (fileDialog.getFile().endsWith(".pdf") || fileDialog.getFile().endsWith(".PDF")) { reportFilename = fileDialog.getFile(); } else { nameChanged = true; reportFilename = fileDialog.getFile() + ".pdf"; } int entscheidung = JOptionPane.YES_OPTION; Path reportFilepath = Path.of(fileDialog.getDirectory(), reportFilename); if (nameChanged && reportFilepath.toFile().exists()) { entscheidung = JOptionPane.showConfirmDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg17"), messages.getString("guimainmemu_msg10"), JOptionPane.YES_NO_OPTION); } if (entscheidung == JOptionPane.YES_OPTION) { try { ReportGenerator.getInstance().generateReport(reportFilepath.toString()); } catch (DocumentException | IOException e) { JOptionPane.showMessageDialog(JMainFrame.getJMainFrame(), messages.getString("guimainmemu_msg11")); } } } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIFooterPanel.java000066400000000000000000000015131506574241500265010ustar00rootroot00000000000000package filius.gui.netzwerksicht; import java.awt.Color; import javax.swing.JLabel; public class GUIFooterPanel extends GUIMainArea { private static final long serialVersionUID = 1L; private JLabel footer; public GUIFooterPanel(int width, int height, String text) { setLayout(null); setOpaque(false); setSize(width, height); footer = new JLabel(text); footer.setForeground(Color.lightGray); footer.setSize(footer.getFontMetrics(footer.getFont()).stringWidth(text) + 20, footer.getFontMetrics(footer.getFont()).getHeight()); add(footer); } public void updateViewport() { removeAll(); add(footer); } public void setFooterPos(int x, int y) { footer.setBounds(x, y, footer.getWidth(), footer.getHeight()); } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIKabelItem.java000066400000000000000000000071571506574241500261320ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.io.Serializable; import filius.hardware.Kabel; /** * * Die Klasse GUIKabelItem ist die Verbindung von Grafikdarstellung und "realem" Kabel. Sie enthält Jeweils ein * JCablePanel und ein Kabel. Dazu stellt es die getter und setter Methoden bereit. * * @author Thomas Gerding & Johannes Bade * */ public class GUIKabelItem implements Serializable { private static final long serialVersionUID = 1L; private JCablePanel kabelpanel; private Kabel dasKabel; public GUIKabelItem() { kabelpanel = new JCablePanel(); kabelpanel.setKabelItem(this); } public Kabel getDasKabel() { return dasKabel; } public void setDasKabel(Kabel dasKabel) { this.dasKabel = dasKabel; dasKabel.addObserver(kabelpanel); kabelpanel.updateBounds(); } public JCablePanel getKabelpanel() { return kabelpanel; } public void setKabelpanel(JCablePanel kabelpanel) { this.kabelpanel = kabelpanel; } public String toString() { String result = "["; if (dasKabel != null) result += "dasKabel (id)=" + dasKabel.hashCode() + ", "; else result += "dasKabel=, "; if (kabelpanel != null) { result += "kabelpanel (id)=" + kabelpanel.hashCode() + ", "; if (kabelpanel.getZiel1() != null) { result += "kabelpanel.ziel1 (id)" + kabelpanel.getZiel1().hashCode() + ", "; if (kabelpanel.getZiel1().getKnoten() != null) result += "kabelpanel.ziel1.knoten (name)" + kabelpanel.getZiel1().getKnoten().holeAnzeigeName() + ", "; else result += "kabelpanel.ziel1.knoten=, "; } else result += "kabelpanel.ziel1=, "; if (kabelpanel.getZiel2() != null) { result += "kabelpanel.ziel2 (id)" + kabelpanel.getZiel2().hashCode() + ", "; if (kabelpanel.getZiel2().getKnoten() != null) result += "kabelpanel.ziel2.knoten (name)" + kabelpanel.getZiel2().getKnoten().holeAnzeigeName() + ", "; else result += "kabelpanel.ziel2.knoten=, "; } } else result += "kabelpanel="; result += "]"; return result; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIKnotenItem.java000066400000000000000000000067701506574241500263520ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.event.MouseEvent; import javax.swing.event.MouseInputAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.gui.GUIEvents; import filius.gui.GUIMainMenu; import filius.gui.NatViewerControl; import filius.gui.SatViewerControl; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.Knoten; import filius.hardware.knoten.Notebook; import filius.hardware.knoten.Rechner; import filius.hardware.knoten.Switch; import filius.rahmenprogramm.SzenarioVerwaltung; public class GUIKnotenItem { private static Logger LOG = LoggerFactory.getLogger(GUIKnotenItem.class); private Knoten knoten; private JSidebarButton imageLabel; public JSidebarButton getImageLabel() { return imageLabel; } public void setImageLabel(JSidebarButton label) { this.imageLabel = label; label.addMouseListener(new MouseInputAdapter() { @Override public void mouseReleased(MouseEvent e) { if (GUIContainer.getGUIContainer().getActiveSite() == GUIMainMenu.MODUS_AKTION) { SzenarioVerwaltung.getInstance().setzeGeaendert(); if (e.getButton() == 3) { GUIEvents.getGUIEvents().kontextMenueAktionsmodus(GUIKnotenItem.this, e.getX(), e.getY()); } else if (e.getButton() == 1) { if (GUIKnotenItem.this.getKnoten() instanceof Rechner || GUIKnotenItem.this.getKnoten() instanceof Notebook) { GUIContainer.getGUIContainer().showDesktop(GUIKnotenItem.this); } else if (GUIKnotenItem.this.getKnoten() instanceof Switch) { SatViewerControl.getInstance().showViewer((Switch) GUIKnotenItem.this.getKnoten()); } else if (GUIKnotenItem.this.getKnoten() instanceof Gateway) { NatViewerControl.getInstance().showViewer((Gateway) GUIKnotenItem.this.getKnoten()); } } } } }); } public Knoten getKnoten() { return knoten; } public void setKnoten(Knoten knoten) { this.knoten = knoten; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIMainArea.java000066400000000000000000000050551506574241500257450ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Component; import java.io.Serializable; import javax.swing.JPanel; /** * Diese Klasse dient als Oberklasse für die verschiedenen Sichten im Haupt-Bereich der GUI. */ public class GUIMainArea extends JPanel implements Serializable { private static final long serialVersionUID = 1L; protected double minX = Integer.MAX_VALUE, maxX = 0, minY = Integer.MAX_VALUE, maxY = 0; private void resetClipBounds() { minX = Integer.MAX_VALUE; maxX = 0; minY = Integer.MAX_VALUE; maxY = 0; } private void updateClipBounds(Component elem) { if (elem.getBounds().getMinX() < minX) { minX = Math.max(0, elem.getBounds().getMinX()); } if (elem.getBounds().getMaxX() > maxX) { maxX = Math.min(elem.getBounds().getMaxX(), this.getWidth()); } if (elem.getBounds().getMinY() < minY) { minY = Math.max(0, elem.getBounds().getMinY()); } if (elem.getBounds().getMaxY() > maxY) { maxY = Math.min(elem.getBounds().getMaxY(), this.getHeight()); } } public void removeAll() { super.removeAll(); resetClipBounds(); } public Component add(Component comp) { super.add(comp); updateClipBounds(comp); return comp; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUINetworkPanel.java000066400000000000000000000112311506574241500266720ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Dimension; import java.util.List; import javax.swing.ImageIcon; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.hardware.knoten.Modem; import filius.hardware.knoten.Notebook; import filius.hardware.knoten.Rechner; import filius.hardware.knoten.Switch; import filius.hardware.knoten.Vermittlungsrechner; import filius.rahmenprogramm.IconMode; import filius.rahmenprogramm.Information; public class GUINetworkPanel extends GUIMainArea { private static final long serialVersionUID = 1L; public GUINetworkPanel(int width, int height) { setLayout(null); setPreferredSize(new Dimension(width, height)); setOpaque(false); setBounds(0, 0, width, height); } public void updateViewport(List knoten, List kabel) { removeAll(); for (GUIKnotenItem tempitem : knoten) { Knoten tempKnoten = tempitem.getKnoten(); JSidebarButton templabel = tempitem.getImageLabel(); tempKnoten.addObserver(templabel); tempKnoten.getSystemSoftware().addObserver(templabel); templabel.setSelektiert(false); // When the text is changed, the location is recomputed so that the icon does not move (which might happen // when the text is wider than the icon) templabel.setTextAndUpdateLocation(tempKnoten.holeAnzeigeName()); templabel.setTyp(tempKnoten.holeHardwareTyp()); if (tempitem.getKnoten() instanceof InternetKnoten) { templabel.updateTooltip((InternetKnoten) tempitem.getKnoten()); } IconMode mode = Information.getInformation().getIconMode(); if (tempitem.getKnoten() instanceof Switch) { if (((Switch) tempitem.getKnoten()).isCloud()) templabel.setIcon(new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.SWITCH_CLOUD))); else templabel.setIcon( new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.SWITCH + mode.suffix()))); } else if (tempitem.getKnoten() instanceof Vermittlungsrechner) { templabel.setIcon(new ImageIcon( getClass().getResource("/" + GUIDesignSidebar.VERMITTLUNGSRECHNER + mode.suffix()))); } else if (tempitem.getKnoten() instanceof Rechner) { templabel .setIcon(new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.RECHNER + mode.suffix()))); } else if (tempitem.getKnoten() instanceof Notebook) { templabel.setIcon( new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.NOTEBOOK + mode.suffix()))); } else if (tempitem.getKnoten() instanceof Modem) { templabel.setIcon(new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.MODEM + mode.suffix()))); } else if (tempitem.getKnoten() instanceof Gateway) { templabel .setIcon(new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.GATEWAY + mode.suffix()))); } templabel.setBounds(tempitem.getImageLabel().getBounds()); add(templabel); } for (GUIKabelItem tempcable : kabel) { add(tempcable.getKabelpanel()); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUIPrintPanel.java000066400000000000000000000077231506574241500263500ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Dimension; import java.util.List; import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; @SuppressWarnings("serial") public class GUIPrintPanel extends JPanel { private static final int EMPTY_BORDER = 10; private GUINetworkPanel networkPanel; private GUIDocumentationPanel docuPanel; private GUIFooterPanel footer; public GUIPrintPanel(int width, int height, String footerText) { setOpaque(false); footer = new GUIFooterPanel(width, height + 100, StringUtils.isBlank(footerText) ? "Filius" : footerText); add(footer); networkPanel = new GUINetworkPanel(width, height); networkPanel.setBounds(EMPTY_BORDER, EMPTY_BORDER, width, height); add(networkPanel); docuPanel = new GUIDocumentationPanel(width, height); docuPanel.setBounds(EMPTY_BORDER, EMPTY_BORDER, width, height); add(docuPanel); setSize(footer.getWidth() + 2 * EMPTY_BORDER, footer.getHeight() + 2 * EMPTY_BORDER); } public void updateViewport(List knoten, List kabel, List docuItems) { networkPanel.updateViewport(knoten, kabel); docuPanel.updateViewport(docuItems, false); updateFooterPosition(); footer.updateViewport(); } private void updateFooterPosition() { int footerY = (int) Math.max(networkPanel.maxY, docuPanel.maxY) + 30; int footerX = (int) Math.min(networkPanel.minX, docuPanel.minX) + EMPTY_BORDER; footer.setFooterPos(footerX, footerY); } @Override public Dimension getSize() { return new Dimension(getWidth(), getHeight()); } public int getClipY() { return (int) min(networkPanel.minY, docuPanel.minY, footer.minY); } public int getClipHeight() { int totalMaxY = (int) footer.maxY; return totalMaxY - getClipY() + 2 * EMPTY_BORDER; } public int getClipWidth() { int totalMaxX = (int) max(networkPanel.maxX, docuPanel.maxX, footer.maxX); return totalMaxX - getClipX() + 2 * EMPTY_BORDER; } public int getClipX() { return (int) min(networkPanel.minX, docuPanel.minX, footer.minX); } private double min(double a, double b, double c) { double min = Integer.MAX_VALUE; if (a < min) { min = a; } if (b < min) { min = b; } if (c < min) { min = c; } return min; } private double max(double a, double b, double c) { double max = 0; if (a > max) { max = a; } if (b > max) { max = b; } if (c > max) { max = c; } return max; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GUISidebar.java000066400000000000000000000057511506574241500256440ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Component; import java.awt.Dimension; import java.util.LinkedList; import java.util.List; import javax.swing.BoxLayout; import filius.gui.JBackgroundPanel; public abstract class GUISidebar { protected JBackgroundPanel leistenpanel; protected List buttonList; private static int width = 0; protected GUISidebar() { buttonList = new LinkedList(); leistenpanel = new JBackgroundPanel() { private static final long serialVersionUID = 1L; public Dimension getPreferredSize() { int height = 0; for (Component component : this.getComponents()) { if (component.getWidth() > width) width = component.getWidth(); height += component.getHeight(); } height += 30; return new Dimension(width, height); } }; leistenpanel.setBackgroundImage("gfx/allgemein/leisten_hg.png"); BoxLayout layout = new BoxLayout(leistenpanel, BoxLayout.Y_AXIS); leistenpanel.setLayout(layout); leistenpanel.setEnabled(false); addItemsToSidebar(); } protected abstract void addItemsToSidebar(); public JSidebarButton findButtonAt(int x, int y) { JSidebarButton klickLabel = null; for (JSidebarButton tmpLbl : buttonList) { if (x >= tmpLbl.getX() && y >= tmpLbl.getY() && x <= tmpLbl.getX() + tmpLbl.getWidth() && y <= tmpLbl.getY() + tmpLbl.getHeight()) { klickLabel = tmpLbl; } } return klickLabel; } public JBackgroundPanel getLeistenpanel() { return leistenpanel; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GatewayFirewallConfigTable.java000066400000000000000000000072141506574241500311070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; import javax.swing.table.TableModel; import filius.gui.JExtendedTable; import filius.rahmenprogramm.I18n; import filius.software.firewall.Firewall; import filius.software.firewall.FirewallRule; @SuppressWarnings("serial") public class GatewayFirewallConfigTable extends JExtendedTable implements I18n { public GatewayFirewallConfigTable(TableModel model, boolean editable) { super(model, editable); } public void editingStopped(ChangeEvent evt) { closeEditor(); } void closeEditor() { TableCellEditor editor = getCellEditor(); if (editor != null) { Firewall firewall = (Firewall) ((JFirewallDialog) parentGUI).getFirewall(); FirewallRule rule = firewall.getRuleset().get(editingRow); rule.srcIP = getCurrentValueAt(editingRow, 1); rule.srcMask = getCurrentValueAt(editingRow, 2); rule.destIP = getCurrentValueAt(editingRow, 3); rule.destMask = getCurrentValueAt(editingRow, 4); String protocol = getCurrentValueAt(editingRow, 5); if (protocol.equals("TCP")) { rule.protocol = FirewallRule.TCP; } else if (protocol.equals("UDP")) { rule.protocol = FirewallRule.UDP; } else { rule.protocol = FirewallRule.ALL_PROTOCOLS; } String port = getCurrentValueAt(editingRow, 6); try { rule.port = Integer.valueOf(port); } catch (NumberFormatException e) { rule.port = FirewallRule.ALL_PORTS; } String action = getCurrentValueAt(editingRow, 7); if (messages.getString("jfirewalldialog_msg33").equals(action)) { rule.action = FirewallRule.ACCEPT; } else { rule.action = FirewallRule.DROP; } removeEditor(); if (rule.validateExistingAttributes()) { firewall.updateRule(editingRow, rule); ((JFirewallDialog) parentGUI).updateRuleTable(); } } } private String getCurrentValueAt(int row, int col) { if (row == editingRow && col == editingColumn) { return (String) getCellEditor().getCellEditorValue(); } else { return (String) getValueAt(row, col); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/GatewayPortForwardingConfigTable.java000066400000000000000000000101711506574241500323050ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Component; import java.util.regex.Pattern; import javax.swing.event.ChangeEvent; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; import filius.gui.JExtendedTable; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.vermittlungsschicht.IpPaket; @SuppressWarnings("serial") public class GatewayPortForwardingConfigTable extends JExtendedTable implements I18n { public GatewayPortForwardingConfigTable(TableModel model, boolean editable) { super(model, editable); } public void editingStopped(ChangeEvent evt) { closeEditor(); } void closeEditor() { TableCellEditor editor = getCellEditor(); if (editor != null) { String data = (String) getCellEditor().getCellEditorValue(); setValueAt(data,editingRow,editingColumn); ((JPortForwardingDialog) parentGUI).addEintrag(getCurrentValueAt(editingRow, 0), getCurrentValueAt(editingRow, 1), getCurrentValueAt(editingRow, 2), getCurrentValueAt(editingRow, 3)); removeEditor(); } ((JPortForwardingDialog) parentGUI).aenderungenAnnehmen(); ((DefaultTableModel) getModel()).fireTableDataChanged(); } private String getCurrentValueAt(int row, int col) { if (row == editingRow && col == editingColumn) { String data = null; if (col == 0) { switch(((String) getCellEditor().getCellEditorValue()).toUpperCase()) { case "TCP": data = ""+IpPaket.TCP; break; case "UDP": data = ""+IpPaket.UDP; break; } } else { data = (String) getCellEditor().getCellEditorValue(); } return data; } else { return (String) getValueAt(row, col); } } public Component prepareRenderer(TableCellRenderer renderer, int row, int col) { Component comp = super.prepareRenderer(renderer, row, col); String cellValue = (String) getModel().getValueAt(row, col); Pattern pattern = null; switch (col) { case 0: pattern = EingabenUeberpruefung.musterProtocol; break; case 1: pattern = EingabenUeberpruefung.musterPort; break; case 2: pattern = EingabenUeberpruefung.musterIpAdresse; break; case 3: pattern = EingabenUeberpruefung.musterPort; break; } if (null != cellValue && EingabenUeberpruefung.isGueltig(cellValue, pattern)) { comp.setForeground(EingabenUeberpruefung.farbeRichtig); } else { comp.setForeground(EingabenUeberpruefung.farbeFalsch); } return comp; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JCablePanel.java000066400000000000000000000203131506574241500260150ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.QuadCurve2D; import java.awt.geom.Rectangle2D; import java.util.Observable; import java.util.Observer; import javax.swing.JPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.Hardware; import filius.rahmenprogramm.IconMode; import filius.rahmenprogramm.Information; /** * * @author Johannes Bade */ @SuppressWarnings("deprecation") public class JCablePanel extends JPanel implements Observer { private static Logger LOG = LoggerFactory.getLogger(JCablePanel.class); private static final long serialVersionUID = 1L; private GUIKnotenItem ziel1, ziel2; private GUIKabelItem kabelItem; private QuadCurve2D currCurve = null; private Color kabelFarbe = new Color(64, 64, 64); private final Color farbeKabel = new Color(64, 64, 64); private final Color farbeWireless = new Color(192, 192, 192); private final Color farbeBlinken = new Color(0, 255, 64); private final Color farbeFailure = Color.RED; public JCablePanel() { super(); this.setOpaque(false); } public void updateBounds() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JCablePanel), updateBounds()"); if (null != ziel1 && null != ziel2) { // Theoretisch korrekte Positionen int x1 = (int) (ziel1.getImageLabel().getX()); int x2 = (int) (ziel2.getImageLabel().getX()); int y1 = (int) (ziel1.getImageLabel().getY()); int y2 = (int) (ziel2.getImageLabel().getY()); x1 = (int) (x1 + (0.5 * ziel1.getImageLabel().getWidth())); y1 = (int) (y1 + (0.5 * ziel1.getImageLabel().getHeight())); x2 = (int) (x2 + (0.5 * ziel2.getImageLabel().getWidth())); y2 = (int) (y2 + (0.5 * ziel2.getImageLabel().getHeight())); // Absolut korrekte Positionen (also Sidebar und Menu rausgerechnet) if (x1 > x2) { int t1 = x1; x1 = x2; x2 = t1; } if (y1 > y2) { int t1 = y1; y1 = y2; y2 = t1; } // add 2 for each direction to take care of linewidth setBounds(x1 - 2, y1 - 2, x2 - x1 + 4, y2 - y1 + 4); LOG.trace("JCablePanel (" + this.hashCode() + "), bounds: " + x1 + "/" + y1 + ", " + x2 + "/" + y2 + " (W:" + (x2 - x1) + ", H:" + (y2 - y1) + ")"); } } protected void paintComponent(Graphics g) { super.paintComponent(g); int x1, x2, y1, y2; // Theoretisch korrekte Positionen x1 = (int) (ziel1.getImageLabel().getX() + (0.5 * ziel1.getImageLabel().getWidth())); x2 = (int) (ziel2.getImageLabel().getX() + (0.5 * ziel2.getImageLabel().getWidth())); y1 = (int) (ziel1.getImageLabel().getY() + (0.5 * ziel1.getImageLabel().getHeight())); y2 = (int) (ziel2.getImageLabel().getY() + (0.5 * ziel2.getImageLabel().getHeight())); /* Einfaches Zeichnen */ Graphics2D g2 = (Graphics2D) g; g2.setStroke(new BasicStroke(2)); if (isWireless()) { if (kabelFarbe.equals(farbeKabel)) { kabelFarbe = farbeWireless; } if (!kabelFarbe.equals(farbeBlinken)) { float dash[] = { 10.0f }; g2.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f)); } } g.setColor(kabelFarbe); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int kp1, kp2; if (Information.getInformation().getIconMode().equals(IconMode.SYMBOL)) { // use straight lines in case of the symbols view kp1 = (x1 - this.getX() + x2 - this.getX()) / 2; kp2 = (y1 - this.getY() + y2 - this.getY()) / 2; } else { kp1 = (x1 - this.getX() + x2 - this.getX()) / 4; if ((x1 > x2 && y1 > y2) || (x1 < x2 && y1 < y2)) kp1 = 3 * kp1; // correct X value of control point for falling // lines (upper left to lower right corner) kp2 = (y1 - this.getY() + y2 - this.getY()) / 4; } QuadCurve2D myCurve = new QuadCurve2D.Double(x1 - this.getX(), y1 - this.getY(), kp1, kp2, x2 - this.getX(), y2 - this.getY()); // Kurve malen g2.draw(myCurve); this.currCurve = myCurve; this.setOpaque(false); } private boolean isWireless() { return getKabelItem() != null && getKabelItem().getDasKabel() != null && getKabelItem().getDasKabel().getWireless(); } /* * Method to examine whether the mouse was clicked close to a line representing a cable in filius. ATTENTION: There * are several workarounds necessary to cope with Java's strange boundary handling. Thus, the actual bounds are * tested prior to actually use them for determining a point to be inside a bound of a curve or line, respectively. */ public boolean clicked(int x, int y) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JCablePanel), clicked(" + x + "," + y + ")"); int delta = 10; Rectangle2D absolutePointerRect = new Rectangle2D.Double(x - delta, y - delta, 2 * delta, 2 * delta); boolean hitPanel = getBounds().intersects(absolutePointerRect); Rectangle2D relativePointerRect = new Rectangle2D.Double(x - getX() - delta, y - getY() - delta, 2 * delta, 2 * delta); boolean hitCurve = currCurve.intersects(relativePointerRect); return hitCurve && hitPanel; } public GUIKnotenItem getZiel1() { return ziel1; } public void setZiel1(GUIKnotenItem ziel1) { this.ziel1 = ziel1; } public GUIKnotenItem getZiel2() { return ziel2; } public void setZiel2(GUIKnotenItem ziel2) { this.ziel2 = ziel2; updateBounds(); } public void setKabelItem(GUIKabelItem value) { kabelItem = value; } public GUIKabelItem getKabelItem() { return kabelItem; } /** * Wird genutzt um Kabel blinken zu lassen :) */ public void update(Observable o, Object arg) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JCablePanel), update(" + o + "," + arg + ")"); LOG.trace("update cable: {}", arg); if (arg.equals(Boolean.TRUE)) { kabelFarbe = farbeBlinken; } else if (arg.equals(Hardware.FAILURE)) { LOG.debug("set cable color to red"); kabelFarbe = farbeFailure; } else { resetColor(); } updateUI(); } private void resetColor() { if (isWireless()) { kabelFarbe = farbeWireless; } else { kabelFarbe = farbeKabel; } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JConnectionsDialog.java000066400000000000000000000707501506574241500274430ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.SpringLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.hardware.Kabel; import filius.hardware.NetzwerkInterface; import filius.hardware.Port; import filius.hardware.Verbindung; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.hardware.knoten.LokalerKnoten; import filius.hardware.knoten.Vermittlungsrechner; import filius.rahmenprogramm.I18n; /** * @author stefan * */ @SuppressWarnings("serial") public class JConnectionsDialog extends JDialog implements I18n { private static Logger LOG = LoggerFactory.getLogger(JConnectionsDialog.class); private static final int LINE_HEIGHT = 36; private final ImageIcon nicIcon = new ImageIcon(getClass().getResource("/gfx/hardware/rj45.png")); private Vermittlungsrechner internetSwitch; private Map nicToNodeMap = new HashMap(); private JButton[] btnLocal = new JButton[8]; private JLabel[] lblLocal = new JLabel[8]; private JPanel buttonPanel; private JPanel cablePanel; private JPanel remoteInterfacesPanel, localInterfacesPanel; private JButton[] btnRemote; private JButton btnAddInterface; private JButton btnRemoveInterface; private JLabel[] lblRemote;; private class LinePanel extends JPanel { Point lineStart = new Point(0, 0); Point lineEnd = new Point(0, 0); Color lineColor = new Color(64, 64, 64); LinePanel() { super(); this.setOpaque(false); } public void setStartPoint(int x, int y) { lineStart = new Point(x, y); } public void setEndPoint(int x, int y) { lineEnd = new Point(x, y); } public void setColor(Color col) { lineColor = col; } public String toString() { return "[" + "name='" + getName() + "', " + "start=(" + lineStart.x + "/" + lineStart.y + "), " + "end=(" + lineEnd.x + "/" + lineEnd.y + "), " + "color=" + lineColor.toString() + ", " + "bounds=" + getBounds() + "]"; } public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(lineColor); Graphics2D g2 = (Graphics2D) g; g2.setStroke(new BasicStroke(2)); g2.drawLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y); } } public JConnectionsDialog(Frame owner, Vermittlungsrechner internetSwitch) { super(owner, true); this.internetSwitch = internetSwitch; initComponents(); updateAll(); } private void updateNicToNodeMap(Vermittlungsrechner internetSwitch) { nicToNodeMap.clear(); for (NetzwerkInterface nic : internetSwitch.getNetzwerkInterfaces()) { Knoten node = getConnectedComponent(nic); if (node != null) { nicToNodeMap.put(nic, node); } } } private void updateRemoteInterfaces() { int nicNr = 0; for (NetzwerkInterface nic : internetSwitch.getNetzwerkInterfaces()) { nicNr++; Knoten node = nicToNodeMap.get(nic); if (node != null) { String remoteAddress = ""; Verbindung connection = this.getConnectedCable(nic); Port[] ports = connection.getAnschluesse(); for (Port port : ports) { if (port.getNIC() != null && port.getNIC() != nic) { remoteAddress = port.getNIC().getIp(); } } btnRemote[nicNr - 1].setEnabled(false); if (node instanceof filius.hardware.knoten.InternetKnoten) { lblRemote[nicNr - 1].setText(node.holeAnzeigeName() + " (" + remoteAddress + ")"); } else { lblRemote[nicNr - 1].setText(node.holeAnzeigeName()); } } else { btnRemote[nicNr - 1].setEnabled(false); btnRemote[nicNr - 1].setVisible(false); lblRemote[nicNr - 1].setText(""); } } for (int i = nicNr; i < 8; i++) { btnRemote[i].setEnabled(false); btnRemote[i].setVisible(false); lblRemote[i].setText(""); } } private void updateLocalInterfaces() { int nicNr = 0; Knoten node; for (NetzwerkInterface nic : internetSwitch.getNetzwerkInterfaces()) { nicNr++; lblLocal[nicNr - 1].setText("NIC " + nicNr + ": " + nic.getIp()); node = getConnectedComponent(nic); if (node != null) { btnLocal[nicNr - 1].setOpaque(true); btnLocal[nicNr - 1].setBackground(Color.GREEN); btnLocal[nicNr - 1].setEnabled(true); } else { btnLocal[nicNr - 1].setOpaque(true); btnLocal[nicNr - 1].setBackground(Color.RED); btnLocal[nicNr - 1].setEnabled(true); } } btnAddInterface.setEnabled(nicNr != 8); btnRemoveInterface.setEnabled(nicNr != 0); for (int i = nicNr; i < 8; i++) { btnLocal[nicNr].setOpaque(false); btnLocal[i].setEnabled(false); lblLocal[i].setText(""); } } private void updateConnections() { cablePanel.removeAll(); SpringLayout cableLayout = new SpringLayout(); cablePanel.setLayout(cableLayout); int nicNr = 0; for (NetzwerkInterface nic : internetSwitch.getNetzwerkInterfaces()) { Knoten node = getConnectedComponent(nic); if (node != null) { LinePanel cable = new LinePanel(); int yPos = 25 + (int) ((nicNr + 0.5) * LINE_HEIGHT); cable.setStartPoint(-2, yPos); cable.setEndPoint(282, yPos); cableLayout.putConstraint(SpringLayout.WEST, cable, 0, SpringLayout.WEST, cablePanel); cableLayout.putConstraint(SpringLayout.NORTH, cable, 0, SpringLayout.NORTH, cablePanel); cable.setPreferredSize(new Dimension(280, 700)); cablePanel.add(cable); } nicNr++; } cablePanel.repaint(); } private void initComponents() { JPanel upperCompound, noteCompound, buttonCompound; // - create assignment area upperCompound = new JPanel(); // upperCompound.setBackground(Color.GREEN); upperCompound.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); upperCompound.setLayout(new BoxLayout(upperCompound, BoxLayout.X_AXIS)); upperCompound.setPreferredSize(new Dimension(700, 360)); initRemoteInterfaces(); initLocalInterfaces(); // -- create visual cable connections (middle area) cablePanel = new JPanel(); cablePanel.setPreferredSize(new Dimension(240, 700)); upperCompound.add(remoteInterfacesPanel); upperCompound.add(cablePanel); upperCompound.add(localInterfacesPanel); // - create note area noteCompound = new JPanel(); // noteCompound.setBackground(Color.BLUE); noteCompound.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JTextArea usageNote = new JTextArea(messages.getString("jvermittlungsrechnerkonfiguration_msg19")); usageNote.setOpaque(false); usageNote.setEditable(false); usageNote.setLineWrap(true); usageNote.setWrapStyleWord(true); usageNote.setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); usageNote.setSize(new Dimension(700, 200)); // usageNote.setBorder(BorderFactory.createLineBorder(Color.GRAY,2)); noteCompound.add(Box.createVerticalGlue()); noteCompound.add(usageNote, BorderLayout.CENTER); noteCompound.setMinimumSize(new Dimension(700, 200)); // - create main button area buttonCompound = new JPanel(); // buttonCompound.setBackground(Color.RED); buttonCompound.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JButton btnClose = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg20")); btnClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { Container c = (Container) arg0.getSource(); do { c = c.getParent(); } while (!(c instanceof JDialog)); c.setVisible(false); GUIContainer.getGUIContainer().getProperty().reInit(); GUIContainer.getGUIContainer().getProperty().maximieren(); } }); buttonCompound.add(btnClose); buttonCompound.setMinimumSize(new Dimension(200, 50)); this.getContentPane().add(upperCompound, BorderLayout.NORTH); this.getContentPane().add(noteCompound, BorderLayout.CENTER); this.getContentPane().add(buttonCompound, BorderLayout.SOUTH); } private void initRemoteInterfaces() { remoteInterfacesPanel = new JPanel(); remoteInterfacesPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2)); remoteInterfacesPanel.setPreferredSize(new Dimension(250, 700)); remoteInterfacesPanel.setMaximumSize(remoteInterfacesPanel.getPreferredSize()); remoteInterfacesPanel.setSize(400, remoteInterfacesPanel.getHeight()); JLabel lblRemoteTitle = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg21")); remoteInterfacesPanel.add(lblRemoteTitle); btnRemote = new JButton[8]; lblRemote = new JLabel[8]; for (int i = 0; i < 8; i++) { btnRemote[i] = new JButton(nicIcon); lblRemote[i] = new JLabel(); remoteInterfacesPanel.add(btnRemote[i]); remoteInterfacesPanel.add(lblRemote[i]); } SpringLayout layoutRemote = new SpringLayout(); layoutRemote.putConstraint(SpringLayout.NORTH, lblRemoteTitle, 5, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.HORIZONTAL_CENTER, lblRemoteTitle, 0, SpringLayout.HORIZONTAL_CENTER, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[0], 25, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[0], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[1], 25 + 1 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[1], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[2], 25 + 2 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[2], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[3], 25 + 3 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[3], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[4], 25 + 4 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[4], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[5], 25 + 5 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[5], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[6], 25 + 6 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[6], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.NORTH, btnRemote[7], 25 + 7 * LINE_HEIGHT, SpringLayout.NORTH, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.EAST, btnRemote[7], 0, SpringLayout.EAST, remoteInterfacesPanel); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[0], 0, SpringLayout.VERTICAL_CENTER, btnRemote[0]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[0], -10, SpringLayout.WEST, btnRemote[0]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[1], 0, SpringLayout.VERTICAL_CENTER, btnRemote[1]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[1], -10, SpringLayout.WEST, btnRemote[1]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[2], 0, SpringLayout.VERTICAL_CENTER, btnRemote[2]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[2], -10, SpringLayout.WEST, btnRemote[2]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[3], 0, SpringLayout.VERTICAL_CENTER, btnRemote[3]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[3], -10, SpringLayout.WEST, btnRemote[3]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[4], 0, SpringLayout.VERTICAL_CENTER, btnRemote[4]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[4], -10, SpringLayout.WEST, btnRemote[4]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[5], 0, SpringLayout.VERTICAL_CENTER, btnRemote[5]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[5], -10, SpringLayout.WEST, btnRemote[5]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[6], 0, SpringLayout.VERTICAL_CENTER, btnRemote[6]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[6], -10, SpringLayout.WEST, btnRemote[6]); layoutRemote.putConstraint(SpringLayout.VERTICAL_CENTER, lblRemote[7], 0, SpringLayout.VERTICAL_CENTER, btnRemote[7]); layoutRemote.putConstraint(SpringLayout.EAST, lblRemote[7], -10, SpringLayout.WEST, btnRemote[7]); remoteInterfacesPanel.setLayout(layoutRemote); } private void initLocalInterfaces() { localInterfacesPanel = new JPanel(); localInterfacesPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2)); localInterfacesPanel.setPreferredSize(new Dimension(250, 700)); localInterfacesPanel.setMaximumSize(localInterfacesPanel.getPreferredSize()); localInterfacesPanel.setSize(400, localInterfacesPanel.getHeight()); JLabel lblLocalTitle = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg22")); localInterfacesPanel.add(lblLocalTitle); for (int i = 0; i < 8; i++) { btnLocal[i] = new JButton(nicIcon); btnLocal[i].addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { int currentIdx = -1; int markedIdx = -1; for (int i = 0; i < 8; i++) { if (btnLocal[i].equals(evt.getSource())) { currentIdx = i; } if (btnLocal[i].isOpaque() && btnLocal[i].getBackground().equals(Color.YELLOW)) { markedIdx = i; } } if (markedIdx == currentIdx) { updateLocalInterfaces(); } else if (markedIdx == -1) { btnLocal[currentIdx].setBackground(Color.YELLOW); btnLocal[currentIdx].setOpaque(true); } else if (markedIdx >= 0) { Port port1 = internetSwitch.getNetzwerkInterfaces().get(markedIdx).getPort(); Port port2 = internetSwitch.getNetzwerkInterfaces().get(currentIdx).getPort(); JConnectionsDialog.this.swapConnection(port1, port2); updateAll(); } } }); localInterfacesPanel.add(btnLocal[i]); lblLocal[i] = new JLabel(); localInterfacesPanel.add(lblLocal[i]); } // btnAddInterface = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg24")); btnAddInterface = new JButton("+"); btnRemoveInterface = new JButton("-"); btnAddInterface.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { internetSwitch.hinzuAnschluss(); updateAll(); } }); btnRemoveInterface.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { List nics = internetSwitch.getNetzwerkInterfaces(); if (!nics.isEmpty()) { NetzwerkInterface nic = nics.get(nics.size() - 1); removeConnection(nic.getPort()); internetSwitch.removeNic(nic); updateAll(); } } }); buttonPanel = new JPanel(); buttonPanel.setOpaque(false); buttonPanel.add(btnAddInterface); buttonPanel.add(btnRemoveInterface); localInterfacesPanel.add(buttonPanel); // ### Layout (Local interfaces) SpringLayout layoutLocal = new SpringLayout(); layoutLocal.putConstraint(SpringLayout.NORTH, lblLocalTitle, 5, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.HORIZONTAL_CENTER, lblLocalTitle, 0, SpringLayout.HORIZONTAL_CENTER, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[0], 25, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[0], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[1], 25 + 1 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[1], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[2], 25 + 2 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[2], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[3], 25 + 3 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[3], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[4], 25 + 4 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[4], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[5], 25 + 5 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[5], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[6], 25 + 6 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[6], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.NORTH, btnLocal[7], 25 + 7 * LINE_HEIGHT, SpringLayout.NORTH, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.WEST, btnLocal[7], 0, SpringLayout.WEST, localInterfacesPanel); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[0], 0, SpringLayout.VERTICAL_CENTER, btnLocal[0]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[0], 10, SpringLayout.EAST, btnLocal[0]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[1], 0, SpringLayout.VERTICAL_CENTER, btnLocal[1]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[1], 10, SpringLayout.EAST, btnLocal[1]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[2], 0, SpringLayout.VERTICAL_CENTER, btnLocal[2]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[2], 10, SpringLayout.EAST, btnLocal[2]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[3], 0, SpringLayout.VERTICAL_CENTER, btnLocal[3]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[3], 10, SpringLayout.EAST, btnLocal[3]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[4], 0, SpringLayout.VERTICAL_CENTER, btnLocal[4]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[4], 10, SpringLayout.EAST, btnLocal[4]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[5], 0, SpringLayout.VERTICAL_CENTER, btnLocal[5]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[5], 10, SpringLayout.EAST, btnLocal[5]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[6], 0, SpringLayout.VERTICAL_CENTER, btnLocal[6]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[6], 10, SpringLayout.EAST, btnLocal[6]); layoutLocal.putConstraint(SpringLayout.VERTICAL_CENTER, lblLocal[7], 0, SpringLayout.VERTICAL_CENTER, btnLocal[7]); layoutLocal.putConstraint(SpringLayout.WEST, lblLocal[7], 10, SpringLayout.EAST, btnLocal[7]); layoutLocal.putConstraint(SpringLayout.NORTH, buttonPanel, 0, SpringLayout.SOUTH, btnLocal[7]); layoutLocal.putConstraint(SpringLayout.HORIZONTAL_CENTER, buttonPanel, 0, SpringLayout.HORIZONTAL_CENTER, localInterfacesPanel); localInterfacesPanel.setLayout(layoutLocal); } private void updateAll() { updateNicToNodeMap(internetSwitch); updateRemoteInterfaces(); updateLocalInterfaces(); updateConnections(); } private Knoten getConnectedComponent(NetzwerkInterface nic) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), holeVerbundeneKomponente(" + nic + ")"); if (nic.getPort().getVerbindung() == null) { return null; } Port lokalerAnschluss, entfernterAnschluss; lokalerAnschluss = nic.getPort(); Port[] ports = lokalerAnschluss.getVerbindung().getAnschluesse(); if (ports[0] == lokalerAnschluss) { entfernterAnschluss = ports[1]; } else { entfernterAnschluss = ports[0]; } for (GUIKnotenItem knotenItem : GUIContainer.getGUIContainer().getKnotenItems()) { Knoten knoten = knotenItem.getKnoten(); if (knoten instanceof LokalerKnoten) { for (Port port : ((LokalerKnoten) knoten).getAnschluesse()) { if (port.equals(entfernterAnschluss)) { return knoten; } } } else if (knoten instanceof InternetKnoten) { for (NetzwerkInterface networkInterface : ((InternetKnoten) knoten).getNetzwerkInterfaces()) { if (networkInterface.getPort().equals(entfernterAnschluss)) { return knoten; } } } } return null; } private Kabel getConnectedCable(NetzwerkInterface nic) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), getConnectedCable(" + nic + ")"); Verbindung nicConn = nic.getPort().getVerbindung(); if (nicConn == null) { return null; } for (GUIKabelItem cable : GUIContainer.getGUIContainer().getCableItems()) { if (nicConn.equals(cable.getDasKabel())) { return (Kabel) cable.getDasKabel(); } } return null; } private void removeConnection(Port port) { for (GUIKabelItem cableItem : GUIContainer.getGUIContainer().getCableItems()) { if (cableItem.getDasKabel().getAnschluesse()[0].equals(port) || cableItem.getDasKabel().getAnschluesse()[1].equals(port)) { cableItem.getDasKabel().anschluesseTrennen(); GUIContainer.getGUIContainer().removeCableItem(cableItem); GUIContainer.getGUIContainer().updateViewport(); break; } } } private void swapConnection(Port port1, Port port2) { Kabel kabel1 = null; int portIdx1 = 0; Kabel kabel2 = null; int portIdx2 = 0; for (GUIKabelItem cableItem : GUIContainer.getGUIContainer().getCableItems()) { Kabel cable = cableItem.getDasKabel(); if (cable.getAnschluesse()[0].equals(port1)) { kabel1 = cable; portIdx1 = 0; } else if (cable.getAnschluesse()[1].equals(port1)) { kabel1 = cable; portIdx1 = 1; } else if (cable.getAnschluesse()[0].equals(port2)) { kabel2 = cable; portIdx2 = 0; } else if (cable.getAnschluesse()[1].equals(port2)) { kabel2 = cable; portIdx2 = 1; } } if (kabel1 != null && kabel2 != null && kabel1 != kabel2) { kabel1.anschluesseTrennen(); kabel2.anschluesseTrennen(); Port[] anschluesse = kabel1.getAnschluesse(); port2.setVerbindung(kabel1); anschluesse[portIdx1] = port2; kabel1.setAnschluesse(anschluesse); anschluesse = kabel2.getAnschluesse(); port1.setVerbindung(kabel2); anschluesse[portIdx2] = port1; kabel2.setAnschluesse(anschluesse); } else if (kabel1 == null && kabel2 != null) { kabel2.anschluesseTrennen(); port1.setVerbindung(kabel2); Port[] anschluesse = kabel2.getAnschluesse(); anschluesse[portIdx2] = port1; kabel2.setAnschluesse(anschluesse); } else if (kabel1 != null && kabel2 == null) { kabel1.anschluesseTrennen(); Port[] anschluesse = kabel1.getAnschluesse(); port2.setVerbindung(kabel1); anschluesse[portIdx1] = port2; kabel1.setAnschluesse(anschluesse); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JDHCPKonfiguration.java000066400000000000000000000504021506574241500273070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.SpringLayout; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.JExtendedTable; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.dhcp.DHCPAddressAssignment; import filius.software.dhcp.DHCPServer; import filius.software.system.GatewayFirmware; import filius.software.system.InternetKnotenBetriebssystem; public class JDHCPKonfiguration extends JDialog implements I18n, ItemListener { private static Logger LOG = LoggerFactory.getLogger(JDHCPKonfiguration.class); private static final long serialVersionUID = 1L; private DHCPServer server; private JTextField tfObergrenze; private JTextField tfUntergrenze; private JTextField tfNetzmaske; private JTextField tfGateway; private JTextField tfDNSServer; private JCheckBox cbAktiv; private JCheckBox cbUseInternal; private JTabbedPane tabbedPane; protected JTable staticAddressTable; private InternetKnotenBetriebssystem os; public JDHCPKonfiguration(JFrame owner, String titel, InternetKnotenBetriebssystem bs) { super(owner, titel, true); os = bs; this.server = bs.getDHCPServer(); this.setSize(380, 380); this.setResizable(true); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); Point location = new Point((screen.width / 2) - 190, (screen.height / 2) - 140); this.setLocation(location); initComponents(); } private void initComponents() { JPanel jpDhcp; JLabel lbObergrenze; JLabel lbUntergrenze; JLabel lbNetzmaske; JLabel lbGateway; JLabel lbDNSServer; JButton btUebernehmen; JLabel lbAktiv; JLabel lbUseInternal; final JDialog config = this; SpringLayout layout = new SpringLayout(); jpDhcp = new JPanel(layout); lbUntergrenze = new JLabel(messages.getString("jdhcpkonfiguration_msg1")); tfUntergrenze = new JTextField(server.getUntergrenze()); tfUntergrenze.setPreferredSize(new Dimension(150, 25)); tfUntergrenze.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfUntergrenze); } }); lbObergrenze = new JLabel(messages.getString("jdhcpkonfiguration_msg2")); tfObergrenze = new JTextField(server.getObergrenze()); tfObergrenze.setPreferredSize(new Dimension(150, 25)); tfObergrenze.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfObergrenze); } }); lbNetzmaske = new JLabel(messages.getString("jdhcpkonfiguration_msg3")); tfNetzmaske = new JTextField(server.getSubnetzmaske()); tfNetzmaske.setPreferredSize(new Dimension(150, 25)); tfNetzmaske.setEditable(false); lbGateway = new JLabel(messages.getString("jdhcpkonfiguration_msg4")); tfGateway = new JTextField(server.determineGatewayip()); tfGateway.setPreferredSize(new Dimension(150, 25)); tfGateway.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfGateway); } }); lbDNSServer = new JLabel(messages.getString("jdhcpkonfiguration_msg5")); tfDNSServer = new JTextField(server.determineDnsserverip()); tfDNSServer.setPreferredSize(new Dimension(150, 25)); tfDNSServer.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfDNSServer); } }); jpDhcp.add(lbUntergrenze); jpDhcp.add(lbObergrenze); jpDhcp.add(lbNetzmaske); JPanel borderPanel = new JPanel(); // Panel used to paint border around // gateway/DNS form field borderPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2)); borderPanel.setOpaque(false); jpDhcp.add(borderPanel); jpDhcp.add(tfUntergrenze); jpDhcp.add(tfObergrenze); jpDhcp.add(tfNetzmaske); jpDhcp.add(lbGateway); jpDhcp.add(lbDNSServer); jpDhcp.add(tfGateway); jpDhcp.add(tfDNSServer); btUebernehmen = new JButton(messages.getString("jdhcpkonfiguration_msg7")); btUebernehmen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { speichern(); update(); config.setVisible(false); } }); btUebernehmen.setSize(new Dimension(50, 30)); lbAktiv = new JLabel(messages.getString("jdhcpkonfiguration_msg6")); lbAktiv.setPreferredSize(new Dimension(200, 15)); cbAktiv = new JCheckBox(); cbAktiv.setSelected(server.isAktiv()); lbUseInternal = new JLabel(messages.getString("jdhcpkonfiguration_msg8")); lbUseInternal.setToolTipText(messages.getString("jdhcpkonfiguration_msg9")); cbUseInternal = new JCheckBox(); cbUseInternal.addItemListener(this); cbUseInternal.setToolTipText(messages.getString("jdhcpkonfiguration_msg9")); cbUseInternal.setSelected(server.isOwnSettings()); jpDhcp.add(lbUseInternal); jpDhcp.add(cbUseInternal); if (os instanceof GatewayFirmware) { lbUseInternal.setVisible(false); cbUseInternal.setVisible(false); } jpDhcp.add(lbAktiv); jpDhcp.add(cbAktiv); /* Layout. Set positions with Constraints. */ // Labels: layout.putConstraint(SpringLayout.NORTH, lbUntergrenze, 20, SpringLayout.NORTH, this.getContentPane()); layout.putConstraint(SpringLayout.WEST, lbUntergrenze, 25, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, lbObergrenze, 20, SpringLayout.SOUTH, lbUntergrenze); layout.putConstraint(SpringLayout.WEST, lbObergrenze, 25, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, lbNetzmaske, 20, SpringLayout.SOUTH, lbObergrenze); layout.putConstraint(SpringLayout.WEST, lbNetzmaske, 25, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, lbGateway, 30, SpringLayout.SOUTH, lbNetzmaske); layout.putConstraint(SpringLayout.WEST, lbGateway, 30, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, lbDNSServer, 20, SpringLayout.SOUTH, lbGateway); layout.putConstraint(SpringLayout.WEST, lbDNSServer, 30, SpringLayout.WEST, this.getContentPane()); // Textfields: layout.putConstraint(SpringLayout.NORTH, tfUntergrenze, 0, SpringLayout.NORTH, lbUntergrenze); layout.putConstraint(SpringLayout.WEST, tfUntergrenze, 200, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, tfObergrenze, 0, SpringLayout.NORTH, lbObergrenze); layout.putConstraint(SpringLayout.WEST, tfObergrenze, 200, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, tfNetzmaske, 0, SpringLayout.NORTH, lbNetzmaske); layout.putConstraint(SpringLayout.WEST, tfNetzmaske, 200, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, tfGateway, 0, SpringLayout.NORTH, lbGateway); layout.putConstraint(SpringLayout.WEST, tfGateway, 195, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, tfDNSServer, 0, SpringLayout.NORTH, lbDNSServer); layout.putConstraint(SpringLayout.WEST, tfDNSServer, 195, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.EAST, lbUseInternal, 0, SpringLayout.EAST, tfDNSServer); layout.putConstraint(SpringLayout.NORTH, lbUseInternal, 10, SpringLayout.SOUTH, tfDNSServer); layout.putConstraint(SpringLayout.EAST, cbUseInternal, 0, SpringLayout.WEST, lbUseInternal); layout.putConstraint(SpringLayout.SOUTH, cbUseInternal, 4, SpringLayout.SOUTH, lbUseInternal); /* Layout */ layout.putConstraint(SpringLayout.NORTH, cbAktiv, 25, SpringLayout.SOUTH, lbUseInternal); layout.putConstraint(SpringLayout.WEST, cbAktiv, 25, SpringLayout.WEST, this.getContentPane()); layout.putConstraint(SpringLayout.NORTH, lbAktiv, 4, SpringLayout.NORTH, cbAktiv); layout.putConstraint(SpringLayout.WEST, lbAktiv, 4, SpringLayout.EAST, cbAktiv); layout.putConstraint(SpringLayout.NORTH, borderPanel, 10, SpringLayout.SOUTH, tfNetzmaske); layout.putConstraint(SpringLayout.WEST, borderPanel, 25, SpringLayout.WEST, this.getContentPane()); borderPanel.setPreferredSize(new Dimension(325, 105)); tabbedPane = new JTabbedPane(); tabbedPane.add(messages.getString("jdhcpkonfiguration_msg10"), jpDhcp); tabbedPane.setPreferredSize(new Dimension(360, 320)); getContentPane().add(tabbedPane, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); buttonPanel.setSize(360, 40); buttonPanel.add(btUebernehmen, BorderLayout.CENTER); getContentPane().add(buttonPanel, BorderLayout.SOUTH); tabbedPane.add(messages.getString("jdhcpkonfiguration_msg11"), createStaticConfigPanel()); update(); } private JPanel createStaticConfigPanel() { Box vBox, hBox; DefaultTableModel tabellenModell; TableColumnModel tcm; JScrollPane scrollPane; JPanel staticConfigPanel = new JPanel(new BorderLayout()); vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel macAddressLabel = new JLabel(messages.getString("jdhcpkonfiguration_msg12")); macAddressLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(macAddressLabel); hBox.add(Box.createHorizontalStrut(5)); JTextField macAddressTextField = new JTextField(); macAddressTextField.setPreferredSize(new Dimension(275, 25)); macAddressTextField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterMacAddress, macAddressTextField); } }); hBox.add(macAddressTextField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JLabel ipAddressLabel = new JLabel(messages.getString("jdhcpkonfiguration_msg13")); ipAddressLabel.setPreferredSize(new Dimension(170, 25)); hBox.add(ipAddressLabel); hBox.add(Box.createHorizontalStrut(5)); JTextField ipAddressTextField = new JTextField(); ipAddressTextField.setPreferredSize(new Dimension(275, 25)); ipAddressTextField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, ipAddressTextField); } }); hBox.add(ipAddressTextField); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(5)); JButton addButton = new JButton(messages.getString("jdhcpkonfiguration_msg14")); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (ueberpruefen(EingabenUeberpruefung.musterMacAddress, macAddressTextField) && ueberpruefen(EingabenUeberpruefung.musterIpAdresse, ipAddressTextField)) { ((DefaultTableModel) staticAddressTable.getModel()) .addRow(new Object[] { macAddressTextField.getText(), ipAddressTextField.getText() }); macAddressTextField.setText(""); ipAddressTextField.setText(""); } } }); hBox.add(addButton); hBox.add(Box.createHorizontalStrut(5)); JButton removeButton = new JButton(messages.getString("jdhcpkonfiguration_msg15")); removeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int zeilenNummer = staticAddressTable.getSelectedRow(); if (zeilenNummer != -1) { List macAddresses = new ArrayList<>(); List ipAddresses = new ArrayList<>(); for (int i = 0; i < staticAddressTable.getModel().getRowCount(); i++) { if (i != zeilenNummer) { macAddresses.add((String) staticAddressTable.getModel().getValueAt(i, 0)); ipAddresses.add((String) staticAddressTable.getModel().getValueAt(i, 1)); } } ((DefaultTableModel) staticAddressTable.getModel()).setRowCount(0); for (int i = 0; i < macAddresses.size() && i < ipAddresses.size(); i++) { ((DefaultTableModel) staticAddressTable.getModel()) .addRow(new Object[] { macAddresses.get(i), ipAddresses.get(i) }); } } } }); hBox.add(removeButton); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); tabellenModell = new DefaultTableModel(0, 2); staticAddressTable = new JExtendedTable(tabellenModell, false); staticAddressTable.setIntercellSpacing(new Dimension(5, 5)); staticAddressTable.setRowHeight(30); staticAddressTable.setShowGrid(false); staticAddressTable.setFillsViewportHeight(true); staticAddressTable.setBackground(Color.WHITE); staticAddressTable.setShowHorizontalLines(true); tcm = staticAddressTable.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("jdhcpkonfiguration_msg12")); tcm.getColumn(1).setHeaderValue(messages.getString("jdhcpkonfiguration_msg13")); scrollPane = new JScrollPane(staticAddressTable); vBox.add(scrollPane); staticConfigPanel.add(vBox, BorderLayout.CENTER); return staticConfigPanel; } /** Listens to the check boxes. */ public void itemStateChanged(java.awt.event.ItemEvent e) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JDHCPKonfiguration) itemStateChanged(" + e + "); source=" + e.getItemSelectable()); Object source = e.getItemSelectable(); if (source == cbUseInternal) { // LOG.debug("\titemStateChanged; source==cbUseInternal"); if (cbUseInternal.isSelected()) { server.setOwnSettings(true); } else { server.setOwnSettings(false); } updateGatewayAndDNSSetting(); } } private void updateGatewayAndDNSSetting() { if (os instanceof GatewayFirmware) { tfGateway.setEditable(false); tfDNSServer.setEditable(true); } else if (server.isOwnSettings()) { tfGateway.setEditable(true); tfDNSServer.setEditable(true); } else { tfGateway.setEditable(false); tfDNSServer.setEditable(false); } tfGateway.setText(server.determineGatewayip()); tfDNSServer.setText(server.determineDnsserverip()); } private void update() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JDHCPKonfiguration), update()"); tfObergrenze.setText(server.getObergrenze()); tfUntergrenze.setText(server.getUntergrenze()); tfNetzmaske.setText(server.getSubnetzmaske()); cbUseInternal.setSelected(server.isOwnSettings()); cbAktiv.setSelected(server.isAktiv()); ((DefaultTableModel) staticAddressTable.getModel()).setRowCount(0); for (DHCPAddressAssignment entry : server.holeStaticAssignedAddresses()) { ((DefaultTableModel) staticAddressTable.getModel()).addRow(new Object[] { entry.getMAC(), entry.getIp() }); } updateGatewayAndDNSSetting(); } public boolean ueberpruefen(Pattern pruefRegel, JTextField feld) { if (EingabenUeberpruefung.isGueltig(feld.getText(), pruefRegel)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); JTextField test = new JTextField(); feld.setBorder(test.getBorder()); return true; } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setForeground(EingabenUeberpruefung.farbeFalsch); feld.setBorder(BorderFactory.createLineBorder(EingabenUeberpruefung.farbeFalsch, 1)); return false; } } private void speichern() { if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfObergrenze)) { server.setObergrenze(tfObergrenze.getText()); } if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfUntergrenze)) { server.setUntergrenze(tfUntergrenze.getText()); } if (os instanceof GatewayFirmware) { if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfDNSServer)) { server.setDnsserverip(tfDNSServer.getText()); } } else if (cbUseInternal.isSelected()) { server.setOwnSettings(true); if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfGateway)) { server.setGatewayip(tfGateway.getText()); } if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfDNSServer)) { server.setDnsserverip(tfDNSServer.getText()); } } else { server.setOwnSettings(false); } server.setAktiv(cbAktiv.isSelected()); server.clearStaticAssignments(); for (int i = 0; i < staticAddressTable.getRowCount(); i++) { server.addStaticAssignment((String) staticAddressTable.getValueAt(i, 0), (String) staticAddressTable.getValueAt(i, 1)); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JDocuColorSelector.java000066400000000000000000000060421506574241500274240ustar00rootroot00000000000000package filius.gui.netzwerksicht; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JMenu; import javax.swing.JPanel; import javax.swing.MenuSelectionManager; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.MatteBorder; // Implements a public class JDocuColorSelector extends JMenu { private static final long serialVersionUID = 1L; private int sampleWidth = 30; private int sampleHeight = 30; protected Border standardBorder = new CompoundBorder(new MatteBorder(1, 1, 1, 1, getBackground()), new MatteBorder(1, 1, 1, 1, Color.DARK_GRAY)); protected Border selectedBorder = new MatteBorder(3, 3, 3, 3, new Color(0.48f, 0.73f, 0.98f)); // Light blue border protected Border activeBorder = new MatteBorder(2, 2, 2, 2, Color.BLACK); private Color color; public JDocuColorSelector(String name, Color[] colors, int lineCount, Color selectedColor) { super(name); JPanel p = new JPanel(); p.setBorder(new EmptyBorder(5, 5, 5, 5)); p.setLayout(new GridLayout(lineCount, 1)); for (int i = 0; i < colors.length; i++) { boolean sameColor = (colors[i].getRGB() == selectedColor.getRGB()); ColorPane pn = new ColorPane(colors[i], sameColor); p.add(pn); } add(p); } public Color getColor() { return color; } public void triggerMenuResponse(Color color) { this.color = color; MenuSelectionManager.defaultManager().clearSelectedPath(); fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand())); } private class ColorPane extends JPanel implements MouseListener { private static final long serialVersionUID = 1L; boolean selected; public ColorPane(Color c, boolean selected) { setBackground(c); this.selected = selected; setBorder(selected ? selectedBorder : standardBorder); addMouseListener(this); } public Dimension getPreferredSize() { return new Dimension(sampleWidth, sampleHeight); } public Dimension getMaximumSize() { return getPreferredSize(); } public Dimension getMinimumSize() { return getPreferredSize(); } public void mousePressed(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} public void mouseReleased(MouseEvent e) { triggerMenuResponse(getBackground()); } public void mouseEntered(MouseEvent e) { setBorder(activeBorder); } public void mouseExited(MouseEvent e) { setBorder(selected ? selectedBorder : standardBorder); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JDocuElement.java000066400000000000000000000763411506574241500262470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButtonMenuItem; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.MouseInputAdapter; import filius.gui.GUIContainer; import filius.gui.JMainFrame; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.SzenarioVerwaltung; public class JDocuElement extends JPanel implements I18n { private static final long serialVersionUID = 1L; private int updateType; private Dimension elemMoveOffset; private boolean enabled = true; // The real focus is not handy here // localFocus is true for the last JDocuElement that was clicked on private boolean localFocus = false; // enlargedPanel is true when isText, enabled, and localFocus are all true // In this case the panel is larger to manipulate the text area more easily private boolean enlargedPanel = false; // designMode is true when the panel is created by dragging the icon at design mode private boolean dragDropCreation = false; private Color referenceColor; private Color opaqueColor; private Color transparentColor; private Color borderColor; private boolean isText; private JDocuTextArea textArea; private static final int RECT_CORNER_SIZE = 10; private static final int RECT_MIN_WIDTH = 20; private static final int RECT_MIN_HEIGHT = 20; private static final int TEXT_CORNER_SIZE = 10; private static final int TEXT_MIN_WIDTH = 100; private static final int TEXT_MIN_HEIGHT = 48; private static final int TEXT_BORDER_WIDTH = 7; private static final int TEXT_BORDER_BOTTOM = 30; private static final Color fontColor[] = { Color.BLACK, new Color(0.4f, 0.4f, 0.4f), new Color(0.6f, 0.6f, 0.6f), new Color(0.7f, 0.7f, 0.7f), Color.WHITE, new Color(1f, 0, 0), new Color(0.70f, 0.64f, 0.78f), new Color(0.31f, 0.51f, 0.74f), new Color(0.61f, 0.73f, 0.35f), new Color(0.58f, 0.54f, 0.33f), new Color(0.58f, 0.22f, 0.21f), new Color(0.37f, 0.29f, 0.48f), new Color(0.12f, 0.29f, 0.49f), new Color(0.43f, 0.53f, 0.22f), new Color(0.35f, 0.33f, 0.19f) }; private static final Color panelColor[] = { Color.BLACK, new Color(0.8f, 0.8f, 0.8f), new Color(0.92f, 0.92f, 0.92f), Color.WHITE, new Color(1f, 0.79f, 0.79f), new Color(0.99f, 0.79f, 0.64f), new Color(1f, 1f, 0.89f), new Color(0.83f, 0.81f, 0.71f), new Color(0.87f, 0.83f, 1f), new Color(0.82f, 0.91f, 1f), new Color(0.75f, 1f, 0.86f), new Color(0.80f, 0.87f, 0.68f) }; private static final Color panelFocusColor = new Color(0.22f, 0.6f, 0.98f, 0.3f); // Translucent light blue private static final Color borderFocusColor = new Color(0.48f, 0.73f, 0.98f); // Opaque light blue private static final Color textPanelBorderNormalColor = new Color(0.7f, 0.7f, 0.7f); // Light gray private static final Color transparent = new Color(0, 0, 0, 0); // JDocuTextArea // -------------------------------------------------------- private class JDocuTextArea extends JTextArea { private static final long serialVersionUID = 1L; private JDocuTextArea() { setBackground(transparent); setEnabled(enabled); setWrapping(); initListeners(); } private void initListeners() { addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_DELETE: if (e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK) delete(); break; case KeyEvent.VK_ESCAPE: setLocalFocus(false); // Also release the real focus getRootPane().requestFocusInWindow(); } } }); addMouseListener(new MouseInputAdapter() { @Override public void mouseEntered(MouseEvent e) { if (enabled) setPanelCursor(Cursor.TEXT_CURSOR); } @Override public void mouseExited(MouseEvent e) { if (enabled) setPanelCursor(Cursor.DEFAULT_CURSOR); } }); addMouseListener(new MouseInputAdapter() { @Override public void mousePressed(MouseEvent e) { requestFocusInWindow(); if (enabled && e.getButton() == MouseEvent.BUTTON3) { showPopupMenu(e.getX(), e.getY()); } } }); addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { setLocalFocus(true); } @Override public void focusLost(FocusEvent e) {} }); } private void setWrapping() { // There is something wrong in the behaviour of JTextArea when lineWrap is set from the constructor // with the following two lines: // textArea.setLineWrap(true); // setWrapStyleWord(true); // // The trick to bypass it is to set the line wrap when the text is modified. // Since JTextArea.text is initially empty, that works OK. getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { setLineWrap(true); setWrapStyleWord(true); // Remove it since it is usefull only once getDocument().removeDocumentListener(this); } @Override public void removeUpdate(DocumentEvent e) {} @Override public void changedUpdate(DocumentEvent e) {} }); } } // JDocuElement // -------------------------------------------------------- public JDocuElement(boolean isText) { if (isText) { // A text area textArea = new JDocuTextArea(); add(textArea); this.isText = true; // isText must be set to true only when the text area has been added // The panel is used to move and resize the text area that it contains // It is only visible in documentation mode when the text area has the local focus setColor(Color.BLACK); setOpaque(false); setLayout(null); setSize(100, 50); // Direct call since the listener is not yet set updateTextAreaBounds(); } else { // A simple color panel setSize(100, 50); setColor(panelColor[1]); setOpaque(false); setFocusable(true); } initListeners(); } public JDocuElement(boolean isText, boolean dragDropCreation) { this(isText); this.dragDropCreation = dragDropCreation; } // Called just after the component has been assigned a parent // Handy to do things that can't be done at creation time when the text area lacks a parent public void addNotify() { super.addNotify(); if (!(getParent() instanceof GUIDocumentationPanel)) return; // There is a special treatment when the panel is created by drag-drop if (dragDropCreation) { setLocalFocus(true); } } // Initialization of the listeners private void initListeners() { addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (isText) { updateTextAreaBounds(); repaint(); } } }); addKeyListener(new KeyAdapter() { // Automatic adjustment of the dimensions of the textArea to the panel's dimensions @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_DELETE: delete(); break; case KeyEvent.VK_ESCAPE: setLocalFocus(false); } } }); addMouseListener(new MouseInputAdapter() { @Override public void mousePressed(MouseEvent e) { if (enabled) { if (!isText) { requestFocusInWindow(); if (!localFocus) setLocalFocus(true); } switch (e.getButton()) { case MouseEvent.BUTTON1: if (updateType == GUIContainer.MOVE) elemMoveOffset = new Dimension(e.getX(), e.getY()); break; case MouseEvent.BUTTON3: showPopupMenu(e.getX(), e.getY()); } } } @Override public void mouseReleased(MouseEvent e) { if (enabled) { updateType = GUIContainer.NONE; updateUI(); SzenarioVerwaltung.getInstance().setzeGeaendert(); } } @Override public void mouseExited(MouseEvent e) { if (enabled) { setPanelCursor(Cursor.DEFAULT_CURSOR); } } }); addMouseMotionListener(new MouseInputAdapter() { @Override public void mouseMoved(MouseEvent e) { if (enabled && !(isText && !enlargedPanel)) { boolean left = (e.getX() <= TEXT_BORDER_WIDTH); boolean right = (e.getX() >= getWidth() - TEXT_BORDER_WIDTH); boolean upper = (e.getY() <= TEXT_BORDER_WIDTH); boolean lower = (e.getY() >= getHeight() - TEXT_BORDER_WIDTH); if (left) { if (upper) { updateType = GUIContainer.LEFT_SIZING | GUIContainer.UPPER_SIZING; } else if (lower) { updateType = GUIContainer.LEFT_SIZING | GUIContainer.LOWER_SIZING; } else { updateType = GUIContainer.LEFT_SIZING; } } else if (right) { if (upper) { updateType = GUIContainer.RIGHT_SIZING | GUIContainer.UPPER_SIZING; } else if (lower) { updateType = GUIContainer.RIGHT_SIZING | GUIContainer.LOWER_SIZING; } else { updateType = GUIContainer.RIGHT_SIZING; } } else if (upper) { updateType = GUIContainer.UPPER_SIZING; } else if (lower) { updateType = GUIContainer.LOWER_SIZING; } else { updateType = GUIContainer.MOVE; } switch (updateType) { case GUIContainer.MOVE: setPanelCursor(Cursor.MOVE_CURSOR); break; case GUIContainer.LEFT_SIZING: setPanelCursor(Cursor.W_RESIZE_CURSOR); break; case GUIContainer.RIGHT_SIZING: setPanelCursor(Cursor.E_RESIZE_CURSOR); break; case GUIContainer.UPPER_SIZING: setPanelCursor(Cursor.N_RESIZE_CURSOR); break; case GUIContainer.LOWER_SIZING: setPanelCursor(Cursor.S_RESIZE_CURSOR); break; case GUIContainer.LEFT_SIZING | GUIContainer.UPPER_SIZING: setPanelCursor(Cursor.S_RESIZE_CURSOR); break; case GUIContainer.RIGHT_SIZING | GUIContainer.UPPER_SIZING: setPanelCursor(Cursor.NE_RESIZE_CURSOR); break; case GUIContainer.LEFT_SIZING | GUIContainer.LOWER_SIZING: setPanelCursor(Cursor.SW_RESIZE_CURSOR); break; case GUIContainer.RIGHT_SIZING | GUIContainer.LOWER_SIZING: setPanelCursor(Cursor.SE_RESIZE_CURSOR); break; default: setPanelCursor(Cursor.DEFAULT_CURSOR); } } } @Override public void mouseDragged(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { if (isText && !enlargedPanel) return; boolean left = ((updateType & GUIContainer.LEFT_SIZING) != 0); boolean right = ((updateType & GUIContainer.RIGHT_SIZING) != 0); boolean upper = ((updateType & GUIContainer.UPPER_SIZING) != 0); boolean lower = ((updateType & GUIContainer.LOWER_SIZING) != 0); int elemX = getX(); int elemY = getY(); int elemW = getWidth(); int elemH = getHeight(); int newX, newY, newW, newH; if (enabled) { if (updateType == GUIContainer.MOVE) { setLocation(e.getX() - (int) JDocuElement.this.elemMoveOffset.getWidth() + elemX, e.getY() - (int) JDocuElement.this.elemMoveOffset.getHeight() + elemY); } else if (updateType != GUIContainer.NONE) { int minW = (isText ? TEXT_MIN_WIDTH : RECT_MIN_WIDTH); int minH = (isText ? TEXT_MIN_HEIGHT : RECT_MIN_HEIGHT); if (left) { newX = elemX + e.getX(); newW = elemW - e.getX(); if (newW < minW) { newX = newX - (minW - newW); newW = minW; } } else if (right) { newX = elemX; newW = e.getX(); if (newW < minW) { newW = minW; } } else { newX = elemX; newW = elemW; } if (upper) { newY = elemY + e.getY(); newH = elemH - e.getY(); if (newH < minH) { newY = newY - (minH - newH); newH = minH; } } else if (lower) { newY = elemY; newH = e.getY(); if (newH < minH) { newH = minH; } } else { newY = elemY; newH = elemH; } setBounds(newX, newY, newW, newH); } } } } }); } // popupmenu private void showPopupMenu(int x, int y) { JPopupMenu menu = new JPopupMenu(); if (isText) { // Font size JMenu mFontSize = new JMenu(messages.getString("guievents_msg15")); String fontSizeText[] = { messages.getString("guievents_msg16"), messages.getString("guievents_msg17"), messages.getString("guievents_msg18") }; float fontSize[] = { 12f, 18f, 24f }; float tafs = textArea.getFont().getSize(); for (int i = 0; i < fontSize.length; i++) { JRadioButtonMenuItem mi = new JRadioButtonMenuItem(fontSizeText[i]); mi.putClientProperty("fontSize", fontSize[i]); mi.setSelected(tafs == fontSize[i]); mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JMenuItem smi = (JMenuItem) e.getSource(); float fs = (float) smi.getClientProperty("fontSize"); textArea.setFont(textArea.getFont().deriveFont(fs)); repaint(); } }); mFontSize.add(mi); } menu.add(mFontSize); // Font style int[] fontStyles = { Font.PLAIN, Font.ITALIC, Font.BOLD }; String fontStyleText[] = { messages.getString("guievents_msg19"), messages.getString("guievents_msg20"), messages.getString("guievents_msg21") }; JMenu mFontStyle = new JMenu(messages.getString("guievents_msg14")); int tafst = textArea.getFont().getStyle(); for (int i = 0; i < fontStyles.length; i++) { JRadioButtonMenuItem mi = new JRadioButtonMenuItem(fontStyleText[i]); mi.putClientProperty("fontStyle", i); mi.setSelected(tafst == fontStyles[i]); mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JMenuItem smi = (JMenuItem) e.getSource(); int fs = (int) smi.getClientProperty("fontStyle"); textArea.setFont(textArea.getFont().deriveFont(fontStyles[fs])); repaint(); } }); mFontStyle.add(mi); } menu.add(mFontStyle); // Font color JDocuColorSelector mFontColor = new JDocuColorSelector(messages.getString("guievents_msg13"), fontColor, 3, getColor()); // Couleur mFontColor.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setColor(mFontColor.getColor()); repaint(); } }); menu.add(mFontColor); } else { // Color of the Panel JDocuColorSelector mPanelColor = new JDocuColorSelector(messages.getString("guievents_msg13"), panelColor, 3, getColor()); // Couleur mPanelColor.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setColor(mPanelColor.getColor()); repaint(); } }); menu.add(mPanelColor); } menu.addSeparator(); // Delete Panel JMenuItem miDelete = new JMenuItem(messages.getString("guievents_msg7")); miDelete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { delete(); } }); menu.add(miDelete); menu.show(this, x, y); } private void setPanelCursor(int cursor) { JMainFrame.getJMainFrame().setCursor(Cursor.getPredefinedCursor(cursor)); } private void delete() { GUIContainer.getGUIContainer().removeDocuElement(this); GUIContainer.getGUIContainer().updateViewport(); setPanelCursor(Cursor.DEFAULT_CURSOR); } public String getText() { if (isText) return textArea.getText(); return null; } public void setText(String text) { if (isText) textArea.setText(text); } public Color getColor() { return referenceColor; } public void setColor(Color color) { if (null == color) { color = Color.LIGHT_GRAY; } referenceColor = color; if (isText) { textArea.setForeground(color); } else { int red = color.getRed(); red = 255 + (red - 255) * 8 / 10; int green = color.getGreen(); green = 255 + (green - 255) * 8 / 10; int blue = color.getBlue(); blue = 255 + (blue - 255) * 8 / 10; opaqueColor = new Color(red, green, blue); transparentColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), 200); borderColor = new Color(color.getRed() * 4 / 5, color.getGreen() * 4 / 5, color.getBlue() * 4 / 5); } } public Font getFont() { if (isText) { return textArea.getFont(); } else { return null; } } public void setFont(Font font) { if (isText) { textArea.setFont(font); } } public void setSelected(boolean selected) { repaint(); } @Override public void setEnabled(boolean enabled) { if (enabled == this.enabled) { return; } this.enabled = enabled; setFocusable(enabled); if (isText) { textArea.setEditable(enabled); textArea.setFocusable(enabled); } if (!enabled) { setLocalFocus(false); } } public void setLocalFocus(boolean focused) { if (focused == localFocus) return; // Remove the localFocus from any sibling if (focused) { Component[] c = getParent().getComponents(); for (int i = 0; i < c.length; i++) { if (c[i] instanceof JDocuElement && c[i] != this) { ((JDocuElement) c[i]).setLocalFocus(false); } } } localFocus = focused; if (isText) updatePanelSize(); repaint(); if (dragDropCreation) { if (isText) delayFocusOnTextArea(); dragDropCreation = false; } } // When created with drag-drop, textArea.requestFocusInWindow() does not work when called within setLocalFocus // A delayed call fixes it private void delayFocusOnTextArea() { new java.util.Timer().schedule(new java.util.TimerTask() { @Override public void run() { textArea.requestFocusInWindow(); } }, 100); } private void updateTextAreaBounds() { if (localFocus) { textArea.setBounds(TEXT_BORDER_WIDTH, TEXT_BORDER_WIDTH, getBounds().width - 2 * TEXT_BORDER_WIDTH, getBounds().height - TEXT_BORDER_BOTTOM); } else { textArea.setBounds(1, 1, getBounds().width - 2, getBounds().height - 2); } } private void updatePanelSize() { if (localFocus) { // enlarge the panel if (enlargedPanel) return; // Just in case... Rectangle b = getBounds(); setBounds(b.x - TEXT_BORDER_WIDTH + 1, b.y - TEXT_BORDER_WIDTH + 1, b.width - 2 + 2 * TEXT_BORDER_WIDTH, b.height - 2 + TEXT_BORDER_BOTTOM); enlargedPanel = true; } else { // reduce the panel if (!enlargedPanel) return; // Just in case... // System.out.println("!!!"); Rectangle b = getBounds(); setBounds(b.x - 1 + TEXT_BORDER_WIDTH, b.y - 1 + TEXT_BORDER_WIDTH, b.width - 2 * TEXT_BORDER_WIDTH + 2, b.height - TEXT_BORDER_BOTTOM + 2); enlargedPanel = false; } updateTextAreaBounds(); } // Returns a polygon wrapping the area outside the given rectangle // (used to clip out the rectangle when painting) // if corner is not 0, the corners of the rectangle are clipped triangularly to fit the fillRoundRect private Polygon invertRectangle(Rectangle R, int corner) { int extW = getBounds().width; int extH = getBounds().height; int inW = R.width; int inH = R.height; Polygon clip; if (corner == 0) { clip = new Polygon(new int[] { 0, extW, extW, 0, 0, R.x, R.x, R.x + inW, R.x + inW, 0 }, // x values new int[] { 0, 0, extH, extH, R.y, R.y, R.y + inH, R.y + inH, R.y, R.y }, // y values 10); } else { int ct = corner; int cb = corner + 1; // bottom corners clipping is 1 pixel larger clip = new Polygon( new int[] { 0, extW, extW, 0, 0, R.x + ct, R.x, R.x, R.x + cb, R.x + inW - cb, R.x + inW, R.x + inW, R.x + inW - ct, 0 }, // x values new int[] { 0, 0, extH, extH, R.y, R.y, R.y + ct, R.y + inH - cb, R.y + inH, R.y + inH, R.y + inH - cb, R.y + ct, R.y, R.y }, // y values 14); } return clip; } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (isText) { // Paint the background if (enabled) { if (localFocus) { // Paint the part of the Panel not hidden by the textArea // in order that the user can move and resize it g2.setColor(panelFocusColor); Shape prevClip = g2.getClip(); g2.setClip(invertRectangle(textArea.getBounds(), 0)); g2.fillRoundRect(1, 1, getWidth() - 2, getHeight() - 2, TEXT_CORNER_SIZE, TEXT_CORNER_SIZE); g2.setClip(prevClip); // Draw the border g2.setColor(borderFocusColor); Stroke stroke = new BasicStroke(2); g2.setStroke(stroke); g2.drawRoundRect(1, 1, getWidth() - 3, getHeight() - 3, TEXT_CORNER_SIZE, TEXT_CORNER_SIZE); } else { // Draw the border g2.setColor(textPanelBorderNormalColor); Stroke stroke = new BasicStroke(1); g2.setStroke(stroke); g2.drawRect(textArea.getX() - 1, textArea.getY() - 1, textArea.getWidth() + 1, textArea.getHeight() + 1); } } } else { if (enabled && localFocus) { // Paint the border with a distinctive color g2.setColor(panelFocusColor); Shape prevClip = g2.getClip(); Rectangle R = getBounds(); R.x = 0; R.y = 0; R.grow(-TEXT_BORDER_WIDTH, -TEXT_BORDER_WIDTH); g2.setClip(invertRectangle(R, 3)); g2.fillRoundRect(1, 1, getWidth() - 2, getHeight() - 2, RECT_CORNER_SIZE, RECT_CORNER_SIZE); g2.setClip(prevClip); // Paint the background with the panel color g2.setColor(transparentColor); // g2.fillRect(R.x, R.y,R.width, R.height); g2.fillRoundRect(R.x, R.y, R.width, R.height, TEXT_CORNER_SIZE, TEXT_CORNER_SIZE); // Draw the border g2.setColor(borderFocusColor); Stroke stroke = new BasicStroke(2); g2.setStroke(stroke); g2.drawRoundRect(1, 1, getWidth() - 3, getHeight() - 3, RECT_CORNER_SIZE, RECT_CORNER_SIZE); } else { // Paint the background g2.setColor(enabled ? transparentColor : opaqueColor); g2.fillRoundRect(1, 1, getWidth() - 2, getHeight() - 2, RECT_CORNER_SIZE, RECT_CORNER_SIZE); // Draw the border g2.setColor(borderColor); Stroke stroke = new BasicStroke(1); g2.setStroke(stroke); g2.drawRoundRect(1, 1, getWidth() - 3, getHeight() - 3, RECT_CORNER_SIZE, RECT_CORNER_SIZE); } } } }filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JFirewallDialog.java000066400000000000000000000442511506574241500267230ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Vector; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.ListSelectionModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.ComboBoxTableCellEditor; import filius.rahmenprogramm.I18n; import filius.software.firewall.Firewall; import filius.software.firewall.FirewallRule; public class JFirewallDialog extends JDialog implements I18n { private static Logger LOG = LoggerFactory.getLogger(JFirewallDialog.class); private static final long serialVersionUID = 1L; private static final Color TAB_COLOR = new Color(240, 240, 240); JFirewallDialog jfd = null; private Firewall firewall; DefaultTableModel dtmTabelle; DefaultTableModel dtmTabellePort; JScrollPane spTabelle; JScrollPane spTabellePort; Box boxFirewall; Box boxTabellen; private GatewayFirewallConfigTable ruleTable; private JComboBox defaultPolicyCombo; private JCheckBox activateFirewall; private JCheckBox dropICMP; private JCheckBox onlyFilterSYN; public JFirewallDialog(Firewall firewall, JFrame dummyFrame) { super(dummyFrame, messages.getString("jfirewalldialog_msg1"), true); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + ", constr: JFirewallDialog(" + firewall + "," + dummyFrame + ")"); this.firewall = firewall; jfd = this; erzeugeFenster(); } private Box erzeugeNicBox() { Box vBox, hBox; vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); activateFirewall = new JCheckBox(messages.getString("jfirewalldialog_msg38")); activateFirewall.setOpaque(false); activateFirewall.setSelected(firewall.isActivated()); activateFirewall.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { firewall.setActivated(((JCheckBox) e.getSource()).isSelected()); } }); hBox.add(activateFirewall); hBox.add(Box.createHorizontalGlue()); vBox.add(hBox); JTextArea label = new JTextArea(); label.setEditable(false); label.setLineWrap(true); label.setWrapStyleWord(true); label.setOpaque(false); label.setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); label.setText(messages.getString("jfirewalldialog_msg39")); hBox = Box.createHorizontalBox(); hBox.add(label); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); dropICMP = new JCheckBox(messages.getString("jfirewalldialog_msg40")); dropICMP.setOpaque(false); dropICMP.setSelected(firewall.getDropICMP()); dropICMP.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { firewall.setDropICMP(((JCheckBox) e.getSource()).isSelected()); } }); hBox.add(dropICMP); hBox.add(Box.createHorizontalGlue()); vBox.add(hBox); label = new JTextArea(); label.setEditable(false); label.setLineWrap(true); label.setWrapStyleWord(true); label.setOpaque(false); label.setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); label.setText(messages.getString("jfirewalldialog_msg41")); hBox = Box.createHorizontalBox(); hBox.add(label); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); onlyFilterSYN = new JCheckBox(messages.getString("jfirewalldialog_msg42")); onlyFilterSYN.setOpaque(false); onlyFilterSYN.setSelected(firewall.getFilterSYNSegmentsOnly()); onlyFilterSYN.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { firewall.setFilterSYNSegmentsOnly(((JCheckBox) e.getSource()).isSelected()); } }); hBox.add(onlyFilterSYN); hBox.add(Box.createHorizontalGlue()); vBox.add(hBox); label = new JTextArea(); label.setEditable(false); label.setLineWrap(true); label.setWrapStyleWord(true); label.setOpaque(false); label.setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); label.setText(messages.getString("jfirewalldialog_msg43")); hBox = Box.createHorizontalBox(); hBox.add(label); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); vBox.add(Box.createVerticalStrut(1000)); return vBox; } private Box firewallRuleBox() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", firewallRuleBox()"); JScrollPane scrollPane; Box vBox, hBox; DefaultTableModel model; TableColumnModel columnModel; JButton button; JLabel label; JTextArea textArea; vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); textArea = new JTextArea(); textArea.setText(messages.getString("jfirewalldialog_msg37")); textArea.setLineWrap(true); textArea.setWrapStyleWord(true); textArea.setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); textArea.setEditable(false); textArea.setOpaque(false); hBox.add(textArea); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); label = new JLabel(messages.getString("jfirewalldialog_msg36")); hBox.add(label); hBox.add(Box.createHorizontalStrut(10)); defaultPolicyCombo = new JComboBox(); defaultPolicyCombo.addItem(messages.getString("jfirewalldialog_msg33")); defaultPolicyCombo.addItem(messages.getString("jfirewalldialog_msg34")); defaultPolicyCombo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { LOG.trace("Action listener on default policy combo box is triggered be event '{}'", e.getID()); firewall.setDefaultPolicy(stringToPolicy((String) defaultPolicyCombo.getSelectedItem())); updateRuleTable(); } }); hBox.add(defaultPolicyCombo); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); model = new DefaultTableModel(0, 8); ruleTable = new GatewayFirewallConfigTable(model, true); ruleTable.setParentGUI(this); ruleTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ruleTable.setIntercellSpacing(new Dimension(10, 5)); ruleTable.setRowHeight(30); ruleTable.setShowGrid(true); ruleTable.setFillsViewportHeight(true); ruleTable.setBackground(Color.WHITE); ruleTable.setShowHorizontalLines(true); columnModel = ruleTable.getColumnModel(); columnModel.getColumn(0).setHeaderValue(messages.getString("jfirewalldialog_msg26")); columnModel.getColumn(0).setCellEditor(null); // ID column must be read-only columnModel.getColumn(1).setHeaderValue(messages.getString("jfirewalldialog_msg27")); columnModel.getColumn(2).setHeaderValue(messages.getString("jfirewalldialog_msg28")); columnModel.getColumn(3).setHeaderValue(messages.getString("jfirewalldialog_msg29")); columnModel.getColumn(4).setHeaderValue(messages.getString("jfirewalldialog_msg30")); columnModel.getColumn(5).setHeaderValue(messages.getString("jfirewalldialog_msg31")); String[] protValues = { "*", "TCP", "UDP" }; columnModel.getColumn(5).setCellEditor(new ComboBoxTableCellEditor(protValues)); columnModel.getColumn(6).setHeaderValue(messages.getString("jfirewalldialog_msg35")); columnModel.getColumn(7).setHeaderValue(messages.getString("jfirewalldialog_msg32")); String[] actionValues = { messages.getString("jfirewalldialog_msg33"), messages.getString("jfirewalldialog_msg34") }; columnModel.getColumn(7).setCellEditor(new ComboBoxTableCellEditor(actionValues)); columnModel.getColumn(0).setPreferredWidth(30); columnModel.getColumn(1).setPreferredWidth(130); columnModel.getColumn(2).setPreferredWidth(130); columnModel.getColumn(3).setPreferredWidth(130); columnModel.getColumn(4).setPreferredWidth(130); columnModel.getColumn(5).setPreferredWidth(80); columnModel.getColumn(6).setPreferredWidth(60); columnModel.getColumn(7).setPreferredWidth(80); scrollPane = new JScrollPane(ruleTable); scrollPane.setPreferredSize(new Dimension(555, 300)); vBox.add(scrollPane); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("jfirewalldialog_msg22")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ruleTable.closeEditor(); int rowSel = -1; boolean success = false; try { if (ruleTable.getSelectedRowCount() == 1) { rowSel = ruleTable.getSelectedRow(); String idStr = (String) ruleTable.getValueAt(rowSel, 0); success = firewall.moveUp(Integer.parseInt(idStr)); } } catch (Exception ex) {} updateRuleTable(); if (rowSel >= 0 && success) { ruleTable.setRowSelectionInterval(rowSel - 1, rowSel - 1); } } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("jfirewalldialog_msg23")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ruleTable.closeEditor(); int rowSel = -1; boolean success = false; try { if (ruleTable.getSelectedRowCount() == 1) { rowSel = ruleTable.getSelectedRow(); String idStr = (String) ruleTable.getValueAt(rowSel, 0); success = firewall.moveDown(Integer.parseInt(idStr)); } } catch (Exception ex) {} updateRuleTable(); if (rowSel >= 0 && success) { ruleTable.setRowSelectionInterval(rowSel + 1, rowSel + 1); } } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(30)); button = new JButton(messages.getString("jfirewalldialog_msg24")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ruleTable.closeEditor(); firewall.addRule(); updateRuleTable(); } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("jfirewalldialog_msg25")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ruleTable.closeEditor(); int rowSel = -1; try { if (ruleTable.getSelectedRowCount() == 1) { rowSel = ruleTable.getSelectedRow(); String idStr = (String) ruleTable.getValueAt(rowSel, 0); LOG.debug("(" + this.hashCode() + ") " + getClass() + ", del action: rowSel=" + rowSel + ", rows count=" + firewall.getRuleset().size()); firewall.deleteRule(Integer.parseInt(idStr) - 1); } } catch (Exception ex) { ex.printStackTrace(); } updateRuleTable(); if (rowSel >= 0) { if (rowSel < firewall.getRuleset().size()) { ruleTable.setRowSelectionInterval(rowSel, rowSel); } else if (firewall.getRuleset().size() > 0) { ruleTable.setRowSelectionInterval(firewall.getRuleset().size() - 1, firewall.getRuleset().size() - 1); } } } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); this.updateRuleTable(); return vBox; } public short stringToPolicy(String langPolicy) { if (langPolicy.equals(messages.getString("jfirewalldialog_msg33"))) { return FirewallRule.ACCEPT; } else { return FirewallRule.DROP; } } public String policyToString(short policy) { if (policy == FirewallRule.ACCEPT) { return messages.getString("jfirewalldialog_msg33"); } else if (policy == FirewallRule.DROP) { return messages.getString("jfirewalldialog_msg34"); } else { return ""; } } /* * @author Weyer hier wird das ganze Fenster bestückt */ private void erzeugeFenster() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", erzeugeFenster()"); JTabbedPane tp; JPanel hauptPanel; hauptPanel = new JPanel(new BorderLayout()); tp = new JTabbedPane(); tp.add(messages.getString("jfirewalldialog_msg18"), erzeugeNicBox()); tp.setBackgroundAt(0, TAB_COLOR); tp.add(messages.getString("jfirewalldialog_msg21"), firewallRuleBox()); tp.setBackgroundAt(1, TAB_COLOR); hauptPanel.add(tp, BorderLayout.CENTER); hauptPanel.setBackground(TAB_COLOR); getContentPane().add(hauptPanel); } public void updateRuleTable() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", updateRuleTable()"); DefaultTableModel model; Vector ruleset = firewall.getRuleset(); model = (DefaultTableModel) this.ruleTable.getModel(); model.setRowCount(0); for (int i = 0; i < ruleset.size(); i++) { LOG.debug("Rule #" + (i + 1) + ": " + ruleset.get(i).toString()); Vector row = ruleAsVector(i + 1, ruleset.get(i)); model.addRow(row); } defaultPolicyCombo.setSelectedItem(policyToString(firewall.getDefaultPolicy())); activateFirewall.setSelected(firewall.isActivated()); dropICMP.setSelected(firewall.getDropICMP()); onlyFilterSYN.setSelected(firewall.getFilterSYNSegmentsOnly()); } public Vector ruleAsVector(int idx, FirewallRule rule) { Vector resultVec = new Vector(); resultVec.addElement(Integer.toString(idx)); resultVec.addElement(rule.srcIP); resultVec.addElement(rule.srcMask); resultVec.addElement(rule.destIP); resultVec.addElement(rule.destMask); if (rule.protocol == FirewallRule.TCP) resultVec.addElement("TCP"); else if (rule.protocol == FirewallRule.UDP) resultVec.addElement("UDP"); else resultVec.addElement("*"); // = alle if (rule.port >= 0) resultVec.addElement(Integer.toString(rule.port)); else resultVec.addElement(""); if (rule.action == FirewallRule.ACCEPT) resultVec.addElement(messages.getString("jfirewalldialog_msg33")); else resultVec.addElement(messages.getString("jfirewalldialog_msg34")); return resultVec; } public Firewall getFirewall() { return firewall; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JGatewayConfiguration.java000066400000000000000000000511271506574241500301670ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.LinkedList; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.gui.JMainFrame; import filius.hardware.Hardware; import filius.hardware.Kabel; import filius.hardware.NetzwerkInterface; import filius.hardware.Port; import filius.hardware.Verbindung; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.hardware.knoten.LokalerKnoten; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.firewall.Firewall; import filius.software.system.GatewayFirmware; import filius.software.system.InternetKnotenBetriebssystem; @SuppressWarnings("serial") public class JGatewayConfiguration extends JKonfiguration implements I18n { private static Logger LOG = LoggerFactory.getLogger(JGatewayConfiguration.class); private static final int WAN_TAB_IDX = 2; private static final int LAN_TAB_IDX = 1; private JTextField name; private JTextField ipAddressWANPort; private JTextField netmaskWANPort; private JTextField macAddressWANPort; private JLabel connectedComponentWAN; private JTextField ipAddressLANPort; private JTextField netmaskLANPort; private JTextField macAddressLANPort; private JLabel connectedComponentLAN; private JTextField gatewayName; private JCheckBox ipForwarding; private JCheckBox dhcp; private JTabbedPane tpNetzwerkKarten; private JPortForwardingDialog portForwardingDialog; private Kabel highlightedCable = null; protected JGatewayConfiguration(Hardware hardware) { super(hardware); } public void aenderungenAnnehmen() { Gateway gateway = (Gateway) holeHardware(); GatewayFirmware firmware = (GatewayFirmware) gateway.getSystemSoftware(); gateway.setName(name.getText()); firmware.setStandardGateway(gatewayName.getText()); applyNICConfig(gateway.holeWANInterface(), ipAddressWANPort, netmaskWANPort); firmware.setDHCPKonfiguration(dhcp.isSelected()); applyNICConfig(gateway.holeLANInterface(), ipAddressLANPort, netmaskLANPort); GUIContainer.getGUIContainer().updateViewport(); updateAttribute(); } private void applyNICConfig(NetzwerkInterface nic, JTextField ipAddressTextfield, JTextField netmaskTextfield) { if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, ipAddressTextfield)) { nic.setIp(ipAddressTextfield.getText()); } else { LOG.debug("IP-Adresse ungueltig " + ipAddressTextfield.getText()); } if (ueberpruefen(EingabenUeberpruefung.musterSubNetz, netmaskTextfield)) { nic.setSubnetzMaske(netmaskTextfield.getText()); } else { LOG.debug("Netzmaske ungueltig " + netmaskTextfield.getText()); } } private void showFirewallDialog() { Firewall firewall = ((GatewayFirmware) ((Gateway) holeHardware()).getSystemSoftware()).holeFirewall(); JFirewallDialog firewallDialog = new JFirewallDialog(firewall, JMainFrame.getJMainFrame()); firewallDialog.setBounds(100, 100, 850, 340); firewallDialog.setName(messages.getString("jgatewayconfiguration_msg1")); firewallDialog.updateRuleTable(); firewallDialog.setVisible(true); } private void showPortForwardingDialog() { LinkedList staticNat = ((GatewayFirmware) ((Gateway) holeHardware()).getSystemSoftware()) .getStaticNAT(); portForwardingDialog = new JPortForwardingDialog(staticNat, JMainFrame.getJMainFrame()); portForwardingDialog.setBounds(100, 100, 550, 340); portForwardingDialog.setName(messages.getString("jgatewayconfiguration_msg19")); portForwardingDialog.createStaticTable(); portForwardingDialog.setVisible(true); } private void showDhcpConfiguration() { JDHCPKonfiguration dhcpKonfig = new JDHCPKonfiguration(JMainFrame.getJMainFrame(), messages.getString("jhostkonfiguration_msg8"), (InternetKnotenBetriebssystem) ((InternetKnoten) holeHardware()).getSystemSoftware()); dhcpKonfig.setVisible(true); } protected void initContents() { Box boxNetzwerkKarten; Box vBox; KeyAdapter ipAdresseKeyAdapter; KeyAdapter netzmaskeKeyAdapter; JButton btFirewall; JButton btPortForwarding; JLabel tempLabel; Box tempBox; ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { aenderungenAnnehmen(); } }; FocusListener focusListener = new FocusListener() { public void focusGained(FocusEvent arg0) {} public void focusLost(FocusEvent arg0) { aenderungenAnnehmen(); } }; this.addFocusListener(focusListener); boxNetzwerkKarten = Box.createVerticalBox(); boxNetzwerkKarten.setPreferredSize(new Dimension(440, 150)); boxNetzwerkKarten.setAlignmentX(JComponent.LEFT_ALIGNMENT); boxNetzwerkKarten.setOpaque(false); tpNetzwerkKarten = new JTabbedPane(); tpNetzwerkKarten.setOpaque(false); boxNetzwerkKarten.add(tpNetzwerkKarten); ipAdresseKeyAdapter = new KeyAdapter() { public void keyReleased(KeyEvent e) { JTextField tfQuelle = (JTextField) e.getSource(); ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfQuelle); } }; netzmaskeKeyAdapter = new KeyAdapter() { public void keyReleased(KeyEvent e) { JTextField tfQuelle = (JTextField) e.getSource(); ueberpruefen(EingabenUeberpruefung.musterSubNetz, tfQuelle); } }; vBox = Box.createVerticalBox(); // Attribut Name tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jgatewayconfiguration_msg2")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); name = new JTextField(messages.getString("jgatewayconfiguration_msg3")); name.setPreferredSize(new Dimension(160, 20)); name.addActionListener(actionListener); name.addFocusListener(focusListener); tempBox.add(name); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); // Attribut Gateway tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jgatewayconfiguration_msg9")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); gatewayName = new JTextField(); gatewayName.setPreferredSize(new Dimension(160, 20)); gatewayName.addActionListener(actionListener); gatewayName.addFocusListener(focusListener); gatewayName.addKeyListener(ipAdresseKeyAdapter); tempBox.add(gatewayName); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); // IP forwarding status tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); ipForwarding = new JCheckBox(); ipForwarding.setPreferredSize(new Dimension(160, 20)); ipForwarding.addActionListener(actionListener); ipForwarding.addFocusListener(focusListener); ipForwarding.setOpaque(false); ipForwarding.setEnabled(false); tempBox.add(ipForwarding); tempBox.add(Box.createHorizontalStrut(10)); tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg11")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); btFirewall = new JButton(messages.getString("jgatewayconfiguration_msg4")); btFirewall.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showFirewallDialog(); } }); tempBox.add(btFirewall); tempBox.add(Box.createHorizontalStrut(20)); btPortForwarding = new JButton(messages.getString("jgatewayconfiguration_msg19")); btPortForwarding.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showPortForwardingDialog(); } }); tempBox.add(btPortForwarding); vBox.add(tempBox); // NIC tabs tpNetzwerkKarten.addTab(messages.getString("jgatewayconfiguration_msg17"), vBox); ipAddressLANPort = new JTextField(); netmaskLANPort = new JTextField(); macAddressLANPort = new JTextField(); connectedComponentLAN = new JLabel(); initNICConfigPane(ipAdresseKeyAdapter, netzmaskeKeyAdapter, focusListener, actionListener, ipAddressLANPort, macAddressLANPort, netmaskLANPort, connectedComponentLAN, true, false); ipAddressWANPort = new JTextField(); netmaskWANPort = new JTextField(); macAddressWANPort = new JTextField(); connectedComponentWAN = new JLabel(); initNICConfigPane(ipAdresseKeyAdapter, netzmaskeKeyAdapter, focusListener, actionListener, ipAddressWANPort, macAddressWANPort, netmaskWANPort, connectedComponentWAN, false, true); tpNetzwerkKarten.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { JTabbedPane pane = (JTabbedPane) arg0.getSource(); // Get current tab if (highlightedCable != null) { highlightedCable.setAktiv(false); } Verbindung conn = null; int sel = pane.getSelectedIndex(); if (sel == WAN_TAB_IDX) { conn = ((Gateway) holeHardware()).holeWANInterface().getPort().getVerbindung(); } else if (sel == LAN_TAB_IDX) { conn = ((Gateway) holeHardware()).holeLANInterface().getPort().getVerbindung(); } if (conn != null) { conn.setAktiv(true); highlightedCable = (Kabel) conn; } } }); box.add(boxNetzwerkKarten); updateAttribute(); } private void initNICConfigPane(KeyAdapter ipAdresseKeyAdapter, KeyAdapter netzmaskeKeyAdapter, FocusListener focusListener, ActionListener actionListener, JTextField ipAddressTextfield, JTextField macAddressTextfield, JTextField netmaskTextfield, JLabel connectedComponentLabel, boolean dhcpServer, boolean dhcpConfig) { JLabel tempLabel; Box boxNic = Box.createVerticalBox(); // boxNic.setPreferredSize(new Dimension(800, 200)); Box boxKomponente = Box.createHorizontalBox(); boxKomponente.setMaximumSize(new Dimension(400, 30)); connectedComponentLabel.setPreferredSize(new Dimension(400, 10)); boxKomponente.add(connectedComponentLabel); // show IP address (editable) Box boxIpAdresse = Box.createHorizontalBox(); boxIpAdresse.setMaximumSize(new Dimension(400, 30)); tempLabel = new JLabel(messages.getString("jgatewayconfiguration_msg7")); tempLabel.setPreferredSize(new Dimension(120, 10)); boxIpAdresse.add(tempLabel); boxIpAdresse.add(ipAddressTextfield); // show netmask (editable) Box boxSubnetz = Box.createHorizontalBox(); boxSubnetz.setMaximumSize(new Dimension(400, 30)); tempLabel = new JLabel(messages.getString("jgatewayconfiguration_msg8")); tempLabel.setPreferredSize(new Dimension(120, 10)); boxSubnetz.add(tempLabel); boxSubnetz.add(netmaskTextfield); // show MAC address (not editable) Box boxMacAdresse = Box.createHorizontalBox(); boxMacAdresse.setMaximumSize(new Dimension(400, 30)); tempLabel = new JLabel(messages.getString("jgatewayconfiguration_msg18")); tempLabel.setPreferredSize(new Dimension(120, 10)); boxMacAdresse.add(tempLabel); macAddressTextfield.setEnabled(false); boxMacAdresse.add(macAddressTextfield); boxNic.add(boxKomponente); boxNic.add(Box.createVerticalStrut(5)); boxNic.add(boxIpAdresse); boxNic.add(Box.createVerticalStrut(5)); boxNic.add(boxSubnetz); boxNic.add(Box.createVerticalStrut(5)); boxNic.add(boxMacAdresse); if (dhcpServer) { boxNic.add(Box.createVerticalStrut(10)); Box tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); JButton btDhcp = new JButton(messages.getString("jhostkonfiguration_msg8")); btDhcp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showDhcpConfiguration(); } }); tempBox.add(btDhcp); boxNic.add(tempBox); } else if (dhcpConfig) { boxNic.add(Box.createVerticalStrut(10)); Box tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); dhcp = new JCheckBox(); dhcp.setSelected(false); dhcp.setOpaque(false); dhcp.addActionListener(actionListener); dhcp.setText(messages.getString("jhostkonfiguration_msg7")); tempBox.add(dhcp); boxNic.add(tempBox); } tpNetzwerkKarten.addTab(messages.getString("jgatewayconfiguration_msg10"), new ImageIcon(getClass().getResource("/gfx/allgemein/conn_fail.png")), boxNic); ipAddressTextfield.addKeyListener(ipAdresseKeyAdapter); ipAddressTextfield.addActionListener(actionListener); ipAddressTextfield.addFocusListener(focusListener); netmaskTextfield.addKeyListener(netzmaskeKeyAdapter); netmaskTextfield.addActionListener(actionListener); netmaskTextfield.addFocusListener(focusListener); } public void doUnselectAction() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (jgatewayconfiguration), doUnselectAction()"); if (highlightedCable != null) { highlightedCable.setAktiv(false); highlightedCable = null; tpNetzwerkKarten.setSelectedIndex(0); } } public void highlightConnCable() { if (highlightedCable != null) { highlightedCable.setAktiv(true); } } public JGatewayConfiguration getKonfiguration() { return this; } public void updateAttribute() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (jgatewayconfiguration), updateAttribute()"); Gateway vRechner = (Gateway) holeHardware(); GatewayFirmware bs = (GatewayFirmware) vRechner.getSystemSoftware(); name.setText(vRechner.holeAnzeigeName()); gatewayName.setText(bs.getStandardGateway()); ipForwarding.setSelected(bs.isIpForwardingEnabled()); boolean dhcpEnabled = bs.isDHCPKonfiguration(); dhcp.setSelected(dhcpEnabled); ipAddressWANPort.setEnabled(!dhcpEnabled); netmaskWANPort.setEnabled(!dhcpEnabled); /* Für den Fall, dass später zwischen Full cone und Restricted Cone unterschieden werden soll */ /* * switch (bs.getNatMethod()) { case fullCone: coneTypeSelect.setSelectedIndex(0); break; case restrictedCone: * coneTypeSelect.setSelectedIndex(1); break; } */ updateNICConfigPane("LAN", vRechner.holeLANInterface(), ipAddressLANPort, macAddressLANPort, netmaskLANPort, connectedComponentLAN, LAN_TAB_IDX); updateNICConfigPane("WAN", vRechner.holeWANInterface(), ipAddressWANPort, macAddressWANPort, netmaskWANPort, connectedComponentWAN, WAN_TAB_IDX); } private void updateNICConfigPane(String titlePrefix, NetzwerkInterface nic, JTextField ipAddressTextfield, JTextField macAddressTextfield, JTextField netmaskTextfield, JLabel connectedComponentTextfield, int tabIdx) { ipAddressTextfield.setText(nic.getIp()); netmaskTextfield.setText(nic.getSubnetzMaske()); macAddressTextfield.setText(nic.getMac()); Knoten tempKnoten = holeVerbundeneKomponente(nic); if (tempKnoten == null) { connectedComponentTextfield.setText(messages.getString("jgatewayconfiguration_msg16")); tpNetzwerkKarten.setIconAt(tabIdx, new ImageIcon(getClass().getResource("/gfx/allgemein/conn_fail.png"))); } else { connectedComponentTextfield .setText(messages.getString("jgatewayconfiguration_msg6") + " " + tempKnoten.holeAnzeigeName()); tpNetzwerkKarten.setIconAt(tabIdx, new ImageIcon(getClass().getResource("/gfx/allgemein/conn_ok.png"))); } tpNetzwerkKarten.setTitleAt(tabIdx, titlePrefix + ": " + nic.getIp()); } private Knoten holeVerbundeneKomponente(NetzwerkInterface nic) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (jgatewayconfiguration), holeVerbundeneKomponente(" + nic + ")"); if (nic.getPort().getVerbindung() == null) { return null; } Port lokalerAnschluss = nic.getPort(); Port[] ports = lokalerAnschluss.getVerbindung().getAnschluesse(); Port entfernterAnschluss; if (ports[0] == lokalerAnschluss) { entfernterAnschluss = ports[1]; } else { entfernterAnschluss = ports[0]; } for (GUIKnotenItem node : GUIContainer.getGUIContainer().getKnotenItems()) { if (node.getKnoten() instanceof LokalerKnoten) { for (Port port : ((LokalerKnoten) node.getKnoten()).getAnschluesse()) { if (port == entfernterAnschluss) return node.getKnoten(); } } else if (node.getKnoten() instanceof InternetKnoten) { for (NetzwerkInterface tmpNic : ((InternetKnoten) node.getKnoten()).getNetzwerkInterfaces()) { if (tmpNic.getPort() == entfernterAnschluss) return node.getKnoten(); } } else { LOG.debug("Knotentyp unbekannt."); } } return null; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JHostKonfiguration.java000066400000000000000000000613301506574241500275100ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JRadioButton; import javax.swing.JTextField; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.gui.GUIEvents; import filius.gui.JMainFrame; import filius.hardware.Hardware; import filius.hardware.knoten.Host; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Switch; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.system.Betriebssystem; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.system.SwitchFirmware; @SuppressWarnings("serial") public class JHostKonfiguration extends JKonfiguration implements I18n { private static Logger LOG = LoggerFactory.getLogger(JHostKonfiguration.class); private static final int LABEL_WIDTH = 160; private JTextField name; private JTextField macAdresse; private JTextField ipAdresse; private JTextField netzmaske; private JTextField gateway; private JTextField dns; private JCheckBox dhcp; private JCheckBox ipForwarding; private JButton btDhcp; private JCheckBox useIpAsName; private JCheckBox useMacAsName; private JComboBox ssid; private JLabel ssidLabel; private JRadioButton useLAN; private JRadioButton useWiFi; private ButtonGroup lanWifi; protected JHostKonfiguration(Hardware hardware) { super(hardware); } private void aendereAnzeigeName() { if (holeHardware() != null) { Host host = (Host) holeHardware(); host.setUseIPAsName(useIpAsName.isSelected()); host.setUseMACAsName(useMacAsName.isSelected()); } GUIContainer.getGUIContainer().updateViewport(); updateAttribute(); } private void aenderungenAnnehmen() { Host host; Betriebssystem bs; if (holeHardware() != null) { host = (Host) holeHardware(); if (!useIpAsName.isSelected()) { host.setName(name.getText()); } bs = (Betriebssystem) host.getSystemSoftware(); bs.setzeIPAdresse(ipAdresse.getText()); bs.setzeNetzmaske(netzmaske.getText()); bs.setStandardGateway(gateway.getText()); bs.setDNSServer(dns.getText()); bs.setIpForwardingEnabled(ipForwarding.isSelected()); bs.setDHCPKonfiguration(dhcp.isSelected()); boolean wifiStatusBeforeChange = bs.wireless(); String ssidBeforeChange = bs.getSsid(); bs.nicWireless(useWiFi.isSelected()); if (ssid.getSelectedIndex() >= 1) { ((Betriebssystem) host.getSystemSoftware()).setSsid(ssid.getSelectedItem().toString()); } else { ((Betriebssystem) host.getSystemSoftware()).setSsid(null); } boolean wifiStatusAfterChange = bs.wireless(); String ssidAfterChange = bs.getSsid(); if (!StringUtils.equals(ssidBeforeChange, ssidAfterChange) || wifiStatusBeforeChange != wifiStatusAfterChange) { updateWifiConnection(); } if (dhcp.isSelected()) { bs.getDHCPServer().setAktiv(false); } } else { LOG.debug("GUIRechnerKonfiguration: Aenderungen konnten nicht uebernommen werden."); } GUIContainer.getGUIContainer().updateViewport(); } protected void initContents() { JLabel tempLabel; Box tempBox; ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { aenderungenAnnehmen(); updateAttribute(); } }; FocusListener focusListener = new FocusListener() { public void focusGained(FocusEvent arg0) {} public void focusLost(FocusEvent arg0) { aenderungenAnnehmen(); updateAttribute(); } }; // ======================================================= // Attribut Name tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg1")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); name = new JTextField(messages.getString("jhostkonfiguration_msg2")); name.addActionListener(actionListener); name.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(name); box.add(tempBox, BorderLayout.NORTH); // ======================================================= // Attribut MAC-Adresse tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg9")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); macAdresse = new JTextField(""); macAdresse.setEditable(false); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(macAdresse); box.add(tempBox, BorderLayout.NORTH); // ======================================================= // Attribut IP-Adresse tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg3")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); ipAdresse = new JTextField("192.168.0.1"); ipAdresse.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkIpAddress(); } }); ipAdresse.addActionListener(actionListener); ipAdresse.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(ipAdresse); box.add(tempBox, BorderLayout.NORTH); // ======================================================= // Attribut Netzmaske tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg4")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); netzmaske = new JTextField("255.255.255.0"); netzmaske.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkNetmask(); } }); netzmaske.addActionListener(actionListener); netzmaske.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(netzmaske); box.add(tempBox, BorderLayout.NORTH); // ======================================================= // Attribut Gateway-Adresse tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg5")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); gateway = new JTextField("192.168.0.1"); gateway.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkGatewayAddress(); } }); gateway.addActionListener(actionListener); gateway.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(gateway); box.add(tempBox, BorderLayout.NORTH); // ======================================================= // Attribut Adresse des Domain Name Server tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg6")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); dns = new JTextField("192.168.0.1"); dns.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkDnsAddress(); } }); dns.addActionListener(actionListener); dns.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(dns); box.add(tempBox, BorderLayout.NORTH); // ======================================================= // Wireless / Cable ActionListener wifiLanActionListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { aenderungenAnnehmen(); updateAttribute(); } }; JLabel connectivity = new JLabel(" / "); useLAN = new JRadioButton(); useLAN.setText(messages.getString("jhostkonfiguration_msg14")); useLAN.setOpaque(false); useLAN.addActionListener(wifiLanActionListener); lanWifi = new ButtonGroup(); lanWifi.add(useLAN); useWiFi = new JRadioButton(); useWiFi.setText(messages.getString("jhostkonfiguration_msg13")); useWiFi.setOpaque(false); useWiFi.addActionListener(wifiLanActionListener); lanWifi.add(useWiFi); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(useLAN); tempBox.add(connectivity); tempBox.add(useWiFi); middleBox.add(tempBox); ssidLabel = new JLabel(messages.getString("jhostkonfiguration_msg15")); ssidLabel.setVisible(true); ssidLabel.setOpaque(false); ssidLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox = Box.createHorizontalBox(); tempBox.add(Box.createHorizontalStrut(30)); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(ssidLabel); middleBox.add(tempBox); ssid = new JComboBox<>(); ssid.addItem(messages.getString("jhostkonfiguration_msg16")); ssid.setPreferredSize(new Dimension(LABEL_WIDTH - 60, 15)); ssid.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { LOG.trace("Action listener on SSID combo box is triggered be event '{}'", e.getID()); aenderungenAnnehmen(); } }); tempBox = Box.createHorizontalBox(); tempBox.add(Box.createHorizontalStrut(30)); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(ssid); middleBox.add(tempBox); middleBox.add(Box.createVerticalStrut(160)); // ======================================================= // IP address as name tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg10")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setOpaque(false); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); useIpAsName = new JCheckBox(); useIpAsName.setOpaque(false); useIpAsName.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { aendereAnzeigeName(); } }); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(useIpAsName); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(tempLabel); rightBox.add(tempBox, BorderLayout.NORTH); // ======================================================= // MAC address as name tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg12")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setOpaque(false); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); useMacAsName = new JCheckBox(); useMacAsName.setOpaque(false); useMacAsName.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { aendereAnzeigeName(); } }); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(useMacAsName); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(tempLabel); rightBox.add(tempBox, BorderLayout.NORTH); // ======================================================= // Activation Status IP Forwarding tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg11")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setOpaque(false); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); ipForwarding = new JCheckBox(); ipForwarding.setOpaque(false); ipForwarding.addActionListener(actionListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(ipForwarding); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(tempLabel); rightBox.add(tempBox, BorderLayout.NORTH); // ======================================================= // Attribut Verwendung von DHCP tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg7")); tempLabel.setPreferredSize(new Dimension(LABEL_WIDTH, 10)); tempLabel.setVisible(true); tempLabel.setOpaque(false); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); dhcp = new JCheckBox(); dhcp.setSelected(false); dhcp.setOpaque(false); dhcp.addActionListener(actionListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 35)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(dhcp); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(tempLabel); rightBox.add(tempBox, BorderLayout.NORTH); // =================================================== // DHCP-Server einrichten rightBox.add(Box.createVerticalStrut(10)); tempBox = Box.createHorizontalBox(); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setOpaque(false); btDhcp = new JButton(messages.getString("jhostkonfiguration_msg8")); btDhcp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showDhcpConfiguration(); } }); tempBox.add(btDhcp); rightBox.add(tempBox); rightBox.add(Box.createVerticalStrut(80)); updateAttribute(); } private void showDhcpConfiguration() { JDHCPKonfiguration dhcpKonfig = new JDHCPKonfiguration(JMainFrame.getJMainFrame(), messages.getString("jhostkonfiguration_msg8"), (InternetKnotenBetriebssystem) ((InternetKnoten) holeHardware()).getSystemSoftware()); dhcpKonfig.setVisible(true); } private void checkIpAddress() { ueberpruefen(EingabenUeberpruefung.musterIpAdresse, ipAdresse); } private void checkDnsAddress() { ueberpruefen(EingabenUeberpruefung.musterIpAdresseAuchLeer, dns); } private void checkGatewayAddress() { ueberpruefen(EingabenUeberpruefung.musterIpAdresseAuchLeer, gateway); } private void checkNetmask() { ueberpruefen(EingabenUeberpruefung.musterSubNetz, netzmaske); } public void updateAttribute() { if (holeHardware() != null) { Host host = (Host) holeHardware(); name.setText(host.holeAnzeigeName()); useIpAsName.setSelected(host.isUseIPAsName()); useMacAsName.setSelected(host.isUseMACAsName()); name.setEnabled(!host.isUseIPAsName() && !host.isUseMACAsName()); Betriebssystem bs = (Betriebssystem) host.getSystemSoftware(); macAdresse.setText(bs.primaryMACAddress()); ipAdresse.setText(bs.primaryIPAdresse()); netzmaske.setText(bs.primarySubnetMask()); gateway.setText(bs.getStandardGateway()); dns.setText(bs.getDNSServer()); ipForwarding.setSelected(bs.isIpForwardingEnabled()); dhcp.setSelected(bs.isDHCPKonfiguration()); btDhcp.setEnabled(!dhcp.isSelected()); ipAdresse.setEnabled(!bs.isDHCPKonfiguration()); netzmaske.setEnabled(!bs.isDHCPKonfiguration()); gateway.setEnabled(!bs.isDHCPKonfiguration()); dns.setEnabled(!bs.isDHCPKonfiguration()); useLAN.setSelected(!bs.wireless()); useWiFi.setSelected(bs.wireless()); useWiFi.setEnabled(host.holeFreienPort() != null || bs.wireless()); ssid.setEnabled(useWiFi.isSelected()); ssidLabel.setEnabled(ssid.isEnabled()); updateSsid(); checkIpAddress(); checkDnsAddress(); checkGatewayAddress(); checkNetmask(); } else { LOG.debug("GUIRechnerKonfiguration: keine Hardware-Komponente vorhanden"); } } private void updateWifiConnection() { Host host = (Host) holeHardware(); // Step 1: remove existing wifi connection for (GUIKabelItem cable : GUIContainer.getGUIContainer().getCableItems()) { if (cable.getDasKabel().getWireless() && (host.equals(cable.getKabelpanel().getZiel1().getKnoten()) || host.equals(cable.getKabelpanel().getZiel2().getKnoten()))) { GUIEvents.getGUIEvents().removeConnection(cable); break; } } // Step 2: (Re-)Connect to access point Betriebssystem os = (Betriebssystem) host.getSystemSoftware(); String configuredSSID = os.getSsid(); boolean connectWifi = os.wireless() && null != configuredSSID; if (connectWifi) { GUIKnotenItem wifiAccessPoint = null; GUIKnotenItem hostNode = null; for (GUIKnotenItem item : GUIContainer.getGUIContainer().getKnotenItems()) { if (item.getKnoten() instanceof Switch && configuredSSID.equals(((SwitchFirmware) item.getKnoten().getSystemSoftware()).getSSID())) { wifiAccessPoint = item; } else if (item.getKnoten().equals(host)) { hostNode = item; } else if (null != wifiAccessPoint && null != hostNode) { break; } } if (null != wifiAccessPoint) { GUIEvents.getGUIEvents().createConnection(hostNode, wifiAccessPoint); } } GUIContainer.getGUIContainer().updateCables(); } private void updateSsid() { List ssidList = new ArrayList<>(); for (int i = 1; i < ssid.getItemCount(); i++) { ssidList.add(ssid.getItemAt(i)); } Host host = (Host) holeHardware(); String configuredSSID = ((Betriebssystem) host.getSystemSoftware()).getSsid(); List accessPointSsidList = new ArrayList<>(); for (GUIKnotenItem item : GUIContainer.getGUIContainer().getKnotenItems()) { if (item.getKnoten() instanceof Switch) { String accessPointSsid = ((SwitchFirmware) item.getKnoten().getSystemSoftware()).getSSID(); accessPointSsidList.add(accessPointSsid); if (!ssidList.contains(accessPointSsid)) { ssid.addItem(accessPointSsid); } } } for (int i = ssid.getItemCount() - 1; i >= 1; i--) { if (!accessPointSsidList.contains(ssid.getItemAt(i))) { ssid.removeItemAt(i); } } if (null != configuredSSID && accessPointSsidList.contains(configuredSSID)) { ssid.setSelectedItem(configuredSSID); } else { ssid.setSelectedIndex(0); } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JKonfiguration.java000066400000000000000000000074641506574241500266620ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.util.HashMap; import java.util.regex.Pattern; import javax.swing.JTextField; import filius.gui.ControlPanel; import filius.hardware.Hardware; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.Host; import filius.hardware.knoten.Modem; import filius.hardware.knoten.Switch; import filius.hardware.knoten.Vermittlungsrechner; import filius.rahmenprogramm.EingabenUeberpruefung; @SuppressWarnings({ "deprecation", "serial" }) public class JKonfiguration extends ControlPanel { private Hardware hardware; protected static HashMap instances = new HashMap(); protected JKonfiguration(Hardware hardware) { super(); this.hardware = hardware; if (hardware != null) { hardware.addObserver(this); reInit(); updateAttribute(); } minimieren(); } public static JKonfiguration getInstance(Hardware hardware) { if (hardware == null) { return new JKonfiguration(null); } JKonfiguration newInstance; if (!instances.containsKey(hardware)) { if (hardware instanceof Host) { newInstance = new JHostKonfiguration(hardware); } else if (hardware instanceof Modem) { newInstance = new JModemKonfiguration(hardware); } else if (hardware instanceof Switch) { newInstance = new JSwitchKonfiguration(hardware); } else if (hardware instanceof Vermittlungsrechner) { newInstance = new JVermittlungsrechnerKonfiguration(hardware); } else if (hardware instanceof Gateway) { newInstance = new JGatewayConfiguration(hardware); } else { newInstance = new JKonfiguration(null); } instances.put(hardware, newInstance); } return instances.get(hardware); } public Hardware holeHardware() { return hardware; } /** * Funktion die waehrend der Eingabe ueberprueft ob die bisherige Eingabe einen korrekten Wert darstellt. * * @author Johannes Bade & Thomas Gerding * @param pruefRegel * @param feld */ public boolean ueberpruefen(Pattern pruefRegel, JTextField feld) { if (EingabenUeberpruefung.isGueltig(feld.getText(), pruefRegel)) { feld.setForeground(EingabenUeberpruefung.farbeRichtig); return true; } else { feld.setForeground(EingabenUeberpruefung.farbeFalsch); return false; } } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JModemKonfiguration.java000066400000000000000000000242711506574241500276370ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTextField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.hardware.Hardware; import filius.hardware.knoten.Modem; import filius.rahmenprogramm.I18n; import filius.software.system.ModemFirmware; @SuppressWarnings({ "serial", "deprecation" }) public class JModemKonfiguration extends JKonfiguration implements I18n, Observer { private static Logger LOG = LoggerFactory.getLogger(JModemKonfiguration.class); private static final String CMD_DISCONNECT = "Trennen"; private static final String CMD_START_SERVER = "ServerStarten"; private static final String CMD_START_CLIENT = "ClientStarten"; private JTextField name; private JCheckBox cbServerModus; private JTextField tfIpAdresse; private JTextField tfPort; private JButton btStartStop; protected JModemKonfiguration(Hardware hardware) { super(hardware); ((Modem) holeHardware()).getSystemSoftware().addObserver(this); } private void aenderungenAnnehmen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JModemKonfiguration), aenderungenAnnehmen()"); Modem modem; ModemFirmware firmware; modem = (Modem) holeHardware(); firmware = (ModemFirmware) modem.getSystemSoftware(); modem.setName(name.getText()); firmware.setIpAdresse(tfIpAdresse.getText()); try { firmware.setPort(Integer.parseInt(tfPort.getText())); } catch (Exception e) {} if (cbServerModus.isSelected()) { ((ModemFirmware) modem.getSystemSoftware()).setMode(ModemFirmware.SERVER); } else { ((ModemFirmware) modem.getSystemSoftware()).setMode(ModemFirmware.CLIENT); } GUIContainer.getGUIContainer().updateViewport(); } protected void initContents() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JModemKonfiguration), initAttributEingabeBox(" + box + ")"); FocusListener configFocusListener = new FocusListener() { public void focusGained(FocusEvent arg0) {} public void focusLost(FocusEvent arg0) { aenderungenAnnehmen(); updateAttribute(); } }; ActionListener configActionListener = new ActionListener() { public void actionPerformed(ActionEvent e) { aenderungenAnnehmen(); updateAttribute(); } }; JLabel tempLabel = new JLabel(messages.getString("jmodemkonfiguration_msg1")); tempLabel.setPreferredSize(new Dimension(140, 10)); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); name = new JTextField(""); name.addActionListener(configActionListener); name.addFocusListener(configFocusListener); Box tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(name); box.add(tempBox, BorderLayout.NORTH); tempBox = Box.createHorizontalBox(); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); cbServerModus = new JCheckBox(); cbServerModus.setText(messages.getString("jmodemkonfiguration_msg2")); cbServerModus.setOpaque(false); cbServerModus.addActionListener(configActionListener); tempBox.add(cbServerModus); box.add(tempBox); tempBox = Box.createHorizontalBox(); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempLabel = new JLabel(messages.getString("jmodemkonfiguration_msg3")); tempLabel.setPreferredSize(new Dimension(140, 10)); tempBox.add(tempLabel); tfIpAdresse = new JTextField("192.168.0.21"); tfIpAdresse.setEnabled(false); tfIpAdresse.setPreferredSize(new Dimension(100, 18)); tfIpAdresse.setText(((ModemFirmware) ((Modem) holeHardware()).getSystemSoftware()).getIpAdresse()); tfIpAdresse.addActionListener(configActionListener); tfIpAdresse.addFocusListener(configFocusListener); tempBox.add(tfIpAdresse); box.add(tempBox); tempBox = Box.createHorizontalBox(); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempLabel = new JLabel(messages.getString("jmodemkonfiguration_msg4")); tempLabel.setPreferredSize(new Dimension(140, 10)); tempBox.add(tempLabel); tfPort = new JTextField("1234"); tfPort.setPreferredSize(new Dimension(100, 18)); tfPort.setText("" + ((ModemFirmware) ((Modem) holeHardware()).getSystemSoftware()).getPort()); tfPort.addActionListener(configActionListener); tfPort.addFocusListener(configFocusListener); tempBox.add(tfPort); box.add(tempBox); tempBox = Box.createHorizontalBox(); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); btStartStop = new JButton(messages.getString("jmodemkonfiguration_msg2")); btStartStop.setPreferredSize(new Dimension(300, 30)); btStartStop.setActionCommand(CMD_START_SERVER); btStartStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ModemFirmware firmware = (ModemFirmware) ((Modem) holeHardware()).getSystemSoftware(); aenderungenAnnehmen(); if (e.getActionCommand().equals(CMD_START_CLIENT)) { firmware.starteClient(); } else if (e.getActionCommand().equals(CMD_START_SERVER)) { firmware.starteServer(); } else if (e.getActionCommand().equals(CMD_DISCONNECT)) { firmware.trennen(); } updateAttribute(); } }); tempBox.add(btStartStop); box.add(tempBox); updateAttribute(); } @Override public synchronized void updateAttribute() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JModemKonfiguration), updateAttribute()"); Modem modem; ModemFirmware firmware; boolean aktiv; modem = (Modem) holeHardware(); firmware = (ModemFirmware) modem.getSystemSoftware(); name.setText(modem.holeAnzeigeName()); tfIpAdresse.setText(firmware.getIpAdresse()); tfPort.setText("" + firmware.getPort()); if (firmware.getMode() == ModemFirmware.CLIENT) { cbServerModus.setSelected(false); tfIpAdresse.setEnabled(true); btStartStop.setPreferredSize(new Dimension(300, 30)); btStartStop.setActionCommand(CMD_START_CLIENT); aktiv = modem.istModemVerbindungAktiv(); btStartStop.setText(aktiv ? messages.getString("jmodemkonfiguration_msg6") : messages.getString("jmodemkonfiguration_msg5")); } else { cbServerModus.setSelected(true); tfIpAdresse.setEnabled(false); btStartStop.setPreferredSize(new Dimension(300, 30)); btStartStop.setActionCommand(CMD_START_SERVER); aktiv = firmware.istServerBereit(); btStartStop.setText(aktiv ? messages.getString("jmodemkonfiguration_msg8") : messages.getString("jmodemkonfiguration_msg7")); } if (aktiv) { btStartStop.setActionCommand(CMD_DISCONNECT); tfIpAdresse.setEnabled(false); tfPort.setEnabled(false); cbServerModus.setEnabled(false); } else { tfPort.setEnabled(true); cbServerModus.setEnabled(true); } } public void update(Observable arg0, Object arg1) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JModemKonfiguration), update(" + arg0 + "," + arg1 + ")"); updateAttribute(); } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JPortForwardingDialog.java000066400000000000000000000204651506574241500301260ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.LinkedList; import java.util.Vector; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.ComboBoxTableCellEditor; import filius.rahmenprogramm.I18n; public class JPortForwardingDialog extends JDialog implements I18n { private static Logger LOG = LoggerFactory.getLogger(JPortForwardingDialog.class); private static final long serialVersionUID = 1L; private static final Color TAB_COLOR = new Color(240, 240, 240); JPortForwardingDialog jpfd = null; private LinkedList staticNat; private GatewayPortForwardingConfigTable staticTable; public JPortForwardingDialog(LinkedList staticNat2, JFrame dummyFrame) { super(dummyFrame, messages.getString("jgatewayconfiguration_msg19"), true); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + ", constr: JPortForwardingDialog(" + staticNat2 + "," + dummyFrame + ")"); this.staticNat = staticNat2; jpfd = this; JScrollPane scrollPane; Box vBox, hBox; DefaultTableModel model; TableColumnModel columnModel; JButton button; vBox = Box.createVerticalBox(); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); model = new DefaultTableModel(0, 4); staticTable = new GatewayPortForwardingConfigTable(model, true); staticTable.setParentGUI(this); staticTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); staticTable.setIntercellSpacing(new Dimension(10, 5)); staticTable.setRowHeight(30); staticTable.setShowGrid(true); staticTable.setFillsViewportHeight(true); staticTable.setBackground(Color.WHITE); staticTable.setShowHorizontalLines(true); columnModel = staticTable.getColumnModel(); columnModel.getColumn(0).setHeaderValue(messages.getString("jportforwarding_msg4")); String[] protValues = {"TCP","UDP"}; columnModel.getColumn(0).setCellEditor(new ComboBoxTableCellEditor(protValues)); columnModel.getColumn(1).setHeaderValue(messages.getString("jportforwarding_msg5")); columnModel.getColumn(2).setHeaderValue(messages.getString("jportforwarding_msg6")); columnModel.getColumn(3).setHeaderValue(messages.getString("jportforwarding_msg7")); columnModel.getColumn(0).setPreferredWidth(80); columnModel.getColumn(1).setPreferredWidth(60); columnModel.getColumn(2).setPreferredWidth(130); columnModel.getColumn(3).setPreferredWidth(60); scrollPane = new JScrollPane(staticTable); scrollPane.setPreferredSize(new Dimension(555, 300)); vBox.add(scrollPane); vBox.add(Box.createVerticalStrut(10)); hBox = Box.createHorizontalBox(); hBox.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("jgatewayconfiguration_msg21")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { neuerEintrag(); } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(10)); button = new JButton(messages.getString("jgatewayconfiguration_msg22")); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { markiertenEintragLoeschen(); } }); hBox.add(button); hBox.add(Box.createHorizontalStrut(10)); vBox.add(hBox); vBox.add(Box.createVerticalStrut(10)); getContentPane().add(vBox); getContentPane().setBackground(TAB_COLOR); } public void neuerEintrag() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", neuerEintrag()"); aenderungenAnnehmen(); Vector eintrag = new Vector(); eintrag.addElement("TCP"); eintrag.addElement(""); eintrag.addElement(""); eintrag.addElement(""); ((DefaultTableModel) this.staticTable.getModel()).addRow(eintrag); this.staticTable.setRowSelectionInterval(this.staticTable.getModel().getRowCount() - 1, this.staticTable.getModel().getRowCount() - 1); } public void markiertenEintragLoeschen() { if (this.staticTable.getSelectedRow() > -1) { entferneEintrag(this.staticTable.getSelectedRow()); aenderungenAnnehmen(); } } private void entferneEintrag(int row) { ((DefaultTableModel) this.staticTable.getModel()).removeRow(row); } public void aenderungenAnnehmen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", aenderungenAnnehmen()"); Vector tableData; if (this.staticTable.getCellEditor() != null) { this.staticTable.getCellEditor().stopCellEditing(); } tableData = ((DefaultTableModel) this.staticTable.getModel()).getDataVector(); staticNat.clear(); for (int i = 0; i < tableData.size(); i++) { String[] portFordwardingEntry = extractRowData(i); addEintrag(portFordwardingEntry[0], portFordwardingEntry[1], portFordwardingEntry[2], portFordwardingEntry[3]); } updateAttribute(); } private String[] extractRowData(int rowIdx) { Vector rowData = (Vector) ((DefaultTableModel) this.staticTable.getModel()).getDataVector().elementAt(rowIdx); String[] portFordwardingEntry = new String[rowData.size()]; for (int j = 0; j < portFordwardingEntry.length; j++) { portFordwardingEntry[j] = (String) rowData.elementAt(j); } return portFordwardingEntry; } public void updateAttribute() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", updateAttribute()"); DefaultTableModel model; model = (DefaultTableModel) this.staticTable.getModel(); model.setRowCount(0); this.createStaticTable(); model.fireTableDataChanged(); } public void createStaticTable() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", createStaticTable()"); DefaultTableModel model = (DefaultTableModel) this.staticTable.getModel(); for (int i = 0; i < staticNat.size(); i++) { model.addRow(staticNat.get(i)); } } public LinkedList getStaticNATTable() { return staticNat; } public void addEintrag(String protocol, String port, String lanIp, String lanPort) { String[] data = new String[4]; data[0] = protocol; data[1] = port; data[2] = lanIp; data[3] = lanPort; staticNat.add(data); } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JSidebarButton.java000066400000000000000000000205701506574241500266010ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.SwingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.JMainFrame; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.Host; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; @SuppressWarnings({ "serial", "deprecation" }) public class JSidebarButton extends JLabel implements Observer, I18n { private static Logger LOG = LoggerFactory.getLogger(JSidebarButton.class); private static final int BORDER = 8; private String typ; private boolean selektiert; private boolean modemVerbunden; private boolean isTemporaryText; private String originalText; private Color originalTextColor; public boolean isSelektiert() { return selektiert; } public void setSelektiert(boolean selektiert) { this.selektiert = selektiert; } public String getTyp() { return typ; } public void setTyp(String typ) { this.typ = typ; } public JSidebarButton() { initAlignment(); } private void initAlignment() { this.setVerticalTextPosition(SwingConstants.BOTTOM); this.setHorizontalTextPosition(SwingConstants.CENTER); this.setAlignmentX(0.5f); this.setBorder(BorderFactory.createEmptyBorder(BORDER, BORDER, BORDER, BORDER)); } public JSidebarButton(Icon icon, String typ) { super(icon, JLabel.CENTER); this.typ = typ; initAlignment(); } public JSidebarButton(String text, Icon icon, String typ) { super(text, icon, JLabel.CENTER); this.typ = typ; initAlignment(); } @Override public Rectangle getBounds() { return new Rectangle((int) super.getBounds().getX(), (int) super.getBounds().getY(), this.getWidth(), this.getHeight()); } public boolean inBounds(int x, int y) { return x >= this.getX() && x <= this.getX() + this.getWidth() && y >= this.getY() && y <= this.getY() + this.getHeight(); } public int getWidth() { int width = 0; if (null != this.getText()) { width = this.getFontMetrics(this.getFont()).stringWidth(this.getText()); } if (this.getIcon() != null && this.getIcon().getIconWidth() > width) { width = this.getIcon().getIconWidth(); } width += 5 + 2 * BORDER; return width; } public int getHeight() { int height; height = this.getFontMetrics(this.getFont()).getHeight(); if (this.getIcon() != null) { height += this.getIcon().getIconHeight(); } height += 5; return height; } public Dimension getPreferredSize() { return new Dimension(getWidth(), getHeight()); } // Update location after a text change so that the icon does not move public void setTextAndUpdateLocation(String text) { int icoW = (getIcon() != null ? getIcon().getIconWidth() : 0); if (icoW % 2 == 1) icoW--; // Trick to avoid a one pixel shift when the icon's width is odd int dW = (getWidth() - icoW) / 2; super.setText(text); int dW2 = (getWidth() - icoW) / 2; setBounds(getX() - dW2 + dW, getY(), getWidth(), getHeight()); // SetLocation won't be enough here } // Update location after the initial text assignment so that the icon does not move public void initTextAndUpdateLocation(String text) { setTextAndUpdateLocation(text); setLocation(getX(), getY() + 10); // The reason for this 10px down is not clear but it is necessary to fix the // icon at the end of the drag-and-drop creation } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; if (selektiert) { g.setColor(new Color(0, 0, 0)); Graphics2D g2 = (Graphics2D) g; Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 1, new float[] { 2 }, 0); g2.setStroke(stroke); g2.drawRect(0, 0, this.getWidth() - 1, this.getHeight() - 1); // Use same color as the one used for multiple selection g2.setColor(new Color(0.80f, 0.92f, 1f, 0.2f)); g2.fillRect(0, 0, this.getWidth() - 1, this.getHeight() - 1); } if (modemVerbunden) { g2d.setColor(new Color(0, 255, 0)); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.fillOval((this.getWidth() / 2) - 6, (this.getHeight() / 2) - 6, 12, 12); } } public void update(Observable o, Object arg) { LOG.trace("(JSidebarButton), update(" + o + "," + arg + ")"); if (arg != null && arg.equals(Boolean.TRUE)) { modemVerbunden = true; } else if (arg != null && arg.equals(Boolean.FALSE)) { modemVerbunden = false; } else if (arg != null && arg instanceof String) { JOptionPane.showMessageDialog(JMainFrame.getJMainFrame(), arg); } else if (arg != null && arg instanceof Host) { this.setText(((Host) arg).holeAnzeigeName()); } if (arg != null && arg instanceof InternetKnoten) { updateTooltip((InternetKnoten) arg); } this.updateUI(); } void updateTooltip(InternetKnoten knoten) { StringBuilder tooltip = new StringBuilder(); tooltip.append("
");

        tooltip.append(messages.getString("jsidebar_tooltip_ipAddress")).append(":");
        String gateway = null;
        String dns = null;
        for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) {
            tooltip.append("\n ").append(nic.getIp()).append(" / ").append(nic.getSubnetzMaske());
            tooltip.append(" (").append(nic.getMac()).append(")");
            gateway = nic.getGateway();
            dns = nic.getDns();
        }
        tooltip.append("\n").append(messages.getString("jsidebar_tooltip_gateway")).append(": ").append(gateway);
        if (knoten instanceof Host) {
            tooltip.append("\n").append(messages.getString("jsidebar_tooltip_dnsServer")).append(": ").append(dns);
        }

        tooltip.append("
"); setToolTipText(tooltip.toString()); } public void setTemporaryText(String text) { if (!isTemporaryText) { originalText = getText(); originalTextColor = getForeground(); isTemporaryText = true; } setForeground(Color.GRAY); setTextAndUpdateLocation(text); } public void resetText() { setForeground(originalTextColor); setTextAndUpdateLocation(originalText); isTemporaryText = false; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JSwitchKonfiguration.java000066400000000000000000000233401506574241500300330ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTextField; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.gui.GUIEvents; import filius.hardware.Hardware; import filius.hardware.knoten.Switch; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.IconMode; import filius.rahmenprogramm.Information; import filius.software.system.SwitchFirmware; public class JSwitchKonfiguration extends JKonfiguration implements I18n { private static Logger LOG = LoggerFactory.getLogger(JSwitchKonfiguration.class); private static final long serialVersionUID = 1L; private JTextField name; // Name,Name,20,String,editable,Neuer private JTextField ssid; private JTextField retentionTime; private JCheckBox checkCloud; protected JSwitchKonfiguration(Hardware hardware) { super(hardware); } public void aenderungenAnnehmen() { Switch switchWAP = (Switch) holeHardware(); switchWAP.setName(name.getText()); SwitchFirmware switchFirmware = (SwitchFirmware) switchWAP.getSystemSoftware(); String ssidBeforeChange = switchFirmware.getSSID(); if (checkSSID()) { switchFirmware.setSSID(ssid.getText()); } if (!StringUtils.equals(ssidBeforeChange, switchFirmware.getSSID())) { for (GUIKabelItem cable : GUIContainer.getGUIContainer().getCableItems()) { if (cable.getDasKabel().getWireless() && (switchWAP.equals(cable.getKabelpanel().getZiel1().getKnoten()) || switchWAP.equals(cable.getKabelpanel().getZiel2().getKnoten()))) { GUIEvents.getGUIEvents().removeConnection(cable); } } } try { ((SwitchFirmware) ((Switch) holeHardware()).getSystemSoftware()) .setRetentionTime(Long.parseLong(retentionTime.getText()) * 1000); } catch (NumberFormatException e) {} GUIContainer.getGUIContainer().updateViewport(); updateAttribute(); } public void changeAppearance() { LOG.debug("changeAppearance invoked for Switch"); if (checkCloud.isSelected()) { GUIContainer.getGUIContainer().getLabelforKnoten(((Switch) holeHardware())) .setIcon(new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.SWITCH_CLOUD))); ((Switch) holeHardware()).setCloud(true); GUIContainer.getGUIContainer().updateViewport(); } else { IconMode mode = Information.getInformation().getIconMode(); GUIContainer.getGUIContainer().getLabelforKnoten(((Switch) holeHardware())) .setIcon(new ImageIcon(getClass().getResource("/" + GUIDesignSidebar.SWITCH + mode.suffix()))); ((Switch) holeHardware()).setCloud(false); GUIContainer.getGUIContainer().updateViewport(); } } @Override protected void initContents() { JLabel tempLabel; Box tempBox; Box tempBox2; FocusListener focusListener; ActionListener actionListener; ItemListener itemListener; actionListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { aenderungenAnnehmen(); } }; itemListener = new ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { changeAppearance(); } }; focusListener = new FocusListener() { public void focusGained(FocusEvent arg0) {} public void focusLost(FocusEvent arg0) { aenderungenAnnehmen(); } }; tempBox2 = Box.createVerticalBox(); // switch name tempLabel = new JLabel(messages.getString("jswitchkonfiguration_msg1")); tempLabel.setPreferredSize(new Dimension(140, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); name = new JTextField(messages.getString("jswitchkonfiguration_msg2")); name.addActionListener(actionListener); name.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(name); tempBox2.add(tempBox); tempBox2.add(Box.createVerticalStrut(10)); // ssid tempLabel = new JLabel(messages.getString("jswitchkonfiguration_msg4")); tempLabel.setPreferredSize(new Dimension(140, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); ssid = new JTextField(); ssid.addActionListener(actionListener); ssid.addFocusListener(focusListener); ssid.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkSSID(); } }); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setMaximumSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(ssid); tempBox2.add(tempBox); tempBox2.add(Box.createVerticalStrut(10)); // switch icon checkCloud = new JCheckBox(messages.getString("jswitchkonfiguration_msg3")); checkCloud.setPreferredSize(new Dimension(160, 10)); checkCloud.setVisible(true); checkCloud.setOpaque(false); // checkCloud.setAlignmentX(Component.RIGHT_ALIGNMENT); checkCloud.addItemListener(itemListener); // retention time tempLabel = new JLabel(messages.getString("jswitchkonfiguration_msg5")); tempLabel.setPreferredSize(new Dimension(300, 10)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); retentionTime = new JTextField(); retentionTime.setPreferredSize(new Dimension(100, 10)); retentionTime.addActionListener(actionListener); retentionTime.addFocusListener(focusListener); retentionTime.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { checkRetentionTime(); } }); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setPreferredSize(new Dimension(400, 40)); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); tempBox.add(tempLabel); tempBox.add(Box.createHorizontalStrut(5)); // Platz zw. tempLabel und tempBox.add(retentionTime); rightBox.add(tempBox); rightBox.add(Box.createVerticalStrut(190)); tempBox2.add(checkCloud); box.add(tempBox2, BorderLayout.NORTH); } @Override public void updateAttribute() { Switch switchWAP = (Switch) holeHardware(); name.setText(switchWAP.holeAnzeigeName()); ssid.setText(((SwitchFirmware) switchWAP.getSystemSoftware()).getSSID()); checkSSID(); retentionTime.setText(Long .toString(((SwitchFirmware) ((Switch) holeHardware()).getSystemSoftware()).getRetentionTime() / 1000)); checkRetentionTime(); // eingefügt checkCloud.setSelected(switchWAP.isCloud()); } private boolean checkSSID() { return ueberpruefen(EingabenUeberpruefung.musterServiceSetIdentifier, ssid); } private boolean checkRetentionTime() { return ueberpruefen(EingabenUeberpruefung.musterNurZahlen, retentionTime); } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JVermittlungsrechnerKonfiguration.java000066400000000000000000000624301506574241500326470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.List; import java.util.ListIterator; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.gui.GUIContainer; import filius.gui.JMainFrame; import filius.hardware.Hardware; import filius.hardware.Kabel; import filius.hardware.NetzwerkInterface; import filius.hardware.Port; import filius.hardware.Verbindung; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.hardware.knoten.LokalerKnoten; import filius.hardware.knoten.Vermittlungsrechner; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.firewall.Firewall; import filius.software.system.VermittlungsrechnerBetriebssystem; @SuppressWarnings("serial") public class JVermittlungsrechnerKonfiguration extends JKonfiguration implements I18n { private static Logger LOG = LoggerFactory.getLogger(JVermittlungsrechnerKonfiguration.class); private JDialog changeBasicSettingsDialog; private JTextField name; private JTextField[] ipAdressen; private JTextField[] netzmasken; private JTextField[] macAdressen; private JTextField gateway; private JCheckBox rip; private JCheckBox ipForwarding; private JLabel[] verbundeneKomponente; private JWeiterleitungsTabelle weiterleitungstabelle; private JCheckBox alleEintraegeAnzeigen; private JTabbedPane tpNetzwerkKarten; private Kabel highlightedCable = null; protected JVermittlungsrechnerKonfiguration(Hardware hardware) { super(hardware); } public void aenderungenAnnehmen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), aenderungenAnnehmen()"); Vermittlungsrechner vRechner = (Vermittlungsrechner) holeHardware(); VermittlungsrechnerBetriebssystem bs = (VermittlungsrechnerBetriebssystem) vRechner.getSystemSoftware(); vRechner.setName(name.getText()); bs.setStandardGateway(gateway.getText()); bs.setRipEnabled(rip.isSelected()); ListIterator it = vRechner.getNetzwerkInterfaces().listIterator(); for (int i = 0; it.hasNext(); i++) { NetzwerkInterface nic = (NetzwerkInterface) it.next(); if (ueberpruefen(EingabenUeberpruefung.musterIpAdresse, ipAdressen[i])) { nic.setIp(ipAdressen[i].getText()); } else { LOG.debug("IP-Adresse ungueltig " + ipAdressen[i].getText()); } if (ueberpruefen(EingabenUeberpruefung.musterSubNetz, netzmasken[i])) { nic.setSubnetzMaske(netzmasken[i].getText()); } else { LOG.debug("Netzmaske ungueltig " + netzmasken[i].getText()); } } GUIContainer.getGUIContainer().updateViewport(); updateAttribute(); } private void showFirewallDialog() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), firewallDialogAnzeigen()"); Firewall firewall = ((VermittlungsrechnerBetriebssystem) ((Vermittlungsrechner) holeHardware()) .getSystemSoftware()).holeFirewall(); JFirewallDialog firewallDialog = new JFirewallDialog(firewall, JMainFrame.getJMainFrame()); firewallDialog.setBounds(100, 100, 850, 340); firewallDialog.setName(messages.getString("jvermittlungsrechnerkonfiguration_msg1")); firewallDialog.updateRuleTable(); firewallDialog.setVisible(true); } protected void initContents() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), initAttributEingabeBox(" + box + ")"); Vermittlungsrechner vRechner; NetzwerkInterface tempNic; Knoten tempKnoten; List nicListe; Box boxNetzwerkKarten; Box vBox; Box boxNic; Box boxIpAdresse; Box boxSubnetz; Box boxMacAdresse; Box boxKomponente; KeyAdapter ipAdresseKeyAdapter; KeyAdapter netzmaskeKeyAdapter; FocusListener focusListener; ActionListener actionListener; JButton btFirewall; JButton btNeuerEintrag; JButton btEintragLoeschen; Box boxWeiterleitung; JButton btTabellenDialog; JButton changeBasicSettingsButton; JLabel tempLabel; Box tempBox; actionListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { aenderungenAnnehmen(); } }; focusListener = new FocusListener() { public void focusGained(FocusEvent arg0) {} public void focusLost(FocusEvent arg0) { aenderungenAnnehmen(); } }; this.addFocusListener(focusListener); boxNetzwerkKarten = Box.createVerticalBox(); boxNetzwerkKarten.setPreferredSize(new Dimension(440, 150)); boxNetzwerkKarten.setAlignmentX(JComponent.LEFT_ALIGNMENT); boxNetzwerkKarten.setOpaque(false); tpNetzwerkKarten = new JTabbedPane(); tpNetzwerkKarten.setOpaque(false); boxNetzwerkKarten.add(tpNetzwerkKarten); ipAdresseKeyAdapter = new KeyAdapter() { public void keyReleased(KeyEvent e) { JTextField tfQuelle = (JTextField) e.getSource(); ueberpruefen(EingabenUeberpruefung.musterIpAdresse, tfQuelle); } }; netzmaskeKeyAdapter = new KeyAdapter() { public void keyReleased(KeyEvent e) { JTextField tfQuelle = (JTextField) e.getSource(); ueberpruefen(EingabenUeberpruefung.musterSubNetz, tfQuelle); } }; vBox = Box.createVerticalBox(); // Attribut Name tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg2")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); name = new JTextField(messages.getString("jvermittlungsrechnerkonfiguration_msg3")); name.setPreferredSize(new Dimension(160, 20)); name.addActionListener(actionListener); name.addFocusListener(focusListener); tempBox.add(name); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); // Attribut Gateway tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg9")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); gateway = new JTextField(); gateway.setPreferredSize(new Dimension(160, 20)); gateway.addActionListener(actionListener); gateway.addFocusListener(focusListener); gateway.addKeyListener(ipAdresseKeyAdapter); tempBox.add(gateway); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); // Attribut rip tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); rip = new JCheckBox(); rip.setPreferredSize(new Dimension(160, 20)); rip.addActionListener(actionListener); rip.addFocusListener(focusListener); rip.setOpaque(false); tempBox.add(rip); tempBox.add(Box.createHorizontalStrut(10)); tempLabel = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg26")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); // IP forwarding status tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); ipForwarding = new JCheckBox(); ipForwarding.setPreferredSize(new Dimension(160, 20)); ipForwarding.addActionListener(actionListener); ipForwarding.addFocusListener(focusListener); ipForwarding.setOpaque(false); ipForwarding.setEnabled(false); tempBox.add(ipForwarding); tempBox.add(Box.createHorizontalStrut(10)); tempLabel = new JLabel(messages.getString("jhostkonfiguration_msg11")); tempLabel.setPreferredSize(new Dimension(140, 20)); tempLabel.setVisible(true); tempLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); tempBox.add(tempLabel); vBox.add(tempBox); vBox.add(Box.createVerticalStrut(5)); tempBox = Box.createHorizontalBox(); tempBox.setMaximumSize(new Dimension(400, 40)); btFirewall = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg4")); btFirewall.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showFirewallDialog(); } }); tempBox.add(btFirewall); tempBox.add(Box.createHorizontalStrut(20)); changeBasicSettingsButton = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg23")); changeBasicSettingsButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showConnectionsDialog(); } }); tempBox.add(changeBasicSettingsButton); vBox.add(tempBox); // NIC tabs tpNetzwerkKarten.addTab(messages.getString("jvermittlungsrechnerkonfiguration_msg17"), vBox); vRechner = (Vermittlungsrechner) holeHardware(); nicListe = vRechner.getNetzwerkInterfaces(); ipAdressen = new JTextField[nicListe.size()]; netzmasken = new JTextField[nicListe.size()]; macAdressen = new JTextField[nicListe.size()]; verbundeneKomponente = new JLabel[nicListe.size()]; ListIterator it = nicListe.listIterator(); for (int i = 0; it.hasNext(); i++) { tempNic = (NetzwerkInterface) it.next(); boxNic = Box.createVerticalBox(); boxKomponente = Box.createHorizontalBox(); boxKomponente.setMaximumSize(new Dimension(400, 40)); tempKnoten = holeVerbundeneKomponente(tempNic); if (tempKnoten == null) verbundeneKomponente[i] = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg5")); else verbundeneKomponente[i] = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg6") + " " + tempKnoten.holeAnzeigeName()); verbundeneKomponente[i].setPreferredSize(new Dimension(400, 10)); boxKomponente.add(verbundeneKomponente[i]); // show IP address (editable) boxIpAdresse = Box.createHorizontalBox(); boxIpAdresse.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg7")); tempLabel.setPreferredSize(new Dimension(120, 10)); boxIpAdresse.add(tempLabel); ipAdressen[i] = new JTextField(tempNic.getIp()); boxIpAdresse.add(ipAdressen[i]); // show netmask (editable) boxSubnetz = Box.createHorizontalBox(); boxSubnetz.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg8")); tempLabel.setPreferredSize(new Dimension(120, 10)); boxSubnetz.add(tempLabel); netzmasken[i] = new JTextField(tempNic.getSubnetzMaske()); boxSubnetz.add(netzmasken[i]); // show MAC address (not editable) boxMacAdresse = Box.createHorizontalBox(); boxMacAdresse.setMaximumSize(new Dimension(400, 40)); tempLabel = new JLabel(messages.getString("jvermittlungsrechnerkonfiguration_msg18")); tempLabel.setPreferredSize(new Dimension(120, 10)); boxMacAdresse.add(tempLabel); macAdressen[i] = new JTextField(tempNic.getMac()); macAdressen[i].setEnabled(false); boxMacAdresse.add(macAdressen[i]); boxNic.add(boxKomponente); boxNic.add(Box.createVerticalStrut(5)); boxNic.add(boxIpAdresse); boxNic.add(Box.createVerticalStrut(5)); boxNic.add(boxSubnetz); boxNic.add(Box.createVerticalStrut(5)); boxNic.add(boxMacAdresse); if (tempKnoten == null) { tpNetzwerkKarten.addTab(messages.getString("jvermittlungsrechnerkonfiguration_msg10") + (i + 1), new ImageIcon(getClass().getResource("/gfx/allgemein/conn_fail.png")), boxNic); } else { tpNetzwerkKarten.addTab(messages.getString("jvermittlungsrechnerkonfiguration_msg10") + (i + 1), new ImageIcon(getClass().getResource("/gfx/allgemein/conn_ok.png")), boxNic); } } for (int i = 0; i < ipAdressen.length; i++) { ipAdressen[i].addKeyListener(ipAdresseKeyAdapter); ipAdressen[i].addActionListener(actionListener); ipAdressen[i].addFocusListener(focusListener); netzmasken[i].addKeyListener(netzmaskeKeyAdapter); netzmasken[i].addActionListener(actionListener); netzmasken[i].addFocusListener(focusListener); } tpNetzwerkKarten.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { // LOG.debug("JVermittlungsrechnerKonfiguration, ChangeListener, stateChanged("+arg0+")"); JTabbedPane pane = (JTabbedPane) arg0.getSource(); // Get current tab int sel = pane.getSelectedIndex(); // LOG.debug("\tsource: "+pane+", index="+sel+", getComponentCount="+pane.getComponentCount()); if (highlightedCable != null) { highlightedCable.setAktiv(false); } if (sel > 0 && sel < pane.getComponentCount() - 1) { Verbindung conn = ((NetzwerkInterface) ((Vermittlungsrechner) holeHardware()) .getNetzwerkInterfaces().get(sel - 1)).getPort().getVerbindung(); if (conn != null) { conn.setAktiv(true); highlightedCable = (Kabel) conn; } } weiterleitungstabelle.aenderungenAnnehmen(); } }); /* Weiterleitungs-Tabelle Router */ weiterleitungstabelle = new JWeiterleitungsTabelle(this); JScrollPane spWeiterleitung = new JScrollPane(weiterleitungstabelle); spWeiterleitung.setPreferredSize(new Dimension(300, 120)); spWeiterleitung.addFocusListener(focusListener); tempBox = Box.createHorizontalBox(); tempBox.setOpaque(false); tempBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); tempBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); alleEintraegeAnzeigen = new JCheckBox(); alleEintraegeAnzeigen.setSelected(true); alleEintraegeAnzeigen.setText(messages.getString("jvermittlungsrechnerkonfiguration_msg11")); alleEintraegeAnzeigen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { weiterleitungstabelle.setzeAlleEintraegeAnzeigen(alleEintraegeAnzeigen.isSelected()); weiterleitungstabelle.updateAttribute(); } }); tempBox.add(alleEintraegeAnzeigen, BorderLayout.NORTH); btNeuerEintrag = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg12")); btNeuerEintrag.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { weiterleitungstabelle.neuerEintrag(); } }); tempBox.add(Box.createHorizontalStrut(50)); tempBox.add(btNeuerEintrag); btEintragLoeschen = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg13")); btEintragLoeschen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { weiterleitungstabelle.markiertenEintragLoeschen(); } }); tempBox.add(Box.createHorizontalStrut(5)); tempBox.add(btEintragLoeschen); btTabellenDialog = new JButton(messages.getString("jvermittlungsrechnerkonfiguration_msg14")); btTabellenDialog.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JDialog tabellenDialog; JScrollPane scrollPane; JWeiterleitungsTabelle tabelle; tabellenDialog = new JDialog(filius.gui.JMainFrame.getJMainFrame(), true); tabellenDialog.setTitle(messages.getString("jvermittlungsrechnerkonfiguration_msg15")); tabellenDialog.setSize(600, 400); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); tabellenDialog.setLocation(screenSize.width / 2 - 300, screenSize.height / 2 - 200); tabelle = new JWeiterleitungsTabelle(getKonfiguration()); tabelle.updateAttribute(); scrollPane = new JScrollPane(tabelle); tabellenDialog.getContentPane().add(scrollPane); tabellenDialog.setVisible(true); weiterleitungstabelle.updateAttribute(); } }); tempBox.add(Box.createHorizontalStrut(50)); tempBox.add(btTabellenDialog); boxWeiterleitung = Box.createVerticalBox(); boxWeiterleitung.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); boxWeiterleitung.add(tempBox); boxWeiterleitung.add(spWeiterleitung); tpNetzwerkKarten.addTab(messages.getString("jvermittlungsrechnerkonfiguration_msg15"), boxWeiterleitung); box.add(boxNetzwerkKarten); updateAttribute(); } private void showConnectionsDialog() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), showConnectionsDialog()"); GUIContainer.getGUIContainer().getProperty().minimieren(); // basic dialog creation and settings changeBasicSettingsDialog = new JConnectionsDialog(filius.gui.JMainFrame.getJMainFrame(), (Vermittlungsrechner) holeHardware()); changeBasicSettingsDialog.setTitle(messages.getString("jvermittlungsrechnerkonfiguration_msg23")); // positioning and size Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); changeBasicSettingsDialog.setLocation(screenSize.width / 2 - 300, screenSize.height / 2 - 200); changeBasicSettingsDialog.setSize(750, 530); changeBasicSettingsDialog.setResizable(true); changeBasicSettingsDialog.setVisible(true); } public void doUnselectAction() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), doUnselectAction()"); if (highlightedCable != null) { highlightedCable.setAktiv(false); highlightedCable = null; this.tpNetzwerkKarten.setSelectedIndex(0); } } // method to highlight marked cable; called from GUIMainMenu in case of // switching back to development view public void highlightConnCable() { if (highlightedCable != null) { highlightedCable.setAktiv(true); } } public JVermittlungsrechnerKonfiguration getKonfiguration() { return this; } public void updateAttribute() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), updateAttribute()"); Knoten tempKnoten; Vermittlungsrechner vRechner = (Vermittlungsrechner) holeHardware(); VermittlungsrechnerBetriebssystem bs = (VermittlungsrechnerBetriebssystem) vRechner.getSystemSoftware(); name.setText(vRechner.holeAnzeigeName()); gateway.setText(bs.getStandardGateway()); ipForwarding.setSelected(bs.isIpForwardingEnabled()); rip.setSelected(bs.isRipEnabled()); tpNetzwerkKarten.setEnabledAt(tpNetzwerkKarten.getTabCount() - 1, !bs.isRipEnabled()); ListIterator it = vRechner.getNetzwerkInterfaces().listIterator(); for (int i = 0; it.hasNext() && i < ipAdressen.length; i++) { NetzwerkInterface nic = (NetzwerkInterface) it.next(); ipAdressen[i].setText(nic.getIp()); netzmasken[i].setText(nic.getSubnetzMaske()); tempKnoten = holeVerbundeneKomponente(nic); if (tempKnoten == null) { verbundeneKomponente[i].setText(messages.getString("jvermittlungsrechnerkonfiguration_msg16")); tpNetzwerkKarten.setIconAt(i + 1, new ImageIcon(getClass().getResource("/gfx/allgemein/conn_fail.png"))); } else { verbundeneKomponente[i].setText(messages.getString("jvermittlungsrechnerkonfiguration_msg6") + " " + tempKnoten.holeAnzeigeName()); tpNetzwerkKarten.setIconAt(i + 1, new ImageIcon(getClass().getResource("/gfx/allgemein/conn_ok.png"))); } String tabtitle; if (nic.getIp() != null) { tabtitle = nic.getIp(); } else { tabtitle = messages.getString("jvermittlungsrechnerkonfiguration_msg10") + (i + 1); } tpNetzwerkKarten.setTitleAt(i + 1, tabtitle); } weiterleitungstabelle.updateAttribute(); } private Knoten holeVerbundeneKomponente(NetzwerkInterface nic) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (JVermittlungsrechnerKonfiguration), holeVerbundeneKomponente(" + nic + ")"); if (nic.getPort().getVerbindung() == null) { return null; } Port lokalerAnschluss = nic.getPort(); Port[] ports = lokalerAnschluss.getVerbindung().getAnschluesse(); Port entfernterAnschluss; if (ports[0] == lokalerAnschluss) { entfernterAnschluss = ports[1]; } else { entfernterAnschluss = ports[0]; } for (GUIKnotenItem node : GUIContainer.getGUIContainer().getKnotenItems()) { if (node.getKnoten() instanceof LokalerKnoten) { for (Port port : ((LokalerKnoten) node.getKnoten()).getAnschluesse()) { if (port == entfernterAnschluss) return node.getKnoten(); } } else if (node.getKnoten() instanceof InternetKnoten) { for (NetzwerkInterface tmpNic : ((InternetKnoten) node.getKnoten()).getNetzwerkInterfaces()) { if (tmpNic.getPort() == entfernterAnschluss) return node.getKnoten(); } } else { LOG.warn("Knotentyp unbekannt."); } } return null; } } filius-2.9.4+ds/src/main/java/filius/gui/netzwerksicht/JWeiterleitungsTabelle.java000066400000000000000000000314041506574241500303350ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.netzwerksicht; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Vector; import java.util.regex.Pattern; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Knoten; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.Weiterleitungstabelle; @SuppressWarnings("serial") public class JWeiterleitungsTabelle extends JTable implements I18n { private static Logger LOG = LoggerFactory.getLogger(JWeiterleitungsTabelle.class); private LinkedList editableRows = null; private JVermittlungsrechnerKonfiguration konfig; private boolean standardEintraegeAnzeigen = true; boolean notPersistedRowVisible; public JWeiterleitungsTabelle(JVermittlungsrechnerKonfiguration konfig) { super(new DefaultTableModel(1, 4)); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + ", constr: JWeiterleitungsTabelle(" + konfig + ")"); this.konfig = konfig; this.setRowHeight(20); this.setRowMargin(2); this.setDragEnabled(false); this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); final JWeiterleitungsTabelle tabelle = this; addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (e.getButton() == 3) { JPopupMenu pmRechteMausTaste = new JPopupMenu(); final JMenuItem miLoeschen = new JMenuItem(messages.getString("jweiterleitungstabelle_msg1")); miLoeschen.setActionCommand("del"); final JMenuItem miZeileHinzu = new JMenuItem(messages.getString("jweiterleitungstabelle_msg2")); miZeileHinzu.setActionCommand("add"); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(miZeileHinzu.getActionCommand())) { neuerEintrag(); } else if (e.getActionCommand().equals(miLoeschen.getActionCommand())) { markiertenEintragLoeschen(); } } }; miZeileHinzu.addActionListener(al); miLoeschen.addActionListener(al); pmRechteMausTaste.add(miLoeschen); pmRechteMausTaste.add(miZeileHinzu); pmRechteMausTaste.show(tabelle, e.getX(), e.getY()); } } }); TableColumnModel tcm = getColumnModel(); // Netzwerkziel, Netzwerkmaske, ZielIp, Schnittstelle for (int i = 0; i < getRoutingTabellenSpalten().length; i++) { tcm.getColumn(i).setHeaderValue(getRoutingTabellenSpalten()[i]); } } public void aenderungenAnnehmen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", aenderungenAnnehmen()"); Vector tableData; if (getCellEditor() != null) { getCellEditor().stopCellEditing(); } tableData = ((DefaultTableModel) getModel()).getDataVector(); Weiterleitungstabelle tabelle = ((InternetKnotenBetriebssystem) ((Knoten) konfig.holeHardware()) .getSystemSoftware()).getWeiterleitungstabelle(); tabelle.reset(); for (int i = 0; i < tableData.size(); i++) { if (notPersistedRowVisible && i == tableData.size() - 1) { String[] routingEintrag = extractAndValidateRowData(i); if (tabelle.validate(routingEintrag[0], routingEintrag[1], routingEintrag[2], routingEintrag[3])) { notPersistedRowVisible = false; tabelle.addManuellenEintrag(routingEintrag[0], routingEintrag[1], routingEintrag[2], routingEintrag[3]); } } else if (isCellEditable(i, 1)) { String[] routingEintrag = extractAndValidateRowData(i); tabelle.addManuellenEintrag(routingEintrag[0], routingEintrag[1], routingEintrag[2], routingEintrag[3]); } } updateAttribute(); } private String[] extractAndValidateRowData(int rowIdx) { String tmpString; Vector rowData = (Vector) ((DefaultTableModel) getModel()).getDataVector().elementAt(rowIdx); String[] routingEintrag = new String[rowData.size()]; for (int j = 0; j < routingEintrag.length; j++) { tmpString = (String) rowData.elementAt(j); Pattern pattern = null; switch (j) { case 0: pattern = EingabenUeberpruefung.musterIpAdresse; break; case 1: pattern = EingabenUeberpruefung.musterSubNetz; break; case 2: pattern = EingabenUeberpruefung.musterIpAdresse; break; case 3: pattern = EingabenUeberpruefung.musterIpAdresse; break; } if (null != tmpString && EingabenUeberpruefung.isGueltig(tmpString, pattern)) { routingEintrag[j] = tmpString; } else { routingEintrag[j] = ""; } } return routingEintrag; } public Component prepareRenderer(TableCellRenderer renderer, int row, int col) { Component comp = super.prepareRenderer(renderer, row, col); String cellValue = (String) getModel().getValueAt(row, col); // get the value from the cell Pattern pattern = null; switch (col) { case 0: pattern = EingabenUeberpruefung.musterIpAdresse; break; case 1: pattern = EingabenUeberpruefung.musterSubNetz; break; case 2: pattern = EingabenUeberpruefung.musterIpAdresse; break; case 3: pattern = EingabenUeberpruefung.musterIpAdresse; break; } if (null != cellValue && EingabenUeberpruefung.isGueltig(cellValue, pattern)) { // color the cell accordingly comp.setForeground(EingabenUeberpruefung.farbeRichtig); } else { comp.setForeground(EingabenUeberpruefung.farbeFalsch); } return comp; } public boolean isCellEditable(int row, int column) { if (editableRows == null) { return true; } else if (row >= editableRows.size()) { return true; } else { return editableRows.get(row).booleanValue(); } } public TableCellEditor getCellEditor() { TableCellEditor editor; editor = super.getCellEditor(); if (editor != null) { editor.addCellEditorListener(this); } return editor; } public void editingCanceled(ChangeEvent e) { super.editingCanceled(e); aenderungenAnnehmen(); } public void editingStopped(ChangeEvent e) { super.editingStopped(e); aenderungenAnnehmen(); } public void neuerEintrag() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", neuerEintrag()"); aenderungenAnnehmen(); if (!notPersistedRowVisible) { notPersistedRowVisible = true; Vector eintrag = new Vector(); eintrag.add(""); eintrag.add(""); eintrag.add(""); eintrag.add(""); ((DefaultTableModel) getModel()).addRow(eintrag); editableRows.add(Boolean.TRUE); } setRowSelectionInterval(getModel().getRowCount() - 1, getModel().getRowCount() - 1); } public void markiertenEintragLoeschen() { if (getSelectedRow() > -1) { entferneEintrag(getSelectedRow()); aenderungenAnnehmen(); } } private void entferneEintrag(int row) { if (notPersistedRowVisible) { notPersistedRowVisible = row < getModel().getRowCount() - 1; } ((DefaultTableModel) getModel()).removeRow(row); editableRows.remove(row); } public TableCellRenderer getCellRenderer(int row, int column) { DefaultTableCellRenderer renderer; renderer = new DefaultTableCellRenderer(); renderer.setEnabled(editableRows.get(row).booleanValue()); return renderer; } private String[] getRoutingTabellenSpalten() { String[] cols; cols = new String[4]; cols[0] = messages.getString("jweiterleitungstabelle_msg3"); cols[1] = messages.getString("jweiterleitungstabelle_msg4"); cols[2] = messages.getString("jweiterleitungstabelle_msg5"); cols[3] = messages.getString("jweiterleitungstabelle_msg6"); return cols; } public void setzeAlleEintraegeAnzeigen(boolean flag) { standardEintraegeAnzeigen = flag; } public void updateAttribute() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", updateAttribute()"); ListIterator it, editableIt; String[][] data; Vector tmpData; /* Weiterleitungstabelle aktualisieren */ Weiterleitungstabelle tabelle = ((InternetKnotenBetriebssystem) ((Knoten) konfig.holeHardware()) .getSystemSoftware()).getWeiterleitungstabelle(); List routingTabelle = tabelle.holeTabelle(); Vector notPersistedRow = null; if (notPersistedRowVisible) { notPersistedRow = ((DefaultTableModel) getModel()).getDataVector().lastElement(); } it = routingTabelle.listIterator(); if (standardEintraegeAnzeigen) { editableRows = tabelle.holeManuelleEintraegeFlags(); data = new String[routingTabelle.size()][4]; for (int i = 0; it.hasNext(); i++) { data[i] = (String[]) it.next(); } } else { editableIt = tabelle.holeManuelleEintraegeFlags().listIterator(); tmpData = new Vector(); while (it.hasNext() && editableIt.hasNext()) { if (((Boolean) editableIt.next()).booleanValue()) { tmpData.add((String[]) it.next()); } else { it.next(); } } data = new String[tmpData.size()][4]; editableRows = new LinkedList(); for (int i = 0; i < data.length; i++) { data[i] = (String[]) tmpData.elementAt(i); editableRows.add(Boolean.TRUE); } } ((DefaultTableModel) getModel()).setDataVector(data, getRoutingTabellenSpalten()); if (null != notPersistedRow) { editableRows.add(Boolean.TRUE); ((DefaultTableModel) getModel()).addRow(notPersistedRow); } ((DefaultTableModel) getModel()).fireTableDataChanged(); } } filius-2.9.4+ds/src/main/java/filius/gui/quelltextsicht/000077500000000000000000000000001506574241500232315ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/quelltextsicht/AnwendungsTabelle.java000066400000000000000000000032271506574241500275020ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.quelltextsicht; import javax.swing.JTable; import javax.swing.table.DefaultTableModel; public class AnwendungsTabelle extends JTable { private static final long serialVersionUID = 1L; public AnwendungsTabelle(DefaultTableModel model) { super(model); } public boolean isCellEditable(int row, int column) { if (column == 0) return true; else return false; } } filius-2.9.4+ds/src/main/java/filius/gui/quelltextsicht/FrameSoftwareWizard.java000066400000000000000000000163331506574241500300300ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.quelltextsicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import filius.gui.JMainFrame; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.SzenarioVerwaltung; public class FrameSoftwareWizard extends JDialog implements I18n { private static final long serialVersionUID = 1L; public static final int VERWALTUNG = 1, QUELLTEXT = 2, COMPILER = 3; private int zustand; private PanelCompiler pCompiler = null; private PanelVerwaltung pVerwaltung = null; private PanelQuelltext pQuelltext = null; private JLabel untertitel; private JButton vorButton, zurueckButton; private String anwendungsName, klassenName; private String[] quelltextDateien; public FrameSoftwareWizard() { super(JMainFrame.getJMainFrame(), messages.getString("framesoftwarewizard_msg1"), true); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); this.setSize(720, 600); this.setLocation(screenSize.width / 2 - (this.getWidth() / 2), screenSize.height / 2 - (this.getHeight() / 2)); initKomponenten(); SzenarioVerwaltung.getInstance().setzeGeaendert(); setzeVerwaltung(); } private void initKomponenten() { JPanel panel; getContentPane().removeAll(); untertitel = new JLabel(); untertitel.setBackground(Color.WHITE); untertitel.setOpaque(true); untertitel.setFont(new Font(Font.DIALOG, Font.BOLD, 14)); untertitel.setHorizontalAlignment(JLabel.CENTER); getContentPane().add(untertitel, BorderLayout.NORTH); panel = new JPanel(); panel.setLayout(new FlowLayout()); zurueckButton = new JButton(messages.getString("framesoftwarewizard_msg2")); zurueckButton.setPreferredSize(new Dimension(150, 30)); zurueckButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { zurueck(); } }); panel.add(zurueckButton); vorButton = new JButton(messages.getString("framesoftwarewizard_msg3")); vorButton.setPreferredSize(new Dimension(150, 30)); vorButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { weiter(); } }); panel.add(vorButton); getContentPane().add(panel, BorderLayout.SOUTH); } private void setzeVerwaltung() { JScrollPane scrollPane; if (pVerwaltung == null) { pVerwaltung = new PanelVerwaltung(); } scrollPane = new JScrollPane(pVerwaltung); getContentPane().add(scrollPane, BorderLayout.CENTER); this.validate(); untertitel.setText(messages.getString("framesoftwarewizard_msg4")); zurueckButton.setText(messages.getString("framesoftwarewizard_msg5")); vorButton.setText(messages.getString("framesoftwarewizard_msg3")); zustand = VERWALTUNG; } private void setzeEditor() { if (pQuelltext == null) { pQuelltext = new PanelQuelltext(); if (quelltextDateien.length == 2) { pQuelltext.hinzuEditor(klassenName, quelltextDateien[0]); pQuelltext.hinzuEditor("GUIApplication" + klassenName + "Window", quelltextDateien[1]); } else { pQuelltext.hinzuEditor(klassenName + "Mitarbeiter", quelltextDateien[0]); pQuelltext.hinzuEditor(klassenName, quelltextDateien[1]); pQuelltext.hinzuEditor("GUIApplication" + klassenName + "Window", quelltextDateien[2]); } } getContentPane().add(pQuelltext, BorderLayout.CENTER); this.validate(); untertitel.setText(messages.getString("framesoftwarewizard_msg6")); zurueckButton.setText(messages.getString("framesoftwarewizard_msg2")); vorButton.setText(messages.getString("framesoftwarewizard_msg3")); zustand = QUELLTEXT; } private void setzeCompiler() { JScrollPane scrollPane; if (pCompiler == null) { pCompiler = new PanelCompiler(quelltextDateien, anwendungsName, klassenName); } scrollPane = new JScrollPane(pCompiler); getContentPane().add(scrollPane, BorderLayout.CENTER); this.validate(); untertitel.setText(messages.getString("framesoftwarewizard_msg7")); zurueckButton.setText(messages.getString("framesoftwarewizard_msg2")); vorButton.setText(messages.getString("framesoftwarewizard_msg8")); (new Thread(pCompiler)).start(); zustand = COMPILER; } private void weiter() { String[] array; switch (zustand) { case VERWALTUNG: array = pVerwaltung.holeAnwendung(); if (array != null) { anwendungsName = array[0]; klassenName = array[1]; quelltextDateien = pVerwaltung.initQuelltextDateien(); initKomponenten(); pQuelltext = null; setzeEditor(); } break; case QUELLTEXT: pQuelltext.speicherQuelltexte(); initKomponenten(); pCompiler = null; setzeCompiler(); break; case COMPILER: pCompiler.speichern(); setVisible(false); break; } } private void zurueck() { switch (zustand) { case VERWALTUNG: setVisible(false); break; case QUELLTEXT: initKomponenten(); setzeVerwaltung(); break; case COMPILER: initKomponenten(); setzeEditor(); break; } } } filius-2.9.4+ds/src/main/java/filius/gui/quelltextsicht/PanelCompiler.java000066400000000000000000000160211506574241500266260ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.quelltextsicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.Arrays; import java.util.HashMap; import java.util.ListIterator; import javax.swing.JEditorPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.border.EtchedBorder; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; public class PanelCompiler extends JPanel implements I18n, Runnable { private static Logger LOG = LoggerFactory.getLogger(PanelCompiler.class); private static final long serialVersionUID = 1L; private String[] quelltextDateien; private String anwendungsKlasse; private String anwendungsName; private JEditorPane ausgabe; private JProgressBar progressBar; private boolean fehlerfreiKompiliert = false; public PanelCompiler(String[] quelltextDateien, String anwendungsName, String anwendungsKlasse) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + ", constr: PanelCompiler(" + quelltextDateien + "," + anwendungsName + "," + anwendungsKlasse + ")"); this.quelltextDateien = quelltextDateien; this.anwendungsKlasse = anwendungsKlasse; this.anwendungsName = anwendungsName; setPreferredSize(new Dimension(700, 480)); setMaximumSize(new Dimension(700, 480)); initKomponenten(); } private void initKomponenten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", initKomponenten()"); JScrollPane scrollPane; this.setBorder(new EtchedBorder()); ausgabe = new JEditorPane(); scrollPane = new JScrollPane(ausgabe); scrollPane.setPreferredSize(new Dimension(600, 400)); add(scrollPane, BorderLayout.CENTER); progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setIndeterminate(true); progressBar.setStringPainted(true); progressBar.setPreferredSize(new Dimension(600, 20)); add(progressBar, BorderLayout.SOUTH); } public void run() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", run()"); progressBar.setString(messages.getString("panelcompiler_msg1") + " " + anwendungsName); if (!kompilieren(quelltextDateien)) { ausgabe.setContentType("text/html"); ausgabe.setText(messages.getString("panelcompiler_msg2")); } else if (fehlerfreiKompiliert) { ausgabe.setContentType("text/html"); ausgabe.setText(messages.getString("panelcompiler_msg3")); } remove(progressBar); updateUI(); } public void speichern() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", speichern()"); boolean vorhanden = false; FileWriter fw = null; ListIterator it; HashMap map; if (fehlerfreiKompiliert) { it = Information.getInformation().ladeProgrammListe().listIterator(); while (it.hasNext() && !vorhanden) { map = (HashMap) it.next(); if (map.get("Klasse").equals("filius.software.clientserver." + anwendungsKlasse)) { vorhanden = true; } } if (!vorhanden) { try { fw = new FileWriter(Information.getInformation().getAnwendungenPfad() + "EigeneAnwendungen.txt", true); fw.write("\n" + anwendungsName + ";filius.software.clientserver." + anwendungsKlasse + ";filius.gui.anwendungssicht.GUIApplication" + anwendungsKlasse + "Window;gfx/desktop/icon_clientbaustein.png"); } catch (IOException e) { LOG.debug("EXCEPTION (" + this.hashCode() + "): Konnte Datei nicht erstellen"); ausgabe.setContentType("text/html"); ausgabe.setText(ausgabe.getText() + messages.getString("panelcompiler_msg4")); } finally { if (fw != null) try { fw.close(); } catch (IOException e) {} } } } else { } } private boolean kompilieren(String[] quelltextDateien) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", kompilieren()"); StringWriter strWriter = new StringWriter(); StandardJavaFileManager fileManager; JavaCompiler jc; Iterable kompilierEinheiten; File[] dateien; jc = ToolProvider.getSystemJavaCompiler(); if (jc == null) { LOG.error("Kein Java-Compiler erzeugt!"); fehlerfreiKompiliert = false; return false; } else { fileManager = jc.getStandardFileManager(null, null, null); dateien = new File[quelltextDateien.length]; for (int i = 0; i < dateien.length; i++) { dateien[i] = new File(quelltextDateien[i]); } kompilierEinheiten = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(dateien)); fehlerfreiKompiliert = jc.getTask(strWriter, fileManager, null, null, null, kompilierEinheiten).call(); if (!fehlerfreiKompiliert) { ausgabe.setContentType("text/plain"); ausgabe.setText(strWriter.toString()); } return true; } } } filius-2.9.4+ds/src/main/java/filius/gui/quelltextsicht/PanelQuelltext.java000066400000000000000000000105331506574241500270450ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.quelltextsicht; import java.awt.Font; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Enumeration; import java.util.Hashtable; import javax.swing.JEditorPane; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PanelQuelltext extends JTabbedPane { private static Logger LOG = LoggerFactory.getLogger(PanelQuelltext.class); private static final long serialVersionUID = 1L; /** * Die Editoren fuer die Quelltextdateien. Als Schluessel wird der Dateipfad der Quelltextdatei und als Wert das * EditorPane verwendet. */ private Hashtable editorPanes; /** Konstruktor zur Initialisierung der GUI-Komponenten */ public PanelQuelltext() { super(); editorPanes = new Hashtable(); } /** * Zum Hinzufuegen eines neuen Editors zur Bearbeitung des Quelltextes. * * @param neueDatei * Dateipfad der Datei, in der der bearbeitete Quelltext gespeichert werden soll. Dieser Dateiname muss * den neuen Klassennamen enthalten (z. B. /home/foo/NeueKlasse.java) */ public void hinzuEditor(String klassenName, String neueDatei) { JScrollPane scrollPane; JEditorPane editorPane; editorPane = new JEditorPane(); editorPane.setEditable(true); editorPane.setEnabled(true); editorPane.setContentType("text/plain"); editorPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); editorPane.setText(ladeQuelltext(neueDatei)); scrollPane = new JScrollPane(editorPane); add(klassenName, scrollPane); editorPanes.put(neueDatei, editorPane); } private String ladeQuelltext(String datei) { String quellText = ""; RandomAccessFile quellDatei = null; try { quellDatei = new RandomAccessFile(datei, "r"); for (String line; (line = quellDatei.readLine()) != null;) { quellText += line + "\n"; } } catch (Exception e1) { LOG.debug("", e1); } finally { try { quellDatei.close(); } catch (IOException e) { LOG.debug("", e); } } return quellText; } /** * Methode zum Speichern aller bearbeiteten bzw. neu erstellten Quelltexte */ public void speicherQuelltexte() { Enumeration dateipfade, editors; JEditorPane pane; String pfad; FileWriter writer; dateipfade = editorPanes.keys(); editors = editorPanes.elements(); while (dateipfade.hasMoreElements() && editors.hasMoreElements()) { pfad = (String) dateipfade.nextElement(); pane = (JEditorPane) editors.nextElement(); try { writer = new FileWriter(pfad, false); writer.write(pane.getText()); writer.close(); } catch (IOException e2) { LOG.debug("", e2); } } } } filius-2.9.4+ds/src/main/java/filius/gui/quelltextsicht/PanelVerwaltung.java000066400000000000000000000473101506574241500272170ustar00rootroot00000000000000/* b ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.quelltextsicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.ListIterator; import java.util.Vector; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.border.EtchedBorder; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.ResourceUtil; public class PanelVerwaltung extends JPanel implements I18n { private static Logger LOG = LoggerFactory.getLogger(PanelVerwaltung.class); private static final long serialVersionUID = 1L; private ButtonGroup buttonGroupModus, buttonGroupAnwendungstyp; private JButton buttonLoeschen, buttonSpeichern; private JTable tabelle; private JTextField anwendungsName, klassenName; public PanelVerwaltung() { super(); setPreferredSize(new Dimension(700, 460)); setMaximumSize(new Dimension(700, 460)); initKomponenten(); } public String[] holeAnwendung() { String[] array; array = new String[2]; if (buttonGroupModus.getSelection().getActionCommand().equals("neu")) { array[0] = anwendungsName.getText().trim(); array[1] = klassenName.getText().trim(); if (!array[0].equals("") && !array[1].equals("")) { return array; } else { return null; } } else { if (tabelle.getSelectedRow() == -1) { return null; } else { array[0] = (String) tabelle.getValueAt(tabelle.getSelectedRow(), 0); array[1] = (String) tabelle.getValueAt(tabelle.getSelectedRow(), 1); } if (!array[0].equals("") && !array[1].equals("")) { array[1] = array[1].substring(array[1].lastIndexOf(".") + 1); return array; } else { return null; } } } public String[] initQuelltextDateien() { String quelltext = ""; String[] dateien = null; String klasse; File file; if (buttonGroupModus.getSelection().getActionCommand().equals("neu")) { klasse = klassenName.getText(); if (buttonGroupAnwendungstyp.getSelection().getActionCommand().equals("client")) { dateien = new String[2]; dateien[0] = Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + ".java"; quelltext = ladeQuelltext(ResourceUtil.getResourcePath("tmpl/quelltext_vorlagen/client.txt")); quelltext = quelltext.replaceAll("ClientBaustein", klasse); speicherQuelltext(quelltext, dateien[0]); dateien[1] = Information.getInformation().getAnwendungenPfad() + "filius/gui/anwendungssicht/GUIApplication" + klasse + "Window.java"; quelltext = ladeQuelltext(ResourceUtil.getResourcePath("tmpl/quelltext_vorlagen/client_gui.txt")); quelltext = quelltext.replaceAll("ClientBaustein", klasse); speicherQuelltext(quelltext, dateien[1]); } else { dateien = new String[3]; dateien[0] = Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + "Mitarbeiter.java"; quelltext = ladeQuelltext( ResourceUtil.getResourcePath("tmpl/quelltext_vorlagen/server_mitarbeiter.txt")); quelltext = quelltext.replaceAll("ServerBaustein", klasse); speicherQuelltext(quelltext, dateien[0]); dateien[1] = Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + ".java"; quelltext = ladeQuelltext(ResourceUtil.getResourcePath("tmpl/quelltext_vorlagen/server.txt")); quelltext = quelltext.replaceAll("ServerBaustein", klasse); speicherQuelltext(quelltext, dateien[1]); dateien[2] = Information.getInformation().getAnwendungenPfad() + "filius/gui/anwendungssicht/GUIApplication" + klasse + "Window.java"; quelltext = ladeQuelltext(ResourceUtil.getResourcePath("tmpl/quelltext_vorlagen/server_gui.txt")); quelltext = quelltext.replaceAll("ServerBaustein", klasse); speicherQuelltext(quelltext, dateien[2]); } } else if (tabelle.getSelectedRow() != -1) { klasse = (String) tabelle.getValueAt(tabelle.getSelectedRow(), 1); klasse = klasse.substring(klasse.lastIndexOf(".") + 1); file = new File(Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + "Mitarbeiter.java"); if (file.exists()) { dateien = new String[3]; dateien[0] = Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + "Mitarbeiter.java"; dateien[1] = Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + ".java"; dateien[2] = Information.getInformation().getAnwendungenPfad() + "filius/gui/anwendungssicht/GUIApplication" + klasse + "Window.java"; } else { dateien = new String[2]; dateien[0] = Information.getInformation().getAnwendungenPfad() + "filius/software/clientserver/" + klasse + ".java"; dateien[1] = Information.getInformation().getAnwendungenPfad() + "filius/gui/anwendungssicht/GUIApplication" + klasse + "Window.java"; } } return dateien; } private String ladeQuelltext(String datei) { String quellText = ""; RandomAccessFile quellDatei = null; try { quellDatei = new RandomAccessFile(datei, "r"); for (String line; (line = quellDatei.readLine()) != null;) { quellText += line + "\n"; } } catch (Exception e1) { LOG.debug("", e1); } finally { try { quellDatei.close(); } catch (Exception e) { LOG.debug("", e); } } return quellText; } /** * Methode zum Speichern aller bearbeiteten bzw. neu erstellten Quelltexte */ private void speicherQuelltext(String quelltext, String datei) { FileWriter writer; File file; String verzeichnis; verzeichnis = datei.substring(0, datei.lastIndexOf(System.getProperty("file.separator"))); file = new File(verzeichnis); file.mkdirs(); try { writer = new FileWriter(datei, false); writer.write(quelltext); writer.close(); } catch (IOException e2) { LOG.debug("", e2); } } private void initKomponenten() { JRadioButton radioButton; JLabel label; JScrollPane scrollPane; GridBagConstraints gbc; GridBagLayout gridBag; JPanel panel, tmp; JTextField text; this.setLayout(new BorderLayout()); panel = new JPanel(); panel.setPreferredSize(new Dimension(700, 160)); panel.setMaximumSize(new Dimension(700, 160)); panel.setMinimumSize(new Dimension(700, 160)); gridBag = new GridBagLayout(); panel.setLayout(gridBag); panel.setBorder(new EtchedBorder()); buttonGroupModus = new ButtonGroup(); buttonGroupAnwendungstyp = new ButtonGroup(); radioButton = new JRadioButton(); radioButton.setText(messages.getString("panelverwaltung_msg1")); radioButton.setActionCommand("neu"); radioButton.setSelected(true); radioButton.setAlignmentX(0f); buttonGroupModus.add(radioButton); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(radioButton, gbc); panel.add(radioButton); label = new JLabel(); label.setPreferredSize(new Dimension(10, 0)); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(label, gbc); panel.add(label); radioButton = new JRadioButton(); radioButton.setText(messages.getString("panelverwaltung_msg2")); radioButton.setActionCommand("client"); radioButton.setSelected(true); buttonGroupAnwendungstyp.add(radioButton); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(radioButton, gbc); panel.add(radioButton); radioButton = new JRadioButton(); radioButton.setText(messages.getString("panelverwaltung_msg3")); radioButton.setActionCommand("server"); radioButton.setSelected(false); buttonGroupAnwendungstyp.add(radioButton); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridwidth = 2; gbc.gridy = 1; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(radioButton, gbc); panel.add(radioButton); label = new JLabel(messages.getString("panelverwaltung_msg4")); label.setPreferredSize(new Dimension(150, 20)); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 2; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(label, gbc); panel.add(label); anwendungsName = new JTextField(); anwendungsName.setPreferredSize(new Dimension(200, 20)); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridwidth = 2; gbc.gridy = 2; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(anwendungsName, gbc); panel.add(anwendungsName); label = new JLabel(messages.getString("panelverwaltung_msg5")); label.setPreferredSize(new Dimension(150, 20)); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 3; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(label, gbc); panel.add(label); text = new JTextField("filius.software.clientserver."); text.setEditable(false); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 3; gbc.insets = new Insets(5, 5, 5, 0); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(text, gbc); panel.add(text); klassenName = new JTextField(); klassenName.setPreferredSize(new Dimension(200, 20)); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 3; gbc.insets = new Insets(5, 5, 0, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(klassenName, gbc); panel.add(klassenName); this.add(panel, BorderLayout.NORTH); label = new JLabel(); label.setPreferredSize(new Dimension(680, 0)); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 7; gbc.gridwidth = 5; gridBag.setConstraints(label, gbc); panel.add(label); panel = new JPanel(); panel.setPreferredSize(new Dimension(700, 300)); panel.setMaximumSize(new Dimension(700, 300)); panel.setMinimumSize(new Dimension(700, 300)); gridBag = new GridBagLayout(); panel.setLayout(gridBag); panel.setBorder(new EtchedBorder()); radioButton = new JRadioButton(); radioButton.setText(messages.getString("panelverwaltung_msg7")); radioButton.setActionCommand("modifizieren"); radioButton.setSelected(false); radioButton.setAlignmentX(0f); buttonGroupModus.add(radioButton); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridwidth = 2; gbc.gridy = 4; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(radioButton, gbc); panel.add(radioButton); label = new JLabel(); label.setPreferredSize(new Dimension(10, 0)); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(label, gbc); panel.add(label); initTabelle(); scrollPane = new JScrollPane(tabelle); scrollPane.setPreferredSize(new Dimension(620, 180)); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridwidth = 3; gbc.gridy = 5; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.NORTHWEST; gridBag.setConstraints(scrollPane, gbc); panel.add(scrollPane); tmp = new JPanel(); tmp.setLayout(new FlowLayout()); buttonLoeschen = new JButton(messages.getString("panelverwaltung_msg8")); buttonLoeschen.setPreferredSize(new Dimension(200, 30)); buttonLoeschen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if (tabelle.getSelectedRow() != -1) { ((DefaultTableModel) tabelle.getModel()).removeRow(tabelle.getSelectedRow()); tabelleSpeichern(); updateTabelle(); } } }); tmp.add(buttonLoeschen); buttonSpeichern = new JButton(messages.getString("panelverwaltung_msg9")); buttonSpeichern.setPreferredSize(new Dimension(200, 30)); buttonSpeichern.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { tabelleSpeichern(); updateTabelle(); } }); tmp.add(buttonSpeichern); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridwidth = 4; gbc.gridy = 6; gbc.insets = new Insets(5, 5, 5, 5); gbc.anchor = GridBagConstraints.CENTER; gridBag.setConstraints(tmp, gbc); panel.add(tmp); label = new JLabel(); label.setPreferredSize(new Dimension(680, 0)); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 7; gbc.gridwidth = 6; gridBag.setConstraints(label, gbc); panel.add(label); this.add(panel, BorderLayout.SOUTH); updateTabelle(); } private void tabelleSpeichern() { FileWriter fw = null; String[] zeile; DefaultTableModel dtm; String dateiname; dtm = (DefaultTableModel) tabelle.getModel(); dateiname = Information.getInformation().getAnwendungenPfad() + "EigeneAnwendungen.txt"; try { fw = new FileWriter(dateiname, false); for (int i = 0; i < dtm.getRowCount(); i++) { zeile = new String[4]; for (int j = 0; j < zeile.length; j++) { zeile[j] = (String) dtm.getValueAt(i, j); } fw.write(zeile[0] + ";" + zeile[1] + ";" + zeile[2] + ";" + zeile[3] + "\n"); } } catch (IOException e) { LOG.debug("", e); } finally { if (fw != null) try { fw.close(); } catch (IOException e) {} } } private void initTabelle() { DefaultTableModel model; TableColumnModel tcm; model = new DefaultTableModel(0, 4); tabelle = new AnwendungsTabelle(model); tabelle.setDragEnabled(false); tabelle.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tabelle.setIntercellSpacing(new Dimension(10, 10)); tabelle.setRowHeight(30); tabelle.setShowGrid(false); tabelle.setFillsViewportHeight(true); tabelle.setBackground(Color.WHITE); tabelle.setShowHorizontalLines(true); tcm = tabelle.getColumnModel(); tcm.getColumn(0).setHeaderValue(messages.getString("panelverwaltung_msg10")); tcm.getColumn(0).setPreferredWidth(100); tcm.getColumn(1).setHeaderValue(messages.getString("panelverwaltung_msg11")); tcm.getColumn(1).setPreferredWidth(100); tcm.getColumn(2).setHeaderValue(messages.getString("panelverwaltung_msg12")); tcm.getColumn(2).setPreferredWidth(100); tcm.getColumn(3).setHeaderValue(messages.getString("panelverwaltung_msg13")); tcm.getColumn(3).setPreferredWidth(100); } private void updateTabelle() { HashMap map; ListIterator it; Vector zeile; DefaultTableModel dtm; dtm = (DefaultTableModel) tabelle.getModel(); dtm.setRowCount(0); it = Information.getInformation().ladePersoenlicheProgrammListe().listIterator(); while (it.hasNext()) { map = (HashMap) it.next(); zeile = new Vector(); zeile.addElement((String) map.get("Anwendung")); zeile.addElement((String) map.get("Klasse")); zeile.addElement((String) map.get("GUI-Klasse")); zeile.addElement((String) map.get("gfxFile")); dtm.addRow(zeile); } tabelle.updateUI(); } } filius-2.9.4+ds/src/main/java/filius/gui/schichtensicht/000077500000000000000000000000001506574241500231525ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/gui/schichtensicht/GlobalLayerPath.java000066400000000000000000000276261506574241500270440ustar00rootroot00000000000000package filius.gui.schichtensicht; import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JPanel; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.netzzugangsschicht.Ethernet; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.SystemSoftware; import filius.software.vermittlungsschicht.ArpPaket; import filius.software.vermittlungsschicht.IpPaket; /** * This class is used to visualise the end-to-end path of of an IP packet layer 2 packet (in Filius: IP or ARP packet) * through the TCP/IP layers of all the nodes it went through. The idea was taken from the following website and with * permission of the creator: https://oinf.ch/interactive/tcp-ip-visualisierung/ * * I want to emphasize that this view does not correspond to reality. In packet-switching net- works like the internet a * single node in general cannnot possible track the end-to-end path of a message. This follows immediately from the * properties of the packet-switching network model. * * The purpose of this view is to help learners understand the principle of the layers of the TCP/IP model. * * @author Christoph Irniger */ public class GlobalLayerPath extends JPanel implements I18n { private static final Lauscher LAUSCHER = Lauscher.getLauscher(); private static final int MAX_TTL = 64; private final JFrame OWNER; private final String MAIN_INTERFACE_ID; private final Knoten MAIN_NODE; private final int MAIN_FRAME_NUMBER; private final EthernetFrame MAIN_FRAME; /** local path view of the selected node */ private final LayerPathDialog MAIN_DIALOG; private final LocalLayerPathPanel MAIN_LOCAL_PANEL; private int currentLocalPanelPathIndex = 0; private int currentLocalPanelParametersPathIndex = 0; /** if the active border of the next local panel should be at the end */ private boolean activeBorderOfNextPanelAtEnd; /** * Path of a packet represented as list of ethernet frames encapsulating this packet in chronological order of * creation. */ ArrayList framePath = new ArrayList(); /** * List of {@code LocalLayerPathPanel} objects corresponding to the nodes involved in the path of a packet in * chronological order (i.e. sender node comes first, then all the routers and at last the receiver node). * {@code LocalLayerPathPanel} objects will be created only when they are needed and then added to the list. */ ArrayList localPanelPath = new ArrayList(); /** * List of the parameters which are needed to create an object of {@code LocalLayerPathPanel}. This list will get * filled immediately after opening the global view. */ ArrayList localPanelParametersPath = new ArrayList(); /** * The parameters needed to create the main local panel. */ Object[] MAIN_LOCAL_PANEL_PARAMETERS; public GlobalLayerPath(JFrame owner, String interfaceId, SystemSoftware systemSoftware, int frameNumber) { this.OWNER = owner; MAIN_NODE = (InternetKnoten) systemSoftware.getKnoten(); MAIN_LOCAL_PANEL_PARAMETERS = new Object[] { interfaceId, systemSoftware, frameNumber }; MAIN_INTERFACE_ID = interfaceId; MAIN_FRAME_NUMBER = frameNumber; MAIN_FRAME = LAUSCHER.getFrame(MAIN_INTERFACE_ID, MAIN_FRAME_NUMBER); MAIN_DIALOG = new LayerPathDialog(owner, interfaceId, systemSoftware, frameNumber, this, true); MAIN_LOCAL_PANEL = MAIN_DIALOG.getLocalLayerPathPanel(); MAIN_LOCAL_PANEL.setAsMainNode(true); // overwrite title to "Globaler Weg durch die Schichten" MAIN_DIALOG.setTitle(messages.getString("schichten_globaler_weg_msg1")); fillFramePath(); localPanelPath.add(MAIN_LOCAL_PANEL); fillLocalPanelParametersPath(); // nächste Zeile wird überflüssig, da dort sowieso nur MAIN_LOCA_PANEL drin ist currentLocalPanelParametersPathIndex = localPanelParametersPath.indexOf(MAIN_LOCAL_PANEL_PARAMETERS); MAIN_DIALOG.setVisible(true); } public boolean isActiveBorderOfNextPanelAtEnd() { return activeBorderOfNextPanelAtEnd; } /** * Fills the frame path list with all the frames involved in delivering the IP packet. */ private void fillFramePath() { // if frame is an ARP packet or was received via ethernet broadcast (possible // with ARP and DHCP) if (MAIN_FRAME.getDaten() instanceof ArpPaket || (MAIN_FRAME.getZielMacAdresse().equals(Ethernet.ETHERNET_BROADCAST) && !MAIN_FRAME.getQuellMacAdresse().equals(MAIN_INTERFACE_ID))) { addToFramePath(MAIN_FRAME); } else if (MAIN_FRAME.getDaten() instanceof IpPaket) { for (int ttl = MAX_TTL; ttl > 0; ttl--) { EthernetFrame frame = LAUSCHER.getFrameWithSameContentAndSpecificTTL(MAIN_FRAME, ttl); if (frame != null) { addToFramePath(frame); } else { break; } } } } /** * Adds the frame to the frame path. * * @param frame * frame to be added to {@code framePath} */ private void addToFramePath(EthernetFrame frame) { // if frame is exactly the same (incl. TTL) as the main frame if (frame.sameContent(MAIN_FRAME)) { framePath.add(MAIN_FRAME); } else { framePath.add(frame); } } /** * Fills the local panel parameters array path list with all the parameter arrays for the local panels involved in * delivering the IP packet. */ private void fillLocalPanelParametersPath() { // frame was received via ethernet broadcast if (MAIN_FRAME.getZielMacAdresse().equals(Ethernet.ETHERNET_BROADCAST) && !MAIN_FRAME.getQuellMacAdresse().equals(MAIN_INTERFACE_ID)) { // add sender String interfaceId = MAIN_FRAME.getQuellMacAdresse(); SystemSoftware systemSoftware = LAUSCHER.getSystemSoftwareByID(interfaceId); int frameNumber = LAUSCHER.getFrameNumber(interfaceId, MAIN_FRAME); addToLocalPanelPath(interfaceId, systemSoftware, frameNumber); // add receiver interfaceId = MAIN_INTERFACE_ID; systemSoftware = LAUSCHER.getSystemSoftwareByID(interfaceId); frameNumber = LAUSCHER.getFrameNumber(interfaceId, MAIN_FRAME); addToLocalPanelPath(interfaceId, systemSoftware, frameNumber); } else { EthernetFrame firstFrame = framePath.get(0); // add original sender String interfaceId = firstFrame.getQuellMacAdresse(); SystemSoftware systemSoftware = LAUSCHER.getSystemSoftwareByID(interfaceId); int frameNumber = LAUSCHER.getFrameNumber(interfaceId, firstFrame); addToLocalPanelPath(interfaceId, systemSoftware, frameNumber); // add routers (corresponding to the destination MAC of the frame in the list, // starting with the first) and the final receiver for (EthernetFrame frame : framePath) { interfaceId = frame.getZielMacAdresse(); systemSoftware = LAUSCHER.getSystemSoftwareByID(interfaceId); frameNumber = LAUSCHER.getFrameNumber(interfaceId, frame); addToLocalPanelPath(interfaceId, systemSoftware, frameNumber); } } } /** * Adds the panel of the local view corresponding to the parameters to the local panels path. * * @param interfaceId * ID of the network interface * @param systemSoftware * system software running on the node * @param frameNumber * number of the frame in the data exchange window (starts with 1) */ private void addToLocalPanelPath(String interfaceId, SystemSoftware systemSoftware, int frameNumber) { // if the interface is on the main node and the TTL corresponds to the one of // the main frame (depending if the frame was sent or received), add the main // panel EthernetFrame frame = LAUSCHER.getFrame(interfaceId, frameNumber); if ((systemSoftware.getKnoten()).equals(MAIN_NODE)) { if (!MAIN_LOCAL_PANEL.isForwarded()) { localPanelParametersPath.add(MAIN_LOCAL_PANEL_PARAMETERS); return; } else { if (MAIN_FRAME.getDaten() instanceof IpPaket) { if ((MAIN_LOCAL_PANEL.isSent() && ((IpPaket) MAIN_FRAME.getDaten()).getTtl() == ((IpPaket) frame.getDaten()).getTtl() - 1) || (!MAIN_LOCAL_PANEL.isSent() && ((IpPaket) MAIN_FRAME.getDaten()) .getTtl() == ((IpPaket) frame.getDaten()).getTtl())) { localPanelParametersPath.add(MAIN_LOCAL_PANEL_PARAMETERS); return; } } } } // If the main panel is not (yet) contained in the list, the active border of // the next panel should be at the end (the latest point in time), since if it // shows up, it happened through the "back" button. localPanelParametersPath.add(new Object[] { interfaceId, systemSoftware, frameNumber }); } /** * Switches to the view in the main dialog window to the next or previous local panel of the local panel path list. * * @param switchForward * true, if the view should switch forward to the next local panel, false if it should switch back */ public void switchPanel(Boolean switchForward) { LocalLayerPathPanel oldPanel = localPanelPath.get(currentLocalPanelPathIndex); LocalLayerPathPanel newPanel; oldPanel.setVisible(false); MAIN_DIALOG.remove(oldPanel); if (switchForward) { currentLocalPanelPathIndex++; currentLocalPanelParametersPathIndex++; // if the old panel ist not the last element if (currentLocalPanelPathIndex - 1 < localPanelPath.size() - 1) { newPanel = localPanelPath.get(currentLocalPanelPathIndex); } else { Object[] parameters = localPanelParametersPath.get(currentLocalPanelParametersPathIndex); String interfaceId = (String) parameters[0]; SystemSoftware systemSoftware = (SystemSoftware) parameters[1]; int frameNumber = (int) parameters[2]; activeBorderOfNextPanelAtEnd = false; newPanel = new LocalLayerPathPanel(OWNER, interfaceId, systemSoftware, frameNumber, this, false); localPanelPath.add(newPanel); } } else { currentLocalPanelParametersPathIndex--; // if the old panel ist not the first element if (currentLocalPanelPathIndex > 0) { currentLocalPanelPathIndex--; newPanel = localPanelPath.get(currentLocalPanelPathIndex); MAIN_DIALOG.add(newPanel); } else { Object[] parameters = localPanelParametersPath.get(currentLocalPanelParametersPathIndex); String interfaceId = (String) parameters[0]; SystemSoftware systemSoftware = (SystemSoftware) parameters[1]; int frameNumber = (int) parameters[2]; activeBorderOfNextPanelAtEnd = true; newPanel = new LocalLayerPathPanel(OWNER, interfaceId, systemSoftware, frameNumber, this, false); localPanelPath.add(0, newPanel); } } newPanel.setDividerLocation(oldPanel.getDividerLocation()); MAIN_DIALOG.add(newPanel); newPanel.setVisible(true); } }filius-2.9.4+ds/src/main/java/filius/gui/schichtensicht/LayerPathDialog.java000066400000000000000000000041611506574241500270300ustar00rootroot00000000000000package filius.gui.schichtensicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Toolkit; import javax.swing.JDialog; import javax.swing.JFrame; import filius.rahmenprogramm.I18n; import filius.software.system.SystemSoftware; /** * This class is used as a top container for the local or global layer path view. * * @author Christoph Irniger */ public class LayerPathDialog extends JDialog implements I18n { /** the corresponding JPanel which is contained in this JDialog */ private final LocalLayerPathPanel LOCAL_LAYER_PATH_PANEL; public LayerPathDialog(JFrame owner, String interfaceId, SystemSoftware systemSoftware, int selectedFrameNumber, GlobalLayerPath globalLayerPath, boolean isMainNode) { super(owner, true); // user can resize the JDialog setResizable(true); // initialise BorderLayout BorderLayout borderLayout = new BorderLayout(10, 10); setLayout(borderLayout); LOCAL_LAYER_PATH_PANEL = new LocalLayerPathPanel(owner, interfaceId, systemSoftware, selectedFrameNumber, globalLayerPath, isMainNode); // set size and positioning properties of outer JDialog Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation(screenSize.width / 6, screenSize.height / 6); int dialogWidth = LOCAL_LAYER_PATH_PANEL.getSizeOfMessageDetailsTable().width; int dialogHeight = LOCAL_LAYER_PATH_PANEL.getSizeOfMessageDetailsTable().height + LOCAL_LAYER_PATH_PANEL.getDefaultDividerLocation() + 100; setPreferredSize(new Dimension(dialogWidth, dialogHeight)); setMinimumSize(new Dimension(dialogWidth, dialogHeight)); // Panel is in the center of the BorderLayout add(LOCAL_LAYER_PATH_PANEL, BorderLayout.CENTER); // When in global view this Dialog will be set visible inside the // GlobalLayerPath class if (globalLayerPath == null) { setVisible(true); } } protected LocalLayerPathPanel getLocalLayerPathPanel() { return this.LOCAL_LAYER_PATH_PANEL; } } filius-2.9.4+ds/src/main/java/filius/gui/schichtensicht/LocalLayerPathPanel.java000066400000000000000000000472641506574241500276560ustar00rootroot00000000000000package filius.gui.schichtensicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.util.HashSet; import java.util.NoSuchElementException; import java.util.Set; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSplitPane; import filius.gui.nachrichtensicht.MessageDetailsTable; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.Host; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.hardware.knoten.Vermittlungsrechner; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.system.SystemSoftware; /** * This class is used to visualise the path of a layer 2 packet (in Filius: IP or ARP packet) through the TCP/IP layers * on the selected node (host or router). The idea was taken from the following website and with permission of the * creator: https://oinf.ch/interactive/tcp-ip-visualisierung/ * * @author Christoph Irniger */ public class LocalLayerPathPanel extends JPanel { private final Knoten NODE; private final String INTERFACE_ID; /** frame number which was selected by mouse click, starts with 1 */ private final int FRAME_NUMBER; /** * Corresponding global layer path object, if it exists. If null, we are in the local layer path view. */ private final GlobalLayerPath globalLayerPath; /** if the node sent the frame, otherwise the node received the frame */ private final boolean SENT; /** if the node forwarded an IP or ICMP packet */ private final boolean FORWARDED; private final JSplitPane SPLIT_PANE; private static final int DEFAULT_DIVIDER_LOCATION = 475; // graphical message details tables private MessageDetailsTable tableSent; private MessageDetailsTable tableReceived; // info about the corresponding frame private final String CORR_INTERFACE_ID; private final Integer CORR_FRAME_NUMBER; /** * maximum layer inside the frame (1 = link, 2 = internet, 3 = transport, 4 = application) */ private final int MAX_LAYER_INSIDE_FRAME; /** * maximum layer on which the node operates (1 = link, 2 = internet, 3 = transport, 4 = application) */ private final int MAX_LAYER_OF_OPERATION; /** * current number of hidden layers (0 = none, 1 = Link, 2 = Link & Internet, 3 = Link & Internet & Transport) */ private int hiddenLayers; private static final Color BACKGROUND_COLOR = Color.WHITE; /** * determines if this node should be highlighted as the main node (default is false) */ private boolean isMainNode; public LocalLayerPathPanel(JFrame owner, String interfaceId, SystemSoftware systemSoftware, int selectedFrameNumber, GlobalLayerPath globalLayerPath, boolean isMainNode) { super(); // inherit instance variables from super container (localPathDialog) NODE = systemSoftware.getKnoten(); this.INTERFACE_ID = interfaceId; this.FRAME_NUMBER = selectedFrameNumber; this.globalLayerPath = globalLayerPath; this.isMainNode = isMainNode; MAX_LAYER_OF_OPERATION = ((InternetKnoten) getKnoten()).getMaxLayerOfOperation(); // determine if frame was sent or received SENT = isFrameSent(interfaceId, selectedFrameNumber); // Depending on wheter the message was sent or received, create the primary // message details table (first visible) createPrimaryMessageDetailsTable(); // determine if the packet (inside the frame) was forwarded Object[] corrFrameData = Lauscher.getLauscher().getCorrespondingFrame(this.INTERFACE_ID, this.FRAME_NUMBER, (InternetKnoten) this.NODE); CORR_INTERFACE_ID = (String) corrFrameData[0]; CORR_FRAME_NUMBER = (Integer) corrFrameData[1]; FORWARDED = ((NODE instanceof Vermittlungsrechner || NODE instanceof Gateway) && (CORR_INTERFACE_ID != null && CORR_FRAME_NUMBER != null)); // If the message was forwarded, create the secondary message details table // (only visible if switched to other interface by using the forth or back // button) createSecondaryMessageDetailsTable(); // initialise tables for message details if (SENT) { MAX_LAYER_INSIDE_FRAME = tableSent.getMaxLayerInsideFrame(); tableSent.setLayersInvisible(); if (FORWARDED) { tableReceived.setLayersInvisible(); } } else { MAX_LAYER_INSIDE_FRAME = tableReceived.getMaxLayerInsideFrame(); tableReceived.setLayersInvisible(); } // SplitPane (for visualisation in upper and message details in lower part) SPLIT_PANE = new JSplitPane(); SPLIT_PANE.setOrientation(JSplitPane.VERTICAL_SPLIT); SPLIT_PANE.setDividerLocation(DEFAULT_DIVIDER_LOCATION); // prevent the SplitPane to get user input (mainly to lock the divider) SPLIT_PANE.setEnabled(false); setFirstMessageDetails(); // layout manager for this panel setLayout(new BorderLayout(10, 10)); // add splitPane add(SPLIT_PANE, BorderLayout.CENTER); // set visualisation on top half of the SplitPane SPLIT_PANE.setTopComponent(new LocalLayerPathVisualisation(this, globalLayerPath)); } public Knoten getKnoten() { return NODE; } public String getInterfaceId() { return INTERFACE_ID; } public int getMaxLayerInsideFrame() { return MAX_LAYER_INSIDE_FRAME; } public int getMaxLayerOfOperation() { return MAX_LAYER_OF_OPERATION; } public String getCorrInterfaceId() { return CORR_INTERFACE_ID; } public boolean isSent() { return SENT; } public boolean isForwarded() { return FORWARDED; } public int getDefaultDividerLocation() { return DEFAULT_DIVIDER_LOCATION; } public void setDividerLocation(int dividerLocation) { SPLIT_PANE.setDividerLocation(dividerLocation); } public int getDividerLocation() { return SPLIT_PANE.getDividerLocation(); } public static Color getBackgroundColor() { return BACKGROUND_COLOR; } public boolean isMainNode() { return isMainNode; } public void setAsMainNode(boolean isMainNode) { this.isMainNode = isMainNode; } public Dimension getSizeOfMessageDetailsTable() { return MessageDetailsTable.getSizeOfMessageDetailsTable(); } /** * Checks if the selected frame was sent or received (= not sent). * * @return true if sent, false if received */ public static boolean isFrameSent(String interfaceId, int selectedFrameNumber) { return interfaceId .equals(Lauscher.getLauscher().getFrame(interfaceId, selectedFrameNumber).getQuellMacAdresse()); } /** * Creates the primary message details table which is seen right after opening the local view. * * @return maximum layer that appears inside the frame */ private void createPrimaryMessageDetailsTable() { if (SENT) { tableSent = new MessageDetailsTable(INTERFACE_ID, FRAME_NUMBER, BACKGROUND_COLOR); } else { tableReceived = new MessageDetailsTable(INTERFACE_ID, FRAME_NUMBER, BACKGROUND_COLOR); } } /** * Only has an effect if the node is a router (or home router). If {@code forwarded} is true, creates the secondary * message details table for the other involved interface on the node. */ private void createSecondaryMessageDetailsTable() { if (FORWARDED) { if (SENT) { tableReceived = new MessageDetailsTable(CORR_INTERFACE_ID, CORR_FRAME_NUMBER, BACKGROUND_COLOR); } else { tableSent = new MessageDetailsTable(CORR_INTERFACE_ID, CORR_FRAME_NUMBER, BACKGROUND_COLOR); } } } /** * Determines which message details ({@code messageDetailsSent} or {@code messageDetailsReceived}) are shown first * directly after opening the window of the local path for the first time and hides and greys out layers depending * on the parameters. */ private void setFirstMessageDetails() { // for the sender of a message if (SENT) { SPLIT_PANE.setBottomComponent(tableSent); if (NODE instanceof Host) { // if in local view or if in global view and active border does not start at end // hide all layers except the maximum layer inside the frame (else show all // layers) if (globalLayerPath == null || !globalLayerPath.isActiveBorderOfNextPanelAtEnd()) { for (int i = 1; i < MAX_LAYER_INSIDE_FRAME; i++) { hideLayerData(i, tableSent); } hiddenLayers = MAX_LAYER_INSIDE_FRAME - 1; } else { hiddenLayers = 0; } } else if (NODE instanceof Vermittlungsrechner || NODE instanceof Gateway) { hideLayerData(1, tableSent); hiddenLayers = 1; if (FORWARDED) { hideLayerData(1, tableReceived); } } } // for the receiver of a message else { if (globalLayerPath != null && globalLayerPath.isActiveBorderOfNextPanelAtEnd()) { SPLIT_PANE.setBottomComponent(tableSent); } else { SPLIT_PANE.setBottomComponent(tableReceived); } } // update greyed out cells (cells invisible to the layer) if (SENT) { updateGreyedOutCells(tableSent); // if the IP packet was forwarded, also update the other table, since it could // be needed if (FORWARDED) { updateGreyedOutCells(tableReceived); } } else { updateGreyedOutCells(tableReceived); if (FORWARDED) { updateGreyedOutCells(tableSent); } } } /** * Decides if the message details for the sent or the received message should be displayed. * * @param senderSideIsActive * If true, the visualisation is on the sender side, otherwise on the receiver side. * @param senderSideWasActive * If true, the visualisation was on the sender side in the last step. */ public void updateMessageDetailsDisplay(boolean senderSideIsActive, boolean senderSideWasActive) { if (senderSideIsActive != senderSideWasActive) { if (senderSideIsActive) { // save old divider location int currentDividerLocation = SPLIT_PANE.getDividerLocation(); // set new table SPLIT_PANE.setBottomComponent(tableSent); // set old divider location SPLIT_PANE.setDividerLocation(currentDividerLocation); } else { // save old divider location int currentDividerLocation = SPLIT_PANE.getDividerLocation(); // set new table SPLIT_PANE.setBottomComponent(tableReceived); // set old divider location SPLIT_PANE.setDividerLocation(currentDividerLocation); } } } /** * Hide data of the next higher layer in the message details. Hideable layers are 1 = link, 2 = internet, 3 = * transport. */ public void hideNextHigherLayer() { if (hiddenLayers <= 2 && hiddenLayers >= 0) { hiddenLayers++; if (SENT) { updateGreyedOutCells(tableSent); hideLayerData(hiddenLayers, tableSent); if (FORWARDED) { updateGreyedOutCells(tableReceived); hideLayerData(hiddenLayers, tableReceived); } } else { updateGreyedOutCells(tableReceived); hideLayerData(hiddenLayers, tableReceived); if (FORWARDED) { updateGreyedOutCells(tableSent); hideLayerData(hiddenLayers, tableSent); } } } } /** * Show data of the next lower layer in the message details. Hideable layers are 1 = link, 2 = internet, 3 = * transport. */ public void showNextLowerLayer() { if (hiddenLayers <= 3 && hiddenLayers >= 1) { hiddenLayers--; if (SENT) { updateGreyedOutCells(tableSent); showLayerData(hiddenLayers + 1, tableSent); if (FORWARDED) { updateGreyedOutCells(tableReceived); showLayerData(hiddenLayers + 1, tableReceived); } } else { updateGreyedOutCells(tableReceived); showLayerData(hiddenLayers + 1, tableReceived); if (FORWARDED) { updateGreyedOutCells(tableSent); showLayerData(hiddenLayers + 1, tableSent); } } } } /** * Hide data of a layer in the message details. 1 = link, 2 = internet, 3 = transport. * * @param layerNumber * number for layer * @param table * table in which layer data should be hidden * @throws NoSuchElementException */ private void hideLayerData(int layerNumber, MessageDetailsTable table) throws NoSuchElementException { if (layerNumber > 3 || layerNumber < 1) { throw new NoSuchElementException("layerNumber has to be an integer from 1 to 3."); } else { // determine columns to hide Set columns = new HashSet<>(); int firstColumn = (layerNumber - 1) * 2; columns.add(firstColumn); columns.add(firstColumn + 1); // hide columns for (Component component : table.getComponents()) { if (columns.contains(table.getGridBagLayout().getConstraints(component).gridx) && table.getGridBagLayout().getConstraints(component).gridy > 1) { // set foreground and background to main background (such that the component // gets invisible) component.setBackground(table.getBackground()); component.setForeground(table.getBackground()); // remove border table.setBorderVisible(component, false); } } } } /** * Show data of a layer in the message details. 1 = link, 2 = internet, 3 = transport. * * @param layerNumber * number for layer * @param table * in which layer data should be shown * @throws NoSuchElementException */ private void showLayerData(int layerNumber, MessageDetailsTable table) throws NoSuchElementException { if (layerNumber > 3 || layerNumber < 1) { throw new NoSuchElementException("layerNumber has to be an integer from 1 to 3."); } else { // determine columns to show Set columns = new HashSet<>(); int firstColumn = (layerNumber - 1) * 2; columns.add(firstColumn); columns.add(firstColumn + 1); // show columns for (Component component : table.getComponents()) { if (columns.contains(table.getGridBagLayout().getConstraints(component).gridx)) { int gridy = table.getGridBagLayout().getConstraints(component).gridy; if (gridy > 1) { // set colors component.setForeground(MessageDetailsTable.getLayerColorVar("foregroundColor", layerNumber)); if (gridy == 0) { component.setBackground( MessageDetailsTable.getLayerColorVar("backgroundColorFine", layerNumber)); } else { component.setBackground( MessageDetailsTable.getLayerColorVar("backgroundColor", layerNumber)); // create border table.setBorderVisible(component, true); } } } } } } /** * Set all cells which represent content of the message (i.e. the ones with y coordinate greater than 1) greyed out * according to {@code hiddenlayers} and {@code MAX_LAYER_INSIDE_FRAME}. * * @param table * the table whose cells should be updated */ private void updateGreyedOutCells(MessageDetailsTable table) { // determine columns to be greyed out Set columns = new HashSet<>(); for (int columnNo = (hiddenLayers + 1) * 2; columnNo <= 6; columnNo++) { columns.add(columnNo); } // grey out those components for (Component component : table.getComponents()) { int xCoord = table.getGridBagLayout().getConstraints(component).gridx; int yCoord = table.getGridBagLayout().getConstraints(component).gridy; if (yCoord >= 2) { if (columns.contains(xCoord)) { // if content is in the internet layer if (xCoord >= 2 && xCoord <= 3) { setComponentGreyedOut(component, 2); } // if content is in the transport layer else if (xCoord >= 4 && xCoord <= 5) { setComponentGreyedOut(component, 3); } // if content is in the application layer else if (xCoord == 6) { setComponentGreyedOut(component, 4); } } // if it is not the last column else if (xCoord != 7) { // if content is in the internet layer if (xCoord >= 2 && xCoord <= 3) { resetComponentFromGreyedOut(component, 2); } // if content is in the transport layer else if (xCoord >= 4 && xCoord <= 5) { resetComponentFromGreyedOut(component, 3); } // if content is in the application layer else if (xCoord == 6) { resetComponentFromGreyedOut(component, 4); } } } } } /** * Set a components' foreground color (font color) to "greyed out" and the background color to the corresponding * background color in ultra fine variant. * * @param comp * @param layerNumber */ private void setComponentGreyedOut(Component component, int layerNumber) { component.setForeground(MessageDetailsTable.getLayerColorVar("foregroundColorGreyedOut", layerNumber)); component.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", layerNumber)); } /** * Reset a components' foreground color (font color) to black and the background color to the corresponding * background color. * * @param comp * @param layerNumber */ private void resetComponentFromGreyedOut(Component component, int layerNumber) { // only reset if not hidden if (hiddenLayers < layerNumber) { component.setForeground(Color.BLACK); component.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColor", layerNumber)); } } }filius-2.9.4+ds/src/main/java/filius/gui/schichtensicht/LocalLayerPathVisualisation.java000066400000000000000000001510421506574241500314370ustar00rootroot00000000000000package filius.gui.schichtensicht; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.EtchedBorder; import javax.swing.border.MatteBorder; import filius.gui.nachrichtensicht.MessageDetailsTable; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; /** * This class delivers the upper, visualised part of the local path through the layers of a message. * * @author Christoph Irniger */ public class LocalLayerPathVisualisation extends JPanel implements I18n { /** corresponding local layer path panel object */ private final LocalLayerPathPanel LAYER_PATH_PANEL; /** if null, we are in the local layer path view */ private final GlobalLayerPath globalLayerPath; private final GridBagLayout GB_LAYOUT = new GridBagLayout(); private GridBagConstraints c = new GridBagConstraints(); private int pointInTime; private int minPointInTime; private int maxPointInTime; /** true if the visualisation is on the sender side, otherwise receiver side */ private boolean senderSideIsActive; /** * true if the visualisation was on the sender side in the last step (if there is no last step, it should have the * same value as senderSideIsActive */ private boolean senderSideWasActive; /** * for this and all greater points in time visualisation is on the sender side (for smaller on receiver side) */ private int pointInTimeForSenderSide; /** * x coordinate of the left most element in the GridBag layout after the layer descrtiption and the colored gap */ private final static int X_OFFSET = 2; /** constant to be able to shift the whole visualisation down */ private final static int Y_OFFSET = 1; /** * vertical and horizontal gap between GridBag cells with content (must be divisible by 2) */ private static final int GAP_BETWEEN_CELLS = 12; /** * width of rectangle including border (must be divisible by 2 * RECTANGLE_RATIO) */ private static final int RECTANGLE_WIDTH = 200; /** ratio of width to height of a rectangle including border */ private static final int RECTANGLE_RATIO = 3; /** resulting height of a rectangle including border (must be divisible by 2) */ private static final int RECTANGLE_HEIGHT = RECTANGLE_WIDTH / RECTANGLE_RATIO; /** * instance variables for nested messages visualisation: border width of standard border around nested messages */ private static final int MAIN_BORDER_WIDTH = 1; /** left gap around a nested message */ private static final int INDENT_NESTED_MSGS = 20; /** upper, lower and right gap between neighbouring nested messages */ private static final int BORDER_NESTED_MSGS = 5; /** * side length of squares needed for visualisation of a frame on cable (layer 1) */ private static final int SQUARE_SIDE_LENGTH = RECTANGLE_HEIGHT; /** node name cell height */ private static final int NODE_NAME_CELL_HEIGHT = (int) (0.3 * RECTANGLE_HEIGHT); /** node info cell ratio of width to height */ private static final int NODE_INFO_CELL_HEIGHT = (int) Math.round(RECTANGLE_WIDTH / 1.935); /** color of active border on the outside */ private static final Color ACTIVE_BORDER_COLOR_OUTSIDE = Color.decode("#00E000"); /** color of active border on the inside */ private static final Color ACTIVE_BORDER_COLOR_INSIDE = Color.decode("#00CCC00"); /** color of border around nested messages */ private static final Color INACTIVE_BORDER_COLOR = Color.decode("#8C8C8C"); /** color of arrows */ private static final Color INACTIVE_ARROW_COLOR = Color.BLACK; /** color of arrows */ private static final Color ACTIVE_ARROW_COLOR = ACTIVE_BORDER_COLOR_INSIDE; /** * background color of main node name cell (only relevant if this is the main node) */ private static final Color MAIN_NODE_NAME_BG_COLOR = Color.decode("#AEECB7"); /** * backgroundcolor of main node info cell (only relevant if this is the main node) */ private static final Color MAIN_NODE_INFO_BG_COLOR = Color.decode("#ECFAEE"); /** * background color of non main node name cell (only relevant if this is the main node) */ private static final Color NODE_NAME_BG_COLOR = Color.decode("#D7D7D7"); /** * backgroundcolor of non main node info cell (only relevant if this is the main node) */ private static final Color NODE_INFO_BG_COLOR = Color.decode("#F7F7F7"); private final JButton FORTH_IN_TIME; private final JButton BACK_IN_TIME; public LocalLayerPathVisualisation(LocalLayerPathPanel layerPathPanel, GlobalLayerPath globalLayerPath) { super(); LAYER_PATH_PANEL = layerPathPanel; this.globalLayerPath = globalLayerPath; // set BorderLayout BorderLayout borderLayout = new BorderLayout(); setLayout(borderLayout); setBackground(LocalLayerPathPanel.getBackgroundColor()); // set GridBagLayout for this this.setLayout(GB_LAYOUT); this.setBackground(LocalLayerPathPanel.getBackgroundColor()); c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.NONE; // create buttons (create BufferedImages first, then resize them to an Image) BufferedImage arrowRightBI = null; BufferedImage arrowLeftBI = null; try { arrowRightBI = ImageIO.read(getClass().getResource("/gfx/allgemein/arrow_right.png")); arrowLeftBI = ImageIO.read(getClass().getResource("/gfx/allgemein/arrow_left.png")); } catch (IOException e) { e.printStackTrace(); } Image arrowRight = arrowRightBI.getScaledInstance(RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT, Image.SCALE_SMOOTH); Image arrowLeft = arrowLeftBI.getScaledInstance(RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT, Image.SCALE_SMOOTH); FORTH_IN_TIME = new JButton(new ImageIcon(arrowRight)); FORTH_IN_TIME.setFocusable(false); BACK_IN_TIME = new JButton(new ImageIcon(arrowLeft)); BACK_IN_TIME.setFocusable(false); FORTH_IN_TIME.setPreferredSize(new Dimension(RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT)); BACK_IN_TIME.setPreferredSize(new Dimension(RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT)); // start back and forth logic of buttons backAndForth(); // deactivate back button in local view if the frame was received if (globalLayerPath == null && (!LAYER_PATH_PANEL.isSent() || (!LAYER_PATH_PANEL.isForwarded() && LAYER_PATH_PANEL.isSent()))) { BACK_IN_TIME.setEnabled(false); } // set initial value for senderSideIsActive senderSideIsActive = LAYER_PATH_PANEL.isSent(); senderSideWasActive = senderSideIsActive; // ---------- configuration of points in time and layout ---------- // if the packet was not forwarded (i.e. sent or received) if (!LAYER_PATH_PANEL.isForwarded()) { // configure left and right side (indication of layers and back and forth // buttons) addSidePanel(BACK_IN_TIME, 0); // left side addSidePanel(FORTH_IN_TIME, 7); // right side // set min and max point in time and point in time for senders side if (LAYER_PATH_PANEL.isSent()) { minPointInTime = 4 - LAYER_PATH_PANEL.getMaxLayerInsideFrame(); maxPointInTime = 4; pointInTimeForSenderSide = -1; // should never be reached, since only sent } else { minPointInTime = 0; maxPointInTime = Math.min(LAYER_PATH_PANEL.getMaxLayerInsideFrame(), LAYER_PATH_PANEL.getMaxLayerOfOperation()); pointInTimeForSenderSide = 5; // should never be reached, since only received } // set point in time if (globalLayerPath == null) { pointInTime = minPointInTime; } else { pointInTime = globalLayerPath.isActiveBorderOfNextPanelAtEnd() ? maxPointInTime : minPointInTime; } visualisationSentOrReceived(); } // if the packet was forwarded (only for routers) else { // configure left and right side (indication of layers and back and forth // buttons) addSidePanel(BACK_IN_TIME, 0); // left side addSidePanel(FORTH_IN_TIME, X_OFFSET + 10); // right side // set min and max point in time minPointInTime = 0; maxPointInTime = 5; // set point in time if (globalLayerPath == null || !globalLayerPath.isActiveBorderOfNextPanelAtEnd()) { if (LAYER_PATH_PANEL.isSent()) { pointInTime = minPointInTime + 3; } else { pointInTime = minPointInTime; } } else { pointInTime = maxPointInTime; } // set point in time for senders side pointInTimeForSenderSide = minPointInTime + 3; visualisationForwarded(); } } public int getPointInTime() { return pointInTime; } public int getMaxPointInTime() { return maxPointInTime; } public int getMinPointInTime() { return minPointInTime; } /** * Visualisation of a node if the packet was sent or received. */ private void visualisationSentOrReceived() { c = new GridBagConstraints(); JLabel label; JEditorPane editorPane; // coordinates for the placement inside the GridBagLayout int xCoord0; int xCoord1; int xCoord2; int xCoord3; if (LAYER_PATH_PANEL.isSent()) { // from middle to right xCoord0 = X_OFFSET + 0; xCoord1 = X_OFFSET + 1; xCoord2 = X_OFFSET + 2; xCoord3 = X_OFFSET + 3; // colored layer indicators if (globalLayerPath == null) { coloredLayerIndicators(1, SQUARE_SIDE_LENGTH, true); } else { coloredLayerIndicators(1, 4 * SQUARE_SIDE_LENGTH + RECTANGLE_WIDTH + GAP_BETWEEN_CELLS, true); } coloredLayerIndicators(3, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(4, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(5, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(6, SQUARE_SIDE_LENGTH, true); } else { // from middle to left xCoord0 = X_OFFSET + 3; xCoord1 = X_OFFSET + 2; xCoord2 = X_OFFSET + 1; xCoord3 = X_OFFSET + 0; // colored layer indicators coloredLayerIndicators(X_OFFSET - 1, SQUARE_SIDE_LENGTH, true); coloredLayerIndicators(X_OFFSET, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 1, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 2, SQUARE_SIDE_LENGTH, false); if (globalLayerPath == null) { coloredLayerIndicators(X_OFFSET + 4, SQUARE_SIDE_LENGTH, true); } else { coloredLayerIndicators(X_OFFSET + 4, 4 * SQUARE_SIDE_LENGTH + RECTANGLE_WIDTH + GAP_BETWEEN_CELLS, true); } } // node name cell with empty cell above and below label = setEmptyCell(this, xCoord0, 0 + Y_OFFSET, 1, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); label = setLabel(this, LAYER_PATH_PANEL.getKnoten().holeAnzeigeName(), xCoord0, 1 + Y_OFFSET, 1, 1, true); setSize(label, RECTANGLE_WIDTH, NODE_NAME_CELL_HEIGHT); label.setHorizontalAlignment(SwingConstants.CENTER); if (LAYER_PATH_PANEL.isMainNode()) { label.setBackground(MAIN_NODE_NAME_BG_COLOR); } else { label.setBackground(NODE_NAME_BG_COLOR); } label = setEmptyCell(this, xCoord0, 2 + Y_OFFSET, 1, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); // node info cell with empty cell below String nodeInfo = createNodeInfoByMac(LAYER_PATH_PANEL.getInterfaceId()); editorPane = setEditorPane(this, nodeInfo, xCoord0, 3 + Y_OFFSET, 1, 1, true); setSize(editorPane, RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT); if (LAYER_PATH_PANEL.isMainNode()) { editorPane.setBackground(MAIN_NODE_INFO_BG_COLOR); } else { editorPane.setBackground(NODE_INFO_BG_COLOR); } label = setEmptyCell(this, xCoord0, 4 + Y_OFFSET, 1, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); // from here until the end of the method: create visualisations of rectangular // nested messages and the arrows in between createRectangularNestedMessages(xCoord0, null); // create first wire and if this visualisation is part of the global view before // the main panel, create active arrow here label = new WireWithArrow(null, true, SQUARE_SIDE_LENGTH, SQUARE_SIDE_LENGTH, globalLayerPath != null && globalLayerPath.isActiveBorderOfNextPanelAtEnd()); setFinishedLabel(this, label, xCoord1, 11 + Y_OFFSET, 1, 1); // create square nested messages and if packet was received or this // visualisation is part of the global view before the main panel, create active // border here label = new NestedMessagesVisualisation(1, SQUARE_SIDE_LENGTH, 1, !LAYER_PATH_PANEL.isSent() || (globalLayerPath != null && globalLayerPath.isActiveBorderOfNextPanelAtEnd())); setFinishedLabel(this, label, xCoord2, 11 + Y_OFFSET, 1, 1); // create second wire and if packet was received, create active arrow here label = new WireWithArrow(null, true, SQUARE_SIDE_LENGTH, SQUARE_SIDE_LENGTH, !LAYER_PATH_PANEL.isSent()); setFinishedLabel(this, label, xCoord3, 11 + Y_OFFSET, 1, 1); } /** * Visualisation of a router (Vermittlungsrechner oder Heimrouter) which forwarded the packet. Because of its * symmetric form, the visualisation is created from the left and right side at the same time towards the middle. */ private void visualisationForwarded() { c = new GridBagConstraints(); JLabel label; JEditorPane editorPane; // colored layer indicators coloredLayerIndicators(X_OFFSET - 1, SQUARE_SIDE_LENGTH, true); coloredLayerIndicators(X_OFFSET, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 1, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 2, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 6, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 7, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 8, SQUARE_SIDE_LENGTH, false); coloredLayerIndicators(X_OFFSET + 9, SQUARE_SIDE_LENGTH, true); // create first wire on the left and if packet was received and either not part // of the global view or part of the global view but not before the main panel, // create active arrow here label = new WireWithArrow(null, true, SQUARE_SIDE_LENGTH, SQUARE_SIDE_LENGTH, !LAYER_PATH_PANEL.isSent() && (globalLayerPath == null || !globalLayerPath.isActiveBorderOfNextPanelAtEnd())); setFinishedLabel(this, label, X_OFFSET + 0, 11 + Y_OFFSET, 1, 1); // create last (fourth) wire on the right label = new WireWithArrow(null, true, SQUARE_SIDE_LENGTH, SQUARE_SIDE_LENGTH, false); setFinishedLabel(this, label, X_OFFSET + 8, 11 + Y_OFFSET, 1, 1); // create left side square nested messages and if packet was received and either // not part of the global view or part of the global view but not before the // main panel, create active border here label = new NestedMessagesVisualisation(1, SQUARE_SIDE_LENGTH, 1, !LAYER_PATH_PANEL.isSent() && (globalLayerPath == null || !globalLayerPath.isActiveBorderOfNextPanelAtEnd())); setFinishedLabel(this, label, X_OFFSET + 1, 11 + Y_OFFSET, 1, 1); // create right side square nested messages and if this visualisation is part of // the global view before the main panel, create active border here label = new NestedMessagesVisualisation(1, SQUARE_SIDE_LENGTH, 1, (globalLayerPath != null && globalLayerPath.isActiveBorderOfNextPanelAtEnd())); setFinishedLabel(this, label, X_OFFSET + 7, 11 + Y_OFFSET, 1, 1); // create second wire on the left label = new WireWithArrow(null, true, SQUARE_SIDE_LENGTH, SQUARE_SIDE_LENGTH, false); setFinishedLabel(this, label, X_OFFSET + 2, 11 + Y_OFFSET, 1, 1); // create second last (third) wire on the right and if this visualisation is // part of the global view before the main panel, create active arrow here label = new WireWithArrow(null, true, SQUARE_SIDE_LENGTH, SQUARE_SIDE_LENGTH, globalLayerPath != null && globalLayerPath.isActiveBorderOfNextPanelAtEnd()); setFinishedLabel(this, label, X_OFFSET + 6, 11 + Y_OFFSET, 1, 1); // with GridBag width of 3: node name cell with empty cell above and below label = setEmptyCell(this, X_OFFSET + 3, 0 + Y_OFFSET, 3, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); label = setLabel(this, LAYER_PATH_PANEL.getKnoten().holeAnzeigeName(), X_OFFSET + 3, 1 + Y_OFFSET, 3, 1, true); setSize(label, 2 * RECTANGLE_WIDTH + GAP_BETWEEN_CELLS, NODE_NAME_CELL_HEIGHT); label.setHorizontalAlignment(SwingConstants.CENTER); if (LAYER_PATH_PANEL.isMainNode()) { label.setBackground(MAIN_NODE_NAME_BG_COLOR); } else { label.setBackground(NODE_NAME_BG_COLOR); } label = setEmptyCell(this, X_OFFSET + 3, 2 + Y_OFFSET, 3, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); // left and right node info cell with empty cell below String nodeInfoLeft; String nodeInfoRight; if (LAYER_PATH_PANEL.isSent()) { nodeInfoLeft = createNodeInfoByMac(LAYER_PATH_PANEL.getCorrInterfaceId()); nodeInfoRight = createNodeInfoByMac(LAYER_PATH_PANEL.getInterfaceId()); } else { nodeInfoLeft = createNodeInfoByMac(LAYER_PATH_PANEL.getInterfaceId()); nodeInfoRight = createNodeInfoByMac(LAYER_PATH_PANEL.getCorrInterfaceId()); } editorPane = setEditorPane(this, nodeInfoLeft, X_OFFSET + 3, 3 + Y_OFFSET, 1, 1, true); setSize(editorPane, RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT); label = setEmptyCell(this, X_OFFSET + 3, 4 + Y_OFFSET, 1, 1); if (LAYER_PATH_PANEL.isMainNode()) { editorPane.setBackground(MAIN_NODE_INFO_BG_COLOR); } else { editorPane.setBackground(NODE_INFO_BG_COLOR); } setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); editorPane = setEditorPane(this, nodeInfoRight, X_OFFSET + 5, 3 + Y_OFFSET, 1, 1, true); setSize(editorPane, RECTANGLE_WIDTH, NODE_INFO_CELL_HEIGHT); if (LAYER_PATH_PANEL.isMainNode()) { editorPane.setBackground(MAIN_NODE_INFO_BG_COLOR); } else { editorPane.setBackground(NODE_INFO_BG_COLOR); } label = setEmptyCell(this, X_OFFSET + 5, 4 + Y_OFFSET, 1, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); // from here until the end of the method: create visualisations of rectangular // nested messages and the arrows in between createRectangularNestedMessages(X_OFFSET + 3, false); createRectangularNestedMessages(X_OFFSET + 5, true); // set right arrow in the middle (on layer 2) and if the packet was sent and // this visualisation is part of the global view before the main panel, create // active arrow here label = new Arrow(null, true, GAP_BETWEEN_CELLS, RECTANGLE_HEIGHT, LAYER_PATH_PANEL.isSent() && (globalLayerPath == null || !globalLayerPath.isActiveBorderOfNextPanelAtEnd())); setFinishedLabel(this, label, X_OFFSET + 4, 9 + Y_OFFSET, 1, 1); // The right arrow is located at y coordinate 9 (see above). Thus skip i = 9. label = setLabel(this, "", X_OFFSET + 4, 5 + Y_OFFSET, 1, 1, false); setSize(label, GAP_BETWEEN_CELLS, RECTANGLE_HEIGHT); label.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", 4)); label = setLabel(this, "", X_OFFSET + 4, 7 + Y_OFFSET, 1, 1, false); setSize(label, GAP_BETWEEN_CELLS, RECTANGLE_HEIGHT); label.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", 3)); label = setLabel(this, "", X_OFFSET + 4, 11 + Y_OFFSET, 1, 1, false); setSize(label, GAP_BETWEEN_CELLS, RECTANGLE_HEIGHT); label.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", 1)); } /** * This method colors the empty space between the (nested) messages and the layer indicators on the right and left * in the corresponding backround color (the ultra fine alternative). * * @param xCoord * x coordinate inside the GridBag layout * @param width * width of the labels * @param includingLayer1 * indicates if layer 1 should be drawn (true) or not (false) */ private void coloredLayerIndicators(int xCoord, int width, boolean includingLayer1) { c = new GridBagConstraints(); JLabel label; // layers 2 to 4 for (int layerNumber = 4; layerNumber >= 2; layerNumber--) { label = setLabel(this, "", xCoord + 0, 5 + 2 * (4 - layerNumber) + Y_OFFSET, 1, 1, false); setSize(label, width, RECTANGLE_HEIGHT); label.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", layerNumber)); } // layer 1 if (includingLayer1) { label = setLabel(this, "", xCoord + 0, 11 + Y_OFFSET, 1, 1, false); setSize(label, width, RECTANGLE_HEIGHT); label.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", 1)); } } /** * This method creates a string with all the node info belonging to a MAC address (i.e. interface ID). In the * visualisation this string will be displayed inside an EditorPane. * * @param interfaceId * MAC address of interface * @return String with the resulting node info */ private String createNodeInfoByMac(String interfaceId) { InternetKnoten node = ((InternetKnoten) LAYER_PATH_PANEL.getKnoten()); NetzwerkInterface ni = node.getNetzwerkInterfaceByMac(interfaceId); String[] nodeInfos = new String[5]; nodeInfos[0] = "" + messages.getString("schichten_lokaler_weg_msg6") + " " + ni.getIp() + "
"; nodeInfos[1] = "" + messages.getString("schichten_lokaler_weg_msg7") + " " + ni.getSubnetzMaske() + "
"; nodeInfos[2] = "" + messages.getString("schichten_lokaler_weg_msg8") + " " + ni.getMac() + "
"; nodeInfos[3] = "" + messages.getString("schichten_lokaler_weg_msg9") + " " + ni.getGateway() + "
"; nodeInfos[4] = "" + messages.getString("schichten_lokaler_weg_msg10") + " " + ni.getDns(); String nodeInfo = ""; for (int i = 0; i < nodeInfos.length; i++) { nodeInfo = nodeInfo + nodeInfos[i]; } return nodeInfo; } /** * This method creates the visualisation of the rectangular nested messages. * * @param xCoord * x coordinate of the position in the GridBagLayout * @param isSenderSideWhenForwarded * Indicates if it is the "receiver" (false) or the "sender" (true) side of the visualisation. Has no * effect if the packet was just sent or received. */ private void createRectangularNestedMessages(int xCoord, Boolean isSenderSideWhenForwarded) { JLabel label; boolean activeBorder; boolean arrowPointsDown; int layerOfActiveBorder = Math.min(LAYER_PATH_PANEL.getMaxLayerInsideFrame(), LAYER_PATH_PANEL.getMaxLayerOfOperation()); for (int layerNumber = 4; layerNumber >= 1; layerNumber--) { // if the packet was originally sent or received (i.e. not forwarded) if (!LAYER_PATH_PANEL.isForwarded()) { if (LAYER_PATH_PANEL.isSent() && (globalLayerPath == null || globalLayerPath != null && !globalLayerPath.isActiveBorderOfNextPanelAtEnd())) { // if the packet was sent, the first message (depending on which layers are // involved) gets an active border activeBorder = (layerNumber == layerOfActiveBorder) ? true : false; } else { // active border will not be here (but around the square message for the // "received" visualisation, see creation in visualisationForwarded() activeBorder = false; } arrowPointsDown = LAYER_PATH_PANEL.isSent() ? true : false; } // if the packet was forwarded else { // "sender" side if (isSenderSideWhenForwarded) { // if the packet was sent, create an active border for the first message // (depending on which layers are involved) activeBorder = LAYER_PATH_PANEL.isSent() ? ((layerNumber == layerOfActiveBorder) ? true : false) : false; arrowPointsDown = true; } // "receiver" side else { activeBorder = false; arrowPointsDown = false; } } label = new NestedMessagesVisualisation(layerNumber, RECTANGLE_WIDTH, RECTANGLE_RATIO, activeBorder); setFinishedLabel(this, label, xCoord, 5 + 2 * (4 - layerNumber) + Y_OFFSET, 1, 1); // create vertical arrows below every nested messages rectangle int maxLayerOfOperation = ((InternetKnoten) LAYER_PATH_PANEL.getKnoten()).getMaxLayerOfOperation(); if (layerNumber <= Math.min(LAYER_PATH_PANEL.getMaxLayerInsideFrame(), maxLayerOfOperation) && layerNumber >= 2) { label = new Arrow(arrowPointsDown, null, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS, false); setFinishedLabel(this, label, xCoord, 6 + 2 * (4 - layerNumber) + Y_OFFSET, 1, 1); } } } /** * Right and left side of the visualisation which indicate the layers and showing the buttons "forth" and "back". * * @param backOrForthButton * "back" or "forth" button * @param xCoord * x coordinate inside the GridBag layout */ private void addSidePanel(JButton backOrForthButton, int xCoord) { JLabel label; setStandards(backOrForthButton, xCoord + 0, 3 + Y_OFFSET, 1, 1); this.add(backOrForthButton, c); String[] layers = { messages.getString("rp_lauscher_msg11"), messages.getString("rp_lauscher_msg10"), messages.getString("rp_lauscher_msg9"), messages.getString("rp_lauscher_msg8") }; for (int layerNumber = 4; layerNumber >= 1; layerNumber--) { label = setEmptyCell(this, xCoord + 0, 4 + 2 * (4 - layerNumber) + Y_OFFSET, 1, 1); setSize(label, RECTANGLE_WIDTH, GAP_BETWEEN_CELLS); label = setLabel(this, layers[4 - layerNumber], xCoord + 0, 5 + 2 * (4 - layerNumber) + Y_OFFSET, 1, 1, false); setSize(label, RECTANGLE_WIDTH, RECTANGLE_HEIGHT); label.setHorizontalAlignment(SwingConstants.CENTER); label.setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorFine", layerNumber)); label.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); } } /** * Helper method to create a new JLabel in the GridBagLayout. JLabels are needed for one line content. * * @param container * @param text * @param x_pos * @param y_pos * @param width * @param height * @param needsBorder * @return The JLabel */ private JLabel setLabel(JPanel container, String text, int x_pos, int y_pos, int width, int height, boolean needsBorder) { JLabel label = new JLabel(text); setStandards(label, x_pos, y_pos, width, height); label.setOpaque(true); if (needsBorder) { Border lineBorder = BorderFactory.createLineBorder(Color.BLACK); Border marginBorder = new EmptyBorder(5, 5, 5, 5); label.setBorder(new CompoundBorder(lineBorder, marginBorder)); } container.add(label, c); return label; } /** * Helper method to create a new JEditorPane in the GridBagLayout. JEditorPanes are needed for multiline content. * * @param container * @param text * @param x_pos * @param y_pos * @param width * @param height * @param needsBorder * @return The JEditorPane */ private JEditorPane setEditorPane(JPanel container, String text, int x_pos, int y_pos, int width, int height, boolean needsBorder) { JEditorPane editorPane = new JEditorPane("text/html", text); setStandards(editorPane, x_pos, y_pos, width, height); editorPane.setOpaque(true); editorPane.setEditable(false); editorPane.setFocusable(false); // set font like other labels or textAreas editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); editorPane.setFont(new Font("Dialog", Font.PLAIN, 12)); if (needsBorder) { Border lineBorder = BorderFactory.createLineBorder(Color.BLACK); Border marginBorder = new EmptyBorder(5, 5, 5, 5); editorPane.setBorder(new CompoundBorder(lineBorder, marginBorder)); } container.add(editorPane, c); return editorPane; } /** * Helper method to include a JLabel which is ready to use (and therefore does not have to be returned) into the * GridBagLayout. * * @param container * @param label * @param x_pos * @param y_pos * @param width * @param height */ private void setFinishedLabel(JPanel container, JLabel label, int x_pos, int y_pos, int width, int height) { setStandards(label, x_pos, y_pos, width, height); label.setOpaque(false); container.add(label, c); } /** * Helper method to create a visually empty cell, which is a JLabel, into the GridBagLayout. Empty cells form the * gaps between the rectangles for the nested messages. * * @param container * @param x_pos * @param y_pos * @param width * @param height * @return The JLabel */ private JLabel setEmptyCell(JPanel container, int x_pos, int y_pos, int width, int height) { JLabel label = new JLabel(); setStandards(label, x_pos, y_pos, width, height); label.setOpaque(false); label.setBorder(javax.swing.BorderFactory.createEmptyBorder()); container.add(label, c); return label; } /** * Helper method to set standard GridBagLayout constraints. * * @param component * @param x_pos * @param y_pos * @param width * @param height */ private void setStandards(Component component, int x_pos, int y_pos, int width, int height) { c.gridx = x_pos; c.gridy = y_pos; c.gridwidth = width; c.gridheight = height; } /** * Helper method to set the preferrend, minimum and maximum size of a component. * * @param component * @param width * @param height */ private void setSize(Component component, int width, int height) { Dimension dimensionOfComponent = new Dimension(width, height); component.setPreferredSize(dimensionOfComponent); component.setMinimumSize(dimensionOfComponent); component.setMaximumSize(dimensionOfComponent); } /** * Functionality of the "back" and "forth" buttons to go back and forth in time. The buttons can be triggered by * clicking or by pressing the left or right non-numpad arrow key. */ private void backAndForth() { FORTH_IN_TIME.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { forthButtonPressLogic(); } }); BACK_IN_TIME.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { backButtonPressLogic(); } }); } /** * What to do when the forth button is pressed. */ private void forthButtonPressLogic() { if (pointInTime < maxPointInTime) { pointInTime++; updateButtonsIfForth(); updateSenderSide(); LAYER_PATH_PANEL.updateMessageDetailsDisplay(senderSideIsActive, senderSideWasActive); hideOrShowLayersIfForth(); updateActiveBorder(true); } else if (globalLayerPath != null) { globalLayerPath.switchPanel(true); } } /** * What to do when the back button is pressed. */ private void backButtonPressLogic() { if (pointInTime > minPointInTime) { pointInTime--; updateButtonsIfBack(); updateSenderSide(); LAYER_PATH_PANEL.updateMessageDetailsDisplay(senderSideIsActive, senderSideWasActive); hideOrShowLayersIfBack(); updateActiveBorder(false); } else if (globalLayerPath != null) { globalLayerPath.switchPanel(false); } } /** * Updates "forth" and "back" buttons if button "forth" was pressed. */ private void updateButtonsIfForth() { BACK_IN_TIME.setEnabled(true); if (pointInTime == maxPointInTime) { if (globalLayerPath == null) { FORTH_IN_TIME.setEnabled(false); } else { if (!LAYER_PATH_PANEL.isSent() && !LAYER_PATH_PANEL.isForwarded()) { FORTH_IN_TIME.setEnabled(false); } } } } /** * Updates "forth" and "back" buttons if button "back" was pressed. */ private void updateButtonsIfBack() { FORTH_IN_TIME.setEnabled(true); if (pointInTime == minPointInTime) { if (globalLayerPath == null) { BACK_IN_TIME.setEnabled(false); } else { if (LAYER_PATH_PANEL.isSent() && !LAYER_PATH_PANEL.isForwarded()) { BACK_IN_TIME.setEnabled(false); } } } } /** * Hide or show layers in message details if button "forth" was pressed. */ private void hideOrShowLayersIfForth() { // if not forwarded (means hosts or routers which have sent or received the // packet) if (!LAYER_PATH_PANEL.isForwarded()) { if (LAYER_PATH_PANEL.isSent() && pointInTime < maxPointInTime) { LAYER_PATH_PANEL.showNextLowerLayer(); } else if (!LAYER_PATH_PANEL.isSent() && pointInTime > 1) { LAYER_PATH_PANEL.hideNextHigherLayer(); } // if forwarded (for routers) } else { switch (pointInTime) { case 2: LAYER_PATH_PANEL.hideNextHigherLayer(); break; case 4: LAYER_PATH_PANEL.showNextLowerLayer(); break; } } } /** * Hide or show layers in message details if button "back" was pressed. */ private void hideOrShowLayersIfBack() { // if not forwarded (means hosts or routers which have sent or received the // packet) if (!LAYER_PATH_PANEL.isForwarded()) { if (LAYER_PATH_PANEL.isSent() && pointInTime < maxPointInTime - 1) { LAYER_PATH_PANEL.hideNextHigherLayer(); } else if (!LAYER_PATH_PANEL.isSent() && pointInTime > 0) { LAYER_PATH_PANEL.showNextLowerLayer(); } } else { switch (pointInTime) { case 3: LAYER_PATH_PANEL.hideNextHigherLayer(); break; case 1: LAYER_PATH_PANEL.showNextLowerLayer(); break; } } } /** * Updates the variable {@code senderSideIsActive}. Only has an effect if the node is a router (of home router), * since they can forward IP packets. Thus they have a different frame (containing the packet) on the sender and on * the receiver side. */ private void updateSenderSide() { senderSideWasActive = senderSideIsActive; senderSideIsActive = pointInTime >= pointInTimeForSenderSide; } /** * Updates the active border according to {@code pointInTime} and resets the old one to black. * * @param isForth * true if the "forth" button was pressed, false if the "back" button was pressed */ private void updateActiveBorder(boolean isForth) { int lastPointInTime = isForth ? pointInTime - 1 : pointInTime + 1; // list with all the coordinates of the components with a possible active border List gridBagCoordsWithBorders; // all incoming arrows for the components of list gridBagCoordsWithBorders List gridBagCoordsWithIncomingArrows; if (!LAYER_PATH_PANEL.isForwarded()) { if (LAYER_PATH_PANEL.isSent()) { gridBagCoordsWithBorders = Collections .unmodifiableList(Arrays.asList(new int[] { X_OFFSET + 0, Y_OFFSET + 5 }, new int[] { X_OFFSET + 0, Y_OFFSET + 7 }, new int[] { X_OFFSET + 0, Y_OFFSET + 9 }, new int[] { X_OFFSET + 0, Y_OFFSET + 11 }, new int[] { X_OFFSET + 2, Y_OFFSET + 11 })); gridBagCoordsWithIncomingArrows = Collections.unmodifiableList(Arrays.asList(new int[] { -1, -1 }, new int[] { X_OFFSET + 0, Y_OFFSET + 6 }, new int[] { X_OFFSET + 0, Y_OFFSET + 8 }, new int[] { X_OFFSET + 0, Y_OFFSET + 10 }, new int[] { X_OFFSET + 1, Y_OFFSET + 11 })); } else { gridBagCoordsWithBorders = Collections .unmodifiableList(Arrays.asList(new int[] { X_OFFSET + 1, Y_OFFSET + 11 }, new int[] { X_OFFSET + 3, Y_OFFSET + 11 }, new int[] { X_OFFSET + 3, Y_OFFSET + 9 }, new int[] { X_OFFSET + 3, Y_OFFSET + 7 }, new int[] { X_OFFSET + 3, Y_OFFSET + 5 })); gridBagCoordsWithIncomingArrows = Collections .unmodifiableList(Arrays.asList(new int[] { X_OFFSET + 0, Y_OFFSET + 11 }, new int[] { X_OFFSET + 2, Y_OFFSET + 11 }, new int[] { X_OFFSET + 3, Y_OFFSET + 10 }, new int[] { X_OFFSET + 3, Y_OFFSET + 8 }, new int[] { X_OFFSET + 3, Y_OFFSET + 6 })); } } else { gridBagCoordsWithBorders = Collections.unmodifiableList( Arrays.asList(new int[] { X_OFFSET + 1, Y_OFFSET + 11 }, new int[] { X_OFFSET + 3, Y_OFFSET + 11 }, new int[] { X_OFFSET + 3, Y_OFFSET + 9 }, new int[] { X_OFFSET + 5, Y_OFFSET + 9 }, new int[] { X_OFFSET + 5, Y_OFFSET + 11 }, new int[] { X_OFFSET + 7, Y_OFFSET + 11 })); gridBagCoordsWithIncomingArrows = Collections.unmodifiableList( Arrays.asList(new int[] { X_OFFSET + 0, Y_OFFSET + 11 }, new int[] { X_OFFSET + 2, Y_OFFSET + 11 }, new int[] { X_OFFSET + 3, Y_OFFSET + 10 }, new int[] { X_OFFSET + 4, Y_OFFSET + 9 }, new int[] { X_OFFSET + 5, Y_OFFSET + 10 }, new int[] { X_OFFSET + 6, Y_OFFSET + 11 })); } // go through all components for (Component component : this.getComponents()) { int gridx = GB_LAYOUT.getConstraints(component).gridx; int gridy = GB_LAYOUT.getConstraints(component).gridy; // compare coordinates with list if (gridx == gridBagCoordsWithBorders.get(pointInTime)[0] && gridy == gridBagCoordsWithBorders.get(pointInTime)[1]) { // set current active border to active color ((NestedMessagesVisualisation) component).createAndSetBorder(true); } else if (gridx == gridBagCoordsWithBorders.get(lastPointInTime)[0] && gridy == gridBagCoordsWithBorders.get(lastPointInTime)[1]) { // set previous active border to standard color ((NestedMessagesVisualisation) component).createAndSetBorder(false); } else if (gridx == gridBagCoordsWithIncomingArrows.get(pointInTime)[0] && gridy == gridBagCoordsWithIncomingArrows.get(pointInTime)[1]) { // set current active arrow to active color ((Arrow) component).setColor(ACTIVE_ARROW_COLOR); ((Arrow) component).repaint(); } else if (gridx == gridBagCoordsWithIncomingArrows.get(lastPointInTime)[0] && gridy == gridBagCoordsWithIncomingArrows.get(lastPointInTime)[1]) { // set previous active arrow to standard color ((Arrow) component).setColor(INACTIVE_ARROW_COLOR); ((Arrow) component).repaint(); } } } /** * Class for a rectangular visualisation of nested messages. */ private class NestedMessagesVisualisation extends JLabel { private int layerNumber; private boolean isSquare; private int widthWithBorder; private int heightWithBorder; private int widthInsideBorder; private int heightInsideBorder; private NestedMessagesVisualisation(int layerNumber, int widthWithBorder, int ratio, boolean activeBorder) { this.layerNumber = layerNumber; this.isSquare = ratio == 1; this.widthWithBorder = widthWithBorder; this.heightWithBorder = widthWithBorder / ratio; this.widthInsideBorder = widthWithBorder - 2 * MAIN_BORDER_WIDTH; this.heightInsideBorder = heightWithBorder - 2 * MAIN_BORDER_WIDTH; // create border only if layer message is actually painted if (layerNumber <= LAYER_PATH_PANEL.getMaxLayerInsideFrame() && layerNumber <= LAYER_PATH_PANEL.getMaxLayerOfOperation()) { createAndSetBorder(activeBorder); } } @Override public Dimension getPreferredSize() { return new Dimension(widthWithBorder, heightWithBorder); } @Override public void paintComponent(Graphics g) { // Let UI Delegate paint first, which includes background filling since this // component is opaque. super.paintComponent(g); // origin with respect to border width int xPos = MAIN_BORDER_WIDTH; int yPos = MAIN_BORDER_WIDTH; ; int width = this.widthInsideBorder; int height = this.heightInsideBorder; // set basic layer rectangle FilledRectangle rect = new FilledRectangle(); rect.setX(xPos); rect.setY(yPos); // Only draw nested messages if a message of the layer is in the frame. // Otherwise the rectangle stays insivisble. if (layerNumber <= LAYER_PATH_PANEL.getMaxLayerInsideFrame() && layerNumber <= LAYER_PATH_PANEL.getMaxLayerOfOperation()) { rect.setFillColor(MessageDetailsTable.getLayerColorVar("backgroundColor", layerNumber)); rect.paintFilledRectangle(g, width, height); if (layerNumber < LAYER_PATH_PANEL.getMaxLayerInsideFrame()) { // set other layer rectangles for (int i = layerNumber + 1; i <= LAYER_PATH_PANEL.getMaxLayerInsideFrame(); i++) { if (!isSquare) { width = width - INDENT_NESTED_MSGS - BORDER_NESTED_MSGS; height = height - 2 * BORDER_NESTED_MSGS; xPos = xPos + INDENT_NESTED_MSGS; yPos = yPos + BORDER_NESTED_MSGS; } else { width = width - 2 * BORDER_NESTED_MSGS; height = height - 2 * BORDER_NESTED_MSGS; xPos = xPos + BORDER_NESTED_MSGS; yPos = yPos + BORDER_NESTED_MSGS; } rect.setX(xPos); rect.setY(yPos); rect.setFillColor(MessageDetailsTable.getLayerColorVar("backgroundColor", i)); rect.paintFilledRectangle(g, width, height); } } } else { setOpaque(true); setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", layerNumber)); } } private void createAndSetBorder(boolean activeBorder) { // draw the (main or active) border around the message Border border; if (activeBorder) { MatteBorder outside = BorderFactory.createMatteBorder(1, 1, 1, 1, ACTIVE_BORDER_COLOR_OUTSIDE); MatteBorder inside = BorderFactory.createMatteBorder(2, 2, 2, 2, ACTIVE_BORDER_COLOR_INSIDE); border = BorderFactory.createCompoundBorder(outside, inside); } else { border = BorderFactory.createMatteBorder(1, 1, 1, 1, INACTIVE_BORDER_COLOR); } setBorder(border); } } /** * Class for a filled rectangle for painting. */ private class FilledRectangle { private int xPos; private int yPos; private Color fillColor; public void setX(int xPos) { this.xPos = xPos; } public void setY(int yPos) { this.yPos = yPos; } public void setFillColor(Color fillColor) { this.fillColor = fillColor; } public void paintFilledRectangle(Graphics g, int width, int height) { g.setColor(fillColor); g.fillRect(xPos, yPos, width, height); } } /** * Class for the arrows between the visualisations of nested messages. */ protected class Arrow extends JLabel { protected int rectangleWidth; protected int rectangleHeight; private Boolean pointsDown; // if false points up, if null points left or right private Boolean pointsRight; // if false points left, if null points down or up private Color color; private Arrow(Boolean pointsDown, Boolean pointsRight, int rectangleWidth, int rectangleHeight, boolean activeArrow) { this.pointsDown = pointsDown; this.pointsRight = pointsRight; this.rectangleWidth = rectangleWidth; this.rectangleHeight = rectangleHeight; if (activeArrow) { this.color = ACTIVE_ARROW_COLOR; } else { this.color = INACTIVE_ARROW_COLOR; } } private void setColor(Color color) { this.color = color; } @Override public Dimension getPreferredSize() { return new Dimension(rectangleWidth, rectangleHeight); } @Override public void paintComponent(Graphics g) { // Let UI Delegate paint first, which includes background filling since this // component is opaque. super.paintComponent(g); g.setColor(color); int xMiddle = rectangleWidth / 2; int yMiddle = rectangleHeight / 2; int overallLength = (pointsDown != null && (pointsDown || !pointsDown)) ? rectangleHeight : rectangleWidth; int arrowRectangleWidth = 4; // must be divisible by 2 int headHeight = 6; int halfOfHeadWidth = headHeight; if (pointsDown != null && pointsDown) { // rectangle g.fillRect(xMiddle - arrowRectangleWidth / 2, 0, arrowRectangleWidth, overallLength - headHeight); // head int x1 = xMiddle - halfOfHeadWidth; int x2 = xMiddle + halfOfHeadWidth; int x3 = xMiddle; int y1 = overallLength - headHeight; int y2 = overallLength - headHeight; int y3 = overallLength; g.fillPolygon(new int[] { x1, x2, x3 }, new int[] { y1, y2, y3 }, 3); } else if (pointsDown != null && !pointsDown) { int correction = 1; // up arrow g.fillRect(xMiddle - arrowRectangleWidth / 2, headHeight - 1, arrowRectangleWidth, overallLength - headHeight + correction); // head int x1 = xMiddle - halfOfHeadWidth; int x2 = xMiddle + halfOfHeadWidth; int x3 = xMiddle; int y1 = headHeight - correction; int y2 = headHeight - correction; int y3 = -1; g.fillPolygon(new int[] { x1, x2, x3 }, new int[] { y1, y2, y3 }, 3); } else if (pointsRight != null && pointsRight) { // right arrows are needed on the internet layer setOpaque(true); setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", 2)); // right arrow g.fillRect(0, yMiddle - arrowRectangleWidth / 2, overallLength - headHeight, arrowRectangleWidth); // head int x1 = overallLength - headHeight; int x2 = overallLength; int x3 = overallLength - headHeight; int y1 = yMiddle - halfOfHeadWidth; int y2 = yMiddle; int y3 = yMiddle + halfOfHeadWidth; g.fillPolygon(new int[] { x1, x2, x3 }, new int[] { y1, y2, y3 }, 3); } else if (pointsRight != null && !pointsRight) { // left arrow g.fillRect(headHeight, yMiddle - arrowRectangleWidth / 2, overallLength - headHeight, arrowRectangleWidth); // head int x1 = headHeight; int x2 = 0; int x3 = headHeight; int y1 = yMiddle - halfOfHeadWidth; int y2 = yMiddle; int y3 = yMiddle + halfOfHeadWidth; g.fillPolygon(new int[] { x1, x2, x3 }, new int[] { y1, y2, y3 }, 3); } } } /** * Class for the visualisation of a grey wire (link layer) and an arrow. */ protected class WireWithArrow extends Arrow { private WireWithArrow(Boolean pointsDown, Boolean pointsRight, int rectangleWidth, int rectangleHeight, boolean activeArrow) { super(pointsDown, pointsRight, rectangleWidth, rectangleHeight, activeArrow); } @Override public void paintComponent(Graphics g) { setOpaque(true); setBackground(MessageDetailsTable.getLayerColorVar("backgroundColorUltraFine", 1)); // Let UI Delegate paint first, which includes background filling since this // component is opaque. super.paintComponent(g); g.setColor(Color.BLACK); // wire (ground) g.setColor(MessageDetailsTable.getLayerColorVar("backgroundColor", 1)); g.fillRect(0, this.rectangleHeight - 6, this.rectangleWidth, 6); } } }filius-2.9.4+ds/src/main/java/filius/hardware/000077500000000000000000000000001506574241500211605ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/hardware/Hardware.java000066400000000000000000000044001506574241500235560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware; import java.io.Serializable; import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings({ "serial", "deprecation" }) public abstract class Hardware extends Observable implements Serializable { private static Logger LOG = LoggerFactory.getLogger(Hardware.class); public static Boolean ACTIVE = Boolean.TRUE; public static Boolean INACTIVE = Boolean.FALSE; public static String FAILURE = "Failure"; private Object state = Boolean.FALSE; public String holeHardwareTyp() { return ""; } public void setAktiv(boolean aktiv) { setState(aktiv); } public void setFailure() { setState(FAILURE); } private void setState(Object newState) { LOG.trace("new hardware state: {}", newState); if (!state.equals(newState)) { state = newState; benachrichtigeBeobachter(); } } public void benachrichtigeBeobachter() { setChanged(); notifyObservers(state); } } filius-2.9.4+ds/src/main/java/filius/hardware/Kabel.java000066400000000000000000000035021506574241500230410ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware; @SuppressWarnings("serial") public class Kabel extends Verbindung { public static final String TYPE = messages.getString("hw_kabel_msg1"); private boolean wireless = false; public Kabel() { super(); } public Kabel(Port anschluss1, Port anschluss2) { super(); setAnschluesse(new Port[] { anschluss1, anschluss2 }); } @Override public String holeHardwareTyp() { return TYPE; } public void setWireless(boolean value) { wireless = value; } public boolean getWireless() { return wireless; } } filius-2.9.4+ds/src/main/java/filius/hardware/NetzwerkInterface.java000066400000000000000000000143041506574241500254570ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware; import java.io.Serializable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.Information; import filius.software.vermittlungsschicht.IP; public class NetzwerkInterface implements Serializable { private static Logger LOG = LoggerFactory.getLogger(NetzwerkInterface.class); private static final long serialVersionUID = 1L; /** The node that the interface belongs to */ private InternetKnoten node; private String mac; private String ip; private String subnetzMaske; private String gateway; private String dns; private Port anschluss; private boolean isWireless; public NetzwerkInterface() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (NetzwerkInterface), constr: NetzwerkInterface()"); setMac(Information.getInformation().holeFreieMACAdresse()); // set initial IP address to the same value for all new devices // QUESTION: Is this actually wanted for educational reasons? // Or is it rather annoying to be enforced to change this address for // each device? setIp("192.168.0.10"); setSubnetzMaske("255.255.255.0"); setGateway(""); setDns(""); anschluss = new Port(this); // this.initPersistenceAttributes(); } public NetzwerkInterface(InternetKnoten node) { this(); this.node = node; } private InternetKnoten getNode() { return this.node; } /** * This method is used to mark the MAC address as transient property in order to avoid use of the same MAC address * if the same project is opened twice. */ // private void initPersistenceAttributes() { // BeanInfo info; // try { // info = Introspector.getBeanInfo(NetzwerkInterface.class); // PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors(); // for (PropertyDescriptor descriptor : propertyDescriptors) { // // if (descriptor.getName().equals("mac")) { // descriptor.setValue("transient", Boolean.TRUE); // break; // } // } // } catch (IntrospectionException e) { // e.printStackTrace(); // } // } public Port getPort() { return anschluss; } public void setPort(Port port) { this.anschluss = port; } /** * IP-Adresse des DNS-Servers, der zur Aufloesung von Domainnamen verwendet wird. * * NOTE: die IP-Adresse des DNS-Servers ist kein Parameter des Netzwerk-Interfaces, wird aber aus Gruenden der * Kompatibilitaet hier verwaltet. Jedes Netzwerk-Interface der gleichen Komponente sollte hier den gleichen Wert * haben! */ public String getDns() { return dns; } /** * IP-Adresse des DNS-Servers, der zur Aufloesung von Domainnamen verwendet wird. * * @deprecated die IP-Adresse des DNS-Servers ist kein Parameter des Netzwerk-Interfaces, wird aber aus Gruenden der * Kompatibilitaet hier verwaltet. Jedes Netzwerk-Interface der gleichen Komponente sollte hier den * gleichen Wert haben! */ public void setDns(String dns) { this.dns = dns; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = IP.ipCheck(ip); } public String getMac() { return mac.toUpperCase(); } public void setMac(String mac) { if (mac != null) { Information.getInformation().macHinzufuegen(mac); this.mac = mac; } } public String getSubnetzMaske() { return subnetzMaske; } public void setSubnetzMaske(String subnetzMaske) { this.subnetzMaske = IP.ipCheck(subnetzMaske); } /** * IP-Adresse des Standard-Gateways. Dorthin werden alle Pakete gesendet, fuer dessen Zieladresse kein anderer * Eintrag in der Weiterleitungstabelle vorhanden ist. * * NOTE: die IP-Adresse des Standard-Gateways ist kein Parameter des Netzwerk-Interfaces, wird aber aus Gruenden der * Kompatibilitaet hier verwaltet. Jedes Netzwerk-Interface der gleichen Komponente sollte hier den gleichen Wert * haben! */ public String getGateway() { return gateway; } /** * IP-Adresse des Standard-Gateways. Dorthin werden alle Pakete gesendet, fuer dessen Zieladresse kein anderer * Eintrag in der Weiterleitungstabelle vorhanden ist. * * @deprecated die IP-Adresse des Standard-Gateways ist kein Parameter des Netzwerk-Interfaces, wird aber aus * Gruenden der Kompatibilitaet hier verwaltet. Jedes Netzwerk-Interface der gleichen Komponente sollte * hier den gleichen Wert haben! */ public void setGateway(String gateway) { this.gateway = gateway; } public boolean isWireless() { return isWireless; } public void setWireless(boolean isWireless) { this.isWireless = isWireless; } } filius-2.9.4+ds/src/main/java/filius/hardware/Port.java000066400000000000000000000061161506574241500227530ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware; import java.io.Serializable; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.netzzugangsschicht.EthernetFrame; public class Port implements Serializable { private static Logger LOG = LoggerFactory.getLogger(Port.class); private static final long serialVersionUID = 1L; private LinkedList eingangsPuffer = new LinkedList(); private LinkedList ausgangsPuffer = new LinkedList(); private Verbindung verbindung = null; private NetzwerkInterface nic = null; // constructor with parameter for all other nodes with explicit NIC for each // port public Port(NetzwerkInterface nic) { this.nic = nic; } // constructor without parameters for switches public Port() {} public NetzwerkInterface getNIC() { return nic; } public boolean isPortFrei() { return (verbindung == null); } public boolean setVerbindung(Verbindung verbindung) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Port), setVerbindung(" + verbindung + ")"); if (isPortFrei()) { this.verbindung = verbindung; return true; } else { return false; } } public void entferneVerbindung() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Port), entferneVerbindung()"); this.verbindung = null; } public LinkedList holeAusgangsPuffer() { return ausgangsPuffer; } public LinkedList holeEingangsPuffer() { return eingangsPuffer; } public void setzeEingangsPuffer(LinkedList puffer) { this.eingangsPuffer = puffer; } public Verbindung getVerbindung() { return verbindung; } } filius-2.9.4+ds/src/main/java/filius/hardware/SimplexVerbindung.java000066400000000000000000000126641506574241500255010ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware; import org.apache.commons.lang3.SerializationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.netzzugangsschicht.EthernetFrame; public class SimplexVerbindung implements Runnable { private static Logger LOG = LoggerFactory.getLogger(SimplexVerbindung.class); private boolean threadRunning = true; private Verbindung verbindung = null; private Port anschluss1 = null; private Port anschluss2 = null; /** * @author carsten * @param sender * - Sender der einseitigen Verbindung * @param empfaenger * - Empfaenger der einseitigen Verbindung * @param verbindung * - Verbindung, auf der die einseitige Kommunikation gestartet wird * * Dieser Konstruktor wird innerhalb der Verbindung aufgerufen und in einem Thread gestartet. Davon gibt * es zwei Verbindungen, die die bidirektionale Verbindung zwischen den beiden Hardwares herstellt. */ public SimplexVerbindung(Port anschluss1, Port anschluss2, Verbindung verbindung) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SimplexVerbindung), constr: SimplexVerbindung(" + anschluss1 + "," + anschluss2 + "," + verbindung + ")"); this.anschluss1 = anschluss1; this.anschluss2 = anschluss2; this.verbindung = verbindung; } /** * @author carsten Diese run-Methode des Threads (nur Runnable!) sorgt fuer die einzelnen Kommunikationen auf einer * Verbindung in beide Richtungen */ public void run() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SimplexVerbindung), run()"); EthernetFrame frame; while (threadRunning) { synchronized (anschluss1.holeAusgangsPuffer()) { if (anschluss1.holeAusgangsPuffer().size() < 1) { try { verbindung.setAktiv(false); anschluss1.holeAusgangsPuffer().wait(); } catch (InterruptedException e) {} } if (anschluss1.holeAusgangsPuffer().size() > 0) { frame = (EthernetFrame) anschluss1.holeAusgangsPuffer().removeFirst(); boolean dropFrame = Verbindung.isDrop(); if (dropFrame) { verbindung.setFailure(); } else { verbindung.setAktiv(true); } synchronized (this) { try { Thread.sleep(Verbindung.holeVerzoegerung()); } catch (InterruptedException e) {} } if (!dropFrame) { synchronized (anschluss2.holeEingangsPuffer()) { anschluss2.holeEingangsPuffer().add(SerializationUtils.clone(frame)); anschluss2.holeEingangsPuffer().notify(); } } else { Lauscher.getLauscher().addDroppedDataUnit(frame); LOG.debug("Frame dropped."); } } } } } public Port getPort1() { return anschluss1; } public void setPort1(Port anschluss1) { this.anschluss1 = anschluss1; } public Port getPort2() { return anschluss2; } public void setPort2(Port anschluss2) { this.anschluss2 = anschluss2; } public void anschluesseTrennen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SimplexVerbindung), anschluesseTrennen()"); anschluss1.entferneVerbindung(); anschluss2.entferneVerbindung(); this.setThreadRunning(false); } public Verbindung getVerbindung() { return verbindung; } public void setVerbindung(Verbindung verbindung) { this.verbindung = verbindung; } public boolean isThreadRunning() { return threadRunning; } public void setThreadRunning(boolean threadRunning) { this.threadRunning = threadRunning; } } filius-2.9.4+ds/src/main/java/filius/hardware/Verbindung.java000066400000000000000000000163601506574241500241340ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware; import java.io.Serializable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.VerbindungsException; import filius.rahmenprogramm.I18n; /** * @author carsten * */ public abstract class Verbindung extends Hardware implements Serializable, I18n { private static Logger LOG = LoggerFactory.getLogger(Verbindung.class); private static final long serialVersionUID = 1L; /** * Verzoegerung der Uebertragung in Millisekunden, wenn der Verzoegerungsfaktor 1 ist. */ private static final int MIN_VERZOEGERUNG_IN_MILLIS = 5; /** Faktor der Verzoegerungszeit, der zwischen 1 und 100 */ private static int verzoegerungsFaktor = 10; private static boolean drop = false; public static boolean isDrop() { return drop; } public static void setDrop(boolean drop) { LOG.debug("Drop data: {}", drop); Verbindung.drop = drop; } /** * maximale Anzahl von Hops zum Datenaustausch. Diese Zahl wird verwendet, um eine Round-Trip-Time (RTT) zu * berechnen. Da es auch möglich ist, Datenaustausch mit einem einem virtuellen Rechnernetz ueber eine * 'Modemverbindung' zu erstellen, wird diese Zahl hoch angesetzt.
* Mit dieser Zahl sind die HOPS fuer einen Round-Trip als fuer die Hin- und Zurueck-Uebertragung beim * Datenaustausch mit einem anderen Knoten gemeint. */ private static final int MAX_HOPS = 50; // extend RTT in case of slow machines by this factor; 1: no change private static int extendRTTfactor = 1; private Port[] anschluesse = null; private SimplexVerbindung simplexEins, simplexZwei; private Thread threadSimplexEins; private Thread threadSimplexZwei; public void setAnschluesse(Port[] anschluesse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Verbindung), setAnschluesse(" + anschluesse + ")"); this.anschluesse = anschluesse; try { verbinde(); } catch (VerbindungsException e) { LOG.debug("Unexpected error when connectiong two ports", e); } } private void verbinde() throws VerbindungsException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Verbindung), verbinde()" + "\t" + anschluesse[0].hashCode() + " <-> " + anschluesse[1].hashCode()); try { simplexEins = new SimplexVerbindung(anschluesse[0], anschluesse[1], this); simplexZwei = new SimplexVerbindung(anschluesse[1], anschluesse[0], this); anschluesse[0].setVerbindung(this); anschluesse[1].setVerbindung(this); threadSimplexEins = new Thread(simplexEins); threadSimplexZwei = new Thread(simplexZwei); threadSimplexEins.start(); threadSimplexZwei.start(); } catch (NullPointerException e) { simplexEins = null; simplexZwei = null; anschluesse[0].setVerbindung(null); anschluesse[1].setVerbindung(null); throw new VerbindungsException("EXCEPTION: " + messages.getString("verbindung_msg1")); } } public Port[] getAnschluesse() { return anschluesse; } /** * Define the port that is connected to the given port. * * @throws Exception * If the given parameter is not a port of this connection. */ public Port findConnectedPort(Port port) throws Exception { Port connectedPort = null; if (port == anschluesse[0]) { connectedPort = anschluesse[1]; } else if (port == anschluesse[1]) { connectedPort = anschluesse[0]; } else { throw new Exception("Invalid Parameter"); } return connectedPort; } public void anschluesseTrennen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Verbindung), anschluesseTrennen()"); simplexEins.anschluesseTrennen(); simplexZwei.anschluesseTrennen(); threadSimplexEins.interrupt(); threadSimplexZwei.interrupt(); } /** Value between 1 (low latency) and 100 (highest latency) */ public static int holeVerzoegerungsFaktor() { return verzoegerungsFaktor; } /** * Zum setzen den Verzoegerungsfaktors. Das Produkt daraus und der minimalen Verzoegerung ergibt die tatsaechliche * Verzoegerung bei der Uebertragung zwischen zwei Knoten im Rechnernetz. Der Wert des Faktors muss zwischen 1 und * 100 liegen. Wenn der uebergebene Parameter ausserhalb dieses Bereichs liegt, wird er auf den Minimal- bzw. * Maximalwert gesetzt. * * @param verzoegerungsFaktor */ public static void setzeVerzoegerungsFaktor(int verzoegerungsFaktor) { LOG.trace("INVOKED (static) filius.hardware.Verbindung, setzeVerzoegerungsFaktor(" + verzoegerungsFaktor + ")"); if (verzoegerungsFaktor < 1) { Verbindung.verzoegerungsFaktor = 1; } else if (verzoegerungsFaktor > 100) { Verbindung.verzoegerungsFaktor = 100; } else { Verbindung.verzoegerungsFaktor = verzoegerungsFaktor; } } /** * Gibt die Verzoegerung einer Verbindung zwischen zwei Knoten im Rechnernetz in Millisekunden zurueck. Dazu wird * die minimale Verzoegerungszeit mit dem Verzoegerungsfaktor multipliziert. * * @return Verzoegerung der Uebertragung zwischen zwei Knoten im Rechnernetz in Millisekunden */ public static int holeVerzoegerung() { return verzoegerungsFaktor * MIN_VERZOEGERUNG_IN_MILLIS; } public static void setRTTfactor(int factor) { if (factor >= 1 && factor <= 5) { extendRTTfactor = factor; } } public static int getRTTfactor() { return extendRTTfactor; } /** * maximale Round-Trip-Time (RTT) in Millisekunden
* solange wird auf eine Antwort auf ein Segment gewartet */ public static int holeRTT() { return MAX_HOPS * holeVerzoegerung() * extendRTTfactor; } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/000077500000000000000000000000001506574241500224565ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Gateway.java000066400000000000000000000051251506574241500247250ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ //Netzwerkziel, Netzwerkmaske, ZielIp, Schnittstelle package filius.hardware.knoten; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.I18n; import filius.software.system.GatewayFirmware; @SuppressWarnings("serial") public class Gateway extends InternetKnoten implements I18n { private static Logger LOG = LoggerFactory.getLogger(Gateway.class); public static final String TYPE = messages.getString("hw_gateway_msg1"); @Override public String holeHardwareTyp() { return TYPE; } public Gateway() { super(); setMaxLayerOfOperation(2); setzeAnzahlAnschluesse(2); holeWANInterface().setIp("42.0.0.10"); holeWANInterface().setSubnetzMaske("255.0.0.0"); setSystemSoftware(new GatewayFirmware()); getSystemSoftware().setKnoten(this); setName(TYPE); LOG.debug("New gateway created."); } public NetzwerkInterface holeWANInterface() { return getNetzwerkInterfaces().get(0); } public NetzwerkInterface holeLANInterface() { return getNetzwerkInterfaces().get(1); } public boolean checkWANPortUnconnected() { return holeWANInterface().getPort().isPortFrei(); } public boolean checkLANPortUnconnected() { return holeLANInterface().getPort().isPortFrei(); } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Host.java000066400000000000000000000063441506574241500242450ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.software.system.Betriebssystem; @SuppressWarnings("serial") public abstract class Host extends InternetKnoten { private static Logger LOG = LoggerFactory.getLogger(Host.class); private boolean useIPAsName = false; private boolean useMACAsName = false; public boolean isUseIPAsName() { return useIPAsName; } public void setUseIPAsName(boolean useIPAsName) { this.useIPAsName = useIPAsName; } public boolean isUseMACAsName() { return useMACAsName; } public void setUseMACAsName(boolean useMACAsName) { this.useMACAsName = useMACAsName; } public Host() { super(); setMaxLayerOfOperation(4); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Host), constr: Host()"); this.setzeAnzahlAnschluesse(1); this.setSystemSoftware(new Betriebssystem()); getSystemSoftware().setKnoten(this); LOG.debug("Host " + this.hashCode() + " has OS " + getSystemSoftware().hashCode()); } @Override public String holeAnzeigeName() { if (useIPAsName && useMACAsName) { return getNetzwerkInterfaces().get(0).getIp() + " (" + getNetzwerkInterfaces().get(0).getMac() + ")"; } else if (useIPAsName) { return getNetzwerkInterfaces().get(0).getIp(); } else if (useMACAsName) { return getNetzwerkInterfaces().get(0).getMac(); } else { return getName(); } } public void setIpAdresse(String ip) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Host), setIpAdresse(" + ip + ")"); NetzwerkInterface nic = (NetzwerkInterface) this.getNetzwerkInterfaces().get(0); nic.setIp(ip); } public String getMac() { NetzwerkInterface nic = (NetzwerkInterface) this.getNetzwerkInterfaces().get(0); return nic.getMac(); } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/InternetKnoten.java000066400000000000000000000142111506574241500262670ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.Port; import filius.hardware.Verbindung; public abstract class InternetKnoten extends Knoten { private static Logger LOG = LoggerFactory.getLogger(InternetKnoten.class); private static final long serialVersionUID = 1L; private List netzwerkInterfaces = new LinkedList(); // maximum layer on which the node operates // (1 = link, 2 = internet, 3 = transport, 4 = application) private int maxLayerOfOperation; public int getMaxLayerOfOperation() { return maxLayerOfOperation; } public void setMaxLayerOfOperation(int maxLayerOfOperation) { this.maxLayerOfOperation = maxLayerOfOperation; } public Port holeFreienPort() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnoten), holeFreienPort()"); ListIterator iter = getNetzwerkInterfaces().listIterator(); while (iter.hasNext()) { NetzwerkInterface nic = (NetzwerkInterface) iter.next(); Port anschluss = nic.getPort(); if (anschluss.isPortFrei()) { // LOG.debug("\tfound free port: "+anschluss); return anschluss; } } return null; } protected List defineConnectedPorts() { List connectedPorts = new ArrayList<>(); for (NetzwerkInterface nic : netzwerkInterfaces) { Verbindung connection = nic.getPort().getVerbindung(); if (connection != null) { try { connectedPorts.add(connection.findConnectedPort(nic.getPort())); } catch (Exception e) { LOG.debug(e.getMessage()); } } } return connectedPorts; } /** * {@inheritDoc} */ protected boolean hasPort(Port portToLookup) { boolean result = false; for (NetzwerkInterface nic : netzwerkInterfaces) { if (nic.getPort().equals(portToLookup)) { result = true; break; } } return result; } /** * * Gibt das NetzwerkInterface zurueck, dass die angegebene mac Adresse hat. Falls kein Interface diese Mac-Adresse * besitzt, wird null zurueckgegeben. * * @author Johannes Bade * @param mac * @return */ public NetzwerkInterface getNetzwerkInterfaceByMac(String mac) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnoten), getNetzwerkInterfaceByMac(" + mac + ")"); NetzwerkInterface rueckgabe = null; ListIterator it = this.netzwerkInterfaces.listIterator(); while (it.hasNext()) { NetzwerkInterface ni = (NetzwerkInterface) it.next(); if (ni.getMac().equals(mac)) { rueckgabe = ni; } } return rueckgabe; } public void removeNic(NetzwerkInterface nic) { this.netzwerkInterfaces.remove(nic); } /** * * Gibt die Netzwerkkarte mit der entsprechenden IP zurueck * * @author Thomas * @param mac * @return */ public NetzwerkInterface getNetzwerkInterfaceByIp(String ip) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnoten), getNetzwerkInterfaceByIp(" + ip + ")"); if (ip.equals("127.0.0.1")) { return (NetzwerkInterface) netzwerkInterfaces.get(0); } NetzwerkInterface rueckgabe = null; ListIterator it = this.netzwerkInterfaces.listIterator(); while (it.hasNext()) { NetzwerkInterface ni = (NetzwerkInterface) it.next(); if (ni.getIp().equals(ip)) { rueckgabe = ni; } } return rueckgabe; } public List getNetzwerkInterfaces() { return netzwerkInterfaces; } public void setNetzwerkInterfaces(List netzwerkInterfaces) { this.netzwerkInterfaces = netzwerkInterfaces; } public int holeAnzahlAnschluesse() { return netzwerkInterfaces.size(); } public void hinzuAnschluss() { netzwerkInterfaces.add(new NetzwerkInterface(this)); } public void setzeAnzahlAnschluesse(int anzahlAnschluesse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnoten), setzeAnzahlAnschluesse(" + anzahlAnschluesse + ")"); netzwerkInterfaces = new LinkedList(); for (int i = 0; i < anzahlAnschluesse; i++) { netzwerkInterfaces.add(new NetzwerkInterface(this)); } } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Knoten.java000066400000000000000000000053671506574241500245720ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.Hardware; import filius.hardware.Port; import filius.software.system.SystemSoftware; @SuppressWarnings("serial") public abstract class Knoten extends Hardware { private static Logger LOG = LoggerFactory.getLogger(Knoten.class); private String name; private SystemSoftware systemAnwendung; public abstract Port holeFreienPort(); /** * Check whether this given port is one of the node's ports. */ protected abstract boolean hasPort(Port portToLookup); protected abstract List defineConnectedPorts(); public boolean checkConnected(Knoten knoten) { boolean alreadyConnected = false; for (Port port : defineConnectedPorts()) { if (knoten.hasPort(port)) { alreadyConnected = true; break; } } return alreadyConnected; } public String holeAnzeigeName() { return name; } public String getName() { return name; } public void setName(String name) { this.name = name; if (systemAnwendung != null) { LOG.debug("node with SystemSoftware (" + systemAnwendung.hashCode() + ") now has name '" + name + "'"); } } public SystemSoftware getSystemSoftware() { return systemAnwendung; } public void setSystemSoftware(SystemSoftware systemAnwendung) { this.systemAnwendung = systemAnwendung; } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/LokalerKnoten.java000066400000000000000000000072561506574241500261030ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.Port; import filius.hardware.Verbindung; @SuppressWarnings("serial") public abstract class LokalerKnoten extends Knoten { private static Logger LOG = LoggerFactory.getLogger(LokalerKnoten.class); private LinkedList anschluesse = new LinkedList(); public Port holeFreienPort() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (LokalerKnoten), holeFreienPort()"); for (Port anschluss : anschluesse) { if (anschluss.isPortFrei()) { return anschluss; } } return null; } protected List defineConnectedPorts() { List connectedPorts = new ArrayList<>(); for (Port port : anschluesse) { Verbindung connection = port.getVerbindung(); if (connection != null) { try { connectedPorts.add(connection.findConnectedPort(port)); } catch (Exception e) { LOG.debug(e.getMessage()); } } } return connectedPorts; } /** * {@inheritDoc} */ protected boolean hasPort(Port portToLookup) { boolean result = false; for (Port port : anschluesse) { if (port.equals(portToLookup)) { result = true; break; } } return result; } public Port getErstenAnschluss() { if (anschluesse != null && anschluesse.size() > 0) { return (Port) anschluesse.getFirst(); } return null; } public LinkedList getAnschluesse() { return anschluesse; } public void setAnschluesse(LinkedList anschluesse) { this.anschluesse = anschluesse; } public int holeAnzahlAnschluesse() { return anschluesse.size(); } public void setzeAnzahlAnschluesse(int anzahlAnschluesse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (LokalerKnoten), setzeAnzahlAnschluesse(" + anzahlAnschluesse + ")"); LinkedList anschluesse; anschluesse = new LinkedList(); for (int i = 0; i < anzahlAnschluesse; i++) { anschluesse.add(new Port()); } setAnschluesse(anschluesse); } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Modem.java000066400000000000000000000046521506574241500243710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.system.ModemFirmware; public class Modem extends LokalerKnoten { private static Logger LOG = LoggerFactory.getLogger(Modem.class); public static final String TYPE = "Modem"; private static final long serialVersionUID = 1L; private boolean modemVerbindungAktiv = false; @Override public String holeHardwareTyp() { return TYPE; } public Modem() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Modem), constr: Modem()"); this.setzeAnzahlAnschluesse(1); this.setSystemSoftware(new ModemFirmware()); getSystemSoftware().setKnoten(this); this.setName(TYPE); } public void setzeModemVerbindungAktiv(boolean verbindungAktiv) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Modem), setzeVerbindungAktiv(" + verbindungAktiv + ")"); this.modemVerbindungAktiv = verbindungAktiv; this.setChanged(); this.notifyObservers(verbindungAktiv); } public boolean istModemVerbindungAktiv() { return modemVerbindungAktiv; } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Notebook.java000066400000000000000000000036541506574241500251110ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; public class Notebook extends Host implements I18n { private static Logger LOG = LoggerFactory.getLogger(Notebook.class); public static final String TYPE = messages.getString("hw_notebook_msg1"); @Override public String holeHardwareTyp() { return TYPE; } /** * Konstruktor wird nur zu Hilfszwecken in verschiedenen Klassen benutzt */ public Notebook() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Notebook), constr: Notebook()"); this.setName(messages.getString("hw_notebook_msg2")); } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Rechner.java000066400000000000000000000037311506574241500247130ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; /** * @author Nadja */ @SuppressWarnings("serial") public class Rechner extends Host implements I18n { private static Logger LOG = LoggerFactory.getLogger(Rechner.class); public static final String TYPE = messages.getString("hw_rechner_msg1"); @Override public String holeHardwareTyp() { return TYPE; } /** * Konstruktor wird nur zu Hilfszwecken in verschiedenen Klassen benutzt */ public Rechner() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Rechner), constr: Rechner()"); this.setName(messages.getString("hw_rechner_msg2")); } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Switch.java000066400000000000000000000042721506574241500245670ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.hardware.knoten; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.software.system.SwitchFirmware; @SuppressWarnings("serial") public class Switch extends LokalerKnoten implements I18n { private static Logger LOG = LoggerFactory.getLogger(Switch.class); public static final String TYPE = messages.getString("hw_switch_msg1"); private boolean showAsCloud = false; @Override public String holeHardwareTyp() { return TYPE; } public Switch() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Switch), constr: Switch()"); this.setzeAnzahlAnschluesse(24); this.setSystemSoftware(new SwitchFirmware()); getSystemSoftware().setKnoten(this); this.setName(TYPE); } public void setCloud(boolean newVal) { showAsCloud = newVal; } public boolean isCloud() { return showAsCloud; } } filius-2.9.4+ds/src/main/java/filius/hardware/knoten/Vermittlungsrechner.java000066400000000000000000000050371506574241500274000ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ //Netzwerkziel, Netzwerkmaske, ZielIp, Schnittstelle package filius.hardware.knoten; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.software.system.VermittlungsrechnerBetriebssystem; @SuppressWarnings("serial") public class Vermittlungsrechner extends InternetKnoten implements I18n { private static Logger LOG = LoggerFactory.getLogger(Vermittlungsrechner.class); public static final String TYPE = messages.getString("hw_vermittlungsrechner_msg1"); @Override public String holeHardwareTyp() { return TYPE; } public Vermittlungsrechner() { super(); setMaxLayerOfOperation(2); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Vermittlungsrechner), constr: Vermittlungsrechner()"); this.setzeAnzahlAnschluesse(1); this.setSystemSoftware(new VermittlungsrechnerBetriebssystem()); getSystemSoftware().setKnoten(this); this.setName(TYPE); } public List getMacs() { List macs = new ArrayList(); int count = getNetzwerkInterfaces().size(); for (int i = 0; i < count; i++) macs.add(getNetzwerkInterfaces().get(0).getMac()); return macs; } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/000077500000000000000000000000001506574241500224025ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/Base64.java000066400000000000000000001743611506574241500243050ustar00rootroot00000000000000package filius.rahmenprogramm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Base64 { private static Logger LOG = LoggerFactory.getLogger(Base64.class); /* ******** P U B L I C F I E L D S ******** */ /** No options specified. Value is zero. */ public final static int NO_OPTIONS = 0; /** Specify encoding. */ public final static int ENCODE = 1; /** Specify decoding. */ public final static int DECODE = 0; /** Specify that data should be gzip-compressed. */ public final static int GZIP = 2; /** Don't break lines when encoding (violates strict Base64 specification) */ public final static int DONT_BREAK_LINES = 8; /** * Encode using Base64-like encoding that is URL- and Filename-safe as described in Section 4 of RFC3548: * http://www.faqs.org/rfcs/rfc3548.html. It is important to * note that data encoded this way is not officially valid Base64, or at the very least should not be * called Base64 without also specifying that is was encoded using the URL- and Filename-safe dialect. */ public final static int URL_SAFE = 16; /** * Encode using the special "ordered" dialect of Base64 described here: * http://www.faqs.org/qa/rfcc- 1940.html. */ public final static int ORDERED = 32; /* ******** P R I V A T E F I E L D S ******** */ /** Maximum line length (76) of Base64 output. */ private final static int MAX_LINE_LENGTH = 76; /** The equals sign (=) as a byte. */ private final static byte EQUALS_SIGN = (byte) '='; /** The new line character (\n) as a byte. */ private final static byte NEW_LINE = (byte) '\n'; /** Preferred encoding. */ private final static String PREFERRED_ENCODING = "UTF-8"; // I think I end up not using the BAD_ENCODING indicator. // private final static byte BAD_ENCODING = -9; // Indicates error in // encoding private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in // encoding private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in // encoding /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ /** The 64 valid Base64 values. */ // private final static byte[] ALPHABET; /* * Rechner platform me be something funny like EBCDIC, so we hardcode these values. */ private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' }; /** * Translates a Base64 value to either its 6-bit reconstruction value or a negative number indicating some other * meaning. **/ private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal // 0 // - // 8 -5, -5, // Whitespace: Tab and Linefeed -9, -9, // Decimal 11 - 12 -5, // Whitespace: Carriage Return -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - // 26 -9, -9, -9, -9, -9, // Decimal 27 - 31 -5, // Whitespace: Space -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 62, // Plus sign at decimal 43 -9, -9, -9, // Decimal 44 - 46 63, // Slash at decimal 47 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine -9, -9, -9, // Decimal 58 - 60 -1, // Equals sign at decimal 61 -9, -9, -9, // Decimal 62 - 64 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through // 'N' 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' // through 'Z' -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' // through 'm' 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' // through 'z' -9, -9, -9, -9 // Decimal 123 - 126 /* * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 140 - 152 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 179 - 191 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 218 - 230 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ /** * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: * http://www.faqs.org /rfcs/rfc3548.html. Notice that the last * two bytes become "hyphen" and "underscore" instead of "plus" and "slash." */ private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' }; /** * Used in decoding URL- and Filename-safe dialects of Base64. */ private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal // 0 // - // 8 -5, -5, // Whitespace: Tab and Linefeed -9, -9, // Decimal 11 - 12 -5, // Whitespace: Carriage Return -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - // 26 -9, -9, -9, -9, -9, // Decimal 27 - 31 -5, // Whitespace: Space -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 -9, // Plus sign at decimal 43 -9, // Decimal 44 62, // Minus sign at decimal 45 -9, // Decimal 46 -9, // Slash at decimal 47 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine -9, -9, -9, // Decimal 58 - 60 -1, // Equals sign at decimal 61 -9, -9, -9, // Decimal 62 - 64 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through // 'N' 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' // through 'Z' -9, -9, -9, -9, // Decimal 91 - 94 63, // Underscore at decimal 95 -9, // Decimal 96 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' // through 'm' 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' // through 'z' -9, -9, -9, -9 // Decimal 123 - 126 /* * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 140 - 152 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 179 - 191 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 218 - 230 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ /** * I don't get the point of this technique, but it is described here: * http://www.faqs.org/qa/rfcc- 1940.html. */ private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' }; /** * Used in decoding the "ordered" dialect of Base64. */ private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal // 0 // - // 8 -5, -5, // Whitespace: Tab and Linefeed -9, -9, // Decimal 11 - 12 -5, // Whitespace: Carriage Return -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - // 26 -9, -9, -9, -9, -9, // Decimal 27 - 31 -5, // Whitespace: Space -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 -9, // Plus sign at decimal 43 -9, // Decimal 44 0, // Minus sign at decimal 45 -9, // Decimal 46 -9, // Slash at decimal 47 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine -9, -9, -9, // Decimal 58 - 60 -1, // Equals sign at decimal 61 -9, -9, -9, // Decimal 62 - 64 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' // through 'M' 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' // through 'Z' -9, -9, -9, -9, // Decimal 91 - 94 37, // Underscore at decimal 95 -9, // Decimal 96 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' // through 'm' 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' // through 'z' -9, -9, -9, -9 // Decimal 123 - 126 /* * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 140 - 152 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 179 - 191 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // * Decimal 218 - 230 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ /** * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the options specified. It's possible, though * silly, to specify ORDERED and URLSAFE in which case one of them will be picked, though there is no guarantee as * to which one will be picked. */ private final static byte[] getAlphabet(int options) { if ((options & URL_SAFE) == URL_SAFE) return _URL_SAFE_ALPHABET; else if ((options & ORDERED) == ORDERED) return _ORDERED_ALPHABET; else return _STANDARD_ALPHABET; } // end getAlphabet /** * Returns one of the _SOMETHING_DECODABET byte arrays depending on the options specified. It's possible, though * silly, to specify ORDERED and URL_SAFE in which case one of them will be picked, though there is no guarantee as * to which one will be picked. */ private final static byte[] getDecodabet(int options) { if ((options & URL_SAFE) == URL_SAFE) return _URL_SAFE_DECODABET; else if ((options & ORDERED) == ORDERED) return _ORDERED_DECODABET; else return _STANDARD_DECODABET; } // end getAlphabet /** Defeats instantiation. */ private Base64() {} /** * Prints command line usage. * * @param msg * A message to include with usage info. */ private final static void usage(String msg) { System.err.println(msg); System.err.println("Usage: java Base64 -e|-d inputfile outputfile"); } // end usage /* ******** E N C O D I N G M E T H O D S ******** */ /** * Encodes up to the first three bytes of array threeBytes and returns a four-byte array in Base64 * notation. The actual number of significant bytes in your array is given by numSigBytes. The array * threeBytes needs only be as big as numSigBytes. Code can reuse a byte array by passing a * four-byte array as b4. * * @param b4 * A reusable byte array to reduce array instantiation * @param threeBytes * the array to convert * @param numSigBytes * the number of significant bytes in your array * @return four byte array in Base64 notation. * @since 1.5.1 */ private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options) { encode3to4(threeBytes, 0, numSigBytes, b4, 0, options); return b4; } // end encode3to4 /** *

* Encodes up to three bytes of the array source and writes the resulting four Base64 bytes to * destination. The source and destination arrays can be manipulated anywhere along their length by * specifying srcOffset and destOffset. This method does not check to make sure your arrays * are large enough to accomodate srcOffset + 3 for the source array or destOffset * + 4 for the destination array. The actual number of significant bytes in your array is given by * numSigBytes. *

*

* This is the lowest level of the encoding methods with all possible parameters. *

* * @param source * the array to convert * @param srcOffset * the index where conversion begins * @param numSigBytes * the number of significant bytes in your array * @param destination * the array to hold the conversion * @param destOffset * the index where output will be put * @return the destination array * @since 1.3 */ private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset, int options) { byte[] ALPHABET = getAlphabet(options); // 1 2 3 // 01234567890123456789012345678901 Bit position // --------000000001111111122222222 Array position from threeBytes // --------| || || || | Six bit groups to index ALPHABET // >>18 >>12 >> 6 >> 0 Right shift necessary // 0x3f 0x3f 0x3f Additional AND // Create buffer with zero-padding if there are only one or two // significant bytes passed in the array. // We have to shift left 24 in order to flush out the 1's that appear // when Java treats a value as negative that is cast from a byte to an // int. int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0); switch (numSigBytes) { case 3: destination[destOffset] = ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; return destination; case 2: destination[destOffset] = ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; destination[destOffset + 3] = EQUALS_SIGN; return destination; case 1: destination[destOffset] = ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2] = EQUALS_SIGN; destination[destOffset + 3] = EQUALS_SIGN; return destination; default: return destination; } // end switch } // end encode3to4 /** * Serializes an object and returns the Base64-encoded version of that serialized object. If the object cannot be * serialized or there is another error, the method will return null. The object is not GZip-compressed * before being encoded. * * @param serializableObject * The object to encode * @return The Base64-encoded object * @since 1.4 */ public static String encodeObject(java.io.Serializable serializableObject) { return encodeObject(serializableObject, NO_OPTIONS); } // end encodeObject /** * Serializes an object and returns the Base64-encoded version of that serialized object. If the object cannot be * serialized or there is another error, the method will return null. *

* Valid options: * *

     *   GZIP: gzip-compresses object before encoding it.
     *   DONT_BREAK_LINES: don't break lines at 76 characters
     *     Note: Technically, this makes your encoding non-compliant.
     * 
*

* Example: encodeObject( myObj, Base64.GZIP ) or *

* Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) * * @param serializableObject * The object to encode * @param options * Specified options * @return The Base64-encoded object * @see Base64#GZIP * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public static String encodeObject(java.io.Serializable serializableObject, int options) { // Streams java.io.ByteArrayOutputStream baos = null; java.io.OutputStream b64os = null; java.io.ObjectOutputStream oos = null; java.util.zip.GZIPOutputStream gzos = null; // Isolate options int gzip = (options & GZIP); int dontBreakLines = (options & DONT_BREAK_LINES); try { // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); b64os = new Base64.OutputStream(baos, ENCODE | options); // GZip? if (gzip == GZIP) { gzos = new java.util.zip.GZIPOutputStream(b64os); oos = new java.io.ObjectOutputStream(gzos); } // end if: gzip else oos = new java.io.ObjectOutputStream(b64os); oos.writeObject(serializableObject); } // end try catch (java.io.IOException e) { LOG.debug("", e); return null; } // end catch finally { try { oos.close(); } catch (Exception e) {} try { gzos.close(); } catch (Exception e) {} try { b64os.close(); } catch (Exception e) {} try { baos.close(); } catch (Exception e) {} } // end finally // Return value according to relevant encoding. try { return new String(baos.toByteArray(), PREFERRED_ENCODING); } // end try catch (java.io.UnsupportedEncodingException uue) { return new String(baos.toByteArray()); } // end catch } // end encode /** * Encodes a byte array into Base64 notation. Does not GZip-compress data. * * @param source * The data to convert * @since 1.4 */ public static String encodeBytes(byte[] source) { return encodeBytes(source, 0, source.length, NO_OPTIONS); } // end encodeBytes /** * Encodes a byte array into Base64 notation. *

* Valid options: * *

     *   GZIP: gzip-compresses object before encoding it.
     *   DONT_BREAK_LINES: don't break lines at 76 characters
     *     Note: Technically, this makes your encoding non-compliant.
     * 
*

* Example: encodeBytes( myData, Base64.GZIP ) or *

* Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) * * * @param source * The data to convert * @param options * Specified options * @see Base64#GZIP * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public static String encodeBytes(byte[] source, int options) { return encodeBytes(source, 0, source.length, options); } // end encodeBytes /** * Encodes a byte array into Base64 notation. Does not GZip-compress data. * * @param source * The data to convert * @param off * Offset in array where conversion should begin * @param len * Length of data to convert * @since 1.4 */ public static String encodeBytes(byte[] source, int off, int len) { return encodeBytes(source, off, len, NO_OPTIONS); } // end encodeBytes /** * Encodes a byte array into Base64 notation. *

* Valid options: * *

     *   GZIP: gzip-compresses object before encoding it.
     *   DONT_BREAK_LINES: don't break lines at 76 characters
     *     Note: Technically, this makes your encoding non-compliant.
     * 
*

* Example: encodeBytes( myData, Base64.GZIP ) or *

* Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) * * * @param source * The data to convert * @param off * Offset in array where conversion should begin * @param len * Length of data to convert * @param options * Specified options * @param options * alphabet type is pulled from this (standard, url-safe, ordered) * @see Base64#GZIP * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public static String encodeBytes(byte[] source, int off, int len, int options) { // Isolate options int dontBreakLines = (options & DONT_BREAK_LINES); int gzip = (options & GZIP); // Compress? if (gzip == GZIP) { java.io.ByteArrayOutputStream baos = null; java.util.zip.GZIPOutputStream gzos = null; Base64.OutputStream b64os = null; try { // GZip -> Base64 -> ByteArray baos = new java.io.ByteArrayOutputStream(); b64os = new Base64.OutputStream(baos, ENCODE | options); gzos = new java.util.zip.GZIPOutputStream(b64os); gzos.write(source, off, len); gzos.close(); } // end try catch (java.io.IOException e) { LOG.debug("", e); return null; } // end catch finally { try { gzos.close(); } catch (Exception e) {} try { b64os.close(); } catch (Exception e) {} try { baos.close(); } catch (Exception e) {} } // end finally // Return value according to relevant encoding. try { return new String(baos.toByteArray(), PREFERRED_ENCODING); } // end try catch (java.io.UnsupportedEncodingException uue) { return new String(baos.toByteArray()); } // end catch } // end if: compress // Else, don't compress. Better not to use streams at all then. else { // Convert option to boolean in way that code likes it. boolean breakLines = dontBreakLines == 0; int len43 = len * 4 / 3; byte[] outBuff = new byte[(len43) // Main 4:3 + ((len % 3) > 0 ? 4 : 0) // Account for padding + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New // lines int d = 0; int e = 0; int len2 = len - 2; int lineLength = 0; for (; d < len2; d += 3, e += 4) { encode3to4(source, d + off, 3, outBuff, e, options); lineLength += 4; if (breakLines && lineLength == MAX_LINE_LENGTH) { outBuff[e + 4] = NEW_LINE; e++; lineLength = 0; } // end if: end of line } // en dfor: each piece of array if (d < len) { encode3to4(source, d + off, len - d, outBuff, e, options); e += 4; } // end if: some padding needed // Return value according to relevant encoding. try { return new String(outBuff, 0, e, PREFERRED_ENCODING); } // end try catch (java.io.UnsupportedEncodingException uue) { return new String(outBuff, 0, e); } // end catch } // end else: don't compress } // end encodeBytes /* ******** D E C O D I N G M E T H O D S ******** */ /** * Decodes four bytes from array source and writes the resulting bytes (up to three of them) to * destination. The source and destination arrays can be manipulated anywhere along their length by * specifying srcOffset and destOffset. This method does not check to make sure your arrays * are large enough to accomodate srcOffset + 4 for the source array or destOffset * + 3 for the destination array. This method returns the actual number of bytes that were converted from * the Base64 encoding. *

* This is the lowest level of the decoding methods with all possible parameters. *

* * * @param source * the array to convert * @param srcOffset * the index where conversion begins * @param destination * the array to hold the conversion * @param destOffset * the index where output will be put * @param options * alphabet type is pulled from this (standard, url-safe, ordered) * @return the number of decoded bytes converted * @since 1.3 */ private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset, int options) { byte[] DECODABET = getDecodabet(options); // Example: Dk== if (source[srcOffset + 2] == EQUALS_SIGN) { // Two ways to do the same thing. Don't know which way I like best. // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 // ) // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12); destination[destOffset] = (byte) (outBuff >>> 16); return 1; } // Example: DkL= else if (source[srcOffset + 3] == EQUALS_SIGN) { // Two ways to do the same thing. Don't know which way I like best. // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 // ) // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6); destination[destOffset] = (byte) (outBuff >>> 16); destination[destOffset + 1] = (byte) (outBuff >>> 8); return 2; } // Example: DkLE else { try { // Two ways to do the same thing. Don't know which way I like // best. // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) // >>> 6 ) // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF)); destination[destOffset] = (byte) (outBuff >> 16); destination[destOffset + 1] = (byte) (outBuff >> 8); destination[destOffset + 2] = (byte) (outBuff); return 3; } catch (Exception e) { LOG.debug("EXCEPTION (static), Base64.java):"); LOG.debug("\t" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]])); LOG.debug("\t" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]])); LOG.debug("\t" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]])); LOG.debug("\t" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]])); return -1; } // end catch } } // end decodeToBytes /** * Very low-level access to decoding ASCII characters in the form of a byte array. Does not support automatically * gunzipping or any other "fancy" features. * * @param source * The Base64 encoded data * @param off * The offset of where to begin decoding * @param len * The length of characters to decode * @return decoded data * @since 1.3 */ public static byte[] decode(byte[] source, int off, int len, int options) { byte[] DECODABET = getDecodabet(options); int len34 = len * 3 / 4; byte[] outBuff = new byte[len34]; // Upper limit on size of output int outBuffPosn = 0; byte[] b4 = new byte[4]; int b4Posn = 0; int i = 0; byte sbiCrop = 0; byte sbiDecode = 0; for (i = off; i < off + len; i++) { sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits sbiDecode = DECODABET[sbiCrop]; if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better { if (sbiDecode >= EQUALS_SIGN_ENC) { b4[b4Posn++] = sbiCrop; if (b4Posn > 3) { outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options); b4Posn = 0; // If that was the equals sign, break out of 'for' loop if (sbiCrop == EQUALS_SIGN) break; } // end if: quartet built } // end if: equals sign or better } // end if: white space, equals sign or better else { LOG.debug("Invalid Base64 input character at " + i + ": decimal=" + source[i] + " / char=" + (char) source[i]); return null; } // end else: } // each input character byte[] out = new byte[outBuffPosn]; System.arraycopy(outBuff, 0, out, 0, outBuffPosn); return out; } // end decode /** * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it. * * @param s * the string to decode * @return the decoded data * @since 1.4 */ public static byte[] decode(String s) { byte[] decodedBytes = decode(s, NO_OPTIONS); if (null == decodedBytes) { LOG.debug("Could not decode string: {}", s); } return decodedBytes; } /** * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it. * * @param s * the string to decode * @param options * encode options such as URL_SAFE * @return the decoded data * @since 1.4 */ public static byte[] decode(String s, int options) { byte[] bytes; try { bytes = s.getBytes(PREFERRED_ENCODING); } // end try catch (java.io.UnsupportedEncodingException uee) { bytes = s.getBytes(); } // end catch // // Decode bytes = decode(bytes, 0, bytes.length, options); // Check to see if it's gzip-compressed // GZIP Magic Two-Byte Number: 0x8b1f (35615) if (bytes != null && bytes.length >= 4) { int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) { java.io.ByteArrayInputStream bais = null; java.util.zip.GZIPInputStream gzis = null; java.io.ByteArrayOutputStream baos = null; byte[] buffer = new byte[2048]; int length = 0; try { baos = new java.io.ByteArrayOutputStream(); bais = new java.io.ByteArrayInputStream(bytes); gzis = new java.util.zip.GZIPInputStream(bais); while ((length = gzis.read(buffer)) >= 0) { baos.write(buffer, 0, length); } // end while: reading input // No error? Get new bytes. bytes = baos.toByteArray(); } // end try catch (java.io.IOException e) { // Just return originally-decoded bytes } // end catch finally { try { baos.close(); } catch (Exception e) {} try { gzis.close(); } catch (Exception e) {} try { bais.close(); } catch (Exception e) {} } // end finally } // end if: gzipped } // end if: bytes.length >= 2 return bytes; } // end decode /** * Attempts to decode Base64 data and deserialize a Java Object within. Returns null if there was an error. * * @param encodedObject * The Base64 data to decode * @return The decoded and deserialized object * @since 1.5 */ public static Object decodeToObject(String encodedObject) { // Decode and gunzip if necessary byte[] objBytes = decode(encodedObject); java.io.ByteArrayInputStream bais = null; java.io.ObjectInputStream ois = null; Object obj = null; try { bais = new java.io.ByteArrayInputStream(objBytes); ois = new java.io.ObjectInputStream(bais); obj = ois.readObject(); } // end try catch (java.io.IOException e) { LOG.debug("", e); obj = null; } // end catch catch (java.lang.ClassNotFoundException e) { LOG.debug("", e); obj = null; } // end catch finally { try { bais.close(); } catch (Exception e) {} try { ois.close(); } catch (Exception e) {} } // end finally return obj; } // end decodeObject /** * Convenience method for encoding data to a file. * * @param dataToEncode * byte array of data to encode in base64 form * @param filename * Filename for saving encoded data * @return true if successful, false otherwise * * @since 2.1 */ public static boolean encodeToFile(byte[] dataToEncode, String filename) { boolean success = false; Base64.OutputStream bos = null; try { bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE); bos.write(dataToEncode); success = true; } // end try catch (java.io.IOException e) { success = false; } // end catch: IOException finally { try { bos.close(); } catch (Exception e) {} } // end finally return success; } // end encodeToFile /** * Convenience method for decoding data to a file. * * @param dataToDecode * Base64-encoded data as a string * @param filename * Filename for saving decoded data * @return true if successful, false otherwise * * @since 2.1 */ public static boolean decodeToFile(String dataToDecode, String filename) { boolean success = false; Base64.OutputStream bos = null; try { bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE); bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); success = true; } // end try catch (java.io.IOException e) { success = false; } // end catch: IOException finally { try { bos.close(); } catch (Exception e) {} } // end finally return success; } // end decodeToFile /** * Convenience method for reading a base64-encoded file and decoding it. * * @param filename * Filename for reading encoded data * @return decoded byte array or null if unsuccessful * * @since 2.1 */ public static byte[] decodeFromFile(String filename) { byte[] decodedData = null; Base64.InputStream bis = null; try { // Set up some useful variables java.io.File file = new java.io.File(filename); byte[] buffer = null; int length = 0; int numBytes = 0; // Check for size of file if (file.length() > Integer.MAX_VALUE) { LOG.error("File is too big for this convenience method (" + file.length() + " bytes)."); return null; } // end if: file too big for int index buffer = new byte[(int) file.length()]; // Open a stream bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.DECODE); // Read until done while ((numBytes = bis.read(buffer, length, 4096)) >= 0) length += numBytes; // Save in a variable to return decodedData = new byte[length]; System.arraycopy(buffer, 0, decodedData, 0, length); } // end try catch (java.io.IOException e) { LOG.debug("EXCEPTION (static): Error decoding from file " + filename); } // end catch: IOException finally { try { bis.close(); } catch (Exception e) {} } // end finally return decodedData; } // end decodeFromFile /** * Convenience method for reading a binary file and base64-encoding it. * * @param filename * Filename for reading binary data * @return base64-encoded string or null if unsuccessful * * @since 2.1 */ public static String encodeFromFile(String filename) { LOG.trace("INVOKED (static) filius.rahmenprogramm.Base64, encodeFromFile(" + filename + ")"); String encodedData = null; Base64.InputStream bis = null; try { // Set up some useful variables java.io.File file = new java.io.File(filename); byte[] buffer = new byte[Math.max((int) (file.length() * 1.4), 40)]; // Need // max() // for // math // on // small // files // (v2.2.1) int length = 0; int numBytes = 0; // Open a stream bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.ENCODE); // LOG.debug("DEBUG, Base64, encodeFromFile: bis=" + // (bis==null ? "" : bis) ); // Read until done while ((numBytes = bis.read(buffer, length, 4096)) >= 0) length += numBytes; // LOG.debug("DEBUG, Base64, encodeFromFile: length=" + // length ); // Save in a variable to return encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING); // LOG.debug("DEBUG, Base64, encodeFromFile: encodedDate=" // + encodedData ); } // end try catch (java.io.IOException e) { LOG.error("Error encoding from file " + filename); } // end catch: IOException finally { try { bis.close(); } catch (Exception e) {} } // end finally return encodedData; } // end encodeFromFile /** * Reads infile and encodes it to outfile. * * @param infile * Input file * @param outfile * Output file * @since 2.2 */ public static void encodeFileToFile(String infile, String outfile) { String encoded = Base64.encodeFromFile(infile); java.io.OutputStream out = null; try { out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile)); out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output. } // end try catch (java.io.IOException ex) { LOG.debug("", ex); } // end catch finally { try { out.close(); } catch (Exception ex) {} } // end finally } // end encodeFileToFile /** * Reads infile and decodes it to outfile. * * @param infile * Input file * @param outfile * Output file * @since 2.2 */ public static void decodeFileToFile(String infile, String outfile) { byte[] decoded = Base64.decodeFromFile(infile); java.io.OutputStream out = null; try { out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile)); out.write(decoded); } // end try catch (java.io.IOException ex) { LOG.debug("", ex); } // end catch finally { try { out.close(); } catch (Exception ex) {} } // end finally } // end decodeFileToFile /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ /** * A {@link Base64.InputStream} will read data from another java.io.InputStream, given in the constructor, * and encode/decode to/from Base64 notation on the fly. * * @see Base64 * @since 1.3 */ public static class InputStream extends java.io.FilterInputStream { private boolean encode; // Encoding or decoding private int position; // Current position in the buffer private byte[] buffer; // Small buffer holding converted data private int bufferLength; // Length of buffer (3 or 4) private int numSigBytes; // Number of meaningful bytes in the buffer private int lineLength; private boolean breakLines; // Break lines at less than 80 characters private int options; // Record options used to create the stream. private byte[] alphabet; // Local copies to avoid extra method calls private byte[] decodabet; // Local copies to avoid extra method calls /** * Constructs a {@link Base64.InputStream} in DECODE mode. * * @param in * the java.io.InputStream from which to read data. * @since 1.3 */ public InputStream(java.io.InputStream in) { this(in, DECODE); } // end constructor /** * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE mode. *

* Valid options: * *

         *   ENCODE or DECODE: Encode or Decode as data is read.
         *   DONT_BREAK_LINES: don't break lines at 76 characters
         *     (only meaningful when encoding)
         *     Note: Technically, this makes your encoding non-compliant.
         * 
*

* Example: new Base64.InputStream( in, Base64.DECODE ) * * * @param in * the java.io.InputStream from which to read data. * @param options * Specified options * @see Base64#ENCODE * @see Base64#DECODE * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public InputStream(java.io.InputStream in, int options) { super(in); this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode = (options & ENCODE) == ENCODE; this.bufferLength = encode ? 4 : 3; this.buffer = new byte[bufferLength]; this.position = -1; this.lineLength = 0; this.options = options; // Record for later, mostly to determine // which alphabet to use this.alphabet = getAlphabet(options); this.decodabet = getDecodabet(options); } // end constructor /** * Reads enough of the input stream to convert to/from Base64 and returns the next byte. * * @return next byte * @since 1.3 */ public int read() throws java.io.IOException { // Do we need to get data? if (position < 0) { if (encode) { byte[] b3 = new byte[3]; int numBinaryBytes = 0; for (int i = 0; i < 3; i++) { try { int b = in.read(); // If end of stream, b is -1. if (b >= 0) { b3[i] = (byte) b; numBinaryBytes++; } // end if: not end of stream } // end try: read catch (java.io.IOException e) { // Only a problem if we got no data at all. if (i == 0) throw e; } // end catch } // end for: each needed input byte if (numBinaryBytes > 0) { encode3to4(b3, 0, numBinaryBytes, buffer, 0, options); position = 0; numSigBytes = 4; } // end if: got data else { return -1; } // end else } // end if: encoding // Else decoding else { byte[] b4 = new byte[4]; int i = 0; for (i = 0; i < 4; i++) { // Read four "meaningful" bytes: int b = 0; do { b = in.read(); } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC); if (b < 0) break; // Reads a -1 if end of stream b4[i] = (byte) b; } // end for: each needed input byte if (i == 4) { numSigBytes = decode4to3(b4, 0, buffer, 0, options); position = 0; } // end if: got four characters else if (i == 0) { return -1; } // end else if: also padded correctly else { // Must have broken out from above. throw new java.io.IOException("Improperly padded Base64 input."); } // end } // end else: decode } // end else: get data // Got data? if (position >= 0) { // End of relevant data? if ( /* !encode && */position >= numSigBytes) return -1; if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) { lineLength = 0; return '\n'; } // end if else { lineLength++; // This isn't important when decoding // but throwing an extra "if" seems // just as wasteful. int b = buffer[position++]; if (position >= bufferLength) position = -1; return b & 0xFF; // This is how you "cast" a byte that's // intended to be unsigned. } // end else } // end if: position >= 0 // Else error else { // When JDK1.4 is more accepted, use an assertion here. throw new java.io.IOException("Error in Base64 code reading stream."); } // end else } // end read /** * Calls {@link #read()} repeatedly until the end of stream is reached or len bytes are read. Returns * number of bytes read into array or -1 if end of stream is encountered. * * @param dest * array to hold values * @param off * offset for array * @param len * max number of bytes to read into array * @return bytes read into array or -1 if end of stream is encountered. * @since 1.3 */ public int read(byte[] dest, int off, int len) throws java.io.IOException { int i; int b; for (i = 0; i < len; i++) { b = read(); // if( b < 0 && i == 0 ) // return -1; if (b >= 0) dest[off + i] = (byte) b; else if (i == 0) return -1; else break; // Out of 'for' loop } // end for: each byte read return i; } // end read } // end inner class InputStream /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ /** * A {@link Base64.OutputStream} will write data to another java.io.OutputStream, given in the constructor, * and encode/decode to/from Base64 notation on the fly. * * @see Base64 * @since 1.3 */ public static class OutputStream extends java.io.FilterOutputStream { private boolean encode; private int position; private byte[] buffer; private int bufferLength; private int lineLength; private boolean breakLines; private byte[] b4; // Scratch used in a few places private boolean suspendEncoding; private int options; // Record for later private byte[] alphabet; // Local copies to avoid extra method calls private byte[] decodabet; // Local copies to avoid extra method calls /** * Constructs a {@link Base64.OutputStream} in ENCODE mode. * * @param out * the java.io.OutputStream to which data will be written. * @since 1.3 */ public OutputStream(java.io.OutputStream out) { this(out, ENCODE); } // end constructor /** * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE mode. *

* Valid options: * *

         *   ENCODE or DECODE: Encode or Decode as data is read.
         *   DONT_BREAK_LINES: don't break lines at 76 characters
         *     (only meaningful when encoding)
         *     Note: Technically, this makes your encoding non-compliant.
         * 
*

* Example: new Base64.OutputStream( out, Base64.ENCODE ) * * @param out * the java.io.OutputStream to which data will be written. * @param options * Specified options. * @see Base64#ENCODE * @see Base64#DECODE * @see Base64#DONT_BREAK_LINES * @since 1.3 */ public OutputStream(java.io.OutputStream out, int options) { super(out); this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode = (options & ENCODE) == ENCODE; this.bufferLength = encode ? 3 : 4; this.buffer = new byte[bufferLength]; this.position = 0; this.lineLength = 0; this.suspendEncoding = false; this.b4 = new byte[4]; this.options = options; this.alphabet = getAlphabet(options); this.decodabet = getDecodabet(options); } // end constructor /** * Writes the byte to the output stream after converting to/from Base64 notation. When encoding, bytes are * buffered three at a time before the output stream actually gets a write() call. When decoding, bytes are * buffered four at a time. * * @param theByte * the byte to write * @since 1.3 */ public void write(int theByte) throws java.io.IOException { // Encoding suspended? if (suspendEncoding) { super.out.write(theByte); return; } // end if: supsended // Encode? if (encode) { buffer[position++] = (byte) theByte; if (position >= bufferLength) // Enough to encode. { out.write(encode3to4(b4, buffer, bufferLength, options)); lineLength += 4; if (breakLines && lineLength >= MAX_LINE_LENGTH) { out.write(NEW_LINE); lineLength = 0; } // end if: end of line position = 0; } // end if: enough to output } // end if: encoding // Else, Decoding else { // Meaningful Base64 character? if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) { buffer[position++] = (byte) theByte; if (position >= bufferLength) // Enough to output. { int len = Base64.decode4to3(buffer, 0, b4, 0, options); out.write(b4, 0, len); // out.write( Base64.decode4to3( buffer ) ); position = 0; } // end if: enough to output } // end if: meaningful base64 character else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) { throw new java.io.IOException("Invalid character in Base64 data."); } // end else: not white space either } // end else: decoding } // end write /** * Calls {@link #write(int)} repeatedly until len bytes are written. * * @param theBytes * array from which to read bytes * @param off * offset for array * @param len * max number of bytes to read into array * @since 1.3 */ public void write(byte[] theBytes, int off, int len) throws java.io.IOException { // Encoding suspended? if (suspendEncoding) { super.out.write(theBytes, off, len); return; } // end if: supsended for (int i = 0; i < len; i++) { write(theBytes[off + i]); } // end for: each byte written } // end write /** * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without closing the stream. */ public void flushBase64() throws java.io.IOException { if (position > 0) { if (encode) { out.write(encode3to4(b4, buffer, position, options)); position = 0; } // end if: encoding else { throw new java.io.IOException("Base64 input not properly padded."); } // end else: decoding } // end if: buffer partially full } // end flush /** * Flushes and closes (I think, in the superclass) the stream. * * @since 1.3 */ public void close() throws java.io.IOException { // 1. Ensure that pending characters are written flushBase64(); // 2. Actually close the stream // Base class both flushes and closes. super.close(); buffer = null; out = null; } // end close /** * Suspends encoding of the stream. May be helpful if you need to embed a piece of base640-encoded data in a * stream. * * @since 1.5.1 */ public void suspendEncoding() throws java.io.IOException { flushBase64(); this.suspendEncoding = true; } // end suspendEncoding /** * Resumes encoding of the stream. May be helpful if you need to embed a piece of base640-encoded data in a * stream. * * @since 1.5.1 */ public void resumeEncoding() { this.suspendEncoding = false; } // end resumeEncoding } // end inner class OutputStream } // end class Base64 filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/EingabenUeberpruefung.java000066400000000000000000000142661506574241500275250ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.awt.Color; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * Dient dazu Eingaben auf Richtigkeit zu Pruefen. Dazu stehen verschiedene Pattern zur Verfuegung, z.B. fuer * IP-Adressen oder Klassen-Namen. Mit diesen Pattern wird die Funktion isGueltig aufgerufen, die den String auf ein * Muster prueft. * * @author Johannes Bade * */ public class EingabenUeberpruefung implements I18n { private static Logger LOG = LoggerFactory.getLogger(EingabenUeberpruefung.class); // NOTE: include *.*.*.0 to be able to still use this pattern for routing // table configuration as network identifier public static final Pattern musterIpAdresse = Pattern.compile( "^(0*([1-9]?[0-9]?|1[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\\.((0*(1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\\.){2}(0*(1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))$"); public static final Pattern musterIpAdresseAuchLeer = Pattern.compile( "^((0*([1-9]?[0-9]?|1[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\\.((0*(1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\\.){2}(0*(1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))){0,1}$"); public static final Pattern musterSubNetz = Pattern.compile( "^(0*([1-9]|1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\\.((0*([0-9]|1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\\.){2}(0*([0-9]|1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))$"); private static final String rawMailAddress = "[a-zA-Z0-9]([\\-_\\.]{0,1}[a-zA-Z0-9])*@[a-zA-Z0-9]([\\-_\\.]{0,1}[a-zA-Z0-9])*\\.{0,1}"; public static final Pattern musterEmailAdresse = Pattern .compile("^[a-zA-Z0-9 \\-_\\.]* <" + rawMailAddress + ">" + "|" + rawMailAddress + "$"); public static final Pattern musterKlassenName = Pattern.compile("[A-Z]([a-zA-Z]{2,})?"); public static final Pattern musterPort = Pattern .compile("([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])"); public static final Pattern musterPortAuchLeer = Pattern.compile( "(^$|[1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])"); public static final Pattern musterKeineLeerzeichen = Pattern.compile("[^\\s]*"); public static final Pattern musterEmailBenutzername = Pattern.compile("([a-zA-Z0-9]|\\.|\\_|\\-)*"); public static final Pattern musterMindEinZeichen = Pattern.compile("(.){1,}"); public static final Pattern musterNurZahl = Pattern.compile("\\d"); public static final Pattern musterNurZahlen = Pattern.compile("\\d*"); public static final Pattern musterDomain = Pattern.compile( "^([a-zA-Z]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]){0,1}(\\.[a-zA-Z]([a-zA-Z0-9\\\\-]{0,61}[a-zA-Z0-9]){0,1})*){0,1}\\.{0,1}$"); public static final Pattern musterSubnetBinary = Pattern.compile("^11*0*$"); public static final Pattern musterMacAddress = Pattern.compile("^[0-9a-fA-f]{2}(:[0-9a-fA-f]{2}){5}$"); public static final Pattern musterServiceSetIdentifier = Pattern.compile("^[ -~]+$"); public static final Pattern musterProtocol = Pattern.compile("^[Tt][Cc][Pp]$|^[Uu][Dd][Pp]$|^6$|^17$"); // ermöglicht auch die Eingabe eines Protocols als Zahl public static final Color farbeFalsch = new Color(255, 20, 20); public static final Color farbeRichtig = new Color(0, 0, 0); /** * Die Funktion isGueltig bekommt einen String und ein Muster (Vorzugsweise ein Muster, dass in der Klasse * EingabenUeberpruefung vorgegeben ist) Moeglicher Aufruf: isGueltig(ipAdresse, * EingabeUeberpruefung.musterIpAdresse) * * @param zuPruefen * @param muster * @return */ public static boolean isGueltig(String zuPruefen, Pattern muster) { LOG.trace("INVOKED (EingabenUeberpruefung), isGueltig(" + zuPruefen + "," + muster + ")"); Matcher m = muster.matcher(zuPruefen); return m.matches(); } public static boolean isValidSubnetmask(String subnet) { LOG.trace("INVOKED (EingabenUeberpruefung), isValidSubnetmask(" + subnet + ")"); String[] token = subnet.split("\\."); String binary = ""; LOG.debug("(EingabenUeberpruefung), '" + token + "', length=" + token.length); if (token.length != 4) return false; try { for (int i = 0; i < token.length; i++) { String currBin = Integer.toBinaryString(Integer.parseInt(token[i])); while (currBin.length() < 8) currBin = "0" + currBin; binary += currBin; LOG.debug("(EingabenUeberpruefung), '" + token[i] + "' ~~> binary (" + i + ") = '" + binary + "'"); } } catch (Exception e) { return false; } if (binary.length() == 32 && isGueltig(binary, EingabenUeberpruefung.musterSubnetBinary)) return true; return false; } public static boolean isValidIpAddress(String addressString) { return isGueltig(addressString, musterIpAdresse); } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/ErzeugeZipDatei.java000066400000000000000000000102361506574241500263070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ErzeugeZipDatei { private static Logger LOG = LoggerFactory.getLogger(ErzeugeZipDatei.class); /** * @author weyer Die Klasse kopiert den Inhalt eines Verzeichnisses in eine Zip-Datei und speichert diese in diesem * Verzeichnis. * @param dir * Verzeichnis, dessen Inhalt in eine Zip-Datei eingelesen werden soll. * @param zipFileName * Der Name, den die Zip-Datei haben soll. * @throws IOException */ public ErzeugeZipDatei(String verzeichnis, String zipFileName) throws IOException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", constr: ErzeugeZipDatei(" + verzeichnis + "," + zipFileName + ")"); // Puffer zum Auslesen der Dateien. // Der vollständige Pfad zur // zukünftigen Zip-Datei. String dirFile = verzeichnis + zipFileName; // Die Dateien werden in diese Datei geschrieben. ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(dirFile)); // Das Verzeichnis, dessen Dateien gesichert werden sollen. File dirZip = new File(verzeichnis); zipDatei(dirZip, zipOut); // Die Zip-Datei ist erzeugt. zipOut.close(); } /** * @author weyer * @param dirZip * @param zipOut * @throws IOException */ public void zipDatei(File verzeichnisZip, ZipOutputStream zipOut) throws IOException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", zipDatei(" + verzeichnisZip + "," + zipOut + ")"); byte[] buf = new byte[4096]; // Alle Dateinamen aus dem Verzeichnis werden in des Array eingelesen. File[] fileArray = verzeichnisZip.listFiles(); String fileName = ""; // Alle Dateien auslesen. for (int i = 0; i < fileArray.length; i++) { // Der Name der Datei wird ermittelt. fileName = fileArray[i].getAbsolutePath(); // Zip-Dateien ignorieren. if (fileName.endsWith(".zip")) continue; if (fileArray[i].isDirectory()) { zipDatei(fileArray[i], zipOut); } else { // Die Datei wird zum Lesen geöffnet. FileInputStream inFile = new FileInputStream(fileName); // Info an Zip-Datei: Jetzt kommt neuer Eintrag. zipOut.putNextEntry(new ZipEntry(fileName)); int len; // Der Inhalt der Datei wird in die Zip-Datei kopiert. while ((len = inFile.read(buf)) > 0) { zipOut.write(buf, 0, len); } inFile.close(); } } } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/FiliusArgs.java000066400000000000000000000052241506574241500253200ustar00rootroot00000000000000package filius.rahmenprogramm; import java.io.File; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; public class FiliusArgs { public String argsString = ""; public String currWD = Information.initArbeitsbereichPfad; public String projectFile; public boolean log = false; public String newWD = null; public boolean nativeLookAndFeel = false; public boolean verbose = false; public boolean help = false; public int rtt = 1; public boolean lowResolution; private Options options = new Options(); public FiliusArgs() { options.addOption("h", false, "Show usage info and exit"); options.addOption("v", false, "Activate verbose log"); options.addOption("w", "wd", true, "The working directory that is used during program execution"); options.addOption("l", false, "Activate logging to log file"); options.addOption("r", true, "Factor to adapt round-trip-time in Filius network: 1 (default) to 5"); options.addOption("n", false, "Activate native look and feel graphical user interface"); options.addOption("s", false, "Adapt Filius ui for small screen resolution (less than 1024x768)"); } public void parseCommandLine(String[] args) throws ParseException { CommandLineParser parser = new DefaultParser(); CommandLine cmd = parser.parse(options, args); if (cmd.hasOption("l")) { log = true; } if (cmd.hasOption("h")) { help = true; } if (cmd.hasOption("v")) { verbose = true; } if (cmd.hasOption("w")) { currWD = cmd.getOptionValue("w"); } if (cmd.hasOption("n")) { nativeLookAndFeel = true; } if (cmd.hasOption("s")) { lowResolution = true; } if (cmd.hasOption("r")) { try { rtt = Integer.parseInt(cmd.getOptionValue("r")); } catch (NumberFormatException e) {} } if (cmd.getArgs().length == 1) { try { File projectfile = new File(cmd.getArgs()[0]); if (projectfile.exists()) { projectFile = projectfile.getAbsolutePath(); } } catch (Exception e) {} } } public void showUsageInformation() { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("filius [OPTIONS] [FILE]", options); } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/FiliusClassLoader.java000066400000000000000000000057571506574241500266330ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author freischlad * * * Aus der Java-Doc fuer die Klasse ClassLoader:
* The ClassLoader class uses a delegation model to search for classes and resources. Each instance of * ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader * instance will delegate the search for the class or resource to its parent class loader before attempting to * find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class * loader", does not itself have a parent but may serve as the parent of a ClassLoader instance. */ public class FiliusClassLoader extends URLClassLoader { private static Logger LOG = LoggerFactory.getLogger(FiliusClassLoader.class); private static FiliusClassLoader classLoader; protected FiliusClassLoader(ClassLoader parent) throws MalformedURLException { super(new URL[] { new File(Information.getInformation().getAnwendungenPfad()).toURI().toURL() }, parent); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + ", constr: FiliusClassLoader(" + parent + ")"); } public static FiliusClassLoader getInstance(ClassLoader parent) { LOG.trace("INVOKED (static) filius.rahmenprogramm.FiliusClassLoader, getInstance()"); if (classLoader == null) { try { classLoader = new FiliusClassLoader(parent); } catch (MalformedURLException e) { e.printStackTrace(); } } return classLoader; } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/I18n.java000066400000000000000000000026031506574241500237650ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.util.ResourceBundle; public interface I18n { ResourceBundle messages = Information.getInformation().holeResourceBundle(); } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/IconMode.java000066400000000000000000000027611506574241500247500ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; public enum IconMode { ENTERPRISE("E.png"), SYMBOL("S.png"), DEFAULT(".png"); private final String suffix; private IconMode(String suffix) { this.suffix = suffix; } public String suffix() { return suffix; } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/Information.java000066400000000000000000000653761506574241500255530ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.awt.Frame; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.io.Serializable; import java.nio.charset.Charset; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.ResourceBundle; import java.util.StringTokenizer; import java.util.Vector; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.Main; import filius.gui.GUIContainer; import filius.gui.anwendungssicht.GUIDesktopWindow; import filius.hardware.Verbindung; /** * In dieser Klasse werden die Verwaltungs-Informationen des Rahmenprogramms verwaltet, die unabhaengig von einem * bestimmten Projekt sind. */ public class Information implements Serializable { private static Logger LOG = LoggerFactory.getLogger(Information.class); public enum FeatureMode { FORCE_DISABLE, FORCE_ENABLE, AUTO } private static final long serialVersionUID = 1L; public static final String FILENAME_EDITED_SZENARIO = "edited_scenario.fls"; /** Zur Implementierung des Singleton */ private static Information information = null; private static boolean lowResolution = false; private static boolean posixCommandLineToolBehaviour = false; private static GUIDesktopWindow.Mode desktopWindowMode = GUIDesktopWindow.Mode.STACK; public static GUIDesktopWindow.Mode getDesktopWindowMode() { return desktopWindowMode; } public static boolean isPosixCommandLineToolBehaviour() { return posixCommandLineToolBehaviour; } private static String version = null; public static boolean isLowResolution() { return lowResolution; } public static void setLowResolution(boolean smallResolution) { Information.lowResolution = smallResolution; } public boolean initOk = false; /** * Die MAC-Adressen werden zentral verwaltet, um zu gewaehrleisten, dass keine Adresse mehrfach vergeben wird. */ private Vector macAdressen = new Vector(); /** * Die maximale Anzahl von Vermittlungsstellen wird zur Berechnung des Time-Outs fuer eine TCP-Verbindung genutzt. */ private int maxVermittlungsStellen; /** * Pfad zum Verzeichnis, in dem das Programm ausgefuehrt wird (in dem sich die ausfuehrbare Jar-Datei befindet); *
* der Pfad schliesst mit dem Pfad-Trennzeichen (unter UNIX "/") */ private String programmPfad = null; /** * Pfad zum Verzeichnis, in dem die benutzerspezifischen Daten abgelegt werden;
* der Pfad schliesst mit dem Pfad-Trennzeichen (unter UNIX "/") */ public static String initArbeitsbereichPfad = getHomeDir() // System.getProperty("user.home") + System.getProperty("file.separator"); // actually used working directory, i.e., path to be used after initial // tests private String arbeitsbereichPfad = getHomeDir() // System.getProperty("user.home") + System.getProperty("file.separator") + ".filius" + System.getProperty("file.separator"); /** Lokalisierungsobjekt fuer Standard-Spracheinstellung */ private Locale locale; private String lastOpenedDirectory; private boolean oldExchangeDialog = true; private FeatureMode softwareWizardMode = FeatureMode.FORCE_ENABLE; private boolean gatewayAvailable = true; private IconMode iconMode = IconMode.DEFAULT; private boolean layerVisualization = false; public boolean isLayerVisualization() { return layerVisualization; } public void setLayerVisualization(boolean layerVisualization) { this.layerVisualization = layerVisualization; } public boolean isGatewayAvailable() { return gatewayAvailable; } public FeatureMode getSoftwareWizardMode() { return softwareWizardMode; } public boolean isOldExchangeDialog() { return oldExchangeDialog; } private static String getHomeDir() { return System.getProperty("user.home"); } /** * ensure a correct and well functioning path to write all necessary data */ private boolean checkWD(String currPath) { boolean nowrite = true; String directoryPath; java.util.Random rnd = new java.util.Random(); String randomString = Long.toString(rnd.nextLong()); java.io.File testFile = null; do { directoryPath = currPath + ".filius" + System.getProperty("file.separator"); try { testFile = new java.io.File(directoryPath); // write check, i.e., create random directory and delete it again testFile.mkdirs(); testFile = new java.io.File(directoryPath + randomString); testFile.createNewFile(); if (!testFile.delete()) { throw new Exception("EXCEPTION: Error on deleting test file in write-check"); } nowrite = false; } catch (Exception e) { // open dialog to choose another directory JOptionPane.showMessageDialog(null, "Fehler: Verzeichnis ist nicht schreibbar. Filius benötigt aber Schreibrechte.\n" + "Bitte wählen Sie ein Verzeichnis, für das Sie Schreibrechte besitzen.\n\n" + "Error: Directory is not writeable. But Filius needs write permissions.\n" + "Please choose a directory where you have write permissions.", "Fehler / Error", javax.swing.JOptionPane.ERROR_MESSAGE); JFileChooser fc = new JFileChooser(); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); if (fc.showOpenDialog(new Frame()) == JFileChooser.APPROVE_OPTION) { currPath = fc.getSelectedFile().getAbsolutePath() + System.getProperty("file.separator"); } else { return false; } } } while (nowrite); arbeitsbereichPfad = currPath + ".filius" + System.getProperty("file.separator"); return true; } /** * nicht oeffentlich zugaenglicher Konstruktor, wird aus getInformation() aufgerufen */ private Information() { if (checkWD(initArbeitsbereichPfad)) { init(); initOk = true; } } private Information(String path) { if (checkWD(path)) { init(); initOk = true; } } /** Methode zum Zugriff auf Singleton */ public static Information getInformation() { return getInformation((String) null); } public static Information getInformation(String path) { if (information == null) { if (path != null) information = new Information(path); else information = new Information(); } if (information.initOk) return information; else return null; } /** aktuelle Programmversion */ public static String getVersion() { if (version == null) { Properties properties = new Properties(); try { InputStream in = Information.class.getClassLoader().getResourceAsStream("application.properties"); properties.load(in); in.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } version = properties.getProperty("application.version") + " (" + properties.getProperty("application.buildDate") + ")"; } return version; } /** * Zugriff auf das sprachabhaengige Objekt zur Verwaltung der Texte. * * @return */ public ResourceBundle holeResourceBundle() { ResourceBundle bundle = ResourceBundle.getBundle("filius.messages.MessagesBundle", getLocaleOrDefault()); return bundle; } public Locale getLocale() { return locale; } public Locale getLocaleOrDefault() { if (locale == null) { return Locale.GERMANY; } else { return locale; } } public void setLocale(Locale locale) { this.locale = locale; } /** * Zuruecksetzen aller Einstellungen, die zur Laufzeit von Filius veraendert wurden und alle Projektspezifischen * Daten. */ public void reset() { macAdressen.clear(); GUIContainer.getGUIContainer().getExchangeDialog().reset(); init(); } private void init() { maxVermittlungsStellen = 48; try { initialisiereVerzeichnisse(); } catch (Exception e) { LOG.debug("", e); } SzenarioVerwaltung.loescheVerzeichnisInhalt(getTempPfad()); } /** * Hier werden die Verzeichnisse und die Datei mit den Informationen zu den eigenen Anwendungen erstellt, wenn sie * noch nicht existieren: *

    *
  • Arbeitsbereich: Verzeichnis, in dem alle benutzerspezifischen Daten gespeichert werden
  • *
  • Temp: Verzeichnis, in dem zur Laufzeit temporaere Dateien gespeichert werden
  • *
  • Anwendungen: Verzeichnis, in dem die eigenen Anwendungen gespeichert werden mit den Unterordnern * software/clientserver/ und gui/anwendungssicht/
  • *
  • Datei, in der die Einstellungen zu eigenen Anwendungen gespeichert werden
  • *
*/ private void initialisiereVerzeichnisse() throws FileNotFoundException, IOException { String pfad; pfad = getArbeitsbereichPfad(); if (!(new java.io.File(pfad)).exists()) if (!(new java.io.File(pfad)).mkdirs()) LOG.error(pfad + " konnte nicht erzeugt werden"); pfad = getTempPfad(); if (!(new java.io.File(pfad)).exists()) if (!(new java.io.File(pfad)).mkdirs()) LOG.error(pfad + " konnte nicht erzeugt werden"); pfad = getAnwendungenPfad(); if (!(new java.io.File(pfad)).exists()) if (!(new java.io.File(pfad)).mkdirs()) LOG.error(pfad + " konnte nicht erzeugt werden"); pfad = getAnwendungenPfad() + "filius" + System.getProperty("file.separator") + "software" + System.getProperty("file.separator") + "clientserver" + System.getProperty("file.separator"); if (!(new java.io.File(pfad)).exists()) if (!(new java.io.File(pfad)).mkdirs()) LOG.error(pfad + " konnte nicht erzeugt werden"); pfad = getAnwendungenPfad() + "filius" + System.getProperty("file.separator") + "gui" + System.getProperty("file.separator") + "anwendungssicht" + System.getProperty("file.separator"); if (!(new java.io.File(pfad)).exists()) if (!(new java.io.File(pfad)).mkdirs()) LOG.error(pfad + " konnte nicht erzeugt werden"); pfad = getAnwendungenPfad() + "EigeneAnwendungen.txt"; if (!(new java.io.File(pfad)).exists()) (new java.io.File(getAnwendungenPfad() + "EigeneAnwendungen.txt")).createNewFile(); } /** * Methode zum lesen der Verfuegbaren Programme aus den Konfigurationsdateien. * * @return * @throws IOException */ public List> ladeProgrammListe() { List> tmpList = new LinkedList>(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream(holeAnwendungenDateipfad()), Charset.forName("UTF-8")))) { for (String line; (line = reader.readLine()) != null;) { if (!line.trim().startsWith("#") && !line.trim().equals("")) { HashMap tmpMap = new HashMap(); StringTokenizer st = new StringTokenizer(line, ";"); tmpMap.put("Anwendung", st.nextToken()); tmpMap.put("Klasse", st.nextToken()); tmpMap.put("GUI-Klasse", st.nextToken()); tmpMap.put("gfxFile", st.nextToken()); tmpList.add(tmpMap); } } } catch (IOException e) { LOG.debug("could not read list of available applications", e); } tmpList.addAll(ladePersoenlicheProgrammListe()); return tmpList; } public LinkedList> ladePersoenlicheProgrammListe() { LinkedList> tmpList; tmpList = new LinkedList>(); try (RandomAccessFile desktopFile = new RandomAccessFile( Information.getInformation().getAnwendungenPfad() + "EigeneAnwendungen.txt", "r")) { for (String line; (line = desktopFile.readLine()) != null;) { HashMap tmpMap = new HashMap(); if (!line.trim().equals("")) { StringTokenizer st = new StringTokenizer(line, ";"); tmpMap.put("Anwendung", st.nextToken()); tmpMap.put("Klasse", st.nextToken()); tmpMap.put("GUI-Klasse", st.nextToken()); tmpMap.put("gfxFile", st.nextToken()); tmpList.add(tmpMap); } } } catch (IOException e) { LOG.debug("could not read list of available applications", e); } return tmpList; } private String holeAnwendungenDateipfad() { String pfad = "config/Desktop"; File desktopResource = ResourceUtil.getResourceFile(pfad + "_" + locale.toString() + ".txt"); return desktopResource.getAbsolutePath(); } /** * Pfad zum Verzeichnis, in dem das Programm ausgefuehrt wird (in dem sich die ausfuehrbare Jar-Datei befindet); *
* der Pfad schliesst mit dem Pfad-Trennzeichen (unter UNIX "/") */ public String getProgrammPfad() { if (programmPfad != null) { return programmPfad; } else { String str = System.getProperty("java.class.path"); programmPfad = System.getProperty("user.dir") + System.getProperty("file.separator"); if (str.indexOf("filius.jar") >= 0) { // run from jar file if ((new File(str)).isAbsolute()) programmPfad = ""; // in case of absolute path, delete // "user.dir" entry // da Java beim Aufruf verschiedene Separatoren unterstützt, // wird hier getrennt abgefragt... if (str.indexOf("/") >= 0) { programmPfad += str.substring(0, str.lastIndexOf("/")) + System.getProperty("file.separator"); } else if (str.indexOf("\\") >= 0) { programmPfad += str.substring(0, str.lastIndexOf("\\")) + System.getProperty("file.separator"); } } return programmPfad; } } public String getRelativePathToProgramDir() { String workPath = System.getProperty("user.dir") + File.separator; String progPath = getProgrammPfad(); // Windows system (with drive letters!): if (File.separator.equals("\\")) { if (progPath.substring(1, 3).equals(workPath.substring(1, 3))) { progPath = progPath.substring(2); workPath = workPath.substring(2); } else { // different drives; --> return absolute path return progPath; } } // ///// // remove first File.separator (first character) progPath = progPath.substring(1); workPath = workPath.substring(1); String relativePath = ""; int slashIdx = -1; boolean finished = false; // if workPath is completely part of progPath and at beginning of it: if (progPath.indexOf(workPath) == 0) { if (progPath.length() <= workPath.length()) progPath = ""; else progPath = progPath.substring(workPath.length()); workPath = ""; } else if (workPath.indexOf(progPath) == 0) { // otherway round if (workPath.length() <= progPath.length()) workPath = ""; else workPath = workPath.substring(progPath.length()); progPath = ""; } // further processing while (!finished) { slashIdx = workPath.indexOf(File.separator); if (slashIdx >= 0) { // subdirectories left to be stepped up via // "../" strings relativePath += ".." + File.separator; if (workPath.length() <= slashIdx + 2) workPath = ""; else workPath = workPath.substring(slashIdx + 1); } else { // only append remaining path to step down (again) finished = true; relativePath += progPath; return relativePath; } } return null; } /** Arbeitsbereich: Verzeichnis, in dem alle benutzerspezifischen */ public String getArbeitsbereichPfad() { return arbeitsbereichPfad; } public void setArbeitsbereichPfad(String otherWD) { StringTokenizer tokenizer = null; String token = null; if (otherWD.indexOf("/") >= 0) { tokenizer = new StringTokenizer(otherWD, "/"); } else if (otherWD.indexOf("\\\\") >= 0) { tokenizer = new StringTokenizer(otherWD, "\\\\"); } if (otherWD.startsWith("/") || otherWD.startsWith("\\\\")) { arbeitsbereichPfad = System.getProperty("file.separator"); } else arbeitsbereichPfad = ""; if (tokenizer != null) { while (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); if (!token.isEmpty()) arbeitsbereichPfad += token + System.getProperty("file.separator"); } } else { arbeitsbereichPfad = otherWD + System.getProperty("file.separator"); } arbeitsbereichPfad += ".filius" + System.getProperty("file.separator"); } /** * Temp: Verzeichnis, in dem zur Laufzeit temporaere Dateien gespeichert werden */ public String getTempPfad() { return getArbeitsbereichPfad() + "temp" + System.getProperty("file.separator"); } /** * Anwendungen: Verzeichnis, in dem die eigenen Anwendungen gespeichert werden mit den Unterordnern * software/clientserver/ und gui/anwendungssicht/ */ public String getAnwendungenPfad() { return getArbeitsbereichPfad() + "anwendungen" + System.getProperty("file.separator"); } /** * Automatische Erzeugung einer MAC-Adresse, funktioniert mit Hexadezimal-Zahlen */ public String holeFreieMACAdresse() { Random r = new Random(); String[] mac; String neueMac; mac = new String[6]; for (int i = 0; i < mac.length; i++) { mac[i] = Integer.toHexString(r.nextInt(255)); if (mac[i].length() == 1) mac[i] = "0" + mac[i]; } neueMac = mac[0] + ":" + mac[1] + ":" + mac[2] + ":" + mac[3] + ":" + mac[4] + ":" + mac[5]; if (macPruefen(neueMac)) { return neueMac; } else { return holeFreieMACAdresse(); } } /** Eintragen einer verwendeten MAC-Adresse */ public void macHinzufuegen(String mac) { macAdressen.addElement(mac); } /** Pruefen, ob es sich um eine verfuegbare, gueltige MAC-Adresse handelt. */ private boolean macPruefen(String mac) { boolean macOK = true; for (int i = 0; i < macAdressen.size(); i++) { if (mac.equals((String) macAdressen.elementAt(i))) macOK = false; } if (mac.equalsIgnoreCase("ff:ff:ff:ff:ff:ff")) { macOK = false; } return macOK; } public int getMaxVermittlungsStellen() { return maxVermittlungsStellen; } public void setMaxVermittlungsStellen(int maxVermittlungsStellen) { this.maxVermittlungsStellen = maxVermittlungsStellen; } public void loadIni() throws IOException { File tmpFile = ResourceUtil.getResourceFile("config/filius.ini"); if (tmpFile.exists()) { RandomAccessFile iniFile = null; try { iniFile = new RandomAccessFile(tmpFile.getAbsolutePath(), "r"); int width = 0; int height = 0; for (String line; (line = iniFile.readLine()) != null;) { if (!line.trim().equals("") && !line.trim().startsWith("#")) { StringTokenizer st = new StringTokenizer(line, "="); if (st.hasMoreTokens()) { String configKey = st.nextToken().trim(); if (st.hasMoreTokens()) { String configValue = st.nextToken(); if (configKey.equalsIgnoreCase("locale")) { String language = configValue.substring(0, configValue.indexOf("_")); String country = configValue.substring(configValue.indexOf("_") + 1); this.setLocale(new Locale(language, country)); } else if (configKey.equalsIgnoreCase("rtt")) { if (Verbindung.getRTTfactor() == 1) { Verbindung.setRTTfactor(Integer.parseInt(configValue)); } } else if (configKey.equalsIgnoreCase("native-look-n-feel")) { if (configValue.trim().equals("1")) { Main.activateNativeLookAndFeel(); } } else if (configKey.equalsIgnoreCase("posix-behaviour")) { if (configValue.trim().equals("1")) { posixCommandLineToolBehaviour = true; } } else if (configKey.equalsIgnoreCase("desktop-mode")) { desktopWindowMode = GUIDesktopWindow.Mode .getMode(Integer.parseInt(configValue.trim())); } else if (configKey.equalsIgnoreCase("old-exchange-dialog")) { if (configValue.trim().equals("1")) { this.oldExchangeDialog = true; } else if (configValue.trim().equals("0")) { this.oldExchangeDialog = false; } } else if (configKey.equalsIgnoreCase("software-wizard")) { if (configValue.trim().equals("1")) { softwareWizardMode = FeatureMode.FORCE_ENABLE; } else if (configValue.trim().equals("0")) { softwareWizardMode = FeatureMode.FORCE_DISABLE; } else if (configValue.trim().equals("2")) { softwareWizardMode = FeatureMode.AUTO; } } else if (configKey.equalsIgnoreCase("pane-width")) { try { width = Integer.parseInt(configValue); } catch (NumberFormatException e) {} } else if (configKey.equalsIgnoreCase("pane-height")) { try { height = Integer.parseInt(configValue); } catch (NumberFormatException e) {} } else if (configKey.equalsIgnoreCase("gateway")) { if (configValue.trim().equals("1")) { this.gatewayAvailable = true; } else if (configValue.trim().equals("0")) { this.gatewayAvailable = false; } } } } } } if (width > 0 && height > 0) { GUIContainer.getGUIContainer(width, height); } } finally { if (iniFile != null) iniFile.close(); } } } public String getLastOpenedDirectory() { return lastOpenedDirectory; } public void setLastOpenedDirectory(String lastOpenedDirectory) { this.lastOpenedDirectory = lastOpenedDirectory; } public void setIconMode(IconMode mode) { this.iconMode = mode; } public IconMode getIconMode() { // TODO Auto-generated method stub return iconMode; } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/KnotenPersistenceDelegate.java000066400000000000000000000052341506574241500303470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.beans.DefaultPersistenceDelegate; import java.beans.Encoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Host; import filius.hardware.knoten.Modem; import filius.hardware.knoten.Switch; import filius.hardware.knoten.Vermittlungsrechner; public class KnotenPersistenceDelegate extends DefaultPersistenceDelegate { private static Logger LOG = LoggerFactory.getLogger(KnotenPersistenceDelegate.class); public void writeObject(Object oldInstance, Encoder out) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", writeObject()"); if (oldInstance instanceof Host) { // LOG.debug("\tObjekt der Klasse Host"); super.writeObject((Host) oldInstance, out); } else if (oldInstance instanceof Vermittlungsrechner) { // LOG.debug("\tObjekt der Klasse Vermittlungsrechner"); super.writeObject((Vermittlungsrechner) oldInstance, out); } else if (oldInstance instanceof Modem) { // LOG.debug("\tObjekt der Klasse Modem"); super.writeObject((Modem) oldInstance, out); } else if (oldInstance instanceof Switch) { // LOG.debug("\tObjekt der Klasse Switch"); super.writeObject((Switch) oldInstance, out); } else { // LOG.debug("\tObjekt der Klasse 'unbekannt'"); super.writeObject(oldInstance, out); } } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/LadeZipDatei.java000066400000000000000000000065551506574241500255570ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LadeZipDatei { private static Logger LOG = LoggerFactory.getLogger(LadeZipDatei.class); public void extrahierenArchiv(File archiv, File zielDir) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", extrahiereArchiv(" + archiv + "," + zielDir + ")"); if (!zielDir.exists()) { zielDir.mkdir(); } ZipFile zipFile = new ZipFile(archiv); Enumeration eingaenge = zipFile.entries(); byte[] buffer = new byte[16384]; int laenge; while (eingaenge.hasMoreElements()) { ZipEntry eingang = (ZipEntry) eingaenge.nextElement(); String eingangFileName = eingang.getName(); File verzeichnis = baueVerzeichnisHierarchieFuer(eingangFileName, zielDir); if (!verzeichnis.exists()) { verzeichnis.mkdirs(); } if (!eingang.isDirectory()) { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(new File(zielDir, eingangFileName))); BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(eingang)); while ((laenge = bis.read(buffer)) > 0) { bos.write(buffer, 0, laenge); } bos.flush(); bos.close(); bis.close(); } } } private File baueVerzeichnisHierarchieFuer(String eingangName, File zielDir) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", baueVerzeichnisHierarchieFuer(" + eingangName + "," + zielDir + ")"); int lastIndex = eingangName.lastIndexOf('/'); String internalPathToEntry = eingangName.substring(0, lastIndex + 1); return new File(zielDir, internalPathToEntry); } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/ResourceUtil.java000066400000000000000000000025501506574241500256740ustar00rootroot00000000000000package filius.rahmenprogramm; import java.io.File; import java.net.URL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.util.UriUtils; public class ResourceUtil { private static Logger LOG = LoggerFactory.getLogger(ResourceUtil.class); public static File getResourceFile(String relativePath) { String path = getResourcePath(relativePath); if (path == null) { return null; } return new File(path); } public static String getResourcePath(String relativePath) { String urlEncodedPath = getResourceUrlEncodedPath(relativePath); String path = null; if (urlEncodedPath != null) { LOG.debug(urlEncodedPath); path = UriUtils.decode(urlEncodedPath, "utf8"); LOG.debug("Resolved path: " + path); } return path; } public static String getResourceUrlEncodedPath(String relativePath) { String urlEncodedPath = null; URL systemResource = ClassLoader.getSystemResource(relativePath); if (null == systemResource) { LOG.debug("Resource " + relativePath + " could not be found!"); } else { LOG.debug("Resource " + systemResource); urlEncodedPath = systemResource.getPath().replace("+", "%2b"); } return urlEncodedPath; } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/SzenarioVerwaltung.java000066400000000000000000000575071506574241500271340ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm; import java.beans.ExceptionListener; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Observable; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.skuzzle.semantic.Version; import de.skuzzle.semantic.Version.VersionFormatException; import filius.gui.GUIErrorHandler; import filius.gui.netzwerksicht.GUIDocuItem; import filius.gui.netzwerksicht.GUIKabelItem; import filius.gui.netzwerksicht.GUIKnotenItem; import filius.gui.netzwerksicht.JSidebarButton; public class SzenarioVerwaltung extends Observable implements I18n { private static Logger LOG = LoggerFactory.getLogger(SzenarioVerwaltung.class); private boolean geaendert = false; private String pfad = null; private static SzenarioVerwaltung verwaltung = null; private SzenarioVerwaltung() {} public static SzenarioVerwaltung getInstance() { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, getInstance()"); if (verwaltung == null) { verwaltung = new SzenarioVerwaltung(); } return verwaltung; } public void reset() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", reset()"); pfad = null; geaendert = false; setChanged(); notifyObservers(); } public void setzeGeaendert() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", setzeGeaendert()"); geaendert = true; this.setChanged(); this.notifyObservers(); } public boolean istGeaendert() { return geaendert; } public String holePfad() { return pfad; } /** * Speichern: - der Netzwerkknoten (inkl. Betriebssystem, Anwendungen - auch eigene/erweiterte - und * Konfigurationen) - der Verbindungen - der Quelldateien und des Bytecodes von selbst erstellten Anwendungen * * Loesungsstrategie: - generell einen eigenen ClassLoader verwenden - XML-Datei fuer Objekte und Dateien aus dem * Ordner Anwendungen in einem leeren temporaeren Ordner speichern und daraus ein neues ZIP-Archiv erstellen, dass * an beliebigem Ort gespeichert werden kann */ public boolean speichern(String datei, List hardwareItems, List kabelItems, List docuItems) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", speichern(" + datei + "," + hardwareItems + "," + kabelItems + ")"); String tmpDir; boolean erfolg = true; tmpDir = Information.getInformation().getTempPfad() + "projekt" + System.getProperty("file.separator"); (new File(tmpDir)).mkdirs(); if (!kopiereVerzeichnis(Information.getInformation().getAnwendungenPfad(), tmpDir + "anwendungen")) { LOG.error("Speicherung der eigenen Anwendungen fehlgeschlagen!"); erfolg = false; } if (!netzwerkSpeichern(tmpDir + "konfiguration.xml", hardwareItems, kabelItems, docuItems)) { LOG.error("Speicherung des Netzwerks fehlgeschlagen!"); erfolg = false; } if (!erzeugeZipArchiv(tmpDir, datei)) { LOG.error("Speicherung der Projektdatei fehlgeschlagen!"); erfolg = false; } loescheDateien(tmpDir); return erfolg; } private static boolean netzwerkSpeichern(String datei, List hardwareItems, List kabelItems, List docuItems) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, netzwerkSpeichern(" + datei + "," + hardwareItems + "," + kabelItems + ")"); XMLEncoder mx = null; FileOutputStream fos = null; if (Thread.currentThread().getContextClassLoader() != FiliusClassLoader .getInstance(Thread.currentThread().getContextClassLoader())) Thread.currentThread().setContextClassLoader( FiliusClassLoader.getInstance(Thread.currentThread().getContextClassLoader())); try { fos = new FileOutputStream(datei); mx = new XMLEncoder(new BufferedOutputStream(fos)); mx.setExceptionListener(new ExceptionListener() { public void exceptionThrown(Exception e) { LOG.trace("Error in XML Encoder fetched.", e); } }); mx.writeObject(new String("Filius version: " + filius.rahmenprogramm.Information.getVersion())); mx.writeObject(hardwareItems); mx.writeObject(kabelItems); mx.writeObject(docuItems); return true; } catch (RuntimeException e) { LOG.debug("EXCEPTION: java.lang.RuntimeException raised; Java internal problem, not Filius related!"); return false; } catch (FileNotFoundException e2) { LOG.debug("File to store could not be found.", e2); return false; } catch (Exception e) { return false; } finally { if (mx != null) mx.close(); if (fos != null) { try { fos.close(); } catch (IOException e) {} } } } public boolean laden(String datei, List hardwareItems, List kabelItems, List docuItems) throws FileNotFoundException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", laden(" + datei + "," + hardwareItems + "," + kabelItems + ")"); boolean erfolg = true; String tmpDir; // LOG.debug("SzenarioVerwaltung: Laden des Projekts aus Datei "+datei); tmpDir = Information.getInformation().getTempPfad(); loescheDateien(tmpDir + "projekt"); if (erfolg && !loescheVerzeichnisInhalt(Information.getInformation().getAnwendungenPfad())) { LOG.error("Loeschen vorhandener Anwendungen fehlgeschlagen"); } if (!entpackeZipArchiv(datei, tmpDir)) { LOG.error("Entpacken des Zip-Archivs fehlgeschlagen"); erfolg = false; } if (erfolg && !kopiereVerzeichnis(tmpDir + "projekt/anwendungen", Information.getInformation().getAnwendungenPfad())) { LOG.error("Kopieren der Anwendungen fehlgeschlagen"); } if (erfolg && !netzwerkLaden(tmpDir + "projekt/konfiguration.xml", hardwareItems, kabelItems, docuItems)) { LOG.error("Laden der Netzwerkkonfiguration fehlgeschlagen"); erfolg = false; } if (erfolg) { setCurrentFile(datei, false); } return erfolg; } public void setCurrentFile(String scenarioPath, boolean changeStatus) { pfad = scenarioPath; geaendert = changeStatus; this.setChanged(); this.notifyObservers(); } private static boolean netzwerkLaden(String datei, List hardwareItems, List kabelItems, List docuItems) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, netzwerkLaden(" + datei + "," + hardwareItems + "," + kabelItems + ")"); Object tmpObject = null; if (Thread.currentThread().getContextClassLoader() != FiliusClassLoader .getInstance(Thread.currentThread().getContextClassLoader())) Thread.currentThread().setContextClassLoader( FiliusClassLoader.getInstance(Thread.currentThread().getContextClassLoader())); boolean success = false; try (XMLDecoder xmldec = new XMLDecoder(new BufferedInputStream(new FileInputStream(datei)))) { xmldec.setExceptionListener(new ExceptionListener() { public void exceptionThrown(Exception e) { LOG.debug("", e); } }); Information.getInformation().reset(); tmpObject = xmldec.readObject(); // in newer versions of Filius the version information is put into // the saved file as well // WARNING: former versions expect LinkedList as first element in // the saved file! if (tmpObject instanceof String) { String versionInfo = (String) tmpObject; int startIdx = versionInfo.indexOf(":") + 2; String versionString = versionInfo.substring(startIdx, versionInfo.indexOf(" ", startIdx)); LOG.debug("File saved by Filius in version '" + versionString + "'"); Version fileVersion = null; try { fileVersion = Version.parseVersion(versionString.trim()); } catch (VersionFormatException e) { LOG.debug("Version string of project file is not a valid semantic version."); } Version programVersion = Version .parseVersion(Information.getVersion().substring(0, versionInfo.indexOf(" ")).trim()); if (fileVersion == null) { LOG.debug( "WARNING: version of scenario file could not be evaluated. Certain elements might not be rendered correctly any more!"); } else if (fileVersion.compareTo(programVersion) < 0) { LOG.debug("WARNING: current Filius version is newer (" + filius.rahmenprogramm.Information.getVersion() + ") than version of scenario file, such that certain elements might not be rendered correctly any more!"); } else if (fileVersion.compareTo(programVersion) > 0) { LOG.debug("WARNING: current Filius version is older (" + filius.rahmenprogramm.Information.getVersion() + ") than version of scenario file, such that certain elements might not be rendered correctly!"); } else { LOG.debug("\t...good, current version of Filius is equal to version of scenario file"); } tmpObject = null; } else { LOG.debug("WARNING: Version information of Filius scenario file could not be determined!"); LOG.debug( "WARNING: This usually means, the scenario file was created with Filius before version 1.3.0."); LOG.debug("WARNING: Certain elements might not be rendered correctly any more!"); } hardwareItems.clear(); kabelItems.clear(); docuItems.clear(); if (tmpObject == null) { tmpObject = xmldec.readObject(); } if (tmpObject instanceof List && !((List) tmpObject).isEmpty() && ((List) tmpObject).get(0) instanceof GUIKnotenItem) { List tempList = (List) tmpObject; for (GUIKnotenItem tmpNode : tempList) { if (tmpNode.getImageLabel() == null) { JSidebarButton imageLabel = new JSidebarButton(); tmpNode.setImageLabel(imageLabel); } tmpNode.getImageLabel().setTyp(tmpNode.getKnoten().holeHardwareTyp()); hardwareItems.add(tmpNode); } } tmpObject = xmldec.readObject(); if (tmpObject instanceof List && !((List) tmpObject).isEmpty() && ((List) tmpObject).get(0) instanceof GUIKabelItem) { List tempList = (List) tmpObject; for (GUIKabelItem cable : tempList) { kabelItems.add(cable); } } tmpObject = xmldec.readObject(); if (tmpObject instanceof List && !((List) tmpObject).isEmpty() && ((List) tmpObject).get(0) instanceof GUIDocuItem) { List tempList = (List) tmpObject; for (GUIDocuItem docuItem : tempList) { docuItems.add(docuItem); } } success = true; } catch (FileNotFoundException e) { GUIErrorHandler.getGUIErrorHandler().DisplayError(messages.getString("rp_szenarioverwaltung_msg5")); LOG.debug("", e); success = false; } catch (ArrayIndexOutOfBoundsException e) { LOG.debug("Incomplete project file " + datei); success = true; } return success; } public static boolean erzeugeZipArchiv(String datenOrdner, String archivDatei) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, erzeugeZipArchiv(" + datenOrdner + "," + archivDatei + ")"); FileOutputStream out; ZipOutputStream zipOut; File zipDatei; File ordner; zipDatei = new File(archivDatei); new File(zipDatei.getParent()).mkdirs(); ordner = new File(datenOrdner); if (!ordner.exists()) return false; try { zipDatei.createNewFile(); } catch (IOException e) { LOG.debug("", e); return false; } try { out = new FileOutputStream(zipDatei); zipOut = new ZipOutputStream(out); schreibeZipDatei(zipOut, ordner.getName() + "/", ordner.getAbsolutePath()); try { zipOut.close(); out.close(); } catch (IOException e) { LOG.debug("", e); return false; } } catch (FileNotFoundException e) { LOG.debug("", e); return false; } return true; } private static boolean schreibeZipDatei(ZipOutputStream out, String relPfad, String datei) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, schreibeZipDatei(" + out + "," + relPfad + "," + datei + ")"); File path; boolean result = true; path = new File(datei); if (path.isFile()) { return schreibeZipEntry(out, relPfad, datei); } for (File file : path.listFiles()) { if (file.isDirectory()) { result = schreibeZipDatei(out, relPfad + file.getName() + "/", file.getAbsolutePath() + "/"); } else { result = schreibeZipEntry(out, relPfad + file.getName(), file.getAbsolutePath()); } if (!result) return result; } return result; } private static boolean schreibeZipEntry(ZipOutputStream out, String relPfad, String datei) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, schreibeZipEntry(" + out + "," + relPfad + "," + datei + ")"); ZipEntry zipEntry; byte[] buffer = new byte[0xFFFF]; File quelldatei; quelldatei = new File(datei); if (!quelldatei.exists()) return false; zipEntry = new ZipEntry(relPfad); try (FileInputStream fis = new FileInputStream(quelldatei)) { out.putNextEntry(zipEntry); for (int len; (len = fis.read(buffer)) != -1;) { out.write(buffer, 0, len); } out.closeEntry(); } catch (Exception e) { LOG.debug("ERROR (static): Datei " + datei + " konnte nicht zu zip-Archiv hinzugefuegt werden."); LOG.debug("", e); return false; } return true; } public static boolean entpackeZipArchiv(String archivDatei, String zielOrdner) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, entpackeZipArchiv(" + archivDatei + "," + zielOrdner + ")"); ZipFile zf; File file; InputStream is; BufferedInputStream bis; FileOutputStream fos; BufferedOutputStream bos; file = new File(archivDatei); if (!file.exists()) { LOG.debug("ERROR (static): " + archivDatei + " existiert nicht. Entpacken ist fehlgeschlagen!"); return false; } file = new File(zielOrdner); if (!file.exists()) { file.mkdirs(); } try { zf = new ZipFile(archivDatei); for (Enumeration e = zf.entries(); e.hasMoreElements();) { ZipEntry target = e.nextElement(); file = new File(zielOrdner + target.getName()); if (target.isDirectory()) file.mkdirs(); else { is = zf.getInputStream(target); bis = new BufferedInputStream(is); new File(file.getParent()).mkdirs(); fos = new FileOutputStream(file); bos = new BufferedOutputStream(fos); final int EOF = -1; for (int c; (c = bis.read()) != EOF;) bos.write((byte) c); bos.close(); fos.close(); is.close(); bis.close(); } } zf.close(); } catch (FileNotFoundException e) { LOG.debug("EXCEPTION (static): zipfile not found"); return false; } catch (ZipException e) { LOG.debug("EXCEPTION (static): zip error..."); return false; } catch (IOException e) { LOG.debug("EXCEPTION (static): IO error..."); return false; } return true; } public static boolean loescheVerzeichnisInhalt(String verzeichnis) { // LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, // loescheVerzeichnisInhalt("+verzeichnis+")"); File path; File file; File[] fileListe; path = new File(verzeichnis); if (path.exists()) { fileListe = path.listFiles(); for (int i = 0; i < fileListe.length; i++) { file = fileListe[i]; if (file.isDirectory()) { if (!loescheDateien(file.getAbsolutePath())) { LOG.debug( "ERROR (static): Ordner " + file.getAbsolutePath() + " konnte nicht geloescht werden."); return false; } } else if (!file.delete()) { LOG.debug("ERROR (static): Datei " + file.getAbsolutePath() + " konnte nicht geloescht werden."); return false; } else { } } } return true; } public static boolean loescheDateien(String datei) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, loescheDateien(" + datei + ")"); File path; path = new File(datei); if (!loescheVerzeichnisInhalt(datei)) return false; if (path.delete()) { return true; } else { return false; } } public static boolean kopiereVerzeichnis(String quelle, String ziel) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, kopiereVerzeichnis(" + quelle + "," + ziel + ")"); File quellOrdner, zielOrdner, tmp; quellOrdner = new File(quelle); zielOrdner = new File(ziel); if (!quellOrdner.exists()) return false; if (!zielOrdner.exists()) zielOrdner.mkdirs(); for (File file : quellOrdner.listFiles()) { if (file.isDirectory()) { tmp = new File(zielOrdner.getAbsolutePath() + "/" + file.getName()); kopiereVerzeichnis(file.getAbsolutePath(), tmp.getAbsolutePath()); } else kopiereDatei(file.getAbsolutePath(), zielOrdner.getAbsolutePath() + "/" + file.getName()); } return true; } public static boolean saveStream(InputStream source, String zieldatei) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, saveStream(" + source + "," + zieldatei + ")"); File destfile; FileOutputStream fos = null; byte[] buffer; boolean result = true; destfile = new File(zieldatei); if (source == null || destfile.exists()) result = false; else { try { fos = new FileOutputStream(destfile); buffer = new byte[0xFFFF]; for (int len; (len = source.read(buffer)) != -1;) fos.write(buffer, 0, len); } catch (IOException e) { LOG.debug("", e); result = false; } finally { if (fos != null) try { fos.close(); } catch (IOException e) {} } } return result; } public static boolean kopiereDatei(String quelldatei, String zieldatei) { LOG.trace("INVOKED (static) filius.rahmenprogramm.SzenarioVerwaltung, kopiereDatei(" + quelldatei + "," + zieldatei + ")"); File srcfile, destfile; FileInputStream fis = null; FileOutputStream fos = null; byte[] buffer; boolean result = true; srcfile = new File(quelldatei); destfile = new File(zieldatei); if (!srcfile.exists() || destfile.exists()) result = false; else { try { fis = new FileInputStream(srcfile); fos = new FileOutputStream(destfile); buffer = new byte[0xFFFF]; for (int len; (len = fis.read(buffer)) != -1;) fos.write(buffer, 0, len); } catch (IOException e) { LOG.debug("", e); result = false; } finally { if (fis != null) try { fis.close(); } catch (IOException e) {} if (fos != null) try { fos.close(); } catch (IOException e) {} } } return result; } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/TeeOutputStream.java000066400000000000000000000046721506574241500263700ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ /** * special class extending OutputStream to realise double output * to System.out/err and log file * * @author Christian Eibl * Feb. 2010 */ package filius.rahmenprogramm; import java.io.IOException; import java.io.OutputStream; public class TeeOutputStream extends OutputStream { /** * Attributes */ private OutputStream outFile = null; private OutputStream outScreen = null; public TeeOutputStream(OutputStream fileOS, OutputStream screenOS) { this.outFile = fileOS; this.outScreen = screenOS; } /** * write characters to both OutputStreams */ public void write(int c) throws IOException { if (this.outFile != null) this.outFile.write(c); if (this.outScreen != null) { this.outScreen.write(c); this.outScreen.flush(); } } /** * close streams */ public void close() throws IOException { if (this.outFile != null) { flush(); this.outFile.close(); } if (this.outFile != null) this.outScreen.close(); } /** * flushes file stream */ public void flush() throws IOException { this.outFile.flush(); } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/nachrichten/000077500000000000000000000000001506574241500246705ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/nachrichten/Lauscher.java000066400000000000000000000774521506574241500273200ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm.nachrichten; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Vector; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.SystemSoftware; import filius.software.transportschicht.TcpSegment; import filius.software.transportschicht.UdpSegment; import filius.software.vermittlungsschicht.ArpPaket; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; public class Lauscher implements I18n { private static Logger LOG = LoggerFactory.getLogger(Lauscher.class); public static final String ETHERNET = "", ARP = "ARP", IP = "IP", ICMP = "ICMP", TCP = "TCP", UDP = "UDP"; public static final String HTTP = "HTTP", SMTP = "SMTP", POP = "POP3", DNS = "DNS", DHCP = "DHCP"; public static final String[] SPALTEN = { messages.getString("rp_lauscher_msg1"), messages.getString("rp_lauscher_msg2"), messages.getString("rp_lauscher_msg3"), messages.getString("rp_lauscher_msg4"), messages.getString("rp_lauscher_msg5"), messages.getString("rp_lauscher_msg6"), messages.getString("rp_lauscher_msg7") }; public static final String[] PROTOKOLL_SCHICHTEN = { messages.getString("rp_lauscher_msg8"), messages.getString("rp_lauscher_msg9"), messages.getString("rp_lauscher_msg10"), messages.getString("rp_lauscher_msg11") }; public static final String DROPPED = "dropped packets"; private NumberFormat numberFormatter = NumberFormat.getInstance(Information.getInformation().getLocaleOrDefault()); /** Singleton */ private static Lauscher lauscher = null; private HashMap> beobachter = new HashMap>(); private HashMap> datenEinheiten = new HashMap>(); private List droppedDataUnits = new LinkedList<>(); private HashMap InterfaceById = new HashMap(); private HashMap SystemSoftwareById = new HashMap(); private Lauscher() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", constr: Lauscher()"); reset(); } public void reset() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", reset()"); datenEinheiten.clear(); droppedDataUnits.clear(); this.benachrichtigeBeobachter(null); } public Collection getInterfaceIDs() { return datenEinheiten.keySet(); } public static Lauscher getLauscher() { if (lauscher == null) { lauscher = new Lauscher(); } return lauscher; } public NetzwerkInterface getInterfaceByID(String interfaceId) { return InterfaceById.get(interfaceId); } public SystemSoftware getSystemSoftwareByID(String interfaceId) { return SystemSoftwareById.get(interfaceId); } public void removeIdentifier(String identifier) { datenEinheiten.remove(identifier); beobachter.remove(identifier); } public void addBeobachter(String id, LauscherBeobachter newObserver) { LOG.trace( "INVOKED (" + this.hashCode() + ") " + getClass() + ", addBeobachter(" + id + "," + newObserver + ")"); LinkedList liste; liste = this.beobachter.get(id); if (liste == null) { liste = new LinkedList(); this.beobachter.put(id, liste); } liste.add(newObserver); } private void benachrichtigeBeobachter(String id) { LinkedList liste = new LinkedList(); if (id == null) { for (LinkedList beobachterListe : beobachter.values()) { liste.addAll(beobachterListe); } } else if (null == beobachter.get(id)) { LOG.trace("no observer for {}", id); } else { liste.addAll(beobachter.get(id)); } for (LauscherBeobachter beobachter : liste) { beobachter.update(); } } /** * Hinzufuegen von einem EthernetFrame zu den Daten * * @param networkInterface * @param systemSoftware * @param frame */ public void addDatenEinheit(NetzwerkInterface networkInterface, SystemSoftware systemSoftware, EthernetFrame frame) { String interfaceId = networkInterface.getMac(); LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", addDatenEinheit(" + interfaceId + "," + frame + ")"); if (!frame.isReadByLauscherForMac(interfaceId)) { Object[] frameMitZeitstempel = frameWithTimestamp(frame); LinkedList liste = (LinkedList) datenEinheiten.get(interfaceId); if (liste == null) { liste = new LinkedList(); datenEinheiten.put(interfaceId, liste); } synchronized (liste) { liste.addLast(frameMitZeitstempel); } frame.setReadByLauscherForMac(interfaceId); benachrichtigeBeobachter(interfaceId); } storeInterfaceById(interfaceId, networkInterface); storeSystemSoftwareById(interfaceId, systemSoftware); } public void addDroppedDataUnit(EthernetFrame frame) { Object[] frameMitZeitstempel = frameWithTimestamp(frame); droppedDataUnits.add(frameMitZeitstempel); benachrichtigeBeobachter(DROPPED); } protected Object[] frameWithTimestamp(EthernetFrame frame) { Object[] frameMitZeitstempel = new Object[2]; frameMitZeitstempel[0] = Long.valueOf(System.currentTimeMillis()); frameMitZeitstempel[1] = frame.clone(); return frameMitZeitstempel; } public Object[][] getDaten(String interfaceId, boolean inheritAddress, int offset) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", getDaten(" + interfaceId + ")"); Vector vector; Object[][] daten; vector = datenVorbereiten(interfaceId, inheritAddress, offset); if (vector == null) { daten = new Object[0][SPALTEN.length]; return daten; } else { daten = new Object[vector.size()][SPALTEN.length]; for (int i = 0; i < vector.size(); i++) { daten[i] = (Object[]) vector.elementAt(i); } return daten; } } public void print(String interfaceId) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", print(" + interfaceId + ")"); Object[][] daten; daten = getDaten(interfaceId, false, 1); for (int i = 0; i < daten.length; i++) { for (int j = 0; j < daten[i].length; j++) { LOG.debug("\t" + daten[i][j]); } } } public int getOffsetByTimestamp(String interfaceId, long offsetTimestamp) { LinkedList liste = datenEinheiten.get(interfaceId); int offset = 1; if (liste != null) { synchronized (liste) { for (Object[] frameMitZeitstempel : liste) { long timestamp = ((Long) frameMitZeitstempel[0]).longValue(); if (timestamp >= offsetTimestamp) { break; } offset++; } } } return offset; } private String formatTimestamp(long timestamp) { Calendar zeit = new GregorianCalendar(); zeit.setTimeInMillis(timestamp); String timestampStr = (zeit.get(Calendar.HOUR_OF_DAY) < 10 ? "0" + zeit.get(Calendar.HOUR_OF_DAY) : zeit.get(Calendar.HOUR_OF_DAY)) + ":" + (zeit.get(Calendar.MINUTE) < 10 ? "0" + zeit.get(Calendar.MINUTE) : zeit.get(Calendar.MINUTE)) + ":" + (zeit.get(Calendar.SECOND) < 10 ? "0" + zeit.get(Calendar.SECOND) : zeit.get(Calendar.SECOND)) + "." + (zeit.get(Calendar.MILLISECOND) < 10 ? "00" + zeit.get(Calendar.MILLISECOND) : (zeit.get(Calendar.MILLISECOND) < 100 ? "0" + zeit.get(Calendar.MILLISECOND) : zeit.get(Calendar.MILLISECOND))); return timestampStr; } /** * * @param interfaceId * @param inheritAddress * @param offset * starts with 1 * @return */ private Vector datenVorbereiten(String interfaceId, boolean inheritAddress, int offset) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", datenVorbereiten(" + interfaceId + ")"); Vector daten; LinkedList liste; Object[] frameMitZeitstempel, neuerEintrag; ListIterator it; EthernetFrame frame; IpPaket ipPaket; IcmpPaket icmpPaket; ArpPaket arpPaket; TcpSegment tcpSeg = null; UdpSegment udpSeg = null; liste = datenEinheiten.get(interfaceId); if (liste == null) { return null; } else { daten = new Vector(); synchronized (liste) { it = liste.listIterator(); for (int i = 1; it.hasNext(); i++) { frameMitZeitstempel = (Object[]) it.next(); if (i < offset) { continue; } neuerEintrag = new Object[SPALTEN.length]; neuerEintrag[0] = "" + i; String timestampStr = formatTimestamp((Long) frameMitZeitstempel[0]); neuerEintrag[1] = timestampStr; frame = (EthernetFrame) frameMitZeitstempel[1]; neuerEintrag[2] = frame.getQuellMacAdresse(); neuerEintrag[3] = frame.getZielMacAdresse(); neuerEintrag[4] = ETHERNET; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[0]; neuerEintrag[6] = frame.getTyp(); daten.addElement(neuerEintrag); neuerEintrag = new Object[SPALTEN.length]; neuerEintrag[0] = "" + i; neuerEintrag[1] = timestampStr; if (frame.getTyp().equals(EthernetFrame.IP) && !(frame.getDaten() instanceof IcmpPaket)) { ipPaket = (IpPaket) frame.getDaten(); neuerEintrag[2] = ipPaket.getSender(); neuerEintrag[3] = ipPaket.getEmpfaenger(); neuerEintrag[4] = IP; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[1]; neuerEintrag[6] = messages.getString("rp_lauscher_msg12") + ": " + ipPaket.getProtocol() + ", TTL: " + ipPaket.getTtl(); daten.addElement(neuerEintrag); neuerEintrag = new Object[SPALTEN.length]; neuerEintrag[0] = "" + i; neuerEintrag[1] = timestampStr; String source = null; String dest = null; if (ipPaket.getProtocol() == IpPaket.TCP) { tcpSeg = (TcpSegment) ipPaket.getSegment(); if (inheritAddress) { source = ipPaket.getSender() + ":" + tcpSeg.getQuellPort(); neuerEintrag[2] = source; dest = ipPaket.getEmpfaenger() + ":" + tcpSeg.getZielPort(); neuerEintrag[3] = dest; } else { neuerEintrag[2] = tcpSeg.getQuellPort(); neuerEintrag[3] = tcpSeg.getZielPort(); } neuerEintrag[4] = TCP; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[2]; if (tcpSeg.isSyn()) { neuerEintrag[6] = "SYN"; } else if (tcpSeg.isFin()) { neuerEintrag[6] = "FIN"; } neuerEintrag[6] = ((neuerEintrag[6] == null) ? "" : neuerEintrag[6] + ", ") + "SEQ: " + numberFormatter.format(tcpSeg.getSeqNummer()); if (tcpSeg.isAck()) { neuerEintrag[6] = neuerEintrag[6] + ", ACK: " + numberFormatter.format(tcpSeg.getAckNummer()); } } else if (ipPaket.getProtocol() == IpPaket.UDP) { udpSeg = (UdpSegment) ipPaket.getSegment(); if (inheritAddress) { source = ipPaket.getSender() + ":" + udpSeg.getQuellPort(); neuerEintrag[2] = source; dest = ipPaket.getEmpfaenger() + ":" + udpSeg.getZielPort(); neuerEintrag[3] = dest; } else { neuerEintrag[2] = udpSeg.getQuellPort(); neuerEintrag[3] = udpSeg.getZielPort(); } neuerEintrag[4] = UDP; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[2]; neuerEintrag[6] = ""; } else { LOG.warn("Protokoll der Transportschicht (" + ipPaket.getProtocol() + ") nicht bekannt."); } daten.addElement(neuerEintrag); neuerEintrag = new Object[SPALTEN.length]; neuerEintrag[0] = "" + i; neuerEintrag[1] = timestampStr; neuerEintrag[2] = source; neuerEintrag[3] = dest; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[3]; if (ipPaket.getProtocol() == IpPaket.TCP) { neuerEintrag[6] = tcpSeg.getDaten(); } else if (ipPaket.getProtocol() == IpPaket.UDP) { neuerEintrag[6] = udpSeg.getDaten(); } String payload = (String) neuerEintrag[6]; if (payload != null && !payload.trim().equals("")) { neuerEintrag[4] = classifyApplicationLayerProtocol(payload, ipPaket.getProtocol(), ipPaket.getSegment().getQuellPort(), ipPaket.getSegment().getZielPort()); daten.addElement(neuerEintrag); } } else if (frame.getTyp().equals(EthernetFrame.ARP)) { arpPaket = (ArpPaket) frame.getDaten(); neuerEintrag[2] = arpPaket.getSenderIP(); neuerEintrag[3] = arpPaket.getTargetIP(); neuerEintrag[4] = ARP; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[1]; if (arpPaket.getOperation() == ArpPaket.REQUEST) { neuerEintrag[6] = messages.getString("rp_lauscher_msg13") + " " + arpPaket.getTargetIP(); } else { neuerEintrag[6] = messages.getString("rp_lauscher_msg14") + " " + arpPaket.getSenderMAC(); } neuerEintrag[6] += " " + arpPaket.toString(); daten.addElement(neuerEintrag); } else if (frame.getTyp().equals(EthernetFrame.IP) && frame.getDaten() instanceof IcmpPaket) { icmpPaket = (IcmpPaket) frame.getDaten(); neuerEintrag[2] = icmpPaket.getSender(); neuerEintrag[3] = icmpPaket.getEmpfaenger(); neuerEintrag[4] = ICMP; neuerEintrag[5] = PROTOKOLL_SCHICHTEN[1]; switch (icmpPaket.getIcmpType()) { case 0: neuerEintrag[6] = "ICMP Echo Reply (pong)"; break; case 3: switch (icmpPaket.getIcmpCode()) { case 0: neuerEintrag[6] = "ICMP Network Unreachable"; break; case 1: neuerEintrag[6] = "ICMP Host Unreachable"; break; default: neuerEintrag[6] = "ICMP Destination Unreachable (code " + icmpPaket.getIcmpCode() + ")"; break; } break; case 8: neuerEintrag[6] = "ICMP Echo Request (ping)"; break; case 11: neuerEintrag[6] = "ICMP Time Exeeded (poof)"; break; default: neuerEintrag[6] = "ICMP unknown: " + icmpPaket.getIcmpType() + " / " + icmpPaket.getIcmpCode(); break; } neuerEintrag[6] = neuerEintrag[6] + ", TTL: " + icmpPaket.getTtl() + ", Seq.-No.: " + icmpPaket.getSeqNr(); daten.addElement(neuerEintrag); } } } } return daten; } private String classifyApplicationLayerProtocol(String payload, int transport, int sourcePort, int destPort) { String protocol = ""; if (StringUtils.contains(payload, "//0x00//") || StringUtils.contains(payload, "//0x01//") || StringUtils.contains(payload, "//0x80//") || StringUtils.contains(payload, "//0x81//") || StringUtils.contains(payload, "//0x40//")) { protocol = "GNT"; } else if (sourcePort == 110 && (StringUtils.startsWithIgnoreCase(payload, "+OK") || StringUtils.startsWithIgnoreCase(payload, "-ERR")) || destPort == 110 && (StringUtils.startsWithIgnoreCase(payload, "USER ") || StringUtils.startsWithIgnoreCase(payload, "PASS ") || StringUtils.startsWithIgnoreCase(payload, "STAT ") || StringUtils.startsWithIgnoreCase(payload, "LIST ") || StringUtils.startsWithIgnoreCase(payload, "RETR ") || StringUtils.startsWithIgnoreCase(payload, "DELE ") || StringUtils.startsWithIgnoreCase(payload, "NOOP") || StringUtils.startsWithIgnoreCase(payload, "RSET") || StringUtils.startsWithIgnoreCase(payload, "QUIT") || StringUtils.startsWithIgnoreCase(payload, "TOP"))) { protocol = "POP3"; } else if (sourcePort == 25 && (StringUtils.startsWithIgnoreCase(payload, "1") || StringUtils.startsWithIgnoreCase(payload, "2") || StringUtils.startsWithIgnoreCase(payload, "3") || StringUtils.startsWithIgnoreCase(payload, "4") || StringUtils.startsWithIgnoreCase(payload, "5")) || destPort == 25) { protocol = "SMTP"; } else if (StringUtils.contains(payload, "ID=") && StringUtils.contains(payload, "QR=") && StringUtils.contains(payload, "RCODE=")) { protocol = "DNS"; } else if (StringUtils.contains(payload, "DHCP")) { protocol = "DHCP"; } else if (sourcePort == 521 && destPort == 520 || sourcePort == 520 && destPort == 521) { protocol = "RIP"; } else if (destPort == 80 && StringUtils.containsIgnoreCase(payload, "http/") || sourcePort == 80) { protocol = "HTTP"; } return protocol; } public String[] getHeader() { return Lauscher.SPALTEN; } public List getDroppedDataUnits() { List list = new ArrayList<>(); for (Object[] droppedDataUnit : droppedDataUnits) { list.add(String.format("%s : %s", formatTimestamp((Long) droppedDataUnit[0]), droppedDataUnit[1])); } return list; } public void resetDroppedDataUnits() { droppedDataUnits.clear(); } /** * General: If a frame was forwarded by the node (this means that it is a router), given a received / sent frame, * the method finds the sent / received frame with the same encapsuled IP packet on another interface of the node. * * Specific: Given an interface ID and the frame number in the data exchange window (Datenaustausch), other * interfaces of the node will be searched for a frame which has the same content and a TTL increased by 1 if the * frame was sent or a TTL decreased by 1 if the frame was received.
The first entry of the returned array * contains the network interface ID {@code interfaceId} and the second entry the number of the frame * {@code frameNumber} in the data exchange window. If the two entries are the null pointer each, it means that the * layer 2 (IP or ICMP) packet was originally sent or ultimately received by this node, i.e. that there is no * corresponding frame. * * @param originalInterfaceId * MAC address of the network interface * @param originalFrameNumber * number of the frame in the data exchange window (Datenaustausch) * @param node * The node whose message details table (Datenaustausch) we are on * @return Array of length two. First entry is the interface ID, second entry the frame number in the data exchange * window (starts with 1). If no corresponding frame exists, the two entries are null. * @author Christoph Irniger */ public Object[] getCorrespondingFrame(String originalInterfaceId, int originalFrameNumber, InternetKnoten node) { Object[] interfaceIdAndFrameNumber = new Object[2]; EthernetFrame originalFrame = getFrame(originalInterfaceId, originalFrameNumber); Object data = originalFrame.getDaten(); // Only IP packets can be forwarded if (data instanceof IpPaket) { // Only execute if the node was not the original sender or receiver of the // encapsuled IP packet (e.g. has forwarded the packet) if (!packetOnlyReceivedOrOnlySent(originalFrame, node)) { List netzwerkInterfaces = node.getNetzwerkInterfaces(); boolean found = false; for (NetzwerkInterface netzwerkInterface : netzwerkInterfaces) { String interfaceId = netzwerkInterface.getMac(); LinkedList framesMitZeitstempel = datenEinheiten.get(interfaceId); if (framesMitZeitstempel != null) { boolean sent = originalFrame.getQuellMacAdresse().equals(originalInterfaceId); boolean received = originalFrame.getZielMacAdresse().equals(originalInterfaceId); for (int frameNumber = 0; frameNumber < framesMitZeitstempel.size(); frameNumber++) { EthernetFrame frame = (EthernetFrame) framesMitZeitstempel.get(frameNumber)[1]; // if the original frame was sent, the TTL of the desired frame (the received // one) has to be one more than the one of the original frame, if received (the // sent one) one less int originalTTL = ((IpPaket) data).getTtl(); if (frame.samePayload(originalFrame, originalTTL + 1) && sent || frame.samePayload(originalFrame, originalTTL - 1) && received) { found = true; interfaceIdAndFrameNumber[0] = (String) interfaceId; interfaceIdAndFrameNumber[1] = (Integer) frameNumber + 1; break; } } if (found) { break; } } } } } return interfaceIdAndFrameNumber; } /** * Returns the ethernet frame with the same payload as the frame in the first argument but with a desired TTL. If * the frame in the first argument does not encapsule an IP packet (e.g. an ARP packet) the method returns null. If * no such frame exists it returns null as well. * * @param originalFrame * the frame whose data should be equal to the returned one * @param ttl * the desired TTL * @return frame with same data and specified TTL (null if it does not exist) * @author Christoph Irniger */ public EthernetFrame getFrameWithSameContentAndSpecificTTL(EthernetFrame originalFrame, int ttl) { if (originalFrame.getDaten() instanceof IpPaket) { for (String interfaceId : getInterfaceIDs()) { LinkedList framesMitZeitstempel = datenEinheiten.get(interfaceId); if (framesMitZeitstempel != null) { for (Object[] frameMitZeitstempel : framesMitZeitstempel) { EthernetFrame frame = (EthernetFrame) frameMitZeitstempel[1]; if (frame.samePayload(originalFrame, ttl)) { return frame; } } } } } return null; } /** * This method checks if the node was the original sender or receiver of the layer 2 (IP or ICMP) packet inside the * {@code frame}. If this is not the case, we know that the packet was forwarded by this node. * * @param frame * The frame of which the layer 2 (IP or ICMP) packet should be checked * @param node * The originally selected node * @return true oder false * @author Christoph Irniger */ private boolean packetOnlyReceivedOrOnlySent(EthernetFrame frame, InternetKnoten node) { Object daten = frame.getDaten(); List netzwerkInterfaces = node.getNetzwerkInterfaces(); if (daten instanceof ArpPaket) { return true; } else if (daten instanceof IpPaket) { for (NetzwerkInterface netzwerkInterface : netzwerkInterfaces) { if (((IpPaket) daten).getEmpfaenger().equals(netzwerkInterface.getIp()) || ((IpPaket) daten).getSender().equals(netzwerkInterface.getIp()) || ((IpPaket) daten).getEmpfaenger().equals("255.255.255.255")) { return true; } } return false; } else { return false; } } /** * This helper method returns the frame in the Lauscher data that belongs to the network interface indicated in the * first parameter and the frame number in the data exchange window in the second parameter. * * @param interfaceId * MAC address of the network interface whose data exchange should be searched for the frame * @param frameNumber * number of the frame in the data exchange window * @return frame which belongs to the parameters * @author Christoph Irniger */ public EthernetFrame getFrame(String interfaceId, int frameNumber) { Object[] frameMitZeitstempel = datenEinheiten.get(interfaceId).get(frameNumber - 1); return (EthernetFrame) frameMitZeitstempel[1]; } /** * Returns the frame number (number of a frame in the data exchange window of an interface) to a given interface ID * and ethernet frame. Returns 0 if the frame was not found. * * @param interfaceId * ID of the interface * @param frame * frame which the number should be computed for * @return the frame number in the data exchange window (starts with 1) or 0 if the frame was not found * @author Christoph Irniger */ public int getFrameNumber(String interfaceId, EthernetFrame frame) { LinkedList framesMitZeitstempel = datenEinheiten.get(interfaceId); if (framesMitZeitstempel != null) { for (int frameNumber = 0; frameNumber < framesMitZeitstempel.size(); frameNumber++) { if (((EthernetFrame) framesMitZeitstempel.get(frameNumber)[1]).sameContent(frame)) { return frameNumber + 1; } } } return 0; } /** * Adds the pair {@code interfaceId} and {@code interface} of type {@code NetzwerkInterface} to the HashMap * {@code InterfaceById}. * * @param interfaceId * MAC address of the interface * @param networkInterface * interface belonging to the interface ID * @author Christoph Irniger */ public void storeInterfaceById(String interfaceId, NetzwerkInterface networkInterface) { if (!InterfaceById.keySet().contains(interfaceId)) { InterfaceById.put(interfaceId, networkInterface); } } /** * Adds the pair {@code interfaceId} and {@code systemSoftware} of type {@code SystemSoftware} to the HashMap * {@code SystemSoftwareById}. * * @param interfaceId * MAC address of the interface * @param systemSoftware * systemSoftware (operating system) belonging to the interface ID * @author Christoph Irniger */ public void storeSystemSoftwareById(String interfaceId, SystemSoftware systemSoftware) { if (!SystemSoftwareById.keySet().contains(interfaceId)) { SystemSoftwareById.put(interfaceId, systemSoftware); } } } filius-2.9.4+ds/src/main/java/filius/rahmenprogramm/nachrichten/LauscherBeobachter.java000066400000000000000000000026761506574241500312730ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.rahmenprogramm.nachrichten; import java.io.IOException; import java.io.OutputStream; public interface LauscherBeobachter { void update(); void writeToStream(OutputStream outputStream) throws IOException; } filius-2.9.4+ds/src/main/java/filius/software/000077500000000000000000000000001506574241500212155ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/Anwendung.java000066400000000000000000000243021506574241500240070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.Map; import java.util.Observer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.Information; import filius.software.system.InternetKnotenBetriebssystem; /** * Die Klasse Anwendung ist die Oberklasse aller Anwendungen, die auf einem Rechner installiert werden koennen. Als * beobachtetes Objekt implementiert diese Klasse eine Komponente des Beobachtermusters mit Hilfe der Klasse * AnwendungObservable. Es werden die Standardkomponenten des JDK verwendet. * * @see java.util.Observable * @see filius.software.AnwendungObservable */ public abstract class Anwendung extends Thread { private static Logger LOG = LoggerFactory.getLogger(Anwendung.class); /** Bezeichnung fuer die Anwendung */ private String anwendungsName; /** * Ein Puffer fuer eingehende Kommandos. In dem Puffer werden Objekt-Arrays aus zwei Elementen gespeichert. Das * erste Element ist ein String, der die aufzurufende Methode bestimmt. Das zweite Element ist ein Objekt-Array mit * den Parametern fuer den Methodenaufruf. */ private LinkedList kommandos = new LinkedList(); /** * Das Betriebssystem des Rechners/Vermittlungsrechner, auf dem die Anwendung ausgefuehrt wird. */ protected InternetKnotenBetriebssystem betriebssystem; /** Beobachter der Anwendung */ private AnwendungObservable observable = new AnwendungObservable(); /** Dieses Attribut zeigt an, ob der Thread laeuft. */ protected boolean running = false; /** * Der Konstruktur bewirkt eine Meldung auf der Standardausgabe, dass die Anwendung erzeugt wurde. */ public Anwendung() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), constr: Anwendung()"); try { for (Map appInfo : Information.getInformation().ladeProgrammListe()) { if (this.getClass().getCanonicalName().equals((String) appInfo.get("Klasse"))) { this.setzeAnwendungsName(appInfo.get("Anwendung").toString()); break; } } } catch (Exception e) { LOG.debug("", e); } } /** * Methode fuer das Beobachtermuster: Hinzufuegen eines weiteren Beobachters. * * @param beobachter */ public void hinzuBeobachter(Observer beobachter) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), hinzuBeobachter(" + beobachter + ")"); observable.addObserver(beobachter); } /** Methode zur Benachrichtigung der Beobachter. */ public void benachrichtigeBeobachter(Object daten) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), benachrichtigeBeobachter(" + daten + ")"); observable.notifyObservers(daten); } /** Methode zur Benachrichtigung der Beobachter. */ public void benachrichtigeBeobachter() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), benachrichtigeBeobachter()"); observable.notifyObservers(); } /** * Methode zum Starten des Threads beim Wechsel vom Entwurfs- in den Aktionsmodus. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), starten()"); running = true; synchronized (kommandos) { kommandos.clear(); benachrichtigeBeobachter(); } if (getState().equals(State.NEW)) { start(); } else { synchronized (this) { notifyAll(); } } } /** * Methode zum Anhalten des Threads. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), beenden()"); running = false; if (kommandos != null) { synchronized (kommandos) { kommandos.clear(); kommandos.notifyAll(); } } synchronized (this) { this.interrupt(); } } /** * Methode zur Uebergabe von auszufuehrenden Kommandos. Die Uebergebenen Methodenaufrufe werden in dem Thread * ausgefuehrt, der von dieser Klasse implementiert wird. Damit wird der aufrufende Thread nicht blockiert. Die * Verwendung dieser Moeglichkeit fuer Methodenaufrufe ist also zur Ausfuehrung blockierender Methoden * wichtig. * * @param methode * Der Bezeichner der auszufuehrenden Methode * @param args * die Parameter der Methode */ protected void ausfuehren(String methode, Object[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), ausfuehren(" + methode + "," + args + ")"); Object[] aufruf; aufruf = new Object[2]; aufruf[0] = methode; aufruf[1] = args; synchronized (kommandos) { kommandos.addLast(aufruf); kommandos.notifyAll(); } } /** * Hier wird der Puffer kommandos ueberwacht und wenn dort ein Methodenaufruf vorliegt wird diese Methode * aufgerufen. */ public void run() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Anwendung), run()"); Class[] argumentKlassen; Class klasse; Method method; String methodenName; Object[] args; Object[] aufruf; while (true) { if (running) { synchronized (kommandos) { if (kommandos.size() < 1) { try { kommandos.wait(); } catch (InterruptedException e) {} } } if (kommandos.size() > 0) { aufruf = (Object[]) kommandos.removeFirst(); methodenName = aufruf[0].toString(); args = (Object[]) aufruf[1]; if (args != null) { argumentKlassen = new Class[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] != null) argumentKlassen[i] = args[i].getClass(); } } else { argumentKlassen = null; } klasse = getClass(); // go upwards in inheritance hierarchy until the class was // found containing // the desired method, i.e., exceptions are rather harmless // here while (klasse != null) { try { method = klasse.getDeclaredMethod(methodenName, argumentKlassen); method.setAccessible(true); method.invoke(this, args); klasse = null; } catch (NoSuchMethodException e) { klasse = klasse.getSuperclass(); } catch (Exception e) { LOG.debug("an unexpected error occurred during processing in application.", e); klasse = null; } } } } else { synchronized (this) { try { wait(); } catch (InterruptedException e) {} } } } } /** Methode fuer den Zugriff auf den Anwendungsnamen */ public String holeAnwendungsName() { return anwendungsName; } /** Methode fuer den Zugriff auf den Anwendungsnamen */ public void setzeAnwendungsName(String anwendungsName) { this.anwendungsName = anwendungsName; } /* * method for downward compatibility; older versions of filius possibly used this method, such that some saved * scenarios depend on it! ... or maybe only JAVA demands properties to be set by a "set" method! (required by * XMLDecoder) */ public void setAnwendungsName(String anwendungsName) { setzeAnwendungsName(anwendungsName); } /** * Methode fuer den Zugriff auf das Betriebssystem, auf dem diese Anwendung laeuft. * * @param bs */ public void setSystemSoftware(InternetKnotenBetriebssystem bs) { betriebssystem = bs; } /** * Methode fuer den Zugriff auf das Betriebssystem, auf dem diese Anwendung laeuft. * * @param bs */ public InternetKnotenBetriebssystem getSystemSoftware() { return betriebssystem; } } filius-2.9.4+ds/src/main/java/filius/software/AnwendungObservable.java000066400000000000000000000036551506574241500260240ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software; import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AnwendungObservable extends Observable { private static Logger LOG = LoggerFactory.getLogger(AnwendungObservable.class); public void notifyObservers() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (AnwendungObservable), notifyObservers()"); setChanged(); super.notifyObservers(); } public void notifyObservers(Object daten) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (AnwendungObservable), notifyObservers(" + daten + ")"); setChanged(); super.notifyObservers(daten); } } filius-2.9.4+ds/src/main/java/filius/software/ProtocolDataUnit.java000066400000000000000000000033451506574241500253200ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software; import java.io.Serializable; import filius.hardware.NetzwerkInterface; @SuppressWarnings("serial") public abstract class ProtocolDataUnit implements Serializable { /** The receiving network interface, set on ethernet layer or whenever the PDU is extracted from lower layer PDU. */ private transient NetzwerkInterface rcvNic; public NetzwerkInterface getRcvNic() { return rcvNic; } public void setRcvNic(NetzwerkInterface rcvNic) { this.rcvNic = rcvNic; } } filius-2.9.4+ds/src/main/java/filius/software/Protokoll.java000066400000000000000000000070301506574241500240450ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software; import filius.software.system.SystemSoftware; /** * Diese Klasse ist Oberklasse der Protokolle auf den verschiedenen Schichten. Es werden Protokolle fuer vier Schichten * implementiert: *
    *
  1. Netzzugang
  2. *
  3. Vermittlung
  4. *
  5. Transport
  6. *
  7. Anwendung
  8. *
* Die Anwendungsschicht wird durch die verschiedenen Anwendungen implementiert.
* Peterson und Davie beschreiben zwei moegliche Grundarten der Implementierung des Protokollstapels mit Prozessen / * Threads. Den ersten Ansatz nennen sie Prozess-pro-Protokoll. D. h., fuer jede Schicht wird ein Prozess gestartet, der * Daten von der hoeheren und von der niedrigeren Schicht entgegen nimmt und weitergibt. Der zweite Ansatz ist * Prozess-pro-Nachricht. D. h. für jede Nachricht existiert ein Prozess, der die Verarbeitung auf allen Schichten * veranlasst.
* Hier werden fuer die Weiterleitung der Daten zur naechst hoeheren Schicht jeweils ein Thread implementiert. Der * Versand der Daten von der obersten zur untersten Schicht erfolgt durch den Thread der Anwendung. Damit ist das eine * Verbindung der zwei beschriebenen Ansaetze. * * @author stefan */ public abstract class Protokoll { /** Die Systemsoftware, die auf der Hardware-Komponente laeuft */ private SystemSoftware systemSoftware; /** * Konstruktor fuer Protokolle. Hier wird lediglich die Systemsoftware initialisiert. * * @param systemSoftware * die Systemsoftware der Hardware-Komponente, auf der das Protokoll laeuft. */ public Protokoll(SystemSoftware systemSoftware) { this.systemSoftware = systemSoftware; } /** Methode fuer den Zugriff auf die Systemsoftware */ public SystemSoftware holeSystemSoftware() { return systemSoftware; } /** * Methode zum Starten der Protokoll-Threads. Diese Methode wird beim Wechsel vom Entwurfs- zum Aktionsmodus durch * die Systemsoftware aufgerufen. Hier werden die notwendigen Threads gestartet. */ public abstract void starten(); /** * Methode zum Beenden der Protokoll-Threads. Diese Methode wird beim Wechsel vom Entwurfs- zum Aktionsmodus durch * die Systemsoftware aufgerufen. Hier werden alle vorhandenen Threads dieser Schicht beendet. */ public abstract void beenden(); } filius-2.9.4+ds/src/main/java/filius/software/ProtokollThread.java000066400000000000000000000120211506574241500251710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Diese Klasse ist die Oberklasse von Protokoll-Threads. Die Aufgabe ist, den Puffer zu ueberwachen, und die * Verarbeitung der eingehenden Dateneinheiten zu starten. * * @author stefan * */ public abstract class ProtokollThread extends Thread { private static Logger LOG = LoggerFactory.getLogger(ProtokollThread.class); /** ob der Thread gerade am laufen ist */ protected boolean running = false; /** der von dem Thread zu ueberwachende Puffer */ private LinkedList puffer; /** * Leerer Konstruktur, der nur dann genutzt werden darf, wenn von diesem Thread kein Puffer ueberwacht werden soll. * In diesem Fall muss auch die run()-Methode ueberschrieben werden! */ public ProtokollThread() {} /** * Konstruktur, der ausschliesslich die Initialisierung des zu ueberwachenden Puffers uebernimmt, der als Parameter * uebergeben wird. * * @param puffer * der zu ueberwachende Puffer */ public ProtokollThread(LinkedList puffer) { this.puffer = puffer; } /** * Diese Methode implementiert die Ueberwachung des Puffers in einem eigenen Thread. Solange sich keine Dateneinheit * in dem Puffer befindet, geht der Thread in den wartenden Zustand. Wenn eine Dateneinheit eintrifft, wird diese * mit dem Aufruf der Methode verarbeiteDatenEinheit() zur weiteren Verarbeitung weitergegeben. */ public void run() { LOG.trace(getClass() + " (ProtkollThread), run()"); while (running) { synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(); } catch (InterruptedException e) {} } if (puffer.size() > 0) { verarbeiteDatenEinheit(puffer.removeFirst()); } } } } /** * Diese Methode wird aufgerufen, wenn eine Dateneinheit in dem ueberwachten Puffer eintrifft. * * @param datenEinheit * die im ueberwachten Puffer eingetroffene Dateneinheit */ protected abstract void verarbeiteDatenEinheit(T datenEinheit); /** Methode fuer den Zugriff auf den zu ueberwachenden Puffer */ protected LinkedList holeEingangsPuffer() { return puffer; } /** * Methode zum Starten des Threads beim Wechsel vom Entwurfs- in den Aktionsmodus. Wenn sich der Thread noch in * einem wartenden oder blockierten Zustand wird kein neuer Thread gestartet, sondern lediglich gewaehrleistet, dass * der Thread nicht beendet wird. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ProtokollThread), starten()"); if (!running) { running = true; if (getState().equals(State.WAITING) || getState().equals(State.BLOCKED)) { // LOG.debug(getClass()+"\n\tProtokollThread: Thread laeuft bereits."); } else { start(); } } } /** * Methode zum Beenden des Threads. Wenn der Thread noch in einem wartenden oder blockierten Zustand ist, wird * interrupt() aufgerufen, um die Verarbeitung fortzusetzen, damit der Thread dann beendet werden kann. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ProtokollThread), beenden()"); running = false; if (getState().equals(State.WAITING) || getState().equals(State.BLOCKED)) { interrupt(); } if (this.puffer != null) this.puffer.clear(); } }filius-2.9.4+ds/src/main/java/filius/software/clientserver/000077500000000000000000000000001506574241500237225ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/clientserver/ClientAnwendung.java000066400000000000000000000053401506574241500276540ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.Anwendung; import filius.software.transportschicht.Socket; /** * Die Klasse ClientAnwendung enthaelt die fuer Clients spezifischen Methoden. */ public abstract class ClientAnwendung extends Anwendung { private static Logger LOG = LoggerFactory.getLogger(ClientAnwendung.class); /** * der Socket, der zum Nachrichtenaustausch mit dem Server genutzt wird */ protected Socket socket = null; /** * Methode zur Abfrage, ob der Socket mit einem Server-Socket verbunden ist.
* Diese Methode ist nicht blockierend. */ public boolean istVerbunden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientAnwendung), istVerbunden()"); if (socket != null) { return socket.istVerbunden(); } else { return false; } } /** * Diese Methode ruft beenden() der Oberklasse auf und loescht die Referenz auf den Socket. Der Socket wird also * einfach verworfen und nicht geschlossen.
* Diese Methode ist nicht blockierend. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientAnwendung), beenden()"); super.beenden(); if (socket != null) { socket.beenden(); socket = null; } } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/ClientBaustein.java000066400000000000000000000205011506574241500274740ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.software.transportschicht.TCPSocket; /** *

* In dieser Klasse wird das Client-Programm einer einfachen Client-Server-Anwendung implementiert. Nachrichten an die * graphische Benutzungsoberflaeche werden durch den Aufruf banachrichtigeBeobachter(Object) versendet. *

*

* Aufrufe folgender Methoden des Sockets blockieren: *

*
    *
  • verbinden()
  • *
  • senden()
  • *
  • empfangen()
  • *
  • schliessen()
  • *
*

* Deshalb muessen Methoden dieser Klasse, die Aufrufe dieser Methoden enthalten, ueber die Methode * ausfuehren(String, Object[]) aufgerufen werden. Das bewirkt, dass diese Methoden in einem eigenen Thread * ausgefuehrt werden und damit der aufrufende Thread nicht blockiert. Das ist wichtig, wenn die Aufrufe von der * graphischen Benutzungsoberflaeche ausgeloest werden. *

*

* Ein Beispiel fuer die Verwendung von ausfuehren() ist folgendes:
* public void verbinden(String zielAdresse, Integer port) {
*    Object[] args;

*    args = new Object[2];
*    args[0] = zielAdresse;
*    args[1] = port;

*    ausfuehren("initialisiereSocket", args);
* } *

*

* Dabei wird als erstes Argument der auszufuehrenden blockierenden Methode * uebergeben (hier: initialisiereSocket) und dann in einem Array die zu uebergebenden Parameter (hier: * zielAdresse und port). Der Aufruf der Methode verbinden(zielAdresse, port) * bewirkt also das Ausfuehren der Methode initialisiereSocket(zielAdresse, port) in einem anderen * Thread. Damit blockiert die Methode verbinden nicht. *

*

* Achtung: Die indirekt aufgerufene Methode (d. h. ueber * ausfuehren(String, Object[])) muss als public deklariert sein! *

*/ public class ClientBaustein extends ClientAnwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(ClientBaustein.class); /** Port-Nummer des Servers, an dem Verbindungsanfragen angenommen werden */ private int zielPort = 55555; /** * Adresse des Rechners, auf dem der Server laeuft als Domainname oder IP-Adresse. */ private String zielIPAdresse; /** * Methode zum Verbindungsaufbau zu einem Server. Hier wird der Client-Socket als TCP/IP-Socket erzeugt. Wenn UDP * verwendet werden soll, muss diese Methode ueberschrieben werden.
* Diese Methode ist nicht blockierend. Diese Methode veranlasst den Aufruf von * initialisiereSocket in einem anderen Thread. */ public void verbinden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), verbinden()"); Object[] args; args = new Object[2]; args[0] = zielIPAdresse; args[1] = Integer.valueOf(zielPort); ausfuehren("initialisiereSocket", args); ausfuehren("empfangeNachricht", null); } /** * Methode zum Aufbau einer Verbindung mit einem TCP-Socket. Diese Methode ist blockierend. */ public synchronized void initialisiereSocket(String zielAdresse, Integer port) { LOG.debug("connect client {}:{}", zielAdresse, port); if (!istVerbunden()) { try { socket = new TCPSocket(getSystemSoftware(), zielAdresse, port); socket.verbinden(); benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg2")); } catch (Exception e) { LOG.debug("", e); socket = null; benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg1") + e.getMessage()); } } } /** * Methode zum trennen einer Verbindung. Der Socket wird durch den Aufruf der Methode schliessen() geschlossen und * und der Socket fuer diese Anwendung auf null gesetzt.
* Diese Methode ist blockierend. */ public void trennen() { LOG.debug("disconnect client"); if (socket != null) { socket.schliessen(); benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg3")); } } /** * Diese Methode blockiert bis die Nachricht versand wurde. Der Empfang der Antwort erfolgt asynchron. */ public void senden(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), versendeNachricht(" + nachricht + ")"); if (socket != null && socket.istVerbunden()) { try { socket.senden(nachricht); benachrichtigeBeobachter("<<" + nachricht); } catch (Exception e) { benachrichtigeBeobachter(e.getMessage()); LOG.debug("", e); } } else { benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg4")); } } /** * Methode zum Empfang einer Nachricht vom Socket. Die empfangene Nachricht wird mit * benachrichtigeBeobachter an die GUI weiter gegeben. Diese Methode ist blockierend und sollte nicht * direkt von der GUI aufgerufen werden. */ public void empfangeNachricht() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), empfangeNachricht()"); String nachricht; while (socket != null && socket.istVerbunden()) { try { nachricht = socket.empfangen(Long.MAX_VALUE); if (nachricht != null) { benachrichtigeBeobachter(">>" + nachricht); } else if (null != socket) { socket.schliessen(); benachrichtigeBeobachter( messages.getString("sw_clientbaustein_msg5") + " " + socket.holeZielIPAdresse() + ":" + socket.holeZielPort() + " " + messages.getString("sw_clientbaustein_msg6")); } } catch (Exception e) { benachrichtigeBeobachter(e.getMessage()); LOG.debug("", e); socket = null; } } } /** Methode fuer den Zugriff auf die Server-Adresse */ public String getZielIPAdresse() { return zielIPAdresse; } /** Methode fuer den Zugriff auf die Server-Adresse */ public void setZielIPAdresse(String zielIPAdresse) { this.zielIPAdresse = zielIPAdresse; } /** * Methode fuer den Zugriff auf die Port-Nummer, an dem der Server zu erreichen ist. */ public int getZielPort() { return zielPort; } /** * Methode fuer den Zugriff auf die Port-Nummer, an dem der Server zu erreichen ist. */ public void setZielPort(int zielPort) { this.zielPort = zielPort; } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/ServerAnwendung.java000066400000000000000000000225471506574241500277140ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.ServerSocketException; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.transportschicht.ServerSocket; import filius.software.transportschicht.Socket; import filius.software.transportschicht.SocketSchnittstelle; /** * Diese Klasse ist die Oberklasse fuer Serveranwendungen. Dazu wird ein Server-Socket und Methoden zur * Verbindungsherstellung zur Verfuegung gestellt. Ausserdem wird eine Liste von Mitarbeiter-Threads, die die * Anwendungslogik implementieren bzw. die Verarbeitung eingehender Verbindungsanfragen und Dienstnforderungen * uebernehmen. */ public abstract class ServerAnwendung extends Anwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(ServerAnwendung.class); /** Konstante: UDP oder TCP der Klasse TransportProtokoll */ protected int transportProtokoll; private List boundNICs = Collections.emptyList(); /** der Socket zur Annahme eingehender Verbindungsanfragen */ protected SocketSchnittstelle socket; /** Der TCP-Port, der auf eingehende Verbindungsanfragen wartet. */ protected int port = 55555; /** * Ob der Server aktiv ist, d. h. ob auf eingehende Verbindungsanfragen gewartet wird. */ protected boolean aktiv = false; /** * Liste von Mitarbeitern, die die Bearbeitung der erstellten Verbindungen vornehmen. */ protected LinkedList mitarbeiter; /** Konstruktor zur Initialisierung des verwendeten TransportProtokolls */ public ServerAnwendung(int transportProtokoll) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + ", constr: ServerAnwendung(" + transportProtokoll + ")"); this.transportProtokoll = transportProtokoll; } public void bindAddress(List bind) { this.boundNICs = bind; } /** * Methode fuer den Zugriff auf die Portnummer, auf der eingehende Verbindungen angenommen werden */ public int getPort() { return port; } /** * Methode fuer den Zugriff auf die Portnummer, auf der eingehende Verbindungen angenommen werden */ public void setPort(int port) { this.port = port; } /** * Methode zur Abfrage, ob der Server-Socket auf eingehende Verbindungen wartet. */ public boolean isAktiv() { return aktiv; } /** * Zum aktivieren bzw. deaktivieren des Servers. Wenn der Server aktiv ist, wartet er auf eingehende * Verbindungsanfragen. Sonst ist der Port geschlossen. * * @param flag */ public synchronized void setAktiv(boolean flag) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerAnwendung), setAktiv(" + flag + ")"); aktiv = flag; if (getState().equals(State.WAITING)) { notifyAll(); } if (!flag) { if (socket != null) { socket.schliessen(); } if (null != mitarbeiter) { for (ServerMitarbeiter thread : mitarbeiter) { thread.shutdown(true); } mitarbeiter.clear(); } benachrichtigeBeobachter(messages.getString("sw_serveranwendung_msg1")); } else { benachrichtigeBeobachter(messages.getString("sw_serveranwendung_msg2")); } } /** * Methode zum Starten des Threads beim Wechsel vom Entwurfs- in den Aktionsmodus. Hier wird die Liste der * Mitarbeiter als leere Liste erstellt und die starten()-Methode der Oberklasse zum Starten des Threads aufgerufen. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerAnwendung), starten()"); super.starten(); mitarbeiter = new LinkedList(); ausfuehren("annehmenVerbindungen", null); } // return, whether this application can be used already public boolean isStarted() { return socket != null; } /** * Methode zum Anhalten des Threads. Hier wird die beenden()-Methode der Oberklasse aufgerufen und die * Mitarbeiter-Threads sowie die Socket-Schnittstelle werden beendet. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerAnwendung), beenden()"); super.beenden(); if (null != mitarbeiter) { for (ServerMitarbeiter thread : mitarbeiter) { thread.beenden(); } } if (socket != null) { socket.beenden(); } socket = null; } /** * Methode zum erzeugen eines neuen Mitarbeiters, wenn eine Verbindungsanfrage eingetroffen ist. Diese Methode * muss von Unterklassen ueberschrieben werden, um den Mitarbeiter mit der richtigen Anwengungslogik zu * erzeugen.
* In dieser Methode wird der Mitarbeiter erzeugt und der Liste der Mitarbeiter hinzugefuegt. Das muss in den * Unterklassen implementiert werden. * * @param socket */ protected abstract void neuerMitarbeiter(Socket socket); /** * Methode zum entfernen eines Mitarbeiters, dessen Socket geschlossen und der Thread beendet worden ist.
* In dieser Methode wird der Mitarbeiter nur aus der Liste der verwalteten Threads entfernt. * * @param thread * der nicht mehr aktive Mitarbeiter */ public void entferneMitarbeiter(ServerMitarbeiter thread) { mitarbeiter.remove(thread); } /** * Die Aufgabe des Threads der Server-Anwendung besteht darin, wenn der Server aktiv ist, auf eingehende * Verbindungsanforderungen zu warten. Wenn eine Anforderung erfolgt, wird ein neuer Mitarbeiter mit der Methode * neuerMitarbeiter() erstellt, der die weitere Verarbeitung uebernimmt. */ public void annehmenVerbindungen() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerAnwendung), annehmenVerbindungen()"); Socket transportSocket; while (running) { if (aktiv) { if (socket == null) { socket = new ServerSocket(getSystemSoftware(), port, transportProtokoll, boundNICs); } if (socket != null) { try { transportSocket = ((ServerSocket) socket).oeffnen(); if (transportSocket != null && transportSocket.holeZielIPAdresse() != null) { neuerMitarbeiter(transportSocket); benachrichtigeBeobachter(messages.getString("sw_serveranwendung_msg4") + " " + transportSocket.holeZielIPAdresse() + ":" + transportSocket.holeZielPort() + " " + messages.getString("sw_serveranwendung_msg5")); } } catch (ServerSocketException e) { benachrichtigeBeobachter(messages.getString("sw_serveranwendung_msg6")); setAktiv(false); LOG.debug("tried to establish connection to client - but failed.", e); } catch (Exception e) { benachrichtigeBeobachter(e.getMessage()); LOG.debug("", e); } } } else { if (socket != null) { socket.schliessen(); benachrichtigeBeobachter(messages.getString("sw_serveranwendung_msg3")); } socket = null; synchronized (this) { try { wait(); } catch (InterruptedException e) {} } } } } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/ServerBaustein.java000066400000000000000000000070111506574241500275250ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.transportschicht.Socket; import filius.software.transportschicht.TCPSocket; /** *

* In dieser Klasse wird das Server-Programm einer einfachen Client-Server-Anwendung implementiert. Nachrichten an die * graphische Benutzungsoberflaeche werden durch den Aufruf banachrichtigeBeobachter(Object) versendet. *

*

* In dieser Klasse wird mit einem eigenen Thread der Server-Socket auf eingehende Verbindungen gewartet. Die * Verarbeitung der eingehenden Nachrichten erfolgt durch einen Mitarbeiter, der in der Methode * neuerMitarbeiter(Socket) erzeugt wird. *

*

* Die Verarbeitung eingehender Nachrichten wird von einem Mitarbeiter uebernommen, der erzeugt wird, sobald eine neue * Verbindungsanfrage vom Server-Socket empfangen wurde. *

*

* Einstellungen, die in einer Projektdatei gespeichert werden sollen, muessen in dieser Klasse als Attribute verwaltet * werden und mit Getter- und Setter-Methoden zugaenglich sein (z. B. fuer das Attribut int bspAttr mit * * public int getBspAttr() und public void setBspAttr(int) * ). Attribute, die in der Mitarbeiterklasse vorhanden sind, werden nicht gespeichert! *

*

* Das Server-Programm wird durch Aufruf der Methode der Oberklasse setAktiv(boolean) aktiviert bzw. * deaktiviert (d. h. die Verbindungsannahme wird aktiviert bzw. deaktiviert). *

*/ public class ServerBaustein extends TCPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(ServerBaustein.class); /** * In dieser Methode wird ein neuer Mitarbeiter zur Verarbeitung von eingehenden Nachrichten erzeugt und der Liste * der zu verwaltenden Mitarbeiter hinzugefuegt. */ protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerBaustein), neuerMitarbeiter(" + socket + ")"); ServerMitarbeiter mitarbeiter; if (socket instanceof TCPSocket) { mitarbeiter = new ServerBausteinMitarbeiter(this, (TCPSocket) socket); this.mitarbeiter.add(mitarbeiter); mitarbeiter.starten(); } } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/ServerBausteinMitarbeiter.java000066400000000000000000000062251506574241500317230ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.transportschicht.Socket; /** *

* In dieser Klasse erfolgt die Verarbeitung von eingehenden Nachrichten an einen Server. *

*

* Die Oberklasse ServerMitarbeiter erbt von der Klasse Thread. In der run()-Methode der * Oberklasse wird der Socket auf eingehende Nachrichten ueberwacht. Sobald eine Nachricht eintrifft, wird diese an die * Methode verarbeiteNachricht(String) zur weiteren Verarbeitung weiter gegeben. Ausserdem wird dort der * Socket automatisch geschlossen, wenn das Client-Programm den Verbindungsabbau initiiert. *

*

* In dieser Klasse sollte nur die Methode senden(String) des Sockets verwendet werden! *

*/ public class ServerBausteinMitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(ServerBausteinMitarbeiter.class); /** * Standard-Konstruktor. Wenn der Server auf einem bestimmten Port auf eingehende Verbindungen warten soll, muss die * Port-Nummer hier mit setPort(int) initialisiert werden! */ public ServerBausteinMitarbeiter(ServerAnwendung server, Socket socket) { super(server, socket); } /** * Methode, die automatisch aufgerufen wird, wenn eine neue Nachricht eintrifft. Hier erfolgt die Verarbeitung der * eingehenden Nachricht. */ protected void verarbeiteNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerBausteinMitarbeiter), verarbeiteNachricht(" + nachricht + ")"); try { socket.senden(nachricht); server.benachrichtigeBeobachter("<<" + nachricht); } catch (Exception e) { LOG.debug("", e); server.benachrichtigeBeobachter(e.getMessage()); } } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/ServerMitarbeiter.java000066400000000000000000000153601506574241500302300ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.VerbindungsException; import filius.rahmenprogramm.I18n; import filius.software.transportschicht.Socket; /** * Diese Klasse implementiert einen Thread, der von einem Server fuer eine eingegangene Verbindungsanfrage erzeugt wird. * Damit wird der neu erstellte Socket ueberwacht. * */ public abstract class ServerMitarbeiter extends Thread implements I18n { private static Logger LOG = LoggerFactory.getLogger(ServerMitarbeiter.class); /** Die Server-Anwendung, die diesen Mitarbeiter verwaltet */ protected ServerAnwendung server; /** der Socket, der fuer den Datenaustausch verwendet wird */ protected Socket socket; /** Dieses Attribut zeigt an, ob der Thread laeuft. */ protected boolean running = false; /** * Konstruktor, in dem die zugehoerige ServerAnwendung und der Socket fuer den Datenaustausch implementiert werden. * * @param server * @param socket */ public ServerMitarbeiter(ServerAnwendung server, Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerMitarbeiter), constr: ServerMitarbeiter(" + server + "," + socket + ")"); this.server = server; this.socket = socket; } /** * Mit dieser Methode werden eingehende Nachrichten verarbeitet. Sie enthaelt also die eigentliche Anwendungslogik * und muss daher von den Unterklassen implementiert werden. * * @param nachricht */ protected abstract void verarbeiteNachricht(String nachricht); /** * Mit dieser Methode werden Nachrichten versendet und wenn dies ohne Ausnahme (Exception) stattgefunden hat, an * Beobachter der Server-Anwendung weiter gegeben.
* Diese Methode ist blockierend * * @param nachricht */ protected void sendeNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerMitarbeiter), sendeNachricht(" + nachricht + ")"); try { socket.senden(nachricht); server.benachrichtigeBeobachter("<<" + nachricht); } catch (Exception e) { server.benachrichtigeBeobachter(e.getMessage()); LOG.debug("", e); } } /** * Hier wird auf eingehende Nachrichten gewartet und diese zum einen an Beobachter der Server-Anwendung und zum * anderen zur Verarbeitung an die Methode verarbeitenNachricht() weiter gegeben. */ public void run() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerMitarbeiter), run()"); String nachricht = null; while (running) { try { if (socket.istVerbunden()) { nachricht = socket.empfangen(Long.MAX_VALUE); } if (nachricht != null) { server.benachrichtigeBeobachter(">>" + nachricht); verarbeiteNachricht(nachricht); } else if (socket != null) { socket.schliessen(); running = false; server.benachrichtigeBeobachter( messages.getString("sw_servermitarbeiter_msg1") + " " + socket.holeZielIPAdresse() + ":" + socket.holeZielPort() + " " + messages.getString("sw_servermitarbeiter_msg2")); } nachricht = null; } catch (VerbindungsException e) { LOG.debug("", e); server.benachrichtigeBeobachter(e.getMessage()); socket.schliessen(); running = false; server.entferneMitarbeiter(this); } catch (Exception e) { LOG.debug("", e); server.benachrichtigeBeobachter(e.getMessage()); socket.schliessen(); running = false; server.entferneMitarbeiter(this); } } } /** * Methode zum Starten des Threads beim Wechsel vom Entwurfs- in den Aktionsmodus. Wenn sich der Thread noch in * einem wartenden oder blockierten Zustand wird kein neuer Thread gestartet, sondern lediglich gewaehrleistet, dass * der Thread nicht beendet wird. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerMitarbeiter), starten()"); if (!running) { running = true; if (!getState().equals(State.WAITING) && !getState().equals(State.BLOCKED)) { start(); } } } /** * Methode zum Beenden des Threads. Wenn der Thread noch in einem wartenden oder blockierten Zustand ist, wird * interrupt() aufgerufen, um die Verarbeitung fortzusetzen, damit der Thread dann beendet werden kann. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerMitarbeiter), beenden()"); shutdown(false); } protected void shutdown(boolean graceful) { running = false; if (socket != null && graceful) { socket.schliessen(); } else if (socket != null) { socket.beenden(); } if (getState().equals(State.WAITING) || getState().equals(State.BLOCKED)) { interrupt(); } } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/TCPServerAnwendung.java000066400000000000000000000033321506574241500302520ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.vermittlungsschicht.IpPaket; public abstract class TCPServerAnwendung extends ServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(TCPServerAnwendung.class); public TCPServerAnwendung() { super(IpPaket.TCP); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + ", constr: TCPServerAnwendung()"); } } filius-2.9.4+ds/src/main/java/filius/software/clientserver/UDPServerAnwendung.java000066400000000000000000000033321506574241500302540ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.vermittlungsschicht.IpPaket; public abstract class UDPServerAnwendung extends ServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(UDPServerAnwendung.class); public UDPServerAnwendung() { super(IpPaket.UDP); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + ", constr: UDPServerAnwendung()"); } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/000077500000000000000000000000001506574241500242245ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PeerToPeerAnwendung.java000066400000000000000000000522431506574241500307560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.Anwendung; import filius.software.system.Betriebssystem; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; /** * Diese Klasse verwaltet die notwendigen Daten fuer den Dateiaustausch mit 'Gnutella'. Das ist ein Programm fuer den * Dateiaustausch im Internet in einem Peer-to-Peer-Netzwerk. Fuer die Verarbeitung von Anfragen werden ein * PeerToPeerClient und ein PeerToPeerServer verwendet. *
    *
  • Der Server verarbeitet alle eingehenden Anfragen von anderen Teilnehmern. Und veranlasst gegebenenfalls die * Weiterleitung von Ping-, Pong- und Query-Nachrichten sowie die Verarbeitung von eingehenden HTTP-GET-Anfragen.
  • *
  • Der Client versendet eigene und fremde Anfragen an andere PeerToPeerServer und sorgt fuer die Verarbeitung * eingehende Antworten auf diese Anfragen.
  • *
* */ public class PeerToPeerAnwendung extends Anwendung { private static Logger LOG = LoggerFactory.getLogger(PeerToPeerAnwendung.class); /** * Liste der Teilnehmer im Peer-to-Peer-Netzwerk, die dem Prozess bekannt sind. Die Liste enthaelt die * IP-Adressen(?????) */ private LinkedList bekanntePeerToPeerTeilnehmer = new LinkedList(); /** Liste eigener Anfragen, die verschickt worden sind */ private LinkedList eigeneAnfragen = new LinkedList(); /** * Eine Liste der Anfragen, die von anderen Teilnehmern im Peer-to-Peer-Netzwerk empfangen wurden. wofuer??? */ private LinkedList fremdeAnfragen = new LinkedList(); /** * Liste bereits eingegangener und weitergeleiteter Anfragen (zur Verhinderung von Schleifen) */ private LinkedList schonmalVerschicktListe = new LinkedList(); /** * Liste der Dateien, zu welchen eine Anfrage zum Herunterladen an einen bestimmten Teilnehmer verschickt wurde und * deren Empfang erwartet wird */ private LinkedList erwarteteDateien = new LinkedList(); /** * Liste der im Peer-to-Peer-Netzwerk vorhandenen Dateien, die auf eine Anfrage von anderen Teilnehmern angeboten * wurden */ private LinkedList ergebnisse = new LinkedList(); /** Der Teil der Anwendung, der auf eingehende Anfragen wartet */ private PeerToPeerServer peerToPeerServer; /** Der Teil der Anwendung, der eigene Anfragen verschickt */ private PeerToPeerClient peerToPeerClient; /** * Verzeichnis, in dem die Dateien gespeichert werden, die anderen angeboten werden und in das heruntergeladene * Dateien gespeichert werden */ private DefaultMutableTreeNode verzeichnis; /** maximale Anzahl von Teilnehmern, zu denen eine Verbindung aufgebaut wird */ private int maxTeilnehmerZahl; /** * herkoemmlicher Konstruktor */ public PeerToPeerAnwendung() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), constr: PeerToPeerAnwendung()"); // maximale Teilnehmerzahl mit einem Zufallswert 3, 4 oder 5 belegen setMaxTeilnehmerZahl(Math.round((float) Math.random() * 2) + 3); } /** * Hier wird das Betriebssystem gesetzt und das Standardverzeichnis initialisiert und wenn noetig im Dateisystem * erstellt */ public void setSystemSoftware(InternetKnotenBetriebssystem betriebssystem) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), setSystemSoftware(" + betriebssystem + ")"); super.setSystemSoftware(betriebssystem); Dateisystem dateisystem = betriebssystem.getDateisystem(); dateisystem.erstelleVerzeichnis(betriebssystem.getDateisystem().getRoot(), "peer2peer"); verzeichnis = dateisystem .verzeichnisKnoten(dateisystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "peer2peer"); } /** * Starten der Anwendung */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), starten()"); super.starten(); eigeneAnfragen = new LinkedList(); fremdeAnfragen = new LinkedList(); schonmalVerschicktListe = new LinkedList(); peerToPeerServer = new PeerToPeerServer(this); peerToPeerServer.setSystemSoftware(getSystemSoftware()); peerToPeerServer.starten(); peerToPeerClient = new PeerToPeerClient(this); peerToPeerClient.setSystemSoftware(getSystemSoftware()); peerToPeerClient.starten(); } /** * Beenden der Anwendung */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), beenden()"); super.beenden(); peerToPeerServer.beenden(); peerToPeerClient.beenden(); } /** * Diese Methode erstellt ein Pong-Paket als Antwort auf ein Ping-Paket zurueck. Wenn auf das uebergebene Ping-Paket * schon geantwortet wurde wird null zurueck gegeben. * * @param paket * das Ping-Paket, zu dem das zu erzeugende Pong-Paket die Antwort darstellen soll * @return */ PongPaket erstellePong(PingPaket ping) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), erstellePong(" + ping + ")"); List dateien; Datei aktuelle; PongPaket pong; long anzahlBytes = 0; Betriebssystem bs; if (schonmalVerschicktListe.contains(ping.getGuid())) { return null; } else { bs = (Betriebssystem) getSystemSoftware(); dateien = bs.getDateisystem().holeDateien(verzeichnis); for (int i = 0; i < dateien.size(); i++) { aktuelle = (Datei) dateien.get(i); anzahlBytes = anzahlBytes + aktuelle.holeGroesse(); } pong = new PongPaket(bs.primaryIPAdresse(), 6346, dateien.size(), anzahlBytes); pong.setGuid(ping.getGuid()); pong.setIpAdresse(bs.primaryIPAdresse()); return pong; } } /** * Mit dieser Methode wird ein Ping-Paket versendet. Das kann entweder ein selbst erzeugtes oder ein * weiterzuleitendes Paket sein. Die GUID wird, wenn die TTL nicht abgelaufen ist und das Paket bisher noch nicht * verschickt wurde, durch den Aufruf der entsprechenden Methode des PeerToPeerClient verschickt, der dann einen * Thread startet, der die Antworten darauf verarbeitet. * * @param ping * @param absender */ void sendePing(PingPaket ping, String absender) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), sendePing(" + ping + "," + absender + ")"); if (ping.getTtl() > 0 && !schonmalVerschicktListe.contains(ping.getGuid())) { schonmalVerschicktListe.add(ping.getGuid()); fremdeAnfragen.add(ping.getGuid()); peerToPeerClient.sendePing("", ping, absender); } } /** * wenn eine neue Anfrage-Nachricht eingetroffen ist, wird diese Operation aufgerufen. Sie verarbeitet die Anfrage, * schickt sie weiter, verwirft sie oder sendet ein Antwortpaket * * @param pufferElement * die ampfangene Anfrage */ LinkedList verarbeiteAnfrage(String absender, QueryPaket anfrage) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), verarbeiteAnfrage(" + absender + "," + anfrage + ")"); LinkedList ergebnisListe; Datei ergebnis = null; List dateien; String aktuellerName; String gesuchte; if (!schonmalVerschicktListe.contains(anfrage.getGuid())) { fremdeAnfragen.add(anfrage.getGuid()); schonmalVerschicktListe.add(anfrage.getGuid()); dateien = getSystemSoftware().getDateisystem().holeDateien(verzeichnis); ergebnisListe = new LinkedList(); for (int i = 0; i < dateien.size(); i++) { ergebnis = (Datei) dateien.get(i); aktuellerName = ergebnis.getName().toLowerCase(); gesuchte = anfrage.getSuchKriterien().toLowerCase(); if (aktuellerName.contains(gesuchte)) { ergebnisListe.add(ergebnis); } } if (anfrage.getTtl() > 0) { anfrage.setTtl(anfrage.getTtl() - 1); anfrage.setHops(anfrage.getHops() + 1); peerToPeerClient.sendeAnfrage(anfrage, absender); } return ergebnisListe; } else { return null; } } /** * Diese Methode dient dazu, einen Rechner mit einem PeerToPeer-Netzwerk zu verbinden. Dazu wird die IP-Adresse * eines bekannten Teilnehmers uebergeben.
* Die Liste der bisher bekannten Teilnehmer wird geloescht.
* An die IP-Adresse des bekannten Teilnehmers wird ein Ping-Paket versendet. Das Ping-Paket wird durch das Netz * geflutet, jeder Servant, der weniger als die jeweilige maximale Anzahl bekannter Nachbarn hat, antwortet mit * einem Pong. Der Teilnehmer fuegt die Absender der ersten ankommenden Pongs in seine Liste der Nachbarn hinzu, bis * die maximale Anzahl von benachbarten Teilnehmern erreicht ist. * * @param teilnehmerIP * Die IP des Servants, an den das Ping-Paket zuerst geschickt wird */ public void beitretenNetzwerk(String teilnehmerIP) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), beitretenNetzwerk(" + teilnehmerIP + ")"); Betriebssystem bs; PingPaket pingPaket; bs = (Betriebssystem) getSystemSoftware(); if (!bs.primaryIPAdresse().equals(teilnehmerIP)) { pingPaket = new PingPaket(); pingPaket.setIp(bs.primaryIPAdresse()); eigeneAnfragen.add(pingPaket.getGuid()); peerToPeerClient.sendePing(teilnehmerIP, pingPaket, bs.primaryIPAdresse()); } } public void resetNetwork() { bekanntePeerToPeerTeilnehmer.clear(); benachrichtigeBeobachter(bekanntePeerToPeerTeilnehmer); } /** * Methode zum herunterladen einer zuvor von einem anderen Teilnehmer im Peer-to-Peer-Netzwerk angebotenen Datei. * * @param ergebnisIndex * Der Index der angebotenen Datei in der Liste der Ergebnisse (Attribut 'ergebnisse'). */ public void herunterladenDatei(int ergebnisIndex) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), herunterladenDatei(" + ergebnisIndex + ")"); StringTokenizer tempTokenizer; String tmpBesitzer; StringTokenizer tempTokenizerDatei; String tmpDateiname; tempTokenizer = new StringTokenizer(ergebnisse.get(ergebnisIndex), "/"); tmpBesitzer = tempTokenizer.nextToken(); tempTokenizerDatei = new StringTokenizer(tempTokenizer.nextToken(), ":"); tmpDateiname = tempTokenizerDatei.nextToken(); erwarteteDateien.add(tmpDateiname); peerToPeerClient.dateiVomTeilnehmerAnfordern(tmpBesitzer, tmpDateiname); benachrichtigeBeobachter(); } /** * Methode zum Suchen von Dateien im Peer-to-Peer-Netzwerk * * @param datei * der Dateiname der zu suchenden Datei */ public void sucheDatei(String datei) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), sucheDatei(" + datei + ")"); Betriebssystem bs; QueryPaket anfragePaket; ergebnisse.clear(); anfragePaket = new QueryPaket("1", datei); eigeneAnfragen.add(anfragePaket.getGuid()); bs = (Betriebssystem) getSystemSoftware(); peerToPeerClient.sendeAnfrage(anfragePaket, bs.primaryIPAdresse()); } /** Zum Abbruch aller Suchanfragen, die zuvor gestartet worden sind. */ public void abbrechenSuche() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), abbrechenSuche()"); peerToPeerClient.abbrechenSuche(); } /** Methode zum Zuruecksetzen der Liste mit Suchergebnissen */ public void loescheSuchergebnisse() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), loescheSuchergebnisse()"); ergebnisse.clear(); } /** * Hilfsmethode fuer den lesenden Zugriff auf die Dateien im Peer-To-Peer-Verzeichnis des eigenen Rechners. */ Datei holeDatei(String dateiName) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), holeDatei()"); Datei datei; datei = (Datei) getSystemSoftware().getDateisystem().holeDatei(verzeichnis, dateiName); return datei; } /** * Hilfsmethode fuer den schreibenden Zugriff auf die Dateien im Peer-To-Peer-Verzeichnis des eigenen Rechners. */ void speicherDatei(Datei datei) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), speicherDatei(" + datei + ")"); getSystemSoftware().getDateisystem().speicherDatei(verzeichnis, datei); } /** * Diese Methode verarbeitet eine eingegangene Nachricht. Sie befindet sich in der PeerToPeerAnwendung (und nicht im * Server oder Client), weil sowohl Client, als auch Server solche Antwortnachrichten erhalten können * * @param antwortPaket * das eingegangene Antwortpaket */ void verarbeiteQueryHit(QueryHitPaket antwortPaket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), verarbeiteQueryHit(" + antwortPaket + ")"); // warte ich selbst auf diese Antwort? if (eigeneAnfragen.contains(antwortPaket.getGuid())) { hinzuErgebnis(antwortPaket); benachrichtigeBeobachter(); } // ich kenne die Anfrage, die Antwort ist nicht fuer mich else if (fremdeAnfragen.contains(antwortPaket.getGuid())) { if (antwortPaket.getTtl() > 0) { antwortPaket.setTtl(antwortPaket.getTtl() - 1); antwortPaket.setHops(antwortPaket.getHops() + 1); peerToPeerServer.sendePaket(antwortPaket); } } } /** * Hier werden eingehende Pong-Pakete verarbeitet. Wenn es sich um die Antwort auf eine eigene Anfrage handelt, wird * die Absenderadresse der Liste der bekannten Teilnehmer im Peer-To-Peer-Netzwerk hinzugefuegt. Andernfalls wird * das Pong-Paket mit dekrementierter TTL und inkrementierter Hop-Zahl weitergeleitet. */ void verarbeitePong(PongPaket pongPaket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), verarbeitePong(" + pongPaket + ")" + "\n\tPong-Nachricht bei '" + getSystemSoftware().getKnoten().holeAnzeigeName() + "' eingetroffen: " + pongPaket.toString()); if (eigeneAnfragen.contains(pongPaket.getGuid())) { hinzuTeilnehmer(pongPaket.getIpAdresse()); } else { pongPaket.setTtl(pongPaket.getTtl() - 1); pongPaket.setHops(pongPaket.getHops() + 1); peerToPeerServer.sendePaket(pongPaket); } } /** Zugriff auf die Liste bekannter Teilnehmer im Peer-To-Peer-Netzwerk. */ public LinkedList holeBekanntePeerToPeerTeilnehmer() { return bekanntePeerToPeerTeilnehmer; } /** * Zugriff auf die Liste der Dateien, die auf eine Anfrage angeboten wurden. * * @return */ public LinkedList holeErgebnisse() { return ergebnisse; } /** * Zugriff auf das Verzeichnis im lokalen Dateisystem, in dem die Dateien gespeichert sind, die anderen * Peer-To-Peer-Teilnehmern angeboten werden. * * @return */ public DefaultMutableTreeNode holeVerzeichnis() { return verzeichnis; } /** * Hier wird der Liste mit Antworten auf eine Suchanfrage im Peer-To-Peer-Netzwerk ein Eintrag hinzugefuegt, wenn * ein Query-Hit-Paket eingetroffen ist. * * @param ergebnis */ void hinzuErgebnis(QueryHitPaket ergebnis) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), hinzuErgebnis(" + ergebnis + ")"); String neuesErgebnis; neuesErgebnis = ergebnis.getIpAdresse() + "/" + ergebnis.getErgebnis(); if (!ergebnisse.contains(neuesErgebnis)) { ergebnisse.add(neuesErgebnis); benachrichtigeBeobachter(); } } /** * In dieser Methode wird ein neu gefundener Teilnehmer der Liste bekannter Teilnehmer des Peer-to-Peer-Netzwerks * mit der IP-Adresse hinzugefuegt. Wenn die IP-Adresse in der Liste bereits vorhanden ist, oder die Anzahl der * bekannten Teilnehmer schon erreicht ist, wird die neue Adresse nicht mehr hinzugefuegt. * * @param ipAdresse */ void hinzuTeilnehmer(String ipAdresse) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerAnwendung), hinzuTeilnehmer(" + ipAdresse + ")"); if (bekanntePeerToPeerTeilnehmer.contains(ipAdresse)) { // LOG.debug(getClass() + "\n\tAn Rechner " // + getSystemSoftware().getKnoten().getName() // + " ist bereits der Teilnehmer " + ipAdresse // + " eingetragen"); } else if (bekanntePeerToPeerTeilnehmer.size() >= maxTeilnehmerZahl) { // Main.debug // .println(getClass() // + "\n\tAn Rechner " // + getSystemSoftware().getKnoten().getName() // + // " ist bereits die maximale Anzahl bekannter Teilnehmer eingetragen." // + "\n\tTeilnehmer " + ipAdresse // + " wird nicht mehr eingetragen"); } else { bekanntePeerToPeerTeilnehmer.add(ipAdresse); benachrichtigeBeobachter(); } } /** * Methode fuer den Zugriff auf die Liste mit GUID's zu eigenen Anfragen, die im Peer-to-Peer-Netzwerk verschickt * worden sind. * * @return */ LinkedList holeEigeneAnfragen() { return eigeneAnfragen; } /** * Zugriff auf die maximale Anzahl von Teilnehmern, die der Programminstanz bekannt sind. Eine maximale Anzahl ist * notwendig, weil nicht jedem Teilnehmer immer alle anderen Teilnehmer bekannt sein koennen. Die Anzahl muss * variieren, weil in dem Fall, dass diese Anzahl fuer alle Programminstanzen identisch ist und die maximale * Teilnehmerzahl gerade erreicht ist, ein neu beitretender Teilnehmer bei keiner Programminstanz hinzugefuegt * werden kann. * * @return */ public int getMaxTeilnehmerZahl() { return maxTeilnehmerZahl; } /** * Methode fuer den Zugriff auf die maximale Anzahl von Teilnehmen im Peer-to-Peer-Netzwerk, die dieser * Programminstanz bekannt sind. */ public void setMaxTeilnehmerZahl(int maxTeilnehmerZahl) { this.maxTeilnehmerZahl = maxTeilnehmerZahl; } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PeerToPeerClient.java000066400000000000000000000223731506574241500302470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.LinkedList; import java.util.ListIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.Anwendung; /** * In dieser Klasse werden Anfragen an andere Teilnehmer im Peer-To-Peer-Netzwerk verschickt bzw. weitergeleitet und * dafuer gesorgt, dass die dazu gehoerigen eingehenden Antworten verarbeitet werden. Dazu wird ein neuer Thread mit * einem PeerToPeerClientMitarbeiter gestartet. * */ class PeerToPeerClient extends Anwendung { private static Logger LOG = LoggerFactory.getLogger(PeerToPeerClient.class); /** * Die zugehoerige PeerToPeerAnwendung, die diesen Client zur Verarbeitung von eigenen Anfragen verwendet. */ private PeerToPeerAnwendung peerToPeerAnwendung; /** * Eine Liste mit allen Mitarbeiter-Threads, die zur Verarbeitung der Antworten auf eigene Anfragen verwendet * werden. */ private LinkedList mitarbeiterEigeneAnfragen = new LinkedList(); /** * Eine Liste mit allen Mitarbeiter-Threads, die zur Verarbeitung von Antworten auf fremde, d. h. weitergeleitete, * Anfragen verwendet werden. */ private LinkedList mitarbeiterFremdeAnfragen = new LinkedList(); /** * Konstruktor, in dem die zugehoerige PeerToPeerAnwendung initialisiert und der Konstruktor der Oberklasse * aufgerufen wird. * * @param peerToPeerAnwendung */ PeerToPeerClient(PeerToPeerAnwendung peerToPeerAnwendung) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClient), constr: PeerToPeerClient(" + peerToPeerAnwendung + ")"); this.peerToPeerAnwendung = peerToPeerAnwendung; } /** Zum beenden der Threads, die durch diesen Client gestartet wurden. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClient), beenden()"); ListIterator it; PeerToPeerClientMitarbeiter m; super.beenden(); it = mitarbeiterEigeneAnfragen.listIterator(); while (it.hasNext()) { m = (PeerToPeerClientMitarbeiter) it.next(); m.beenden(); } it = mitarbeiterFremdeAnfragen.listIterator(); while (it.hasNext()) { m = (PeerToPeerClientMitarbeiter) it.next(); m.beenden(); } } /** * Verschicken eines eigenden oder weitersenden einer fremden Ping-Nachricht. Wenn eine IP-Adresse angegeben wird, * zu dem diese Nachricht verschickt werden soll, wird die Ping-Nachricht als eigene Anfrage behandelt. Eine * Ping-Nachricht wird zum Beitreten zu einem Peer-To-Peer-Netzwerk verschickt. * * @param wohinZuerst * die IP-Adresse des Servents, mit welchem man sich zuerst verbinden moechte * @param pingPaket * das Ping-Paket, welches verschickt wird * @param peerToPeerServerMitarbeiter * der peerToPeerServerMitarbeiter, ueber den eine moegliche Antwort zurueck geschickt wird * @param nichtIP * die IP, an die das Paket nicht weitergeleitet werden darf (weil es daher kommt) */ void sendePing(String wohinZuerst, PingPaket pingPaket, String nichtIP) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClient), sendePing(" + wohinZuerst + "," + pingPaket + "," + nichtIP + ")"); // der erste Schritt => nur an den senden, den man angegeben hat, der // kuemmert sich um das weitere fluten if (!wohinZuerst.equals("")) { PeerToPeerClientMitarbeiter lauscher = new PeerToPeerClientMitarbeiter(peerToPeerAnwendung, wohinZuerst, pingPaket); mitarbeiterEigeneAnfragen.add(lauscher); lauscher.start(); } // Ping weiter fluten (an alle, die in der Liste stehen, ausser an den, // von dem es aktuell kam und // den, wo es urspruenglich her kam else { for (int i = 0; i < peerToPeerAnwendung.holeBekanntePeerToPeerTeilnehmer().size(); i++) { String aktuelle = (String) peerToPeerAnwendung.holeBekanntePeerToPeerTeilnehmer().get(i); if (aktuelle.equals(nichtIP) == false && aktuelle.equals(pingPaket.getIp()) == false) { PeerToPeerClientMitarbeiter lauscher = new PeerToPeerClientMitarbeiter(peerToPeerAnwendung, aktuelle, pingPaket); mitarbeiterFremdeAnfragen.add(lauscher); lauscher.start(); } } } } /** * Damit wird eine initiierte Suchanfrage abgebrochen. D. h., dass alle Threads, die auf eingehende Antworten auf * eine Suchanfrage warten, beendet werden. */ void abbrechenSuche() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClient), abbrechenSuche()"); ListIterator it; PeerToPeerClientMitarbeiter mitarbeiter; it = mitarbeiterEigeneAnfragen.listIterator(); while (it.hasNext()) { mitarbeiter = (PeerToPeerClientMitarbeiter) it.next(); mitarbeiter.beenden(); } } /** * erstellt eine HTTP Nachricht und schickt sie an den Teilnehmer, auf welchem die entsprechende Datei liegt. Auch * dazu wird ein Mitarbeiter-Thread gestartet und dieser in die Liste der Threads fuer eigene Anfragen eingefuegt. * * @param teilnehmerIp * der Teilnehmer, auf welchem die Datei liegt * @param dateiName * der Name der gesuchten Datei */ void dateiVomTeilnehmerAnfordern(String teilnehmerIp, String dateiName) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClient), dateiVomTeilnehmerAnfordern(" + teilnehmerIp + "," + dateiName + ")"); PeerToPeerClientMitarbeiter m; m = new PeerToPeerClientMitarbeiter(peerToPeerAnwendung, teilnehmerIp, dateiName); m.start(); mitarbeiterEigeneAnfragen.add(m); } /** * Initiierung und weiterleitung einer Suchanfrage (Query). Zur weiteren Verarbeitung wird ein neuer * Mitarbeiter-Thread erzeugt. Der Thread wird der Liste fuer eigene Anfragen hinzugefuegt, wenn die GUID in der * Liste der eigenen Anfragen der PeerToPeerAnwendung vorhanden ist. Die Anfrage wird immer an alle bekannten * Teilnehmer im Peer-To-Peer-Netzwerk verschickt. * * @param anfragePaket * das weiterzusendende Paket * @param absenderIP * die IP-Adresse des Absenders * @param peerToPeerServerMitarbeiter * der PeerToPeerServermitarbeiter, an den moegliche Antworten zurueck uebergeben werden muessen */ void sendeAnfrage(QueryPaket anfragePaket, String absenderIP) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClient), sendeAnfrage(" + anfragePaket + "," + absenderIP + ")"); LinkedList dieNachbarn; PeerToPeerClientMitarbeiter lauscher; String nachbar; dieNachbarn = peerToPeerAnwendung.holeBekanntePeerToPeerTeilnehmer(); /* Anfrage an ALLE direkten Nachbarn */ for (int i = 0; i < dieNachbarn.size(); i++) { nachbar = (String) dieNachbarn.get(i); if (!nachbar.equals(absenderIP)) { lauscher = new PeerToPeerClientMitarbeiter(peerToPeerAnwendung, nachbar, anfragePaket); if (peerToPeerAnwendung.holeEigeneAnfragen().contains(anfragePaket.getGuid())) { mitarbeiterEigeneAnfragen.add(lauscher); } else { mitarbeiterFremdeAnfragen.add(lauscher); } lauscher.start(); } } } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PeerToPeerClientMitarbeiter.java000066400000000000000000000170521506574241500324350ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.system.Datei; import filius.software.transportschicht.TCPSocket; import filius.software.www.HTTPNachricht; /** * In dieser Klasse werden die Mitarbeiter-Threads zum Versand von Anfragen und zur Verarbeitung der Antworten * implementiert. * */ class PeerToPeerClientMitarbeiter extends Thread { private static Logger LOG = LoggerFactory.getLogger(PeerToPeerClientMitarbeiter.class); /** * Der Thread kann verschiedene Funktionalitaeten erfuellen. Dazu stehen verschiedene Modi zur verfuegung.: *
    *
  • Versenden einer Ping-Nachricht und Verarbeitung eingehender Pong-Antworten
  • *
  • Versenden einer Suchanfrage (Query) und Verarbeitung der Antworten darauf.
  • *
  • Versenden von HTTP-GET-Anfragen und verarbeiten der Antworten mit der angeforderten Datei.
  • *
*/ private static final int PING = 1, QUERY = 3, HTTP = 4; /** die verwaltende Instanz der Peer-to-Peer-Anwendung */ private PeerToPeerAnwendung peerToPeerAnwendung; /** die IP, zu der der ClientMitarbeiter eine Verbindung aufbaut */ private String ip; /** die verschickte Nachricht als String */ private String nachricht; /** ob der Thread laeuft */ private boolean running; /** * der Socket, der zum Versenden der Nachricht und zum Empfang entsprechender Antworten genutzt wird. */ private TCPSocket socket; /** * der Modus, der die Funktionalitaet des Mitarbeiter-Threads bestimmt * * @see filius.software.dateiaustausch.PeerToPeerClientMitarbeiter#HTTP * @see filius.software.dateiaustausch.PeerToPeerClientMitarbeiter#QUERY * @see filius.software.dateiaustausch.PeerToPeerClientMitarbeiter#PING */ private int modus; /** * Konstruktor fuer einen Mitarbeiter-Thread zur Verarbeitung einer Suchanfrage oder einer Ping-Nachricht * * @param peerToPeerAnwendung * @param ip * @param paket * @see filius.software.dateiaustausch.PeerToPeerClientMitarbeiter#QUERY * @see filius.software.dateiaustausch.PeerToPeerClientMitarbeiter#PING */ public PeerToPeerClientMitarbeiter(PeerToPeerAnwendung peerToPeerAnwendung, String ip, PeerToPeerPaket paket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClientMitarbeiter), constr: PeerToPeerClientMitarbeiter(" + peerToPeerAnwendung + "," + ip + "," + paket + ")"); this.peerToPeerAnwendung = peerToPeerAnwendung; this.ip = ip; this.nachricht = paket.toString(); if (paket instanceof PingPaket) { modus = PING; } else { modus = QUERY; } running = true; } /** * Konstruktor fuer einen Mitarbeiter-Thread zur Verarbeitung einer HTTP-GET-Anfrage * * @param peerToPeerAnwendung * @param ip * @param paket * @see filius.software.dateiaustausch.PeerToPeerClientMitarbeiter#HTTP */ public PeerToPeerClientMitarbeiter(PeerToPeerAnwendung anwendung, String ip, String dateiname) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClientMitarbeiter), constr: PeerToPeerClientMitarbeiter(" + anwendung + "," + ip + "," + dateiname + ")"); HTTPNachricht nachricht; this.ip = ip; peerToPeerAnwendung = anwendung; modus = HTTP; nachricht = new HTTPNachricht(HTTPNachricht.CLIENT); nachricht.setPfad(dateiname); nachricht.setHost(ip); nachricht.setMethod(HTTPNachricht.GET); this.nachricht = nachricht.toString(); running = true; } /** * Hier wird die Funktionalitaet des Threads implementiert. *
    *
  1. Aufbau einer TCP/IP-Verbindung zur Ziel-IP-Adresse
  2. *
  3. Versenden der im Konstruktor initialisierten Nachricht
  4. *
  5. Verarbeiten der Antworten: *
      *
    • im HTTP-Modus: empfangen der Datei und speichern im lokalen Dateisystem
    • *
    • im QUERY- und PING-Modus: Empfang und Verarbeitung aller eingehenden Nachrichten bis der Thread beendet * wird.
    • *
    *
  6. *
  7. schliessen des Sockets fuer die TCP/IP-Verbindung
  8. *
*/ public void run() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerClientMitarbeiter), run()"); String antwort; HTTPNachricht http, abfrage; QueryHitPaket queryHitPaket; PongPaket pongPaket; Datei datei; try { socket = new TCPSocket(this.peerToPeerAnwendung.getSystemSoftware(), ip, 6346); socket.verbinden(); socket.senden(nachricht); if (modus == HTTP) { antwort = socket.empfangen(); http = new HTTPNachricht(antwort); if (http.getStatusCode() == 200) { abfrage = new HTTPNachricht(nachricht); datei = new Datei(); datei.setDateiTyp(http.getContentType()); datei.setName(abfrage.getPfad()); datei.setDateiInhalt(http.getDaten()); peerToPeerAnwendung.speicherDatei(datei); } } else if (modus == QUERY) { while (running) { antwort = socket.empfangen(); if (antwort != null) { queryHitPaket = new QueryHitPaket(antwort); peerToPeerAnwendung.verarbeiteQueryHit(queryHitPaket); } } } else if (modus == PING) { while (running) { antwort = socket.empfangen(); if (antwort != null) { pongPaket = new PongPaket(antwort); peerToPeerAnwendung.verarbeitePong(pongPaket); } } } } catch (Exception e) { LOG.debug("", e); } finally { socket.schliessen(); } } /** Methode zum beenden des Mitarbeiter-Threads */ public void beenden() { running = false; socket.beenden(); } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PeerToPeerPaket.java000066400000000000000000000137441506574241500300770ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.Random; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Nadja Haßler * */ public class PeerToPeerPaket { private static Logger LOG = LoggerFactory.getLogger(PeerToPeerPaket.class); /* * Attribute ---------------------------------------------------------------- ----------------------------- */ protected int guid; // 16 Byte lange zufaellige Zahl zur Identifizierung von // Paketen, 128 Bit, also Zahlen zwischen 0 und // 6.805647338418769*10^38, hier ist die aber nur 2^31-1 // gross hoechstens protected String payload; // gibt Art des Pakets an: 0x00 (Ping), 0x01 // (Pong), 0x80 (Query), 0x81 (Query-Hit) oder // 0x40 (Push), Groesse 1 Byte protected int ttl; // die Lebensdauer eines Pakets, Groesse 1 Byte protected int hops; // die Anzahl der Hops, die das Paket schon // zurueckgelegt hat, Groesse 1 Byte protected long payloadLength; // Laenge der Nutzdaten in Bits /* * Konstruktoren ------------------------------------------------------------ --------------------------------- */ /** * einziger benoetiger Konstruktor */ public PeerToPeerPaket() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (PeerToPeerPaket), constr: PeerToPeerPaket()"); this.guid = guidErstellen(); this.payload = ""; this.ttl = 8; this.hops = 0; } /** * wandelt einen String (wenn möglich) in ein PeerToPeerPaket um */ public PeerToPeerPaket(String string) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (PeerToPeerPaket), constr: PeerToPeerPaket(" + string + ")"); // String wird nach "//" getrennt StringTokenizer tk = new StringTokenizer(string, "//"); // Absichern der Informationen guid = Integer.parseInt(tk.nextToken()); payload = tk.nextToken(); hops = Integer.parseInt(tk.nextToken()); ttl = Integer.parseInt(tk.nextToken()); payloadLength = Integer.parseInt(tk.nextToken()); } /* * Operationen -------------------------------------------------------------- ------------------------------- */ protected long anzahlBenoetigterBits(long zahl) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (PeerToPeerPaket), anzahlBenoetigterBits(" + zahl + ")"); int exponent = 0; while (potenzieren(2, exponent) < zahl) { exponent++; } return exponent; } private int potenzieren(int basis, int exponent) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (PeerToPeerPaket), potenzieren(" + basis + "," + exponent + ")"); /* * int ergebnis=1; for (int i=0;i ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.ListIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.ServerMitarbeiter; import filius.software.clientserver.TCPServerAnwendung; import filius.software.transportschicht.Socket; /** * In dieser Klasse wird der Server der Peer-to-Peer-Anwendung implementiert. D. h., dass hier eingehende Anfragen aus * dem Peer-to-Peer-Netzwerk empfangen und verarbeitet werden.
* Zur Verarbeitung der Anfragen wird jeweils ein neuer Mitarbeiter in einem eigenen Thread gestartet. */ public class PeerToPeerServer extends TCPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(PeerToPeerServer.class); /** * die Instanz der Peer-to-Peer-Anwendung, fuer die eingehende Anfragen verarbeitet werden. */ private PeerToPeerAnwendung peerToPeerAnwendung; /** * Konstruktor zur Initialisierung der Peer-to-Peer-Anwendung und setzen des Ports 6346, bei dem auf eingehende * Anfragen gewartet wird. Außerdem wird der Konstruktor der Oberklasse aufgerufen. * * @param peerToPeerAnwendung */ PeerToPeerServer(PeerToPeerAnwendung peerToPeerAnwendung) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServer), constr: PeerToPeerServer(" + peerToPeerAnwendung + ")"); this.peerToPeerAnwendung = peerToPeerAnwendung; setPort(6346); setAktiv(true); } /** * Der Thread der Oberklasse ruft diese Methode auf, sobald eine Verbindungsanfrage eingegangen ist. Hier wird ein * Mitarbeiter in einem neuen Thread gestartet, der fuer die Verarbeitung der eingehenden Anfrage genutzt wird. */ protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServer), neuerMitarbeiter(" + socket + ")"); PeerToPeerServerMitarbeiter neuerMitarbeiter; neuerMitarbeiter = new PeerToPeerServerMitarbeiter(this, socket, peerToPeerAnwendung); neuerMitarbeiter.starten(); mitarbeiter.add(neuerMitarbeiter); } /** * Diese Methode wird genutzt, um Antwortpakete ueber einen Mitarbeiter-Thread zu verschicken. Die Auswahl des * Mitarbeiter-Threads erfolgt an Hand der GUID der Nachricht. * * @param paket */ void sendePaket(PeerToPeerPaket paket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServer), sendePaket(" + paket + ")"); ListIterator it; PeerToPeerServerMitarbeiter m; it = mitarbeiter.listIterator(); while (it.hasNext()) { m = (PeerToPeerServerMitarbeiter) it.next(); if (m.holeGuid() == paket.getGuid()) { m.senden(paket.toString()); } } } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PeerToPeerServerMitarbeiter.java000066400000000000000000000220161506574241500324610ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.ServerMitarbeiter; import filius.software.system.Betriebssystem; import filius.software.system.Datei; import filius.software.transportschicht.Socket; import filius.software.www.HTTPNachricht; /** * In dieser Klasse wird ein Thread implementiert, der eingehende Anfragen an eine Peer-to-Peer-Anwendung verarbeitet. */ public class PeerToPeerServerMitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(PeerToPeerServerMitarbeiter.class); private PeerToPeerAnwendung peerToPeerAnwendung; /** die GUID der eingegangenen zu verarbeitenden Nachricht */ private int guid; /** * Aufruf des Konstruktors der Oberklasse und Initialisierung der zugehoerigen Instanz von PeerToPeerAnwendung * * @param server * @param socket * @param peerToPeerAnwendung */ PeerToPeerServerMitarbeiter(PeerToPeerServer server, Socket socket, PeerToPeerAnwendung peerToPeerAnwendung) { super(server, socket); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServerMitarbeiter), constr: PeerToPeerServerMitarbeiter(" + server + "," + socket + "," + peerToPeerAnwendung + ")"); this.peerToPeerAnwendung = peerToPeerAnwendung; } /** Methode zum versenden von Antwortnachrichten */ void senden(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServerMitarbeiter), senden(" + nachricht + ")"); if (socket != null && socket.istVerbunden()) { try { socket.senden(nachricht); } catch (Exception e) { LOG.debug("", e); } } } /** * Zugriff auf die GUID der Anfrage-Nachricht, die durch diesen Mitarbeiter-Thread verarbeitet wird. * * @return */ int holeGuid() { return guid; } /** * Diese Operation verarbeitet eine eingegangene HTTP-Anfrage: *
    *
  1. Lesen der gesuchten Datei aus lokalem Dateisystem
  2. *
  3. Wenn die Datei vorhanden ist, Versenden der Datei; wenn die Datei nicht vorhanden ist, verschicken einer * HTTP-Antwort mit dem Fehler-Status-Code 404
  4. *
  5. Versenden der Antwort ueber den geoeffneten Socket
  6. *
* * @param element * die zu verarbeitende HTTP Anfrage in Form eines TcpPufferElements */ private void httpAnfrageVerarbeiten(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServerMitarbeiter), httpAnfrageVerarbeiten(" + nachricht + ")"); HTTPNachricht http, antwort; Datei datei; http = new HTTPNachricht(nachricht); antwort = new HTTPNachricht(HTTPNachricht.SERVER); datei = peerToPeerAnwendung.holeDatei(http.getPfad()); if (datei != null) { antwort.setStatusCode(200); antwort.setContentType(datei.getDateiTyp()); antwort.setDaten(datei.getDateiInhalt()); } else { antwort.setStatusCode(404); } try { socket.senden(antwort.toString()); } catch (Exception e) { LOG.debug("", e); } } /** * Methode zur Verarbeitung einer eingehenden Ping-Nachricht. *
    *
  1. Absender des Ping-Pakets wird der Liste der bekannten Teilnehmer im Peer-to-Peer-Netzwerk hinzugefuegt.
  2. *
  3. Erzeugen einer entsprechenden Pong-Nachricht, wenn auf diese Ping-Nachricht nicht schon geantwortet * wurde.
  4. *
  5. Erhoehung des Hop-Zahlers und Dekrementierung des TTL-Zaehlers der Ping-Nachricht.
  6. *
  7. Weiterleitung des Ping-Pakets
  8. *
* * @param pingPaket */ private void verarbeitePing(PingPaket pingPaket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServerMitarbeiter), verarbeitePing(" + pingPaket + ")"); String pongNachricht; peerToPeerAnwendung.hinzuTeilnehmer(pingPaket.getIp()); pongNachricht = peerToPeerAnwendung.erstellePong(pingPaket).toString(); if (pongNachricht != null) { try { socket.senden(pongNachricht); } catch (Exception e) { LOG.debug("", e); } } pingPaket.setHops(pingPaket.getHops() + 1); pingPaket.setTtl(pingPaket.getTtl() - 1); guid = pingPaket.getGuid(); peerToPeerAnwendung.sendePing(pingPaket, socket.holeZielIPAdresse()); } /** * Methode zur Verarbeitung einer eingehenden Suchanfrage (Query). *
    *
  1. Erstellen der Ergebnisliste fuer eine Suchanfrage. Wenn auf die Query-Nachricht bereits geantwortet wurde, * wird keine Liste erzeugt. Ausserdem wird damit zugleich veranlasst, dass die Suchanfrage ggf. weitergeleitet * wird.
  2. *
  3. *
      *
    • Wenn eine Liste mit mindestens einem Eintrag vorliegt, wird fuer jede Datei eine Antwortnachricht ueber den * Socket verschickt (Query-Hit).
    • *
    *
  4. *
* * @param queryPaket */ private void verarbeiteQuery(QueryPaket queryPaket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServerMitarbeiter), verarbeiteQuery(" + queryPaket + ")"); List dateien; Betriebssystem bs; QueryHitPaket antwortPaket; guid = queryPaket.getGuid(); dateien = peerToPeerAnwendung.verarbeiteAnfrage(socket.holeZielIPAdresse(), queryPaket); if (dateien != null && dateien.size() > 0) { bs = (Betriebssystem) peerToPeerAnwendung.getSystemSoftware(); for (Datei aktuelleDatei : dateien) { antwortPaket = new QueryHitPaket(dateien.size(), 6346, bs.primaryIPAdresse(), "2", "", " "); antwortPaket.setGuid(queryPaket.getGuid()); antwortPaket.setHops(0); antwortPaket.setTtl(8); antwortPaket.setErgebnis(aktuelleDatei.getName() + ": " + aktuelleDatei.holeGroesse() + " B"); try { socket.senden(antwortPaket.toString()); } catch (Exception e) { LOG.debug("", e); } } socket.schliessen(); } } /** * Wenn eine Nachricht auf dem zu ueberwachenden Socket eintrifft, wird die Verarbeitung an diese Methode delegiert. *
* Unterschieden wird eine HTTP-GET-Anfrage, eine eingehende Ping-Nachricht und eine eingehende Suchanfrage (Query). */ protected void verarbeiteNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (PeerToPeerServerMitarbeiter), verarbeiteNachricht(" + nachricht + ")"); PeerToPeerPaket paket; PingPaket pingPaket; QueryPaket queryPaket; if (nachricht != null) { if (nachricht.startsWith("GET")) { httpAnfrageVerarbeiten(nachricht); } else { paket = new PeerToPeerPaket(nachricht); guid = paket.getGuid(); if (paket.getPayload().equals("0x00")) { pingPaket = new PingPaket(nachricht); verarbeitePing(pingPaket); } else if (paket.getPayload().equals("0x80")) { queryPaket = new QueryPaket(nachricht); verarbeiteQuery(queryPaket); } } } } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PingPaket.java000066400000000000000000000055351506574241500267610ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Nadja Haßler */ public class PingPaket extends PeerToPeerPaket { private static Logger LOG = LoggerFactory.getLogger(PingPaket.class); private String ip; public PingPaket() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (PingPaket), constr: PingPaket()"); setPayload("0x00"); this.setPayloadLength(0); this.setIp(""); } /** * wandelt einen String (wenn möglich) in ein PingPaket um */ public PingPaket(String string) { LOG.debug( "INVOKED (" + this.hashCode() + ") " + getClass() + " (PingPaket), constr: PingPaket(" + string + ")"); // String wird nach "//" getrennt StringTokenizer tk = new StringTokenizer(string, "//"); // Absichern der Informationen guid = Integer.parseInt(tk.nextToken()); payload = tk.nextToken(); hops = Integer.parseInt(tk.nextToken()); ttl = Integer.parseInt(tk.nextToken()); payloadLength = Integer.parseInt(tk.nextToken()); ip = tk.nextToken(); } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } /** * wandelt ein PingPaket in einen String um * * @return der String das PingPaket verpackt als String */ public String toString() { return getGuid() + "//" + getPayload() + "//" + getHops() + "//" + getTtl() + "//" + getPayloadLength() + "//" + getIp(); } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/PongPaket.java000066400000000000000000000124771506574241500267720ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Nadja Haßler */ public class PongPaket extends PeerToPeerPaket { private static Logger LOG = LoggerFactory.getLogger(PongPaket.class); private String ipAdresse; // Die IP-Adresse des antwortenden Rechners // (der, der Pong schickt) private int port; // Portnummer, an welcher der antwortende Rechner // eingehende Verbindungen annehmen kann private int anzahlZurVerfuegungStehenderDateien; // selbsterklaerend private long anzahlZurVerfuegungStehenderKBs; // selbsterklaerend public PongPaket(String ip, int port, int anzahlZurVerfuegungStehenderDateien, long anzahlZurVerfuegungStehenderKBs) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (PongPaket), constr: PongPaket(" + ip + "," + port + "," + anzahlZurVerfuegungStehenderDateien + "," + anzahlZurVerfuegungStehenderKBs + ")"); setPayload("0x01"); this.ipAdresse = ip; this.port = port; this.anzahlZurVerfuegungStehenderDateien = anzahlZurVerfuegungStehenderDateien; this.anzahlZurVerfuegungStehenderKBs = anzahlZurVerfuegungStehenderKBs; this.setPayloadLength(this.payloadLengthBerechnen()); } /** * wandelt einen String (wenn möglich) in ein PongPaket um * * @param string * der umzuwandelnde String */ public PongPaket(String string) { LOG.debug( "INVOKED (" + this.hashCode() + ") " + getClass() + " (PongPaket), constr: PongPaket(" + string + ")"); // String wird nach "//" getrennt StringTokenizer tk = new StringTokenizer(string, "//"); // Absichern der Informationen guid = Integer.parseInt(tk.nextToken()); payload = tk.nextToken(); hops = Integer.parseInt(tk.nextToken()); ttl = Integer.parseInt(tk.nextToken()); payloadLength = Integer.parseInt(tk.nextToken()); ipAdresse = tk.nextToken(); port = Integer.parseInt(tk.nextToken()); anzahlZurVerfuegungStehenderDateien = Integer.parseInt(tk.nextToken()); anzahlZurVerfuegungStehenderKBs = Integer.parseInt(tk.nextToken()); } public long payloadLengthBerechnen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (PongPaket), payloadLengthBerechnen()"); long bits = 0; bits = ipAdresse.length() * 8 + anzahlBenoetigterBits(port) + anzahlBenoetigterBits(anzahlZurVerfuegungStehenderDateien) + anzahlBenoetigterBits(anzahlZurVerfuegungStehenderKBs); return bits; } public int getAnzahlZurVerfuegungStehenderDateien() { return anzahlZurVerfuegungStehenderDateien; } public void setAnzahlZurVerfuegungStehenderDateien(int anzahlZurVerfuegungStehenderDateien) { this.anzahlZurVerfuegungStehenderDateien = anzahlZurVerfuegungStehenderDateien; } public long getAnzahlZurVerfuegungStehenderKBs() { return anzahlZurVerfuegungStehenderKBs; } public void setAnzahlZurVerfuegungStehenderKBs(int anzahlZurVerfuegungStehenderKBs) { this.anzahlZurVerfuegungStehenderKBs = anzahlZurVerfuegungStehenderKBs; } public String getIpAdresse() { return ipAdresse; } public void setIpAdresse(String ipAdresse) { this.ipAdresse = ipAdresse; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } /** * wandelt ein PongPaket in einen String um * * @param pongPaket * das umzuwandelnde PongPaket * @return der String das PongPaket verpackt als String */ public String toString() { return getGuid() + "//" + getPayload() + "//" + getHops() + "//" + getTtl() + "//" + getPayloadLength() + "//" + getIpAdresse() + "//" + getPort() + "//" + getAnzahlZurVerfuegungStehenderDateien() + "//" + getAnzahlZurVerfuegungStehenderKBs(); } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/QueryHitPaket.java000066400000000000000000000134621506574241500276340ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Nadja Haßler */ public class QueryHitPaket extends PeerToPeerPaket { private static Logger LOG = LoggerFactory.getLogger(QueryHitPaket.class); private int anzahlHits; private int port; private String ipAdresse; private String geschwindigkeit; // (in kb/sek) private String ergebnis;// =new LinkedList(); private String serventIdentifizierung; public QueryHitPaket(int anzahlHips, int port, String ip, String speed, String ergebnisListe, String si) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (QueryHitPaket), constr: QueryHitPaket(" + anzahlHips + "," + port + "," + ip + "," + speed + "," + ergebnisListe + "," + si + ")"); setPayload("0x81"); this.anzahlHits = anzahlHips; this.port = port; this.ipAdresse = ip; this.geschwindigkeit = speed; this.ergebnis = ergebnisListe; this.serventIdentifizierung = si; this.setPayloadLength(payloadLengthBerechnen()); } /** * wandelt einen String (wenn möglich) in ein QueryHitPaket um * * @param string * der umzuwandelnde String */ public QueryHitPaket(String string) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (QueryHitPaket), constr: QueryHitPaket(" + string + ")"); // String wird nach "//" getrennt StringTokenizer tk = new StringTokenizer(string, "//"); // Absichern der Informationen guid = Integer.parseInt(tk.nextToken()); payload = tk.nextToken(); hops = Integer.parseInt(tk.nextToken()); ttl = Integer.parseInt(tk.nextToken()); payloadLength = Integer.parseInt(tk.nextToken()); anzahlHits = Integer.parseInt(tk.nextToken()); port = Integer.parseInt(tk.nextToken()); ipAdresse = tk.nextToken(); geschwindigkeit = tk.nextToken(); ergebnis = tk.nextToken(); serventIdentifizierung = tk.nextToken(); } public long payloadLengthBerechnen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (QueryHitPaket), payloadLengthBerechnen()"); long ergebnisZahl = 0; ergebnisZahl = ergebnisZahl + anzahlBenoetigterBits(anzahlHits) + anzahlBenoetigterBits(port) + ipAdresse.length() * 8 + (geschwindigkeit.length() * 8) + ergebnis.length() * 8 + serventIdentifizierung.length() * 8; // LOG.debug(" hits: " + anzahlBenoetigterBits(anzahlHits) // + " port: " + anzahlBenoetigterBits(port) + " ip: " // + ipAdresse.length() * 8 + " geschw: " // + (geschwindigkeit.length() * 8) + " ergebnis: " // + ergebnis.length() * 8 + " serverID: " // + serventIdentifizierung.length() * 8); return ergebnisZahl; } public int getAnzahlHits() { return anzahlHits; } public void setAnzahlHits(int anzahlHips) { this.anzahlHits = anzahlHips; } public String getErgebnis() { return ergebnis; } public void setErgebnis(String ergebnisListe) { this.ergebnis = ergebnisListe; } public String getIpAdresse() { return ipAdresse; } public void setIpAdresse(String ipAdresse) { this.ipAdresse = ipAdresse; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getServentIdentifizierung() { return serventIdentifizierung; } public void setServentIdentifizierung(String serventIdentifizierung) { this.serventIdentifizierung = serventIdentifizierung; } public String getGeschwindigkeit() { return geschwindigkeit; } public void setGeschwindigkeit(String speed) { this.geschwindigkeit = speed; } /** * wandelt ein QueryHitPaket in einen String um * * @return der String das QueryHitPaket verpackt als String */ public String toString() { if (getErgebnis().equals("")) { setErgebnis(" "); } if (getServentIdentifizierung().equals("")) { setServentIdentifizierung(" "); } return getGuid() + "//" + getPayload() + "//" + getHops() + "//" + getTtl() + "//" + getPayloadLength() + "//" + getAnzahlHits() + "//" + getPort() + "//" + getIpAdresse() + "//" + getGeschwindigkeit() + "//" + getErgebnis() + "//" + getServentIdentifizierung(); } } filius-2.9.4+ds/src/main/java/filius/software/dateiaustausch/QueryPaket.java000066400000000000000000000100341506574241500271570ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dateiaustausch; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Nadja Haßler */ public class QueryPaket extends PeerToPeerPaket { private static Logger LOG = LoggerFactory.getLogger(QueryPaket.class); private String minimaleGeschwindigkeit; // (in kb/sek) private String suchKriterien; public QueryPaket(String mg, String sk) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (QueryPaket), constr: QueryPaket(" + mg + "," + sk + ")"); setPayload("0x80"); this.minimaleGeschwindigkeit = mg; this.suchKriterien = sk; this.setPayloadLength(payloadLengthBerechnen()); } /** * wandelt einen String (wenn möglich) in ein QueryPaket um * * @param string * der umzuwandelnde String */ public QueryPaket(String string) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (QueryPaket), constr: QueryPaket(" + string + ")"); // String wird nach "//" getrennt StringTokenizer tk = new StringTokenizer(string, "//"); // Absichern der Informationen guid = Integer.parseInt(tk.nextToken()); payload = tk.nextToken(); hops = Integer.parseInt(tk.nextToken()); ttl = Integer.parseInt(tk.nextToken()); payloadLength = Integer.parseInt(tk.nextToken()); minimaleGeschwindigkeit = tk.nextToken(); suchKriterien = tk.nextToken(); } public int payloadLengthBerechnen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (QueryPaket), payloadLengthBerechnen()"); int ergebnis = 0; ergebnis = ergebnis + minimaleGeschwindigkeit.length() * 8 + suchKriterien.length() * 8; return ergebnis; } public String getMinimaleGeschwindigkeit() { return minimaleGeschwindigkeit; } public void setMinimaleGeschwindigkeit(String minimaleGeschwindigkeit) { this.minimaleGeschwindigkeit = minimaleGeschwindigkeit; } public String getSuchKriterien() { return suchKriterien; } public void setSuchKriterien(String suchKriterien) { this.suchKriterien = suchKriterien; } /** * wandelt ein QueryPaket in einen String um * * @return der String das QueryPaket verpackt als String */ public String toString() { if (getMinimaleGeschwindigkeit().equals("")) { setMinimaleGeschwindigkeit(" "); } if (getSuchKriterien().equals("")) { setSuchKriterien(" "); } return getGuid() + "//" + getPayload() + "//" + getHops() + "//" + getTtl() + "//" + getPayloadLength() + "//" + getMinimaleGeschwindigkeit() + "//" + getSuchKriterien(); } } filius-2.9.4+ds/src/main/java/filius/software/dhcp/000077500000000000000000000000001506574241500221335ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/dhcp/DHCPAddressAssignment.java000066400000000000000000000047021506574241500270560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dhcp; import filius.rahmenprogramm.I18n; /** * * @author Johannes Klebert * */ public class DHCPAddressAssignment implements I18n { private String ip; private String mac; /** * Lease time of an address assignment as unix timestamp (in millis). If the lease time is '0' the assignment does * not expire. */ private long leaseTime; public DHCPAddressAssignment(String mac, String ip, long leaseTime) { this.ip = ip; this.mac = mac; this.leaseTime = leaseTime; } public DHCPAddressAssignment() {} public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public long getLeaseTime() { return leaseTime; } public void setLeaseTime(long leaseTime) { this.leaseTime = leaseTime; } public String getMAC() { return mac; } public void setMAC(String owner) { this.mac = owner; } public boolean isExpired() { return leaseTime != 0 && leaseTime < System.currentTimeMillis(); } public String toString() { return messages.getString("sw_ipeintrag_msg1") + mac + messages.getString("sw_ipeintrag_msg2") + ip; } } filius-2.9.4+ds/src/main/java/filius/software/dhcp/DHCPClient.java000066400000000000000000000262711506574241500246630ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dhcp; import static filius.software.dhcp.DHCPClient.State.ASSIGN_IP; import static filius.software.dhcp.DHCPClient.State.DECLINE; import static filius.software.dhcp.DHCPClient.State.DISCOVER; import static filius.software.dhcp.DHCPClient.State.FINISH; import static filius.software.dhcp.DHCPClient.State.INIT; import static filius.software.dhcp.DHCPClient.State.REQUEST; import static filius.software.dhcp.DHCPClient.State.VALIDATE; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.NoValidDhcpResponseException; import filius.exception.TimeOutException; import filius.exception.VerbindungsException; import filius.gui.GUIContainer; import filius.gui.netzwerksicht.GUIKnotenItem; import filius.hardware.Verbindung; import filius.hardware.knoten.Knoten; import filius.software.clientserver.ClientAnwendung; import filius.software.system.Betriebssystem; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.system.SystemSoftware; import filius.software.transportschicht.UDPSocket; import filius.software.vermittlungsschicht.ARP; public class DHCPClient extends ClientAnwendung { private static Logger LOG = LoggerFactory.getLogger(DHCPClient.class); enum State { ASSIGN_IP, FINISH, DISCOVER, REQUEST, VALIDATE, DECLINE, INIT } private static final String IP_ADDRESS_CURRENT_NETWORK = "0.0.0.0"; private static final int MAX_ERROR_COUNT = 10; private State zustand; public void starten() { LOG.debug( "INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DHCPClient), starten()"); super.starten(); ausfuehren("configure", null); } /** * es muss gewaehrleistet werden, dass der DHCP-Server bereits gestartet worden ist! (sofern denn einer als "aktiv" * gekennzeichnet ist!) */ void waitUntilDhcpServerStarted() { boolean activeDHCPserversStarted = false; try { while (!activeDHCPserversStarted && running) { activeDHCPserversStarted = true; for (DHCPServer dhcpServer : getDHCPServers()) { if (dhcpServer.isAktiv() && !dhcpServer.isStarted()) { activeDHCPserversStarted = false; LOG.debug("DHCP client is waiting for DHCP server on '" + dhcpServer.getSystemSoftware().getKnoten().holeAnzeigeName() + "'. Server has NOT yet started."); break; } } Thread.sleep(100); } LOG.debug("DHCP client on '" + getSystemSoftware().getKnoten().holeAnzeigeName() + "' finished check for DHCP server."); } catch (InterruptedException e) { LOG.debug("", e); } } private List getDHCPServers() { SystemSoftware syssoft; List activeDHCPServers = new ArrayList(); for (GUIKnotenItem knotenItem : GUIContainer.getGUIContainer().getKnotenItems()) { syssoft = knotenItem.getKnoten().getSystemSoftware(); if (syssoft instanceof Betriebssystem) { if (((Betriebssystem) syssoft).getDHCPServer().isAktiv()) { activeDHCPServers.add(((Betriebssystem) syssoft).getDHCPServer()); } } } return activeDHCPServers; } public void configure() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DHCPClient), starteDatenaustausch()"); int fehlerzaehler = 0; zustand = INIT; String oldIpAddress = resetIpConfig(); updateOberserver(); IpConfig config = null; UDPSocket udpSocket = null; InternetKnotenBetriebssystem operatingSystem = (InternetKnotenBetriebssystem) getSystemSoftware(); while (zustand != FINISH && fehlerzaehler < MAX_ERROR_COUNT && running) { try { switch (zustand) { case INIT: waitUntilDhcpServerStarted(); udpSocket = initUdpSocket(); zustand = DISCOVER; break; case DISCOVER: config = discover(udpSocket, operatingSystem.dhcpEnabledMACAddress(), Verbindung.holeRTT()); zustand = VALIDATE; break; case VALIDATE: boolean validAddress = validateOfferedAddress(getSystemSoftware().holeARP(), config.getIpAddress()); zustand = validAddress ? REQUEST : DECLINE; break; case DECLINE: decline(udpSocket, operatingSystem.dhcpEnabledMACAddress(), config, Verbindung.holeRTT()); zustand = DISCOVER; break; case REQUEST: boolean acknowledged = request(udpSocket, operatingSystem.dhcpEnabledMACAddress(), config, Verbindung.holeRTT()); zustand = acknowledged ? ASSIGN_IP : DISCOVER; break; case ASSIGN_IP: operatingSystem.setzeIPAdresse(config.getIpAddress()); operatingSystem.setzeNetzmaske(config.getSubnetMask()); operatingSystem.setStandardGateway(config.getRouter()); operatingSystem.setDNSServer(config.getDnsServer()); default: zustand = FINISH; } } catch (NoValidDhcpResponseException | TimeOutException | VerbindungsException e) { fehlerzaehler++; } } if (fehlerzaehler == MAX_ERROR_COUNT || !running) { LOG.error("kein DHCPACK erhalten"); operatingSystem.setzeIPAdresse(oldIpAddress); } udpSocket.schliessen(); updateOberserver(); } private void updateOberserver() { InternetKnotenBetriebssystem operatingSystem = (InternetKnotenBetriebssystem) getSystemSoftware(); Knoten node = operatingSystem.getKnoten(); node.benachrichtigeBeobachter(); operatingSystem.benachrichtigeBeobacher(node); } private String resetIpConfig() { InternetKnotenBetriebssystem operatingSystem = (InternetKnotenBetriebssystem) getSystemSoftware(); String oldIpAddress = operatingSystem.primaryIPAdresse(); operatingSystem.setzeIPAdresse("0.0.0.0"); return oldIpAddress; } UDPSocket initUdpSocket() throws VerbindungsException { socket = new UDPSocket(getSystemSoftware(), "255.255.255.255", 67, 68); ((UDPSocket) socket).verbinden(); return (UDPSocket) socket; } boolean request(UDPSocket socket, String clientMacAddress, IpConfig config, long socketTimeoutMillis) throws NoValidDhcpResponseException, TimeOutException { socket.sendeBroadcast(IP_ADDRESS_CURRENT_NETWORK, DHCPMessage .createRequestMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); DHCPMessage result = receiveResponse(socket, socketTimeoutMillis, clientMacAddress, config.getDhcpServer(), DHCPMessageType.ACK, DHCPMessageType.NACK); return null != result && result.getType() == DHCPMessageType.ACK; } void decline(UDPSocket socket, String clientMacAddress, IpConfig config, long socketTimeoutMillis) throws NoValidDhcpResponseException { socket.sendeBroadcast(IP_ADDRESS_CURRENT_NETWORK, DHCPMessage .createDeclineMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); } IpConfig discover(UDPSocket socket, String clientMacAddress, long socketTimeoutMillis) throws NoValidDhcpResponseException, TimeOutException { socket.sendeBroadcast(IP_ADDRESS_CURRENT_NETWORK, DHCPMessage.createDiscoverMessage(clientMacAddress).toString()); DHCPMessage offer = receiveResponse(socket, socketTimeoutMillis, clientMacAddress, null, DHCPMessageType.OFFER); return new IpConfig(offer.getYiaddr(), offer.getRouter(), offer.getSubnetMask(), offer.getDnsServer(), offer.getServerIdentifier()); } private DHCPMessage receiveResponse(UDPSocket socket, long socketTimeoutMillis, String clientMacAddress, String serverIdentifier, DHCPMessageType... messageTypes) throws NoValidDhcpResponseException, TimeOutException { DHCPMessage responseMessage = null; long start = System.currentTimeMillis(); long duration = 0; do { String response = socket.empfangen(socketTimeoutMillis - duration); if (null == response) { throw new TimeOutException("No response from DHCP server received."); } responseMessage = DHCPMessage.fromString(response); boolean invalidMessageType = !ArrayUtils.contains(messageTypes, responseMessage.getType()); boolean forOtherClient = !clientMacAddress.equalsIgnoreCase(responseMessage.getChaddr()); boolean fromOtherServer = null != serverIdentifier && !serverIdentifier.equals(responseMessage.getServerIdentifier()); if (invalidMessageType || forOtherClient || fromOtherServer) { responseMessage = null; } duration = System.currentTimeMillis() - start; } while (null == responseMessage && duration < socketTimeoutMillis); if (null == responseMessage) { throw new NoValidDhcpResponseException("No valid server response received"); } return responseMessage; } boolean validateOfferedAddress(ARP arp, String offeredAddress) { String macAddress = arp.holeARPTabellenEintrag(offeredAddress, 1, false); return null == macAddress; } } filius-2.9.4+ds/src/main/java/filius/software/dhcp/DHCPMessage.java000066400000000000000000000275011506574241500250260ustar00rootroot00000000000000package filius.software.dhcp; import java.util.Objects; import java.util.StringTokenizer; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; /** * Class representing a simplified dhcp message (e.g. without the not necessarily needed ciaddr, siaddr) */ public class DHCPMessage { private static final String FIELD_SEPARATOR = "\n"; private static final String PARAM_SERVERIDENT = "serverident"; private static final String PARAM_REQUESTED = "requested"; private static final String PARAM_DNSSERVER = "dnsserver"; private static final String PARAM_SUBNETMASK = "subnetmask"; private static final String PARAM_ROUTER = "router"; private static final String PARAM_CHADDR = "chaddr"; private static final String PARAM_YIADDR = "yiaddr"; private static final String BLANK_IP_ADDRESS = "0.0.0.0"; DHCPMessageType type = DHCPMessageType.DISCOVER; String yiaddr = BLANK_IP_ADDRESS; String chaddr; String optionSubnetMask; String optionRouter; String optionDnsServer; String optionServerIdentifier; String optionRequestedAddress; DHCPMessage() {} /** DHCP message type */ public DHCPMessageType getType() { return type; } /** Your IP address (i.e. in case of DHCP offer the available address for the client to use) */ public String getYiaddr() { return yiaddr; } /** Client hardware address (i.e. the clients MAC address) */ public String getChaddr() { return chaddr; } /** IP subnet mask to use */ public String getSubnetMask() { return optionSubnetMask; } /** IP address of the router to use */ public String getRouter() { return optionRouter; } /** IP address of the DNS server to use */ public String getDnsServer() { return optionDnsServer; } /** IP address of the DHCP server */ public String getServerIdentifier() { return optionServerIdentifier; } /** IP address the client requests from the server */ public String getRequestedAddress() { return optionRequestedAddress; } public String toString() { StringBuilder builder = new StringBuilder(type.toString()); builder.append(FIELD_SEPARATOR).append(PARAM_YIADDR).append("=").append(yiaddr); if (StringUtils.isNoneEmpty(chaddr)) { builder.append(FIELD_SEPARATOR).append(PARAM_CHADDR).append("=").append(chaddr); } if (StringUtils.isNoneEmpty(optionRouter)) { builder.append(FIELD_SEPARATOR).append(PARAM_ROUTER).append("=").append(optionRouter); } if (StringUtils.isNoneEmpty(optionSubnetMask)) { builder.append(FIELD_SEPARATOR).append(PARAM_SUBNETMASK).append("=").append(optionSubnetMask); } if (StringUtils.isNoneEmpty(optionDnsServer)) { builder.append(FIELD_SEPARATOR).append(PARAM_DNSSERVER).append("=").append(optionDnsServer); } if (StringUtils.isNoneEmpty(optionRequestedAddress)) { builder.append(FIELD_SEPARATOR).append(PARAM_REQUESTED).append("=").append(optionRequestedAddress); } if (StringUtils.isNoneEmpty(optionServerIdentifier)) { builder.append(FIELD_SEPARATOR).append(PARAM_SERVERIDENT).append("=").append(optionServerIdentifier); } return builder.toString(); } static DHCPMessage fromString(String dhcpMessageString) { DHCPMessage message = new DHCPMessage(); StringTokenizer tokenizer = new StringTokenizer(dhcpMessageString, "\n="); if (tokenizer.hasMoreTokens()) { message.type = DHCPMessageType.fromString(tokenizer.nextToken()); } while (tokenizer.hasMoreTokens()) { String keyToken = tokenizer.nextToken(); if (tokenizer.hasMoreTokens()) { switch (keyToken) { case PARAM_CHADDR: message.chaddr = tokenizer.nextToken(); break; case PARAM_DNSSERVER: message.optionDnsServer = tokenizer.nextToken(); break; case PARAM_REQUESTED: message.optionRequestedAddress = tokenizer.nextToken(); break; case PARAM_ROUTER: message.optionRouter = tokenizer.nextToken(); break; case PARAM_SERVERIDENT: message.optionServerIdentifier = tokenizer.nextToken(); break; case PARAM_SUBNETMASK: message.optionSubnetMask = tokenizer.nextToken(); break; case PARAM_YIADDR: message.yiaddr = tokenizer.nextToken(); break; } } } return message; } @Override public boolean equals(Object other) { boolean equals = true; if (!(other instanceof DHCPMessage)) { equals = false; } else if (ObjectUtils.notEqual(chaddr, ((DHCPMessage) other).chaddr)) { equals = false; } else if (ObjectUtils.notEqual(optionDnsServer, ((DHCPMessage) other).optionDnsServer)) { equals = false; } else if (ObjectUtils.notEqual(optionRequestedAddress, ((DHCPMessage) other).optionRequestedAddress)) { equals = false; } else if (ObjectUtils.notEqual(optionRouter, ((DHCPMessage) other).optionRouter)) { equals = false; } else if (ObjectUtils.notEqual(optionServerIdentifier, ((DHCPMessage) other).optionServerIdentifier)) { equals = false; } else if (ObjectUtils.notEqual(optionSubnetMask, ((DHCPMessage) other).optionSubnetMask)) { equals = false; } else if (ObjectUtils.notEqual(yiaddr, ((DHCPMessage) other).yiaddr)) { equals = false; } return equals; } @Override public int hashCode() { return Objects.hash(chaddr, optionDnsServer, optionRequestedAddress, optionRouter, optionServerIdentifier, optionSubnetMask, yiaddr); } /** * Der Client sendet ein DHCPDISCOVER-Paket. Im Folgenden sehen Sie einen Ausschnitt aus einer Aufzeichnung des * Netzwerkmonitors mit den IP- und DHCP-Abschnitten eines DHCPDISCOVER-Pakets. Im IP-Abschnitt lautet die * Zieladresse 255.255.255.255 und die Quelladresse 0.0.0.0. Der DHCP-Abschnitt bezeichnet das Paket als ein * "DHCP-Discover"-Paket und bezeichnet den Client an zwei Stellen mithilfe der physischen Adresse der * Netzwerkkarte. Die Werte im Feld CHADDR und im Feld DHCP: CLIENT IDENTIFIER sind identisch. * (https://support.microsoft.com/de-de/kb/169289) */ static DHCPMessage createDiscoverMessage(String clientMacAddress) { DHCPMessage discoverMessage = new DHCPMessage(); discoverMessage.type = DHCPMessageType.DISCOVER; discoverMessage.chaddr = clientMacAddress; return discoverMessage; } /** * Der DHCP-Server reagiert durch Senden eines DHCPOFFER-Pakets. Im IP-Abschnitt des Aufzeichnungsausschnitts weiter * unten ist die Quelladresse nun die IP-Adresse des DHCP-Servers, und die Zieladresse ist die Broadcastadresse * 255.255.255.255. Der DHCP-Abschnitt identifiziert das Paket als ein Angebot (Offer). Das Feld YIADDR enthält die * IP-Adresse, die der Server dem Client anbietet. Das Feld CHADDR enthält noch immer die physische Adresse des * Clients, der die Anforderung ausgegeben hat. Darüber hinaus werden im Abschnitt "DHCP Option Field" die * verschiedenen Optionen aufgeführt, die vom Server zusammen mit der IP-Adresse gesendet werden. In diesem Fall * werden vom Server die Subnetzmaske, der Standardgateway (Router), die Leasedauer, die WINS-Serveradresse * (NetBIOS-Namensdienst) und der NetBIOS-Knotentyp gesendet. (https://support.microsoft.com/de-de/kb/169289) */ static DHCPMessage createOfferMessage(String clientMacAddress, String offeredIpAddress, String subnetMask, String router, String dnsServer, String serverIdentifier) { DHCPMessage offerMessage = new DHCPMessage(); offerMessage.type = DHCPMessageType.OFFER; offerMessage.chaddr = clientMacAddress; offerMessage.yiaddr = offeredIpAddress; offerMessage.optionSubnetMask = subnetMask; offerMessage.optionRouter = router; offerMessage.optionDnsServer = dnsServer; offerMessage.optionServerIdentifier = serverIdentifier; return offerMessage; } /** * Der Client reagiert auf DHCPOFFER durch Senden von DHCPREQUEST. Im IP-Abschnitt der nachfolgenden Aufzeichnung * ist die Quelladresse des Clients noch immer 0.0.0.0, und das Ziel des Pakets ist noch immer 255.255.255.255. Der * Client behält 0.0.0.0 bei, da er keine Bestätigung des Servers erhalten hat, dass die angebotene Adresse * verwendet werden kann. Das Ziel lautet noch immer "Broadcast", da möglicherweise mehr als ein DHCP-Server * reagiert hat und über eine Reservierung für ein Angebot (Offer) an den Client verfügt. Dadurch werden andere * DHCP-Server in Kenntnis gesetzt, dass sie die angebotenen Adressen freigeben und an die verfügbaren Pools * zurückgeben können. Der DHCP-Abschnitt identifiziert das Paket als eine Anforderung (Request) und überprüft die * angebotene Adresse mithilfe des Feldes DHCP: REQUESTED ADDRESS. Das Feld DHCP: SERVER IDENTIFIER enthält die * IP-Adresse des DHCP-Servers, der die Lease anbietet. (https://support.microsoft.com/de-de/kb/169289) */ static DHCPMessage createRequestMessage(String clientMacAddress, String requestedIpAddress, String serverIdentifier) { DHCPMessage requestMessage = new DHCPMessage(); requestMessage.type = DHCPMessageType.REQUEST; requestMessage.chaddr = clientMacAddress; requestMessage.optionRequestedAddress = requestedIpAddress; requestMessage.optionServerIdentifier = serverIdentifier; return requestMessage; } static DHCPMessage createDeclineMessage(String clientMacAddress, String requestedIpAddress, String serverIdentifier) { DHCPMessage declineMessage = new DHCPMessage(); declineMessage.type = DHCPMessageType.DECLINE; declineMessage.chaddr = clientMacAddress; declineMessage.optionRequestedAddress = requestedIpAddress; declineMessage.optionServerIdentifier = serverIdentifier; return declineMessage; } /** * Der DHCP-Server reagiert auf DHCPREQUEST mit DHCPACK und beendet so den Initialisierungszyklus. Die Quelladresse * ist die IP-Adresse des DHCP-Servers, und die Zieladresse ist noch immer 255.255.255.255. Das Feld YIADDR enthält * die Adresse des Clients, und die Felder CHADDR und DHCP: CLIENT IDENTIFIER zeigen die physische Adresse der * Netzwerkkarte des Clients, der die Anforderung ausgegeben hat. Der Abschnitt "DHCP Option" bezeichnet das Paket * als "ACK". (https://support.microsoft.com/de-de/kb/169289) */ static DHCPMessage createAckMessage(String clientMacAddress, String acknowledgedIpAddress, String serverIdentifier) { DHCPMessage ackMessage = new DHCPMessage(); ackMessage.type = DHCPMessageType.ACK; ackMessage.chaddr = clientMacAddress; ackMessage.yiaddr = acknowledgedIpAddress; ackMessage.optionServerIdentifier = serverIdentifier; return ackMessage; } static DHCPMessage createNackMessage(String clientMacAddress, String notAcknowledgedIpAddress, String serverIdentifier) { DHCPMessage nackMessage = new DHCPMessage(); nackMessage.type = DHCPMessageType.NACK; nackMessage.chaddr = clientMacAddress; nackMessage.yiaddr = notAcknowledgedIpAddress; nackMessage.optionServerIdentifier = serverIdentifier; return nackMessage; } } filius-2.9.4+ds/src/main/java/filius/software/dhcp/DHCPMessageType.java000066400000000000000000000014161506574241500256650ustar00rootroot00000000000000package filius.software.dhcp; import org.apache.commons.lang3.StringUtils; public enum DHCPMessageType { DISCOVER("DHCPDISCOVER"), REQUEST("DHCPREQUEST"), ACK("DHCPACK"), NACK("DHCPNAK"), OFFER("DHCPOFFER"), DECLINE( "DHCPDECLINE"); String command; private DHCPMessageType(String command) { this.command = command; } @Override public String toString() { return command; } public static DHCPMessageType fromString(String command) { DHCPMessageType result = null; for (DHCPMessageType type : DHCPMessageType.values()) { if (StringUtils.equalsIgnoreCase(type.toString(), command)) { result = type; break; } } return result; } }filius-2.9.4+ds/src/main/java/filius/software/dhcp/DHCPServer.java000066400000000000000000000372321506574241500247120ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dhcp; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.AddressRequestNotAcceptedException; import filius.exception.NoAvailableAddressException; import filius.hardware.Verbindung; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.clientserver.UDPServerAnwendung; import filius.software.system.GatewayFirmware; import filius.software.transportschicht.Socket; /** * In dieser Klasse und in DHCPServerMitarbeiter wird ein Sever fuer Dynamic Host Configuration Protocol implementiert. *
* In dieser Klasse werden die Einstellungen des Server verwaltet. Das Protokoll zur Vereinbarung einer IP-Adresse wird * durch DHCPServerMitarbeiter realisiert. D. h. zu jeder eingehenden Anfrage wird ein neuer Mitarbeiter erzeugt. */ public class DHCPServer extends UDPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(DHCPServer.class); private static final String DEFAULT_IP_ADDRESS = "0.0.0.0"; /** wie lange ein DHCP-Eintrag gueltig sein soll bzw. die IP-Adresse einer MAC zugewiesen bleibt (Standardwert) */ private static final long DEFAULT_LEASE_TIME_MILLIS = 24 * 60 * 60 * 1000; private static final int DHCP_SERVER_PORT = 67; /** niedrigste IP-Adresse, die durch diesen DHCP-Server vergeben wird */ private String untergrenze = DEFAULT_IP_ADDRESS; /** hoechste IP-Adresse, die durch diesen DHCP-Server vergeben wird */ private String obergrenze = DEFAULT_IP_ADDRESS; /** setting of DHCP server for the router/gateway (not necessarily equal to operating system settings) */ private String dhcpGateway = DEFAULT_IP_ADDRESS; /** setting of DHCP server for the DNS server (not necessarily equal to operating system settings) */ private String dhcpDNS = DEFAULT_IP_ADDRESS; /** whether to use the operating system or the dhcp settings for the attributes router/gateway and DND server */ private boolean useDhcpSettings = false; /** Die zuletzt vergebene IP-Adresse */ String lastOfferedAddress = null; /** Liste mit dynamisch vergebenen IP-Adressen mit zugehoeriger MAC-Adresse */ List dynamicAssignedAddresses = new ArrayList(); /** Liste mit statisch vergebenen IP-Adressen mit zugehoeriger MAC-Adresse */ List staticAssignedAddresses = new ArrayList(); /** Liste mit angebotenen IP-Adressen mit zugehoeriger MAC-Adresse */ List offeredAddresses = new ArrayList(); /** Liste mit IP-Adressen, die von anderen Servern angeboten wurden. */ List blacklist = new ArrayList(); /** * Konstruktor, in dem der UDP-Port 67 gesetzt wird. */ public DHCPServer() { port = DHCP_SERVER_PORT; } String nextAddress() { String nextAddress; if (StringUtils.isBlank(lastOfferedAddress)) { nextAddress = untergrenze; } else { long address = ipToLong(lastOfferedAddress) + 1; long upperLimit = ipToLong(obergrenze); if (address <= upperLimit) { nextAddress = longToIp(address); } else { nextAddress = untergrenze; } } lastOfferedAddress = nextAddress; return nextAddress; } public synchronized String offerAddress(String mac) throws NoAvailableAddressException { String addressToOffer = findStaticOffer(mac); if (addressToOffer == null) { addressToOffer = findDynamicOffer(mac); } return addressToOffer; } private String findDynamicOffer(String mac) throws NoAvailableAddressException { String addressToOffer; addressToOffer = nextAddress(); String firstOffer = addressToOffer; while (!checkAddressAvailable(addressToOffer)) { addressToOffer = nextAddress(); if (StringUtils.equals(firstOffer, addressToOffer)) { throw new NoAvailableAddressException(); } } long leaseTime = System.currentTimeMillis() + 4 * Verbindung.holeRTT(); offeredAddresses.add(new DHCPAddressAssignment(mac, addressToOffer, leaseTime)); return addressToOffer; } private String findStaticOffer(String mac) { String addressToOffer = null; for (DHCPAddressAssignment entry : staticAssignedAddresses) { if (StringUtils.equalsIgnoreCase(mac, entry.getMAC())) { addressToOffer = entry.getIp(); break; } } return addressToOffer; } public synchronized void blacklistAddress(String ip) { long leaseTime = System.currentTimeMillis() + 4 * Verbindung.holeRTT(); blacklist.add(new DHCPAddressAssignment("", ip, leaseTime)); } public synchronized DHCPAddressAssignment requestAddress(String mac, String ip) throws AddressRequestNotAcceptedException { if (assignmentListContains(blacklist, ip)) { throw new AddressRequestNotAcceptedException(); } DHCPAddressAssignment assignment = requestStaticAssignment(mac, ip); if (assignment == null) { assignment = requestDynamicAssignment(mac, ip); } return assignment; } private DHCPAddressAssignment requestStaticAssignment(String mac, String ip) { DHCPAddressAssignment assignment = null; for (DHCPAddressAssignment entry : staticAssignedAddresses) { if (StringUtils.equalsIgnoreCase(mac, entry.getMAC()) && StringUtils.equalsIgnoreCase(ip, entry.getIp())) { assignment = entry; break; } } return assignment; } private DHCPAddressAssignment requestDynamicAssignment(String mac, String ip) throws AddressRequestNotAcceptedException { boolean success = false; for (DHCPAddressAssignment assignment : offeredAddresses) { if (StringUtils.equalsIgnoreCase(ip, assignment.getIp())) { if (StringUtils.equalsIgnoreCase(mac, assignment.getMAC())) { success = true; offeredAddresses.remove(assignment); break; } else { success = false; break; } } } if (!success && checkAddressAvailable(ip)) { success = true; } DHCPAddressAssignment assignment; if (success) { assignment = new DHCPAddressAssignment(mac, ip, System.currentTimeMillis() + DEFAULT_LEASE_TIME_MILLIS); dynamicAssignedAddresses.add(assignment); } else { throw new AddressRequestNotAcceptedException(); } return assignment; } public String holeServerIpAddress() { return getSystemSoftware().primaryIPAdresse(); } /** * Zur Pruefung, ob eine bestimmte IP-Adresse bereits vergeben ist.
* Zunaechst wird dazu die Liste vergebener IP-Adressen aufgeraeumt (d.h. abgelaufene Eintraege entfernt). */ synchronized boolean checkAddressAvailable(String ip) { cleanUpAssignments(); boolean available = true; if (assignmentListContains(dynamicAssignedAddresses, ip)) { available = false; } else if (assignmentListContains(staticAssignedAddresses, ip)) { available = false; } else if (assignmentListContains(offeredAddresses, ip)) { available = false; } else if (assignmentListContains(blacklist, ip)) { available = false; } return available; } private boolean assignmentListContains(List assignmentList, String ip) { boolean contains = false; for (DHCPAddressAssignment assignment : assignmentList) { if (assignment.getIp().equalsIgnoreCase(ip)) { contains = true; break; } } return contains; } public void setOwnSettings(boolean val) { this.useDhcpSettings = val; } /** * Entfernt abgelaufene Eintraege. Wird automatisch vor jeder Suche nach der naechsten freien IP-Adresse * ausgefuehrt. */ synchronized void cleanUpAssignments() { cleanUpAssignmentList(dynamicAssignedAddresses); cleanUpAssignmentList(staticAssignedAddresses); cleanUpAssignmentList(offeredAddresses); cleanUpAssignmentList(blacklist); } private void cleanUpAssignmentList(List assignments) { List expiredAssignments = new LinkedList<>(); for (DHCPAddressAssignment assignment : assignments) { if (assignment.isExpired()) { expiredAssignments.add(assignment); } } for (DHCPAddressAssignment expired : expiredAssignments) { assignments.remove(expired); } } public void starten() { LOG.debug( "INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DHCPServer), starten()"); dynamicAssignedAddresses.clear(); lastOfferedAddress = null; super.starten(); } /** * Diese Methode wird bei der ersten eingehenden DHCP-Anfrage aufgerufen. Der DHCP-Server verfuegt naemlich nur * ueber einen Mitarbeiter, weil es nur einen Port mit der Gegenstelle "0.0.0.0:68" gibt. */ protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DHCPServer), neuerMitarbeiter(" + socket + ")"); DHCPServerMitarbeiter dhcpMitarbeiter; dhcpMitarbeiter = new DHCPServerMitarbeiter(this, socket); dhcpMitarbeiter.starten(); mitarbeiter.add(dhcpMitarbeiter); } public boolean isOwnSettings() { return useDhcpSettings; } boolean inRange(String ipAddress) { long lowerLimit = ipToLong(untergrenze); long upperLimit = ipToLong(obergrenze); long ip = ipToLong(ipAddress); return ip <= upperLimit && ip >= lowerLimit; } static int[] ipToIntArray(String ipAddress) { if (!EingabenUeberpruefung.isGueltig(ipAddress, EingabenUeberpruefung.musterIpAdresse)) { throw new NumberFormatException("Not a valid IP address"); } int[] ipAsArray = new int[4]; StringTokenizer tokenizer = new StringTokenizer(ipAddress, "."); for (int i = 0; i < ipAsArray.length; i++) { ipAsArray[i] = Integer.parseInt(tokenizer.nextToken()); } return ipAsArray; } static long ipToLong(String ipAddress) { int[] ipIntArray = ipToIntArray(ipAddress); long number = 0; for (int value : ipIntArray) { number = 256 * number + value; } return number; } static String longToIp(long ipAsLong) { long firstByte = (ipAsLong & 255 << 24) >> 24; long secondByte = (ipAsLong & 255 << 16) >> 16; long thirdByte = (ipAsLong & 255 << 8) >> 8; long fourthByte = ipAsLong & 255; return String.format("%d.%d.%d.%d", firstByte, secondByte, thirdByte, fourthByte); } public String getObergrenze() { return obergrenze; } public void setObergrenze(String obergrenze) { this.obergrenze = obergrenze; } public String getUntergrenze() { return untergrenze; } public void setUntergrenze(String untergrenze) { this.untergrenze = untergrenze; } public String getDnsserverip() { return dhcpDNS; } public String determineDnsserverip() { String dns; if (getSystemSoftware() instanceof GatewayFirmware || useDhcpSettings) { dns = dhcpDNS; } else { dns = getSystemSoftware().getDNSServer(); if (StringUtils.isEmpty(dns)) { dns = DEFAULT_IP_ADDRESS; } } return dns; } public void setDnsserverip(String ip) { this.dhcpDNS = ip; } public String getGatewayip() { return dhcpGateway; } public String determineGatewayip() { String gateway; if (getSystemSoftware() instanceof GatewayFirmware) { gateway = ((GatewayFirmware) getSystemSoftware()).primaryIPAdresse(); } else if (useDhcpSettings) { gateway = dhcpGateway; } else { gateway = getSystemSoftware().getStandardGateway(); if (StringUtils.isEmpty(gateway)) { gateway = DEFAULT_IP_ADDRESS; } } return gateway; } public void setGatewayip(String ip) { this.dhcpGateway = ip; } public String getSubnetzmaske() { return getSystemSoftware().primarySubnetMask(); } public List holeStaticAssignedAddresses() { return Collections.unmodifiableList(staticAssignedAddresses); } public List getStaticAssignedAddresses() { List entries = new ArrayList<>(); for (DHCPAddressAssignment entry : staticAssignedAddresses) { entries.add(String.format("%s %s", entry.getMAC(), entry.getIp())); } return entries; } public void setStaticAssignedAddresses(List assignedAddresses) { staticAssignedAddresses.clear(); for (String entry : assignedAddresses) { String[] pair = StringUtils.split(entry); staticAssignedAddresses.add(new DHCPAddressAssignment(pair[0], pair[1], 0)); } } public void addStaticAssignment(String mac, String ip) { boolean alreadyExisting = false; for (DHCPAddressAssignment entry : staticAssignedAddresses) { if (StringUtils.equalsIgnoreCase(entry.getMAC(), mac)) { alreadyExisting = true; break; } } if (!alreadyExisting && EingabenUeberpruefung.isGueltig(mac, EingabenUeberpruefung.musterMacAddress) && EingabenUeberpruefung.isGueltig(ip, EingabenUeberpruefung.musterIpAdresse)) { staticAssignedAddresses.add(new DHCPAddressAssignment(mac, ip, 0)); } } public void clearStaticAssignments() { staticAssignedAddresses.clear(); } } filius-2.9.4+ds/src/main/java/filius/software/dhcp/DHCPServerMitarbeiter.java000066400000000000000000000173601506574241500271020ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dhcp; import org.apache.commons.lang3.StringUtils; import filius.exception.AddressRequestNotAcceptedException; import filius.exception.NoAvailableAddressException; import filius.software.clientserver.ServerMitarbeiter; import filius.software.transportschicht.Socket; import filius.software.transportschicht.UDPSocket; /** *

* Der DHCPServerMitarbeiter hat eine andere Funktion als dies bei sonstigen ServerMitarbeiter-Objekten der Fall ist. * Dieser Mitarbeiter verarbeitet alle eingehenden UDP-Segmente, weil alle DHCP-Clients mit der Absender-Adresse 0.0.0.0 * und dem UDP-Port 68 Nachrichten verschicken. Daher funktioniert das Demultiplexen auf der Transportschicht nicht. *

* *

* Dieser Mitarbeiter verwaltet daher fuer jede MAC-Adresse, zu der eine Verbindung besteht, einen eigenen Zustand in * einer Hash-Tabelle, der ueber das folgende Verhalten entscheidet. Der Socket wird nicht geschlossen, weil immer auf * weitere eingehende Verbindungsanfragen gewartet wird. *

* * Die verwendeten DHCP-Nachrichten sind stark vereinfacht, weil sie beobachtet werden können. Der Aufbau einer * DHCP-Nachricht besteht aus folgenden Komponenten: *
    *
  • Nachrichtentyp (s.u.)
  • *
  • Client-IP-Adresse: caddr
  • *
  • Client-MAC-Adresse: maddr
  • *
  • Server-IP-Adresse: saddr
  • *
* * DHCP kennt folgende Nachrichtentypen (Quelle: de.wikipedia.org): *
    *
  • DHCPDISCOVER: Ein Client ohne IP-Adresse sendet eine Broadcast-Anfrage nach Adress-Angeboten an den/die * DHCP-Server im lokalen Netz.
  • *
  • DHCPOFFER: Der/die DHCP-Server antworten mit entsprechenden Werten auf eine DHCPDISCOVER-Anfrage.
  • *
  • DHCPREQUEST: Der Client fordert (eine der angebotenen) IP-Adresse(n), weitere Daten sowie Verlaengerung der * Lease-Zeit von einem der antwortenden DHCP-Server.
  • *
  • DHCPACK: Bestaetigung des DHCP-Servers zu einer DHCPREQUEST-Anforderung
  • *
  • DHCPNAK: Ablehnung einer DHCPREQUEST-Anforderung durch den DHCP-Server
  • *
  • DHCPDECLINE: Ablehnung durch den Client, da die IP-Adresse schon verwendet wird. *
  • *
  • DHCPRELEASE: Der Client gibt die eigene Konfiguration frei, damit die Parameter wieder für andere Clients zur * Verfuegung stehen.
  • *
  • DHCPINFORM: Anfrage eines Clients nach Daten ohne IP-Adresse, z. B. weil der Client eine statische IP-Adresse * besitzt.
  • *
* Durch diesen DHCP-Server werden nicht alle Befehle unterstuetzt. */ public class DHCPServerMitarbeiter extends ServerMitarbeiter { public DHCPServerMitarbeiter(DHCPServer server, Socket socket) { super(server, socket); } /** * Ablauf einer DHCP-Anfrage (Quelle: de.wikipedia.org):
*

* Wenn ein Client erstmalig eine IP-Adresse benötigt, schickt er eine DHCPDISCOVER-Nachricht (mit seiner * MAC-Adresse) als Netzwerk-Broadcast an die verfuegbaren DHCP-Server (es kann durchaus mehrere davon im gleichen * Subnetz geben). Dieser Broadcast hat als Absender-IP-Adresse 0.0.0.0 und als Zieladresse 255.255.255.255, da der * Absender noch keine IP-Adresse besitzt und seine Anfrage "an alle" richtet. Dabei ist der UDP-Quellport 68 und * der UDP-Zielport 67. Die DHCP-Server antworten mit DHCPOFFER und machen Vorschlaege für eine IP-Adresse. Dies * geschieht ebenfalls mit einem Broadcast an die Adresse 255.255.255.255 mit UDP-Quellport 67 und UDP-Zielport 68. *

* *

* Der Client darf nun unter den eingetroffenen Angeboten (DHCP-Offers) waehlen. Wenn er sich für eines entschieden * hat (z. B. wegen laengster Lease-Zeit oder wegen Ablehnung eines speziellen, evtl. falsch konfigurierten * DHCP-Servers, oder einfach für die erste Antwort), kontaktiert er per Broadcast und einem im Paket enthaltenen * Serveridentifier den entsprechenden Server mit der Nachricht DHCPREQUEST. Alle eventuellen weiteren DHCP-Server * werten dies als Absage fuer ihre Angebote. Der vom Client ausgewaehlte Server bestaetigt in einer * DHCPACK-Nachricht (DHCP-Acknowledged) die IP-Adresse mit den weiteren relevanten Daten, oder er zieht sein * Angebot zurück (DHCPNAK, siehe auch sonstiges). *

* *

* Bevor der Client sein Netzwerkinterface mit der zugewiesenen Adresse konfiguriert, sollte er noch pruefen, ob * nicht versehentlich noch ein anderer Rechner die Adresse verwendet. Dies geschieht ueblicherweise durch einen * ARP-Request mit der soeben zugeteilten IP-Adresse. Antwortet ein anderer Host im Netz auf diesen Request, so wird * der Client die vorgeschlagene Adresse mit einer DHCPDECLINE-Nachricht zurueckweisen. *

*/ protected void verarbeiteNachricht(String nachricht) { DHCPMessage dhcpMessage = DHCPMessage.fromString(nachricht); if (DHCPMessageType.DISCOVER.equals(dhcpMessage.getType())) { processDiscover(dhcpMessage.getChaddr()); } else if (DHCPMessageType.REQUEST.equals(dhcpMessage.getType())) { processRequest(dhcpMessage.getChaddr(), dhcpMessage.getRequestedAddress(), dhcpMessage.getServerIdentifier()); } } void processDiscover(String mac) { DHCPServer dhcpServer = (DHCPServer) server; UDPSocket udpSocket = (UDPSocket) socket; try { String serverIpAddress = dhcpServer.holeServerIpAddress(); DHCPMessage response = DHCPMessage.createOfferMessage(mac, dhcpServer.offerAddress(mac), dhcpServer.getSubnetzmaske(), dhcpServer.determineGatewayip(), dhcpServer.determineDnsserverip(), serverIpAddress); udpSocket.sendeBroadcast(serverIpAddress, response.toString()); } catch (NoAvailableAddressException e) {} } void processRequest(String mac, String ip, String serverIdentifier) { DHCPServer dhcpServer = (DHCPServer) server; UDPSocket udpSocket = (UDPSocket) socket; String serverIpAddress = dhcpServer.holeServerIpAddress(); if (StringUtils.equalsIgnoreCase(serverIdentifier, serverIpAddress)) { DHCPMessage response; try { response = DHCPMessage.createAckMessage(mac, dhcpServer.requestAddress(mac, ip).getIp(), serverIpAddress); } catch (AddressRequestNotAcceptedException e) { response = DHCPMessage.createNackMessage(mac, ip, serverIpAddress); } udpSocket.sendeBroadcast(serverIpAddress, response.toString()); } else { dhcpServer.blacklistAddress(ip); } } } filius-2.9.4+ds/src/main/java/filius/software/dhcp/IpConfig.java000066400000000000000000000015011506574241500244710ustar00rootroot00000000000000package filius.software.dhcp; public class IpConfig { private String ipAddress; private String router; private String subnetMask; private String dnsServer; private String dhcpServer; IpConfig(String ipAddress, String router, String subnetMask, String dnsServer, String dhcpServer) { this.ipAddress = ipAddress; this.router = router; this.subnetMask = subnetMask; this.dnsServer = dnsServer; this.dhcpServer = dhcpServer; } public String getIpAddress() { return ipAddress; } public String getRouter() { return router; } public String getSubnetMask() { return subnetMask; } public String getDnsServer() { return dnsServer; } public String getDhcpServer() { return dhcpServer; } } filius-2.9.4+ds/src/main/java/filius/software/dns/000077500000000000000000000000001506574241500220015ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/dns/DNSNachricht.java000066400000000000000000000406641506574241500251260ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Das Format einer DNS-Nachricht wird in RFC 1035 beschrieben.
* The header contains the following fields:
*
    *
  1. ID
  2. *
  3. QR
  4. *
  5. OPCODE
  6. *
  7. AA
  8. *
  9. TC
  10. *
  11. RD
  12. *
  13. RA
  14. *
  15. Z
  16. *
  17. RCODE
  18. *
  19. QDCOUNT
  20. *
  21. ANCOUNT
  22. *
  23. NSCOUNT
  24. *
  25. ARCOUNT
  26. *
* * Resource record format
* The answer, authority, and additional sections all share the same format */ public class DNSNachricht { private static Logger LOG = LoggerFactory.getLogger(DNSNachricht.class); /** * Konstanten fuer das Bit im Kopfteil (Header), das indiziert, ob es sich um eine Anfrage (QUERY) oder eine Antwort * (RESPONSE) handelt. */ public static final int QUERY = 0, RESPONSE = 1; /** * Konstanten fuer den Response-Code: The values have the following interpretation: *
    *
  • 0 - No error condition
  • *
  • 1 - Format error - The name server was unable to interpret the query.
  • *
  • 2 - Server failure - The name server was unable to process this query due to a problem with the name * server.
  • *
  • 3 - Name Error - Meaningful only for responses from an authoritative name server, this code signifies that * the domain name referenced in the query does not exist.
  • *
  • 4 - Not Implemented - The name server does not support the requested kind of query.
  • *
  • 5 - Refused - The name server refuses to perform the specified operation for policy reasons. For example, a * name server may not wish to provide the information to the particular requester, or a name server may not wish to * perform a particular operation (e.g., zone transfer) for particular data.
  • *
*/ public static final int NO_ERROR = 0, FORMAT_ERROR = 1, SERVER_FAILURE = 2, NAME_ERROR = 3, NOT_IMPLEMENTED = 4, REFUSED = 5; /** * ID A 16 bit identifier assigned by the program that generates any kind of query. This identifier is copied the * corresponding reply and can be used by the requester to match up replies to outstanding queries. */ private int id = (int) (Math.random() * 65536); /** Whether this query is from the local resolver (and not from potentially remote server) */ private boolean isLocal = false; /** * QR A one bit field that specifies whether this message is a query (0), or a response (1). */ private int queryResponse = QUERY; /** * OPCODE A four bit field that specifies kind of query in this message. This value is set by the originator of a * query and copied into the response. The values are: *
    *
  • 0 - a standard query (QUERY)
  • *
  • 1 - an inverse query (IQUERY)
  • *
  • 2 - a server status request (STATUS)
  • *
*/ private int opcode = 0; /** * AA Authoritative Answer - this bit is valid in responses, and specifies that the responding name server is an * authority for the domain name in question section. Note that the contents of the answer section may have multiple * owner names because of aliases. The AA bit corresponds to the name which matches the query name, or the first * owner name in the answer section. */ private boolean authoritativeAnswer = false; /** * TC TrunCation - specifies that this message was truncated due to length greater than that permitted on the * transmission channel. */ private boolean truncated = false; /** * RD Recursion Desired - this bit may be set in a query and is copied into the response. If RD is set, it directs * the name server to pursue the query recursively. Recursive query support is optional. */ private boolean recursionDesired = true; /** * RA Recursion Available - this be is set or cleared in a response, and denotes whether recursive query support is * available in the name server. */ private boolean recursionAvailable = true; /** * RCODE Response code - this 4 bit field is set as part of responses.
* Dazu werden in dieser Klasse Konstanten zur Verfuegung gestellt. */ private int responseCode = NO_ERROR; /** * QDCOUNT an unsigned 16 bit integer specifying the number of entries in the question section. */ private int queryCount = 0; /** * ANCOUNT an unsigned 16 bit integer specifying the number of resource records in the answer section. */ private int answerCount = 0; /** * NSCOUNT an unsigned 16 bit integer specifying the number of name server resource records in the authority records * section. */ private int authoritativeAnswerCount = 0; /** ARCOUNT Anzahl der Resource Records in der 'additional section' */ private int additionalCount = 0; /** * Anfragen (query) in der Nachricht */ private LinkedList queries = new LinkedList(); /** Resource Records in der 'answer section' */ private LinkedList answerRecords = new LinkedList(); /** Resource Records in der 'authority section' */ private LinkedList authoratativeRecords = new LinkedList(); /** Resource Records in der 'additional section' */ private LinkedList additionalRecords = new LinkedList(); /** * Konstruktor zur Erzeugung einer DNS-Nachricht mit Standardwerten. Lediglich ob es sich um eine Anfrage oder eine * Antwort handelt, muss als Parameter uebergeben werden. Dazu werden die Konstanten QUERY und RESPONSE verwendet. */ public DNSNachricht(int queryResponse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", DNSNachricht(" + queryResponse + ")"); this.queryResponse = queryResponse; } /** * Ein Konstruktor, der aus einem String, der durch den Aufruf der Methode toString() erzeugt wurde, wieder eine * DNSNachricht erstellt. * * @param nachricht * ein String, der durch die Methode toString() erstellt wurde */ public DNSNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSNachricht), constr: DNSNachricht(" + nachricht + ")"); StringTokenizer lineTokenizer, tokenizer; String line, token; if (nachricht != null) { lineTokenizer = new StringTokenizer(nachricht, "\n"); line = lineTokenizer.nextToken(); tokenizer = new StringTokenizer(line, " "); while (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); if (token.startsWith("ID")) { id = Integer.parseInt(token.substring(3)); } else if (token.startsWith("QR")) { queryResponse = Integer.parseInt(token.substring(3)); } else if (token.startsWith("RCODE")) { responseCode = Integer.parseInt(token.substring(6)); } else if (token.startsWith("QDCOUNT")) { queryCount = Integer.parseInt(token.substring(8)); } else if (token.startsWith("ANCOUNT")) { answerCount = Integer.parseInt(token.substring(8)); } else if (token.startsWith("NSCOUNT")) { authoritativeAnswerCount = Integer.parseInt(token.substring(8)); } else if (token.startsWith("ARCOUNT")) { additionalCount = Integer.parseInt(token.substring(8)); } } for (int i = 0; i < queryCount && lineTokenizer.hasMoreTokens(); i++) { line = lineTokenizer.nextToken(); queries.add(new Query(line)); } for (int i = 0; i < answerCount && lineTokenizer.hasMoreTokens(); i++) { line = lineTokenizer.nextToken(); answerRecords.add(new ResourceRecord(line)); } for (int i = 0; i < authoritativeAnswerCount && lineTokenizer.hasMoreTokens(); i++) { line = lineTokenizer.nextToken(); authoratativeRecords.add(new ResourceRecord(line)); } for (int i = 0; i < additionalCount && lineTokenizer.hasMoreTokens(); i++) { line = lineTokenizer.nextToken(); additionalRecords.add(new ResourceRecord(line)); } } } /** * Diese Methode liefert die Nachricht mit ausgewaehlten Attributen des Kopfteils in der Form * <Attribut>=<Wert> (z. B. "ID=42"), die durch Leerzeichen getrennt sind, und den Datenteil, * der durch einen Zeilenumbruch vom Kopfteil abgetrennt ist. */ public String toString() { StringBuffer buffer = new StringBuffer(); ListIterator it; buffer.append("ID=" + id + " "); buffer.append("QR=" + queryResponse + " "); buffer.append("RCODE=" + responseCode + " "); buffer.append("QDCOUNT=" + queryCount + " "); buffer.append("ANCOUNT=" + answerCount + " "); buffer.append("NSCOUNT=" + authoritativeAnswerCount + " "); buffer.append("ARCOUNT=" + additionalCount + " "); buffer.append("\n"); it = queries.listIterator(); while (it.hasNext()) { buffer.append(it.next().toString() + "\n"); } it = answerRecords.listIterator(); while (it.hasNext()) { buffer.append(it.next().toString() + "\n"); } it = authoratativeRecords.listIterator(); while (it.hasNext()) { buffer.append(it.next().toString() + "\n"); } it = additionalRecords.listIterator(); while (it.hasNext()) { buffer.append(it.next().toString() + "\n"); } return buffer.toString(); } /** * Methode zum hinzufuegen einer Anfrage (query). Das Format muss folgendermassen sein: NAME TYPE CLASS (Bsp.: * web.de. A IN) */ public void hinzuQuery(String anfrage) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSNachricht), hinzuQuery(" + anfrage + ")"); queries.add(new Query(anfrage)); queryCount++; } /** Methode fuer den Zugriff auf die Anfragen (queries) */ public LinkedList holeQueries() { return queries; } /** * Methode fuer den Zugriff auf die Resource-Records in der 'answer section'. */ public LinkedList holeAntwortResourceRecords() { return answerRecords; } /** * Methode fuer den Zugriff auf die Resource-Records in der 'authority section'. */ public LinkedList holeAuthoritativeResourceRecords() { return authoratativeRecords; } /** * Methode fuer den Zugriff auf die Resource-Records in der 'additional section'. */ public LinkedList holeZusatzResourceRecords() { return additionalRecords; } /** retrieve all resource records no matter what section they are in */ public List holeResourceRecords() { List list = new ArrayList<>(); list.addAll(answerRecords); list.addAll(authoratativeRecords); list.addAll(additionalRecords); return list; } /** * Diese Methode fuegt der DNS-Nachricht einen Resource Record hinzu. Das Format muss folgendermassen aussehen: NAME * TYPE CLASS TTL RDATA (Bsp. web.de. A IN 3600 217.72.195.42) */ public void hinzuAntwortResourceRecord(String record) { hinzuAntwortResourceRecord(new ResourceRecord(record)); } public void hinzuAntwortResourceRecords(List rrList) { answerRecords.addAll(rrList); answerCount += rrList.size(); } public void hinzuAntwortResourceRecord(ResourceRecord rr) { answerRecords.add(rr); answerCount++; } /** * Diese Methode fuegt der DNS-Nachricht einen Resource Record hinzu. Das Format muss folgendermassen aussehen: NAME * TYPE CLASS TTL RDATA (Bsp. web.de. A IN 3600 217.72.195.42) */ public void hinzuAuthoritativeResourceRecord(String record) { authoratativeRecords.add(new ResourceRecord(record)); authoritativeAnswerCount++; } public void hinzuAuthoritativeResourceRecords(List rrList) { authoratativeRecords.addAll(rrList); authoritativeAnswerCount += rrList.size(); } /** * @return the authoritativeAnswer */ public boolean isAuthoritativeAnswer() { return authoritativeAnswer; } /** * @param authoritativeAnswer * the authoritativeAnswer to set */ public void setAuthoritativeAnswer(boolean authoritativeAnswer) { this.authoritativeAnswer = authoritativeAnswer; } /** * @return the id */ public int getId() { return id; } /** * @param id * the id to set */ public void setId(int id) { this.id = id; } /** * @return the opcode */ public int getOpcode() { return opcode; } /** * @param opcode * the opcode to set */ public void setOpcode(int opcode) { this.opcode = opcode; } /** * @return the queryResponse */ public int getQueryResponse() { return queryResponse; } /** * @param queryResponse * the queryResponse to set */ public void setQueryResponse(int queryResponse) { this.queryResponse = queryResponse; } /** * @return the recursionAvailable */ public boolean isRecursionAvailable() { return recursionAvailable; } /** * @param recursionAvailable * the recursionAvailable to set */ public void setRecursionAvailable(boolean recursionAvailable) { this.recursionAvailable = recursionAvailable; } /** * @return the recursionDesired */ public boolean isRecursionDesired() { return recursionDesired; } /** * @param recursionDesired * the recursionDesired to set */ public void setRecursionDesired(boolean recursionDesired) { this.recursionDesired = recursionDesired; } /** * @return the responseCode */ public int getResponseCode() { return responseCode; } /** * @param responseCode * the responseCode to set */ public void setResponseCode(int responseCode) { this.responseCode = responseCode; } /** * @return the truncated */ public boolean isTruncated() { return truncated; } /** * @param truncated * the truncated to set */ public void setTruncated(boolean truncated) { this.truncated = truncated; } public boolean isLocal() { return isLocal; } public void setLocal() { this.isLocal = true; } } filius-2.9.4+ds/src/main/java/filius/software/dns/DNSQueryAgent.java000066400000000000000000000060471506574241500253040ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.TimeOutException; import filius.exception.VerbindungsException; import filius.hardware.Verbindung; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.transportschicht.UDPSocket; public class DNSQueryAgent { private static final Logger LOG = LoggerFactory.getLogger(DNSQueryAgent.class); /** * Methode zum Abruf eines Resource Record. Das Rueckgabeformat ist NAME TYPE CLASS TTL RDATA (Bsp. web.de. A IN * 3600 217.72.195.42) * * @param typ * @param domainname * must end with '.' * @return DNS response from remote server or null if there is no answer at all. */ public DNSNachricht query(String typ, String domainname, String dnsServer, InternetKnotenBetriebssystem os) throws TimeOutException { DNSNachricht antwort = null; if (dnsServer != null && !dnsServer.equals("")) { try { DNSNachricht anfrage = new DNSNachricht(DNSNachricht.QUERY); anfrage.hinzuQuery(domainname + " " + typ + " IN"); UDPSocket socket = new UDPSocket(os, dnsServer, 53); socket.verbinden(); socket.senden(anfrage.toString()); String tmp = socket.empfangen(5 * Verbindung.holeRTT()); socket.schliessen(); if (tmp != null) { antwort = new DNSNachricht(tmp); } else { LOG.debug("Timeout while waiting for DNS query response"); throw new TimeOutException(); } } catch (VerbindungsException e) { LOG.debug("Could not connect to DNS server", e); } } return antwort; } } filius-2.9.4+ds/src/main/java/filius/software/dns/DNSServer.java000066400000000000000000000276771506574241500245020ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.TimeOutException; import filius.software.clientserver.UDPServerAnwendung; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.transportschicht.Socket; public class DNSServer extends UDPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(DNSServer.class); private boolean recursiveResolutionEnabled = false; private Resolver resolver; public boolean isRecursiveResolutionEnabled() { return recursiveResolutionEnabled; } public void setRecursiveResolutionEnabled(boolean recursiveResolutionEnabled) { this.recursiveResolutionEnabled = recursiveResolutionEnabled; } public DNSServer() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), constr: DNSServer()"); setPort(53); } @Override public void setSystemSoftware(InternetKnotenBetriebssystem bs) { super.setSystemSoftware(bs); resolver = ((InternetKnotenBetriebssystem) bs).holeDNSClient(); } public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), starten()"); super.starten(); Dateisystem dateisystem = getSystemSoftware().getDateisystem(); if (!dateisystem.dateiVorhanden(Dateisystem.FILE_SEPARATOR + "dns", "hosts")) { dateisystem.erstelleVerzeichnis(dateisystem.getRoot(), "dns"); Datei hostsFile = new Datei(); hostsFile.setName("hosts"); hostsFile.setDateiInhalt(""); dateisystem.speicherDatei(Dateisystem.FILE_SEPARATOR + "dns", hostsFile); } } public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), beenden()"); super.beenden(); } public List holeResourceRecords() { return leseRecordListe(); } public void hinzuRecord(String domainname, String typ, String rdata) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), hinzuRecord(" + domainname + "," + typ + "," + rdata + ")"); ResourceRecord rr; rr = new ResourceRecord(domainname, typ, rdata); List rrList = leseRecordListe(); rrList.add(rr); this.schreibeRecordListe(rrList); } private List leseRecordListe() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + ", initialisiereRecordListe()"); Dateisystem dateisystem = getSystemSoftware().getDateisystem(); Datei hosts = dateisystem.holeDatei(Dateisystem.FILE_SEPARATOR + "dns" + Dateisystem.FILE_SEPARATOR + "hosts"); List resourceRecords = new LinkedList(); if (hosts != null) { StringTokenizer tokenizer = new StringTokenizer(hosts.getDateiInhalt(), "\n"); while (tokenizer.hasMoreTokens()) { String line = tokenizer.nextToken().trim(); if (!line.equals("") && !(line.split(" ", 5).length < 4)) { ResourceRecord rr = new ResourceRecord(line); resourceRecords.add(rr); } } } return resourceRecords; } private void schreibeRecordListe(List records) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), schreibeRecordListe()"); StringBuffer text = new StringBuffer(); for (ResourceRecord resourceRecord : records) { text.append(resourceRecord.toString() + "\n"); } Dateisystem dateisystem = getSystemSoftware().getDateisystem(); Datei hostsFile = dateisystem.holeDatei(dateisystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "dns", "hosts"); if (hostsFile == null) { hostsFile = new Datei(); hostsFile.setName("hosts"); dateisystem.erstelleVerzeichnis(dateisystem.getRoot(), "dns"); dateisystem.speicherDatei(dateisystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "dns", hostsFile); } hostsFile.setDateiInhalt(text.toString()); } public void changeSingleEntry(int recordIdx, int partIdx, String type, String newValue) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", changeSingleEntry(" + recordIdx + "," + partIdx + "," + type + "," + newValue + ")"); List rrList = leseRecordListe(); int countA = 0; // iterating whole list is necessary, since MX and A records are mixed // in the records list! :-( for (ResourceRecord rrec : rrList) { if (rrec.getType().equals(type)) { countA++; } if (countA - 1 == recordIdx) { if (partIdx == 0) { // change URL rrec.setDomainname(newValue); } else if (partIdx == 3) { // change IP rrec.setRdata(newValue); } } } this.schreibeRecordListe(rrList); benachrichtigeBeobachter(type); } public DNSNachricht answer(Query query) { benachrichtigeBeobachter(messages.getString("sw_dnsservermitarbeiter_msg1") + query); DNSNachricht antwort = new DNSNachricht(DNSNachricht.RESPONSE); antwort.hinzuAntwortResourceRecords(answerWithLocalData(query)); if (antwort.holeAntwortResourceRecords().isEmpty() && isRecursiveResolutionEnabled()) { antwort.hinzuAntwortResourceRecords(answerWithRemoteData(query)); } if (antwort.holeAntwortResourceRecords().isEmpty()) { antwort.hinzuAntwortResourceRecords(defineNameserverRecords(query)); } return antwort; } private List defineNameserverRecords(Query query) { List answerResourceRecords = new ArrayList<>(); List records = leseRecordListe(); ResourceRecord nameserverNSRecord = ResourceRecord.findApplicableNSRecord(query.holeDomainname(), records); if (null != nameserverNSRecord) { answerResourceRecords.add(nameserverNSRecord); ResourceRecord nameserverARecord = ResourceRecord.findRecord(nameserverNSRecord.getRdata(), ResourceRecord.ADDRESS, records); if (null != nameserverARecord) { answerResourceRecords.add(nameserverARecord); } } return answerResourceRecords; } List answerWithRemoteData(Query query) { List answerResourceRecords = new ArrayList<>(); List recordList = leseRecordListe(); ResourceRecord nsRecord = ResourceRecord.findApplicableNSRecord(query.holeDomainname(), recordList); String dnsServerAddress; if (null != nsRecord) { ResourceRecord addressNsRecord = ResourceRecord.findRecord(nsRecord.getRdata(), ResourceRecord.ADDRESS, recordList); if (null != addressNsRecord) { dnsServerAddress = addressNsRecord.getRdata(); answerResourceRecords.addAll(resolveWithNameserver(query, dnsServerAddress)); } } if (answerResourceRecords.isEmpty() && !getSystemSoftware().getDNSServer().equals(getSystemSoftware().primaryIPAdresse())) { dnsServerAddress = getSystemSoftware().getDNSServer(); answerResourceRecords.addAll(resolveWithNameserver(query, dnsServerAddress)); } return answerResourceRecords; } private List resolveWithNameserver(Query query, String dnsServerAddress) { List answerResourceRecords = new ArrayList<>(); if (StringUtils.isNoneBlank(dnsServerAddress)) { try { DNSNachricht remoteResponse = resolver.resolve(query.holeDomainname(), query.holeTyp(), dnsServerAddress); answerResourceRecords.addAll(remoteResponse.holeAntwortResourceRecords()); answerResourceRecords.addAll(remoteResponse.holeAuthoritativeResourceRecords()); } catch (TimeOutException e) { LOG.debug("Could not retrieve answer for DNS query: " + query, e); } } return answerResourceRecords; } List answerWithLocalData(Query query) { List answerResourceRecords = new ArrayList<>(); List recordList = leseRecordListe(); List matchingRecordList = ResourceRecord.findRecords(query.holeDomainname(), query.holeTyp(), recordList); for (ResourceRecord responseRecord : matchingRecordList) { answerResourceRecords.add(responseRecord); if (responseRecord.getType().equals(ResourceRecord.MAIL_EXCHANGE) || responseRecord.getType().equals(ResourceRecord.NAME_SERVER)) { ResourceRecord addressForMxOrNsRecord = ResourceRecord.findRecord(responseRecord.getRdata(), ResourceRecord.ADDRESS, recordList); if (addressForMxOrNsRecord != null) { answerResourceRecords.add(addressForMxOrNsRecord); } } } return answerResourceRecords; } public void loescheResourceRecord(String domainname, String typ) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), loescheResourceRecord(" + domainname + "," + typ + ")"); List rrList = leseRecordListe(); for (ResourceRecord rr : rrList) { if (rr.getDomainname().equalsIgnoreCase(domainname) && rr.getType().equals(typ)) { rrList.remove(rr); break; } } this.schreibeRecordListe(rrList); } protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), neuerMitarbeiter(" + socket + ")"); DNSServerMitarbeiter dnsMitarbeiter = new DNSServerMitarbeiter(this, socket); dnsMitarbeiter.starten(); mitarbeiter.add(dnsMitarbeiter); } } filius-2.9.4+ds/src/main/java/filius/software/dns/DNSServerMitarbeiter.java000066400000000000000000000053161506574241500266540ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.ServerMitarbeiter; import filius.software.transportschicht.Socket; import filius.software.transportschicht.UDPSocket; public class DNSServerMitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(DNSServerMitarbeiter.class); public DNSServerMitarbeiter(DNSServer server, Socket socket) { super(server, socket); } protected void verarbeiteNachricht(String dateneinheit) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServerMitarbeiter), verarbeiteNachricht(" + dateneinheit + ")"); DNSNachricht nachricht = new DNSNachricht(dateneinheit); DNSNachricht antwort = new DNSNachricht(DNSNachricht.RESPONSE); antwort.setId(nachricht.getId()); for (Query query : nachricht.holeQueries()) { DNSNachricht queryResponse = ((DNSServer) server).answer(query); antwort.hinzuAntwortResourceRecords(queryResponse.holeAntwortResourceRecords()); antwort.hinzuAuthoritativeResourceRecords(queryResponse.holeAuthoritativeResourceRecords()); } if (socket != null) { ((UDPSocket) socket).senden(antwort.toString()); server.benachrichtigeBeobachter( messages.getString("sw_dnsservermitarbeiter_msg2") + "\n>>>>\n" + antwort.toString() + "<<<<"); } } } filius-2.9.4+ds/src/main/java/filius/software/dns/DNSVerknuepfung.java000066400000000000000000000047031506574241500256740ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; public class DNSVerknuepfung { private String wert; // kann eine IP oder eine MailDomain sein private String domain; private long ttl; // Lebensdauer private String typ; // kann a, mx oder ns(zur Zeit nicht implementiert) sein /** * Ist die DNSVerknuepfung vom typ mx, dann enthält wert keine IP sondern eine domain. Beispiel: typ: mx domain: * gmx.de wert: mail.gmx.de * * In dem Fall MUSS es dann noch eine weitere DNSVerknüpfung geben * * typ: a domain: mail.gmx.de wert: 81.25.21.123 * * * @return */ public long getTtl() { return ttl; } public void setTtl(long ttl) { this.ttl = ttl; } public String getTyp() { return typ; } public void setTyp(String typ) { this.typ = typ; } public String getWert() { return wert; } public void setWert(String wert) { this.wert = wert; } public String getDomain() { return domain; } public void setDomain(String domain) { this.domain = domain; } public String toString() { String str = wert + " " + ttl + " " + typ + " " + domain; return str; } } filius-2.9.4+ds/src/main/java/filius/software/dns/Query.java000066400000000000000000000107561506574241500237620ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * Question section format
* The question section is used to carry the "question" in most queries, i.e., the parameters that define what is being * asked. The section contains QDCOUNT (usually 1) entries, each of the following format: ... *
    *
  1. QNAME a domain name represented as a sequence of labels, where each label consists of a length octet followed by * that number of octets. The domain name terminates with the zero length octet for the null label of the root. Note * that this field may be an odd number of octets; no padding is used.
  2. *
  3. QTYPE a two octet code which specifies the type of the query. The values for this field include all codes valid * for a TYPE field, together with some more general codes which can match more than one type of RR.
  4. *
  5. QCLASS a two octet code that specifies the class of the query. For example, the QCLASS field is IN for the * Internet.
  6. *
*/ public class Query { private static Logger LOG = LoggerFactory.getLogger(Query.class); /** QNAME a domain name */ private String qname = null; /** * QTYPE a code which specifies the type of the query. Zur Belegung dieses Attributs sollten die entsprechenden * Konstanten der Klasse {@link ResourceRecord} verwendet werden */ private String qtype = null; /** * QCLASS a code that specifies the class of the query. For example, the QCLASS field is IN for the Internet. */ private String qclass = "IN"; /** * Ein Konstruktor, der aus einem String, der durch den Aufruf der Methode toString() erzeugt wurde, wieder eine * Query erstellt. * * @param nachricht * ein String, der durch die Methode toString() erstellt wurde */ public Query(String query) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Query), constr: Query(" + query + ")"); StringTokenizer tokenizer; String token; tokenizer = new StringTokenizer(query, " "); if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); qname = token; } if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); qtype = token; } if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); qclass = token; } } /** * @param qname * a domain name * @param qtype * a code which specifies the type of the query. Zur Belegung dieses Attributs sollten die entsprechenden * Konstanten der Klasse {@link ResourceRecord} verwendet werden */ public Query(String qname, String qtype) { this.qname = qname; this.qtype = qtype; } /** * Methode zum Abruf einer Anfrage (query). Das Rueckgabeformat ist NAME TYPE CLASS (Bsp.: web.de. A IN) */ public String toString() { return qname + " " + qtype + " " + qclass; } public String holeDomainname() { return qname; } public String holeTyp() { return qtype; } public String holeKlasse() { return qclass; } } filius-2.9.4+ds/src/main/java/filius/software/dns/Resolver.java000066400000000000000000000241771506574241500244600ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.TimeOutException; import filius.software.clientserver.ClientAnwendung; /** * Ausschnitt aus dem RFC 1035:
* Question section format
* The question section is used to carry the "question" in most queries, i.e., the parameters that define what is being * asked. The section contains QDCOUNT (usually 1) entries, each of the following format: ... *
    *
  1. QNAME a domain name represented as a sequence of labels, where each label consists of a length octet followed by * that number of octets. The domain name terminates with the zero length octet for the null label of the root. Note * that this field may be an odd number of octets; no padding is used.
  2. *
  3. QTYPE a two octet code which specifies the type of the query. The values for this field include all codes valid * for a TYPE field, together with some more general codes which can match more than one type of RR.
  4. *
  5. QCLASS a two octet code that specifies the class of the query. For example, the QCLASS field is IN for the * Internet.
  6. *
* * Resource record format
* The answer, authority, and additional sections all share the same format: a variable number of resource records, * where the number of records is specified in the corresponding count field in the header. Each resource record has the * following format: ... *
    *
  1. NAME a domain name to which this resource record pertains.
  2. *
  3. TYPE two octets containing one of the RR type codes. This field specifies the meaning of the data in the RDATA * field.
  4. *
  5. CLASS two octets which specify the class of the data in the RDATA field.
  6. *
  7. TTL a 32 bit unsigned integer that specifies the time interval (in seconds) that the resource record may be * cached before it should be discarded. Zero values are interpreted to mean that the RR can only be used for the * transaction in progress, and should not be cached.
  8. *
  9. RDLENGTH an unsigned 16 bit integer that specifies the length in octets of the RDATA field.
  10. *
  11. RDATA a variable length string of octets that describes the resource. The format of this information varies * according to the TYPE and CLASS of the resource record. For example, the if the TYPE is A and the CLASS is IN, the * RDATA field is a 4 octet ARPA Internet address.
  12. *
*/ public class Resolver extends ClientAnwendung { private static Logger LOG = LoggerFactory.getLogger(Resolver.class); private static Pattern LOCALHOST_PATTERN = Pattern.compile("localhost\\.?", Pattern.CASE_INSENSITIVE); private DNSQueryAgent queryAgent = new DNSQueryAgent(); public String holeIPAdresse(String domainname) throws TimeOutException { return holeIPAdresse(domainname, getSystemSoftware().getDNSServer()); } /** * Methode zur Aufloesung eines Domainnamens zu einer IP-Adresse. * * @param domainname * @return * @throws TimeOutException */ public String holeIPAdresse(String domainname, String dnsServer) throws TimeOutException { if (!domainname.matches(".*\\.$")) { domainname += "."; } DNSNachricht data = resolveA(domainname, dnsServer); ResourceRecord rr = ResourceRecord.findRecord(domainname, ResourceRecord.ADDRESS, data.holeResourceRecords()); return null != rr ? rr.getRdata() : null; } /** * Resolve domain name (but not an IP address!) * * @throws TimeOutException */ public DNSNachricht resolveA(String domainname, String dnsServer) throws TimeOutException { return resolve(domainname, ResourceRecord.ADDRESS, dnsServer); } public DNSNachricht resolveA(String domainname) throws TimeOutException { return resolve(domainname, ResourceRecord.ADDRESS, getSystemSoftware().getDNSServer()); } public String holeIPAdresseMailServer(String domainname) throws TimeOutException { if (!domainname.matches(".*\\.$")) { domainname += "."; } List dataRecords = resolveMX(domainname, getSystemSoftware().getDNSServer()) .holeResourceRecords(); ResourceRecord mxRecord = ResourceRecord.findRecord(domainname, ResourceRecord.MAIL_EXCHANGE, dataRecords); ResourceRecord aRecord = null == mxRecord ? null : ResourceRecord.findRecord(mxRecord.getRdata(), ResourceRecord.ADDRESS, dataRecords); return aRecord == null ? null : aRecord.getRdata(); } protected List extractRankedResourceRecordList(DNSNachricht antwort, String type, String domainname) { List relevantResourceRecords = new ArrayList<>(); if (antwort != null) { relevantResourceRecords .addAll(extractResourceRecords(antwort.holeAntwortResourceRecords(), type, domainname)); relevantResourceRecords .addAll(extractResourceRecords(antwort.holeAuthoritativeResourceRecords(), type, domainname)); relevantResourceRecords .addAll(extractResourceRecords(antwort.holeZusatzResourceRecords(), type, domainname)); } return relevantResourceRecords; } public DNSNachricht resolveMX(String domainname) throws TimeOutException { return resolve(domainname, ResourceRecord.MAIL_EXCHANGE, getSystemSoftware().getDNSServer()); } public DNSNachricht resolveMX(String domainname, String dnsServerAddress) throws TimeOutException { return resolve(domainname, ResourceRecord.MAIL_EXCHANGE, dnsServerAddress); } public DNSNachricht resolve(String domainname, String type, String dnsServerAddress) throws TimeOutException { if (!domainname.matches(".*\\.$")) { domainname += "."; } DNSNachricht response = new DNSNachricht(DNSNachricht.RESPONSE); String serverToQuery = null; if (LOCALHOST_PATTERN.matcher(domainname).matches()) { response.setLocal(); response.hinzuAntwortResourceRecord(ResourceRecord.LOCALHOST_ADDRESS); } else { serverToQuery = dnsServerAddress; } Set queriedDnsServer = new HashSet<>(); while (serverToQuery != null) { DNSNachricht remoteResponse = queryAgent.query(type, domainname, serverToQuery, getSystemSoftware()); if (remoteResponse == null) { break; } copyResourceRecords(remoteResponse, response, domainname, type); List data = extractRankedResourceRecordList(remoteResponse, type, domainname); if (!data.isEmpty()) { if (ResourceRecord.MAIL_EXCHANGE.equals(type) || ResourceRecord.NAME_SERVER.equals(type)) { copyResourceRecords(remoteResponse, response, data.get(0).getRdata(), ResourceRecord.ADDRESS); } break; } String nextServerToQuery = extractAddressForNameServer(remoteResponse.holeResourceRecords(), domainname); serverToQuery = queriedDnsServer.add(nextServerToQuery) ? nextServerToQuery : null; } LOG.debug("resolved dns query for {} {}: {}", domainname, type, response.holeResourceRecords()); return response; } private void copyResourceRecords(DNSNachricht source, DNSNachricht target, String domainname, String type) { target.hinzuAntwortResourceRecords( extractResourceRecords(source.holeAntwortResourceRecords(), type, domainname)); target.hinzuAuthoritativeResourceRecords( extractResourceRecords(source.holeAuthoritativeResourceRecords(), type, domainname)); } public DNSNachricht resolveNS(String domainname) throws TimeOutException { return resolve(domainname, ResourceRecord.NAME_SERVER, getSystemSoftware().getDNSServer()); } protected String extractAddressForNameServer(List rrList, String domain) { String dnsServerAddress = null; ResourceRecord nsRecord = ResourceRecord.findApplicableNSRecord(domain, rrList); if (null != nsRecord) { ResourceRecord aRecord = ResourceRecord.findRecord(nsRecord.getRdata(), ResourceRecord.ADDRESS, rrList); if (null != aRecord) { dnsServerAddress = aRecord.getRdata(); } } return dnsServerAddress; } protected List extractResourceRecords(List liste, String typ, String domainname) { List relevantRR = new ArrayList<>(); for (ResourceRecord rr : liste) { if (rr.getDomainname().equalsIgnoreCase(domainname) && rr.getType().equals(typ)) { relevantRR.add(rr); } } return relevantRR; } } filius-2.9.4+ds/src/main/java/filius/software/dns/ResourceRecord.java000066400000000000000000000241161506574241500255760ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.dns; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.EingabenUeberpruefung; /** * Resource record format
* The answer, authority, and additional sections all share the same format: a variable number of resource records, * where the number of records is specified in the corresponding count field in the header. Each resource record has the * following format: ... *
    *
  1. NAME a domain name to which this resource record pertains.
  2. *
  3. TYPE two octets containing one of the RR type codes. This field specifies the meaning of the data in the RDATA * field.
  4. *
  5. CLASS two octets which specify the class of the data in the RDATA field.
  6. *
  7. TTL a 32 bit unsigned integer that specifies the time interval (in seconds) that the resource record may be * cached before it should be discarded. Zero values are interpreted to mean that the RR can only be used for the * transaction in progress, and should not be cached.
  8. *
  9. RDLENGTH an unsigned 16 bit integer that specifies the length in octets of the RDATA field.
  10. *
  11. RDATA a variable length string of octets that describes the resource. The format of this information varies * according to the TYPE and CLASS of the resource record. For example, the if the TYPE is A and the CLASS is IN, the * RDATA field is a 4 octet ARPA Internet address.
  12. *
*/ public class ResourceRecord { private static Logger LOG = LoggerFactory.getLogger(ResourceRecord.class); public static final String ADDRESS = "A", MAIL_EXCHANGE = "MX", NAME_SERVER = "NS"; public static ResourceRecord LOCALHOST_ADDRESS = new ResourceRecord("localhost", ADDRESS, "127.0.0.1"); /** NAME a domain name to which this resource record pertains. */ private String domainname = ""; /** * TYPE two octets containing one of the RR type codes. This field specifies the meaning of the data in the RDATA * field. Zur Belegung dieses Attributs sollten die entsprechenden Konstanten verwendet werden */ private String type = ADDRESS; /** * TTL a 32 bit unsigned integer that specifies the time interval (in seconds) that the resource record may be * cached before it should be discarded. */ private int ttl = 3600; /** * RDATA a string that describes the resource. The format of this information varies according to the TYPE and CLASS * of the resource record. For example, if the TYPE is A and the CLASS is IN, the RDATA field is a 4 octet ARPA * Internet address. */ private String rdata = ""; public ResourceRecord(String domainname, String typ, String rdata) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ResourceRecord), constr: ResourceRecord(" + domainname + "," + typ + "," + rdata + ")"); setDomainname(domainname); this.type = typ; setRdata(rdata); } /** * Ein Konstruktor, der aus einem String, der durch den Aufruf der Methode toString() erzeugt wurde, wieder einen * Resource-Record erstellt. * * @param nachricht * ein String, der durch die Methode toString() erstellt wurde */ public ResourceRecord(String rr) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ResourceRecord), constr: ResourceRecord(" + rr + ")"); StringTokenizer tokenizer; String token; tokenizer = new StringTokenizer(rr, " "); if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); setDomainname(token); } if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); setType(token); } if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); try { setTtl(Integer.parseInt(token)); } catch (java.lang.NumberFormatException e) { LOG.debug("EXCEPTION: NumberFormatException, TTL field: '" + token + "'"); LOG.debug("", e); } } if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); setRdata(token); } } /** * Diese Methode liefert einen Resource Record als String zurueck. Methode zum Abruf eines Resource Record. Das * Rueckgabeformat ist NAME TYPE CLASS TTL RDATA (Bsp. web.de. A IN 3600 217.72.195.42) */ public String toString() { return getDomainname() + " " + type + " " + ttl + " " + getRdata(); } /** * @return the domainname */ public String getDomainname() { return domainname; } /** * @param domainname * the domainname to set */ public void setDomainname(String domainname) { if (EingabenUeberpruefung.isGueltig(domainname, EingabenUeberpruefung.musterDomain)) { if (domainname.matches(".*\\.$")) { this.domainname = domainname; } else { this.domainname = domainname + "."; } } } /** * @return the rdata */ public String getRdata() { if ((this.type.equals(NAME_SERVER) || this.type.equals(MAIL_EXCHANGE)) && !rdata.matches(".*\\.")) { return rdata + "."; } return rdata; } /** * @param rdata * the rdata to set */ public void setRdata(String rdata) { if ((this.type.equals(NAME_SERVER) || this.type.equals(MAIL_EXCHANGE)) && !rdata.matches(".*\\.")) { this.rdata = rdata + "."; } else { this.rdata = rdata; } } /** * Find the exact matching record for given domain and type. * * @param domainname * the domainname to look for in field domainname of the resource record * @param typ * one of A, NS, MX * @param recordList * the list to search in * @return the resource record or null if no applicable record could be found */ public static ResourceRecord findRecord(String domainname, String typ, List recordList) { for (ResourceRecord rr : recordList) { if (rr.getDomainname().equalsIgnoreCase(domainname) && rr.getType().equals(typ)) { return rr; } } return null; } /** * Find the exact matching records for given domain and type. * * @param domainname * the domainname to look for in field domainname of the resource record * @param typ * one of A, NS, MX * @param recordList * the list to search in * @return the resource record list, emty if no applicable record could be found */ public static List findRecords(String domainname, String typ, List recordList) { List matchingRecords = new ArrayList<>(); for (ResourceRecord rr : recordList) { if (rr.getDomainname().equalsIgnoreCase(domainname) && rr.getType().equals(typ)) { matchingRecords.add(rr); } } return matchingRecords; } /** * Find the best matching NS record for the given domain. * * @return the resource record or null if no applicable record could be found */ public static ResourceRecord findApplicableNSRecord(String domainname, List recordList) { String domain; String[] parts = domainname.split("\\."); for (int i = 0; i < parts.length; i++) { domain = implodeDomain(parts, i); for (ResourceRecord rr : recordList) { if (domain.equalsIgnoreCase(rr.getDomainname()) && NAME_SERVER.equals(rr.getType())) { return rr; } } } for (ResourceRecord rr : recordList) { if (".".equalsIgnoreCase(rr.getDomainname()) && NAME_SERVER.equals(rr.getType())) { return rr; } } return null; } private static String implodeDomain(String[] parts, int start) { StringBuffer domain = new StringBuffer(); for (int i = start; i < parts.length; i++) { domain.append(parts[i]); domain.append("."); } return domain.toString(); } /** * @return the ttl */ public int getTtl() { return ttl; } /** * @param ttl * the ttl to set */ public void setTtl(int ttl) { this.ttl = ttl; } /** * @return the type */ public String getType() { return type; } /** * @param type * the type to set */ public void setType(String type) { this.type = type; } } filius-2.9.4+ds/src/main/java/filius/software/email/000077500000000000000000000000001506574241500223045ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/email/AddressEntry.java000066400000000000000000000063011506574241500255560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.io.Serializable; /** * @author stefan * */ public class AddressEntry implements Serializable { private String name; private String mailAddress; public AddressEntry() {} public AddressEntry(String entry) { initFromString(entry); } private void initFromString(String entry) { String trimmedEntry = entry.trim(); int addressStartPos = trimmedEntry.indexOf("<"); int addressEndPos = trimmedEntry.indexOf(">"); if (addressStartPos >= 0 && addressEndPos > addressStartPos) { mailAddress = trimmedEntry.substring(addressStartPos + 1, addressEndPos).trim(); if (addressStartPos > 0) { name = trimmedEntry.substring(0, addressStartPos).trim(); } } else if (!trimmedEntry.contains("<") && !trimmedEntry.contains(">") && trimmedEntry.contains("@")) { mailAddress = trimmedEntry; } else { name = trimmedEntry; } } public AddressEntry(Object address) { if (address instanceof String) { this.initFromString((String) address); } else if (address instanceof AddressEntry) { AddressEntry addressEntry = (AddressEntry) address; this.name = addressEntry.name; this.mailAddress = addressEntry.mailAddress; } } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMailAddress() { return mailAddress; } public void setMailAddress(String mailAddress) { this.mailAddress = mailAddress; } @Override public String toString() { if (name == null && mailAddress == null) return null; else if (name == null) return "<" + mailAddress + ">"; else if (mailAddress == null) return name; else return name + " <" + mailAddress + ">"; } } filius-2.9.4+ds/src/main/java/filius/software/email/Email.java000066400000000000000000000206661506574241500242100ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Andre Asschoff * */ @SuppressWarnings("serial") public class Email implements Serializable { private static Logger LOG = LoggerFactory.getLogger(Email.class); private AddressEntry absender; private List empfaenger = new ArrayList(); private List cc = new ArrayList(); private List bcc = new ArrayList(); private String betreff; private String text = ""; /** * dient dazu, um festzustellen, ob eine Email schon mal abgerufen, und somit wahrscheinlich schon mal gelesen * wurde. Standardmaessig ist das true, d.h. sie wurde noch nicht gelesen. */ private boolean neu = true; /** * dieser Wert wird vom Server, wenn erhalten gesetzt */ private String dateReceived = ""; /** * wird als zu loeschen markiert, wenn sich ausgeloggt wird, wird */ private boolean delete = false; /** sie endgueltig geloescht. Sie wird dann nur nicht mehr angezeigt. */ private boolean versendet = false; public Email() {} /** * Dies ist der Konstruktor der Email. Immer wenn eine neue Email erzeugt wird, wird ihr der String ihrer Attribute * übergeben, die dann ausgelesen werden, und in ihren jeweiligen Attributen gespeichert werden. Bsp.:
* * From: * To: , * Cc: * Bcc: * Subject: Eine kurze Nachricht * * Das ist der Nachrichtentext. * * * @param nachricht */ public Email(String nachricht) { String fieldName, fieldData; String[] liste; String[] emaildaten = nachricht.split("\n"); int pos1, pos2; boolean header = true; for (int i = 0; i < emaildaten.length; i++) { pos1 = 0; pos2 = emaildaten[i].indexOf(":"); if (pos2 > pos1) { fieldName = emaildaten[i].substring(pos1, pos2).trim(); fieldData = emaildaten[i].substring(pos2 + 1).trim(); } else { fieldName = null; fieldData = null; } if (header) { if ("from".equalsIgnoreCase(fieldName)) { absender = new AddressEntry(fieldData); } else if ("to".equalsIgnoreCase(fieldName)) { empfaenger.clear(); liste = fieldData.split(","); for (int j = 0; j < liste.length; j++) { empfaenger.add(new AddressEntry(liste[j])); } } else if ("cc".equalsIgnoreCase(fieldName)) { cc.clear(); liste = fieldData.split(","); for (int j = 0; j < liste.length; j++) { cc.add(new AddressEntry(liste[j])); } } else if ("bcc".equalsIgnoreCase(fieldName)) { bcc.clear(); liste = fieldData.split(","); for (int j = 0; j < liste.length; j++) { bcc.add(new AddressEntry(liste[j])); } } else if ("subject".equalsIgnoreCase(fieldName)) { betreff = fieldData; } else if (text.equals("")) { header = false; } } else { text = text + emaildaten[i] + "\n"; } } } /** * In dieser Methode werden die Attribute der Email wieder zu einem langen String zusammen gesetzt. Er hat * anschliessend wieder die gleiche Form, wie der String, der beim Erzeugen eines neuen Email-Objektes mit * uebergeben wurde.
* Achtung: Die BCC-Empfaenger werden nicht mit ausgegeben!
* Bsp.:
* * From: * To: , * Cc: * Subject: Eine kurze Nachricht * * Das ist der Nachrichtentext. * */ public String toString() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Email), toString()"); String ergebnis; String toListe = "", ccListe = ""; toListe = holeEmpfaengerListe(); for (Object rcpt : cc) { ccListe += rcpt.toString() + ", "; } if (!ccListe.isEmpty()) { ccListe = ccListe.substring(0, ccListe.length() - 2); } ergebnis = ""; if (absender != null) { ergebnis += "From: " + absender.toString() + "" + "\n"; } if (!toListe.equals("")) { ergebnis += "To: " + toListe + "\n"; } if (!ccListe.equals("")) { ergebnis += "Cc: " + ccListe + "\n"; } if (betreff != null) { ergebnis += "Subject: " + betreff.trim() + "\n"; } if (!dateReceived.equals("")) { ergebnis += "Date Received: " + dateReceived + "\n"; } if (text != null) { ergebnis += "\n" + text.trim(); } return ergebnis; } public String holeEmpfaengerListe() { String toListe = ""; for (Object rcpt : empfaenger) { toListe += rcpt.toString() + ", "; } if (!toListe.isEmpty()) { toListe = toListe.substring(0, toListe.length() - 2); } return toListe; } public boolean getDelete() { return delete; } public void setDelete(boolean delete) { this.delete = delete; } public boolean getNeu() { return neu; } public void setNeu(boolean neu) { this.neu = neu; } public AddressEntry getAbsender() { return new AddressEntry(absender); } public List getEmpfaenger() { return this.empfaenger; } public String getBetreff() { return betreff; } public String getText() { return text; } public void setAbsender(String absender) { this.absender = new AddressEntry(absender); } public void setAbsender(AddressEntry absender) { this.absender = absender; } public void setBetreff(String betreff) { this.betreff = betreff; } public void setEmpfaenger(List empfaenger) { this.empfaenger = empfaenger; } public void setText(String text) { this.text = text; } public String getDateReceived() { return dateReceived; } public void setDateReceived(String dateReceived) { this.dateReceived = dateReceived; } public boolean isVersendet() { return versendet; } public void setVersendet(boolean versendet) { this.versendet = versendet; } public List getBcc() { return this.bcc; } public void setBcc(List bcc) { this.bcc = bcc; } public List getCc() { return this.cc; } public void setCc(List cc) { this.cc = cc; } } filius-2.9.4+ds/src/main/java/filius/software/email/EmailAnwendung.java000066400000000000000000000233311506574241500260470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.Anwendung; import filius.software.system.Datei; /** * * @author Andre Asschoff * */ public class EmailAnwendung extends Anwendung { private static Logger LOG = LoggerFactory.getLogger(EmailAnwendung.class); // Attribute private Vector adressbuch = new Vector(); private List erstellteNachrichten = new ArrayList(); private List empfangeneNachrichten = new ArrayList(); private List gesendeteNachrichten = new ArrayList(); private POP3Client pop3client; private SMTPClient smtpclient; private EmailKonto konto; /** * Startet die Email-Anwendung und für Sie jeweils einen Pop3- und Smtp-Client. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (EmailAnwendung), starten()"); super.starten(); pop3client = new POP3Client(this); pop3client.starten(); smtpclient = new SMTPClient(this); smtpclient.starten(); } /** * Methode beendet die EmailAnwendung inkl. der dazu gehörigen smtp und pop3 clients. Dazu wird die Methode der * Superklasse aufgerufen und der Socket geschlossen. */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (EmailAnwendung), beenden()"); super.beenden(); if (pop3client != null) pop3client.beenden(); if (smtpclient != null) smtpclient.beenden(); } /** * ruft die Methode versendeEmail in SMTPClient auf, um eine Email zu versenden. Diese Methode selbst ist nicht * blockierend und übernimmt auch den Verbindungs- auf-,bzw. abbau. * * @param email * @param remoteServerIP */ public void versendeEmail(String remoteServerIP, Email email, String absender) { String rcpts = EmailUtils.addressEntryListToString(email.getEmpfaenger()); if (email.getCc().size() > 0) { rcpts += ","; rcpts += EmailUtils.addressEntryListToString(email.getCc()); } if (email.getBcc().size() > 0) { rcpts += ","; rcpts += EmailUtils.addressEntryListToString(email.getBcc()); } smtpclient.versendeEmail(remoteServerIP, email, absender, rcpts); } /** * * @param emailAdresse * @param benutzername * @param passwort * @param pop3Port * @param pop3Server */ public void emailsAbholenEmails(String benutzername, String passwort, String pop3Port, String pop3Server) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (EmailAnwendung), emailsAbholenEmails(" + benutzername + "," + passwort + "," + pop3Port + "," + pop3Server + ")"); pop3client.emailsHolen(pop3Server, pop3Port, benutzername, passwort); } public boolean kontaktHinzufuegen(String name, String vorname, String strasse, int hausnr, int plz, String wohnort, String email, String telefon) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailAnwendung), kontaktHinzufuegen(" + name + "," + vorname + "," + strasse + "," + hausnr + "," + plz + "," + wohnort + "," + email + "," + telefon + ")"); if (EingabenUeberpruefung.isGueltig(name, EingabenUeberpruefung.musterMindEinZeichen) && EingabenUeberpruefung.isGueltig(vorname, EingabenUeberpruefung.musterMindEinZeichen) && EingabenUeberpruefung.isGueltig(email, EingabenUeberpruefung.musterEmailAdresse)) { try { Kontakt kontaktNeu = new Kontakt(); kontaktNeu.setName(name); kontaktNeu.setVorname(vorname); kontaktNeu.setStrasse(strasse); kontaktNeu.setHausnr(hausnr); kontaktNeu.setPlz(plz); kontaktNeu.setWohnort(wohnort); kontaktNeu.setEmail(email); kontaktNeu.setTelefon(telefon); getAdressbuch().add(kontaktNeu); } catch (Exception e) { LOG.debug("", e); return false; } } else { return false; } return true; } public boolean kontaktLoeschen(String name, String vorname, String email) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailAnwendung), kontaktLoeschen(" + name + "," + vorname + "," + email + ")"); if (EingabenUeberpruefung.isGueltig(name, EingabenUeberpruefung.musterMindEinZeichen) && EingabenUeberpruefung.isGueltig(vorname, EingabenUeberpruefung.musterMindEinZeichen) && EingabenUeberpruefung.isGueltig(email, EingabenUeberpruefung.musterEmailAdresse)) { for (Kontakt kontakt : adressbuch) { if (email.equalsIgnoreCase(kontakt.getEmail())) { if (name.equals(kontakt.getName()) && vorname.equals(kontakt.getVorname())) { adressbuch.remove(kontakt); return true; } } } } return false; } public void speichern() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailAnwendung), speichern()"); Datei datei = new Datei(); datei.setDateiInhalt(konto.toString()); datei.setName("konten.txt"); datei.setDateiTyp("text/txt"); getSystemSoftware().getDateisystem().speicherDatei(getSystemSoftware().getDateisystem().getRoot(), datei); } public void laden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailAnwendung), laden()"); Datei datei = getSystemSoftware().getDateisystem().holeDatei(getSystemSoftware().getDateisystem().getRoot(), "konten.txt"); if (datei != null) { String kontenString = datei.getDateiInhalt(); String[] konten = kontenString.split("\n"); for (int i = 0; i < konten.length; i++) { this.konto = new EmailKonto(konten[i]); } } } public POP3Client holePOP3Client() { return pop3client; } public Vector getAdressbuch() { return adressbuch; } public void setAdressbuch(Vector adressbuch) { this.adressbuch = adressbuch; } public List getEmpfangeneNachrichten() { return empfangeneNachrichten; } public void removeReceivedMail(int index) { if (empfangeneNachrichten.size() > index && 0 <= index) { empfangeneNachrichten.remove(index); } } public void setEmpfangeneNachrichten(List empfangeneNachrichten) { this.empfangeneNachrichten = empfangeneNachrichten; } public List getErstellteNachrichten() { return erstellteNachrichten; } public void setErstellteNachrichten(List erstellteNachrichten) { this.erstellteNachrichten = erstellteNachrichten; } public List getGesendeteNachrichten() { return gesendeteNachrichten; } public void addGesendeteNachricht(Email gesendeteNachricht) { if (!gesendeteNachrichten.contains(gesendeteNachricht)) { this.gesendeteNachrichten.add(gesendeteNachricht); } } public void removeSentMail(int index) { if (gesendeteNachrichten.size() > index && 0 <= index) { gesendeteNachrichten.remove(index); } } public void setGesendeteNachrichten(List gesendeteNachrichten) { this.gesendeteNachrichten = gesendeteNachrichten; } public Map holeKontoListe() { if (konto != null) { return Collections.singletonMap(this.konto.getBenutzername(), this.konto); } return Collections. emptyMap(); } public void setzeKonto(EmailKonto konto) { this.konto = konto; } public void setzeKontoListe(Map kontoListe) { if (!kontoListe.isEmpty()) { this.konto = kontoListe.values().toArray(new EmailKonto[kontoListe.size()])[0]; } } } filius-2.9.4+ds/src/main/java/filius/software/email/EmailKonto.java000066400000000000000000000122351506574241500252140ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Andre Asschoff. * */ public class EmailKonto { private static Logger LOG = LoggerFactory.getLogger(EmailKonto.class); private String benutzername; private String passwort; private String nachname = ""; private String vorname = ""; private String pop3server, smtpserver, pop3port, smtpport; private String emailAdresse; private List nachrichten = new LinkedList(); public EmailKonto() {} /* Liest durch Semicolon getrennte Werte ein */ public EmailKonto(String kontoString) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (EmailKonto), constr: EmailKonto(" + kontoString + ")"); String[] teile = kontoString.split(";"); if (teile.length > 8) { pop3server = teile[0]; smtpserver = teile[1]; pop3port = teile[2]; smtpport = teile[3]; benutzername = teile[4]; passwort = teile[5]; nachname = teile[6]; vorname = teile[7]; emailAdresse = teile[8]; } } public String toString() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (EmailKonto), toString()"); String ergebnis = "" + pop3server + ";" + smtpserver + ";" + pop3port + ";" + smtpport + ";" + benutzername + ";" + passwort + ";" + nachname + ";" + vorname + ";" + emailAdresse; return ergebnis; } public String getBenutzername() { return benutzername; } public String getPasswort() { return passwort; } public List getNachrichten() { return nachrichten; } public void setBenutzername(String benutzername) { this.benutzername = benutzername; } public void setPasswort(String passwort) { this.passwort = passwort; } public void setNachrichten(List nachrichten) { this.nachrichten = nachrichten; } public String getNachname() { return nachname; } public void setNachname(String nachname) { if (nachname != null) this.nachname = nachname; else this.nachname = ""; } public String getVorname() { return vorname; } public void setVorname(String vorname) { if (vorname != null) this.vorname = vorname; else this.vorname = ""; } /** * @return the pop3port */ public String getPop3port() { return pop3port; } /** * @param pop3port * the pop3port to set */ public void setPop3port(String pop3port) { this.pop3port = pop3port; } /** * @return the pop3server */ public String getPop3server() { return pop3server; } /** * @param pop3server * the pop3server to set */ public void setPop3server(String pop3server) { this.pop3server = pop3server; } /** * @return the smtpport */ public String getSmtpport() { return smtpport; } /** * @param smtpport * the smtpport to set */ public void setSmtpport(String smtpport) { this.smtpport = smtpport; } /** * @return the smtpserver */ public String getSmtpserver() { return smtpserver; } /** * @param smtpserver * the smtpserver to set */ public void setSmtpserver(String smtpserver) { this.smtpserver = smtpserver; } /** * @return the emailAdresse */ public String getEmailAdresse() { return emailAdresse; } /** * @param emailAdresse * the emailAdresse to set */ public void setEmailAdresse(String emailAdresse) { this.emailAdresse = emailAdresse; } } filius-2.9.4+ds/src/main/java/filius/software/email/EmailServer.java000066400000000000000000000567101506574241500253760ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.text.DateFormat; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.CreateAccountException; import filius.exception.DeleteAccountException; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; /** * * @author Andre Asschoff * * Der emailServer muss wohl neu aufgerollt werden, da ich hier überhaupt kein Thread brauche. Dies wird durch * POP3- und SMTPServer realisiert. Daher wird der nun überarbeitet. 13.12.2006 */ public class EmailServer extends Anwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(EmailServer.class); public static final String LINE_SEPARATOR = "----"; private List listeBenutzerkonten = new LinkedList(); private String mailDomain = "filius.de"; private POP3Server pop3; private SMTPServer smtp; /** * Dieses Attribut gibt an, ob der Server bereit ist, auf eingehende Verbindungsanfragen zu antworten. */ private boolean aktiv = false; private DefaultMutableTreeNode verzeichnis; // Konstruktoren // keine gesondert implementierten /** * Ob der Server eingehende Verbindungsanfragen annmimmt. Das gilt sowohl fuer SMTP wie auch fuer POP3 */ public boolean isAktiv() { return aktiv; } /** * Ob der Server eingehende Verbindungsanfragen annmimmt. Das gilt sowohl fuer SMTP wie auch fuer POP3.
* In dieser Methode wird also sowohl das Attribut dieser Klasse gesetzt, das fuer SMTP und POP3 gilt, wie auch die * jeweiligen Attribute des POP3Server und des SMTPServer. */ public void setAktiv(boolean aktiv) { this.aktiv = aktiv; if (pop3 != null) pop3.setAktiv(aktiv); if (smtp != null) smtp.setAktiv(aktiv); } // Methoden /** * Hier wird die ueberladene Methode der Oberklasse aufgerufen, die Benutzerkonten gespeichert und POP3Server und * SMTPServer beendet. */ public void beenden() { LOG.debug( "INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), beenden()"); super.beenden(); kontenSpeichern(); if (pop3 != null) pop3.beenden(); if (smtp != null) smtp.beenden(); } /** * Diese Methode wird beim Wechsel vom Entwurfs- in den Aktionsmodus aufgerufen. Hier wird zuerst die ueberladene * Methode der Oberklasse aufgerufen. Dann werden die Benutzerkonten aus der Datei des Servers geladen. Danach wird * ein neuer POP3Server und ein neuer SMTPServer erzeugt und initialisiert. D. h., dass sie auch deren * starten()-Methode aufgerufen wird und das Attribut 'aktiv' gesetzt wird, das angibt, ob ein Server auf eingehende * Verbindungsanfragen wartet. */ public void starten() { LOG.debug( "INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), starten()"); super.starten(); Datei konten = getSystemSoftware().getDateisystem().holeDatei(verzeichnis, "konten.txt"); if (konten == null) { konten = new Datei("konten.txt", "txt", ""); getSystemSoftware().getDateisystem().speicherDatei(verzeichnis, konten); } kontenLaden(); pop3 = new POP3Server(110, this); pop3.setSystemSoftware(getSystemSoftware()); pop3.setAktiv(aktiv); pop3.starten(); smtp = new SMTPServer(25, this); smtp.setSystemSoftware(getSystemSoftware()); smtp.setAktiv(aktiv); smtp.starten(); } /** * FUNKTIONIERT In dieser Methode wird ein neuer Benutzer hinzugefuegt, d.h. ein neues Konto in der Liste * "listeBenutzerkonten" aller Nutzer erstellt. Dazu muessen der Methode alle für einen Account relevanten Daten * uebergeben werden. Zunaechst wird dann abgefragt, ob es in dieser Liste bereits den Benutzernamen gibt, den der * Client für sein Konto moechte, wenn ja, gibts einen Fehler, wenn nein, ist er akzeptiert. Hier ist darauf zu * achten, dass sich der Benutzername aus dem benutzernamen + "@" + Name des Email- Servers zusammensetzt. Dies muss * explizizt abgefragt werden (siehe letzter Satz) Danach muss abgefragt werden, ob nicht alle anderen Felder wie * Vorname, Strasse, usw. auch bereits einem existierenden Kontakt in der Liste entsprechen. Unterscheiden sich * diese ganzen sekundaeren Angaben in mindestens einem Punkt, kann der Kontakt schliesslich vervollstaendigt und * der "listeBenutzerkonten" hinzugefuegt werden. Der Benutzername ent- spricht dann dem gewuenschten Begriff oder * Namen gefolgt von einem "@" und dem Namen des EmailServers. * * @param benutzername * @param passwort * @param nachname * @param vorname * @return boolean * @throws CreateAccountException */ public boolean benutzerHinzufuegen(String benutzername, String passwort, String nachname, String vorname) throws CreateAccountException { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), benutzerHinzufuegen(" + benutzername + "," + passwort + "," + nachname + "," + vorname + ")"); try { EmailKonto konto = new EmailKonto(); for (EmailKonto kontoAusListe : getListeBenutzerkonten()) { if (kontoAusListe.getBenutzername().equalsIgnoreCase(benutzername)) { return false; } else if (kontoAusListe.getBenutzername().equals(benutzername) && (kontoAusListe.getNachname().equalsIgnoreCase(nachname)) && (kontoAusListe.getVorname().equals(vorname))) { return false; } else if ((kontoAusListe.getNachname().equals("")) || (kontoAusListe.getVorname().equals(""))) { return false; } } konto.setBenutzername(benutzername); konto.setPasswort(passwort); konto.setNachname(nachname); konto.setVorname(vorname); synchronized (getListeBenutzerkonten()) { getListeBenutzerkonten().add(konto); } benachrichtigeBeobachter(); } catch (Exception e) { throw new CreateAccountException("-ERR This account could not be created. Please try again!"); } kontenSpeichern(); return true; } /** * FUNKIONIERT Diese Methode loescht einen Benutzeraccount aus der Liste "listeBenutzerkonten" des EmailServers. * Dazu wird die gesamte Liste durchlaufen und nach dem entsprechenden Benutzernamen gesucht, der geloescht werden * soll. Danach wird das Passwort abgefragt, die auch als Parameter der Methode uebergeben werden. Sind beide * richtig, wird das Konto aus der Liste entfernt. Klappt das wird true zurückgegeben, andernfalls eine Exception * geworfen. Hier muss auch wieder beachtet werden dass sich der vollständige Benutzername aus dem eigentlichen * benutzernamen, den der User angibt, + "@" + Name des EmailServers ergibt. Der User muss aber nur seinen * Benutzernamen eingeben, der wird dann vervollständigt. * * @param benutzername * @param passwort * @return boolean * @throws DeleteAccountException */ public boolean kontoLoeschen(String benutzername, String passwort) throws DeleteAccountException { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), kontoLoeschen(" + benutzername + "," + passwort + ")"); try { for (EmailKonto konto : getListeBenutzerkonten()) { if (konto.getBenutzername().equals(benutzername)) { if (konto.getPasswort().equals(passwort)) { return kontoLoeschen(konto); } } } } catch (Exception e) { throw new DeleteAccountException(messages.getString("sw_emailserver_msg1")); } return false; } public boolean kontoLoeschen(EmailKonto konto) { synchronized (getListeBenutzerkonten()) { getListeBenutzerkonten().remove(konto); } kontenSpeichern(); benachrichtigeBeobachter(); return true; } /** * FUNKTIONIERT Hier wird ein EmailKonto zu einer bestehenden Verbindung gesucht. Es wird eine VerbindungsId, die * beim Aufbau einer neuen Verbindung gespeichert wird, uebergeben. Mit ihr wird zunaechst die Liste aller Ver- * bindungen durchlaufen, und abgefragt, ob es eine Verbindung mit dieser uebergebenen Id gibt. Trifft das zu, wird * aus ihr der Benuztername(bname) und das Passwort herausgelesen und temporaer in der Methode ge- speichert. Mit * diesen beiden nun wird die "listeBenutzerkonten", die Liste aller EmailPostfächer, bzw. registrierter Benutzer * dieses EmailServers durchlaufen, um zu suchen, ob es einen Nutzer gibt, der dieses Konto mit dem bname und * Passwort hat. Gibt es das, wird dieses Konto zurueckgegeben. * * @param verbindungsId * @return EmailKonto */ public EmailKonto sucheKonto(String benName, String passwd) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), sucheKonto(" + benName + "," + passwd + ")"); for (EmailKonto konto : getListeBenutzerkonten()) { if (konto.getBenutzername().equalsIgnoreCase(benName) && konto.getPasswort().equals(passwd)) { return konto; } } return null; } /** * Methode, die das Konto anhand des Namens sucht. * * @param benName * @return */ public EmailKonto sucheKonto(String benName) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), sucheKonto(" + benName + ")"); for (EmailKonto tempKonto : listeBenutzerkonten) { if (tempKonto.getBenutzername().equalsIgnoreCase(benName)) { return tempKonto; } } return null; } /** * NACHTRAG 2008: Methode ist void, gibt aber boolean erfolg aus, außerdem wird sie in "nachrichtenKommunikation" * aufgerufen, die selbst keinerlei Aufgabe mehr hat! Hier werden die Benutzerkonten zunaechst in einer Datei * gespeichert, anschließend in das virtuelle Baumverzeichnis eingefuegt * */ public void kontenSpeichern() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), kontenSpeichern()"); String tmp = listeBenutzerkontenZuString(listeBenutzerkonten); Datei konten = getSystemSoftware().getDateisystem().holeDatei(verzeichnis, "konten.txt"); konten.setDateiInhalt(tmp); } private String nltobr(String originalText) { return originalText.replaceAll("\n", "|"); } private String brtonl(String originalText) { return originalText.replaceAll("\\|", "\n"); } private String undoSpecialChar(String escapeStr) { String result = escapeStr.replaceAll("&&00036&&", "\\$"); result = result.replaceAll("&&00059&&", ";"); result = result.replaceAll("&&00124&&", "\\|"); result = result.replaceAll("&&00035&&", "\\#"); return result; } private String replaceSpecialChar(String mailtext) { String result = mailtext.replaceAll("\\$", "&&00036&&"); result = result.replaceAll(";", "&&00059&&"); result = result.replaceAll("\\|", "&&00124&&"); result = result.replaceAll("#", "&&00035&&"); return result; } /** * NACHTRAG 2008: 2te "while-Schleife" scheint ueberfluessig, scheint dennoch zu gehen. Hier wird ein Benutzerkonto * aus der Liste der Benutzerkonten in einen String umgewandelt. Der String hat die Form * benutzername+kontoDomain+passwort+nachname+vorname+nachrichten. Dies brauche ich fuer SPEICHERUNG * * @param benutzerkonten * @return */ private String listeBenutzerkontenZuString(List benutzerkonten) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), listeBenutzerkontenZuString(" + benutzerkonten + ")"); String ergebnis = ""; for (EmailKonto konto : benutzerkonten) { String emailStr = ""; for (Email email : konto.getNachrichten()) { emailStr = emailStr + "#" + (email.getAbsender() != null ? email.getAbsender() : "") + "$" + EmailUtils.addressEntryListToString(email.getEmpfaenger()) + "$" + EmailUtils.addressEntryListToString(email.getCc()) + "$" + EmailUtils.addressEntryListToString(email.getBcc()) + "$" + email.getDateReceived() + "$" + (email.getBetreff() != null ? email.getBetreff() : "") + "$" + nltobr(replaceSpecialChar(email.getText())); } ergebnis = ergebnis + konto.getBenutzername() + ";" + this.mailDomain + ";" + konto.getPasswort() + ";" + konto.getNachname() + ";" + konto.getVorname() + ";" + emailStr + "\n"; } return ergebnis; } /** * Das brauche ich fuers LADEN, ist der Gegensatz zu listeBenutzerkontenZuString. * * @param speicherung * @return */ private List stringZuListeBenutzerkonten(String speicherung) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), stringZuListeBenutzerkonten(" + speicherung + ")"); List temp = new LinkedList(); String[] strArray; StringTokenizer speicherungTokenizer = new StringTokenizer(speicherung, "\n"); while (speicherungTokenizer.hasMoreTokens()) { EmailKonto konto = new EmailKonto(); String emailKontoInString = speicherungTokenizer.nextToken(); StringTokenizer emailKontoInStringTokenizer = new StringTokenizer(emailKontoInString, ";"); konto.setBenutzername(emailKontoInStringTokenizer.nextToken()); emailKontoInStringTokenizer.nextToken(); konto.setPasswort(emailKontoInStringTokenizer.nextToken()); konto.setNachname(emailKontoInStringTokenizer.nextToken()); konto.setVorname(emailKontoInStringTokenizer.nextToken()); if (emailKontoInStringTokenizer.hasMoreTokens()) { String nachrichten = emailKontoInStringTokenizer.nextToken(); try { String[] nachrichtenArray = nachrichten.split("#"); for (int i = 0; i < nachrichtenArray.length; i++) { strArray = nachrichtenArray[i].split("\\$"); if (strArray.length == 7) { Email email = new Email(); email.setAbsender(strArray[0]); email.setEmpfaenger(EmailUtils.stringToAddressEntryList(strArray[1])); email.setCc(EmailUtils.stringToAddressEntryList(strArray[2])); email.setBcc(EmailUtils.stringToAddressEntryList(strArray[3])); email.setDateReceived(strArray[4]); email.setBetreff(strArray[5]); email.setText(undoSpecialChar(brtonl(strArray[6]))); konto.getNachrichten().add(email); } } } catch (Exception e) { LOG.debug("", e); } } temp.add(konto); } return temp; } /** Hier werden die Benutzerkonten wieder geladen */ public void kontenLaden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), kontenLaden()"); Datei konten = getSystemSoftware().getDateisystem().holeDatei(verzeichnis, "konten.txt"); if (konten != null) { setListeBenutzerkonten(stringZuListeBenutzerkonten(konten.getDateiInhalt())); } else { LOG.error("Konten laden fehlgeschlagen"); } } public void setSystemSoftware(InternetKnotenBetriebssystem bs) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), setSystemSoftware(" + bs + ")"); super.setSystemSoftware(bs); getSystemSoftware().getDateisystem().erstelleVerzeichnis(getSystemSoftware().getDateisystem().getRoot(), "mailserver"); this.verzeichnis = Dateisystem.verzeichnisKnoten(getSystemSoftware().getDateisystem().getRoot(), "mailserver"); } public synchronized void setListeBenutzerkonten(List listeBenutzerkonten) { this.listeBenutzerkonten = listeBenutzerkonten; } public void setPOP3Server(POP3Server pop3) { this.pop3 = pop3; } public void setSMTPServer(SMTPServer smtp) { this.smtp = smtp; } public synchronized List getListeBenutzerkonten() { return listeBenutzerkonten; } public POP3Server getPOP3Server() { return pop3; } public SMTPServer getSMTPServer() { return smtp; } public POP3Server holePop3() { return pop3; } public SMTPServer holeSmtp() { return smtp; } public String getMailDomain() { return mailDomain; } public void setMailDomain(String mailDomain) { if (mailDomain != null && EingabenUeberpruefung.isGueltig(mailDomain, EingabenUeberpruefung.musterDomain)) { this.mailDomain = mailDomain; } } public void emailWeiterleiten(Email email, String absender, String rcpt) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), emailWeiterleiten(" + email + "," + absender + "," + rcpt + ")"); Object[] args; args = new Object[3]; args[0] = email; args[1] = absender; args[2] = rcpt; ausfuehren("weiterleiten", args); } /** * hier wird die Email versendet, dazu wird der SMTPClient benutzt, dessen verwende Methode aufgerufen wird. 1. * erstellen eines neuen SMTPClienten 2. starten des Clienten 3. versenden Es wird die Email übergeben und genau ein * Empfaenger, an den die Email weiter- geleitet werden soll. Dies wird so oft wiederholt in "verarbeiteEmail", bis * alle Empfaenger, an die weitergeleitet werden soll, durchgelaufen sind.
* Diese Methode blockiert den Thread! * * @param email * @param rcpt */ public void weiterleiten(Email email, String absender, String rcpt) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EmailServer), weiterleiten(" + email + "," + absender + "," + rcpt + ")"); SMTPClient clientFuerWeiterleitung = new SMTPClient(this); clientFuerWeiterleitung.starten(); clientFuerWeiterleitung.versendeEmail(null, email, absender, rcpt); } public void sendUnknownReceiverResponse(Email email, String sender, List unknownRecipients) { if (!sender.equalsIgnoreCase("<>")) { Email unknownRecipientResponse = new Email(); unknownRecipientResponse.setAbsender("noreply@" + mailDomain); unknownRecipientResponse.setEmpfaenger(Arrays.asList(email.getAbsender())); unknownRecipientResponse.setBetreff("Non Delivery Notification"); StringBuilder mailBody = new StringBuilder(); mailBody.append("The mail could not be delivered either because user or mail domain is unknown.") .append("\n"); mailBody.append("Message could not be delivered to the following recipients:\n"); for (String unknownRecipient : unknownRecipients) { mailBody.append("\t").append(unknownRecipient).append("\n"); } mailBody.append("\nDate: ").append(DateFormat.getInstance().format(new Date())).append("\n"); mailBody.append("Mail-Server: ").append(this.getSystemSoftware().primaryIPAdresse()).append("\n"); mailBody.append("\n______________________________________________________________________\n"); mailBody.append(email.toString()); unknownRecipientResponse.setText(mailBody.toString()); AddressEntry senderAddress = new AddressEntry(sender); if (pruefeAufSelbeDomain(senderAddress.getMailAddress())) { EmailKonto empfaengerKonto = sucheKonto(senderAddress.getMailAddress().split("@")[0]); empfaengerKonto.getNachrichten().add(unknownRecipientResponse); benachrichtigeBeobachter(messages.getString("sw_smtpmitarbeiter_msg12") + " " + empfaengerKonto.getBenutzername() + " " + messages.getString("sw_smtpmitarbeiter_msg13")); } else { this.weiterleiten(unknownRecipientResponse, "<>", senderAddress.toString()); } } } /** * Methode prüft, ob die Domain, an die die Email geschickt werden soll, mit der des eigenen MailServers * übereinstimmt. * * @param str * @return boolean */ public boolean pruefeAufSelbeDomain(String str) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + ", pruefeAufSelbeDomain(" + str + ")"); String[] emailAdresse = str.split("@"); String pruefdomain = emailAdresse[1].substring(0, emailAdresse[1].length()); if (mailDomain.equals(pruefdomain)) { return true; } return false; } } filius-2.9.4+ds/src/main/java/filius/software/email/EmailUtils.java000066400000000000000000000047021506574241500252220ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.LinkedList; import java.util.List; /** * @author stefan * */ public class EmailUtils { /** * Wandelt einen String in eine Liste von Adressen, jeder der durch Kommata getrennten Werte des Strings wird ein * eigenes Listenelement. */ public static List stringToAddressEntryList(String addresses) { List ergebnis = new LinkedList(); String[] argument = addresses.split(","); for (String address : argument) { if (!address.trim().isEmpty()) { ergebnis.add(new AddressEntry(address)); } } return ergebnis; } /** * Diese Mehtode wandelt eine Liste von Adressen in einen String, die einzelnen Listenelemente durch Kommata * getrennt. */ public static String addressEntryListToString(List addresses) { StringBuffer str = new StringBuffer(); for (AddressEntry address : addresses) { str.append(address.toString()); str.append(","); } if (str.length() > 1) { return str.substring(0, str.length() - 1); } else { return ""; } } } filius-2.9.4+ds/src/main/java/filius/software/email/Kontakt.java000066400000000000000000000061301506574241500245620ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Kontakt { private static Logger LOG = LoggerFactory.getLogger(Kontakt.class); // Attribute private String name; private String vorname; private String strasse; private int hausnr; private int plz; private String wohnort; private String email; private String telefon; // Konstruktor(en) public Kontakt() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Kontakt), constr: Kontakt()"); } public Kontakt(String emailAdresse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Kontakt), constr: Kontakt(" + emailAdresse + ")"); this.email = emailAdresse; } // Methoden public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getHausnr() { return hausnr; } public void setHausnr(int hausnr) { this.hausnr = hausnr; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPlz() { return plz; } public void setPlz(int plz) { this.plz = plz; } public String getStrasse() { return strasse; } public void setStrasse(String strasse) { this.strasse = strasse; } public String getTelefon() { return telefon; } public void setTelefon(String telefon) { this.telefon = telefon; } public String getVorname() { return vorname; } public void setVorname(String vorname) { this.vorname = vorname; } public String getWohnort() { return wohnort; } public void setWohnort(String wohnort) { this.wohnort = wohnort; } } filius-2.9.4+ds/src/main/java/filius/software/email/POP3Client.java000066400000000000000000000317231506574241500250350ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.LinkedList; import java.util.ListIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.clientserver.ClientAnwendung; import filius.software.transportschicht.TCPSocket; public class POP3Client extends ClientAnwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(POP3Client.class); private EmailAnwendung anwendung; public POP3Client(EmailAnwendung anwendung) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), constr: POP3Client(" + anwendung + ")"); this.anwendung = anwendung; this.setSystemSoftware(anwendung.getSystemSoftware()); } /** * Diese Methode startet die blockierenden Methoden zum E-Mail-Abruf in dem Thread der Anwendung. Deshalb ist der * Aufruf dieser Methode nicht blockierend ! * * @param pop3Server * @param pop3Port * @param benName * @param pw */ public void emailsHolen(String pop3Server, String pop3Port, String benName, String pw) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), emailsHolen(" + pop3Server + "," + pop3Port + "," + benName + "," + pw + ")"); Object[] args; args = new Object[2]; args[0] = pop3Server; args[1] = new Integer(pop3Port); ausfuehren("initialisiereSocket", args); args = new Object[2]; args[0] = benName; args[1] = pw; ausfuehren("starteVerarbeitung", args); ausfuehren("schliesseSocket", null); } /** * Hier wird ein neuer Socket erzeugt und verbunden * * @param zielAdresse * @param port */ public void initialisiereSocket(String zielAdresse, Integer port) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), initialisiereSocket(" + zielAdresse + "," + port + ")"); try { socket = new TCPSocket(getSystemSoftware(), zielAdresse, port); socket.verbinden(); } catch (Exception e) { LOG.debug("", e); socket = null; anwendung.benachrichtigeBeobachter(e); } } /** * Hier wird das Schema runtergebetet, nach dem Emails vom POP3Server abgeholt werden - benutzerauthentifizierung * * benutzername * passwort - status der Mailbox - emails auflisten - email abrufen * * @return boolean */ public void starteVerarbeitung(String benName, String pw) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), starteVerarbeitung(" + benName + "," + pw + ")"); boolean erfolg = true; if (socket != null && socket.istVerbunden()) { try { erfolg = socket.empfangen().startsWith("+"); if (erfolg) erfolg = eingabeBenutzername(benName); if (erfolg) erfolg = eingabePasswort(pw); if (erfolg) erfolg = alleEmailsAbrufen(); sitzungBeenden(); } catch (Exception e) { LOG.debug("", e); anwendung.benachrichtigeBeobachter(e); } anwendung.benachrichtigeBeobachter(); } } private boolean alleEmailsAbrufen() throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), alleEmailsAbrufen()"); String[] statusteile; int anzahlMails = 0; String auflistung = ""; LinkedList emailIDs = new LinkedList(); statusteile = statusAbrufen().split(" "); if (statusteile.length > 0) { anzahlMails = Integer.parseInt(statusteile[0]); } if (anzahlMails > 0) { // Auflistung aller Emails (Mit Nummer (Leerzeichen) Gre in Bytes) auflistung = emailsAuflisten(); // nach jeder Email wird getrennt String[] zeilen = auflistung.split("\n"); // es wird bei 1 angefangen um das "+OK" zu Beginn der Übertragung // auszusparen for (int i = 1; i < zeilen.length; i++) { String zeile = zeilen[i]; String[] zeichen = zeile.split(" "); emailIDs.add(Integer.parseInt(zeichen[0])); // es wird immer der // Index zur Liste // hinzugefügt } } /* * Aus dem String aus emailsAuflisten wird eine LinkedList der Email-IDs erzeugt. Diese wird iteriert und RETR * fr jede Mail aufgerufen. Die Emails werden als String geliefert */ ListIterator iter = emailIDs.listIterator(); while (iter.hasNext()) { int id = (Integer) iter.next(); emailAbrufen(id); emailLoeschen(id); } return true; } /** * Diese Methode ist blockierend. * */ public void schliesseSocket() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), schliesseSocket()"); if (socket != null) { socket.schliessen(); socket = null; benachrichtigeBeobachter(messages.getString("sw_pop3client_msg1")); } } /** * FUNKTIONIERT * * @param benutzername * @throws Exception */ private boolean eingabeBenutzername(String benutzername) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), eingabeBenutzername(" + benutzername + ")"); if (EingabenUeberpruefung.isGueltig(benutzername, EingabenUeberpruefung.musterMindEinZeichen)) { socket.senden("USER " + benutzername); String empfangen = socket.empfangen(); if (empfangen.startsWith("+OK")) return true; else throw new Exception(empfangen); } else { return false; } } /** * FUNKTIONIERT * * @param passwort * @throws Exception */ private boolean eingabePasswort(String passwort) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), eingabePasswort(" + passwort + ")"); if (EingabenUeberpruefung.isGueltig(passwort, EingabenUeberpruefung.musterMindEinZeichen)) { socket.senden("PASS " + passwort); String z = socket.empfangen(); if (z.startsWith("+OK")) return true; else throw new Exception(z); } else { return false; } } /** * FUNKTIONIERT Hiermit wird der Status der Mailbox abgefragt. Es wird der Befehl pop3-maessig (STAT) an den * POP3Server gesendet. * * @throws Exception * */ private String statusAbrufen() throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), statusAbrufen()"); String antwort, ergebnis; String[] temp; socket.senden("STAT"); antwort = socket.empfangen(); if (antwort.startsWith("+OK")) { temp = antwort.split(" "); ergebnis = temp[1] + " " + temp[2]; return ergebnis; } else { throw new Exception(antwort); } } /** * FUNKTIONIERT Hier wird der Befehl LIST gesendet, einmal ohne Attribut, wenn alle emails aufgelistet werden * sollen, und einmal mit, wenn nur die Email mit dem Index i aufgelistet werden soll. * * @param i * @throws Exception */ private boolean emailsAuflisten(int i) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), emailsAuflisten(" + i + ")"); String ergebnis; socket.senden("LIST " + i); ergebnis = socket.empfangen(); if (ergebnis.startsWith("+OK")) return true; else throw new Exception(ergebnis); } /** * In dieser Methode werden alle Emails aufgelistet. * * @return String * @throws Exception */ private String emailsAuflisten() throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), emailsAuflisten()"); String ergebnis; socket.senden("LIST"); ergebnis = socket.empfangen(); if (ergebnis.startsWith("+OK")) return ergebnis; else throw new Exception(ergebnis); } /** * FUNKTIONIERT ruft eine email vom Server ab. Es wird die Email mit dem als Attribut uebergebenen Index abgerufen. * * @param i * @throws Exception */ private void emailAbrufen(int i) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), emailsAbrufen(" + i + ")"); int pos; Email neMail; socket.senden("RETR " + i); String result = socket.empfangen(); if (result.startsWith("+OK")) { // hier wird das Email-Objekt dem Nachrichtenkonto hinzugefuegt pos = result.indexOf("\n"); if (pos > 0) { neMail = new Email(result.substring(pos + 1)); anwendung.getEmpfangeneNachrichten().add(neMail); } } else { throw new Exception(result); } } /** * FUNKTIONIERT UEBERARBEITEN DENNOCH 2008 Hier wird der Befehl zum loeschen, d.h. eine Email als zu loeschen nach * dem Ende der Sitzung markiert, gesendet. * * @param i * @throws Exception */ private boolean emailLoeschen(int i) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), emailLoeschen(" + i + ")"); String ergebnis; socket.senden("DELE " + i); ergebnis = socket.empfangen(); if (ergebnis.startsWith("+OK")) return true; else throw new Exception(ergebnis); } /** * UEBERARBEITEN Hiermit wird die Sitzung beendet. Das heißt auch, dass der eigene Thread, die Kommunikation beendet * werden muss. * * @throws Exception * */ private boolean sitzungBeenden() throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), sitzungBeenden()"); String ergebnis; socket.senden("QUIT"); ergebnis = socket.empfangen(); if (ergebnis.startsWith("+OK")) return true; else throw new Exception(ergebnis); } /** * FUNKTIONIERT Diese Methode provoziert lediglich einen response vom Server... * */ private String noop() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Client), noop()"); String ergebnis = ""; try { socket.senden("NOOP"); ergebnis = socket.empfangen(); } catch (Exception e) { LOG.debug("", e); ergebnis = "-ERR NOOP failure in Email-Client"; } return ergebnis; } } filius-2.9.4+ds/src/main/java/filius/software/email/POP3Mitarbeiter.java000066400000000000000000000744751506574241500261010ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.ServerMitarbeiter; import filius.software.transportschicht.TCPSocket; /** * * @author Andre Asschoff * * In der POP3 Schicht empfangeDaten gehe ich direkt auf TCP, ueberwache den IncomingPuffer (TCP- * PufferElemente) um den von mir benaetigten String direkt auszulesen und zu bearbeiten. */ public class POP3Mitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(POP3Mitarbeiter.class); private EmailServer emailServer; private String benutzername; private String password; private boolean benAuth = false; private boolean transactionState = false; private boolean authenticationState = true; // solange nicht angemeldet // "true" // das Konto, mit dem gearbeitet wird, auf das man sich angemeldet hat private EmailKonto aktivesKonto; public POP3Mitarbeiter(TCPSocket socket, POP3Server pop3Server) { super(pop3Server, socket); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), constr: POP3Mitarbeiter(" + socket + "," + pop3Server + ")"); this.socket = socket; this.emailServer = pop3Server.holeEmailServer(); emailServer.benachrichtigeBeobachter(EmailServer.LINE_SEPARATOR); sendeAntwort("+OK POP3 server ready"); } @Override protected void verarbeiteNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), verarbeiteNachricht(" + nachricht + ")"); emailServer.benachrichtigeBeobachter(socket.holeZielIPAdresse() + "< " + nachricht); // ist der Befehl fuer den POP3Server (USER, STAT, ...) String befehl = ""; // ist das Array, das die einzelnen Elemente der Clientdaten aufnimmt String[] incoming = new String[3]; // 3 Elemente fuer Befehl, // Information und eins als Toni // Polster incoming = nachricht.split(" "); // das erste Element ist immer der Befehl, bspw. DELE, RETR, STAT... befehl = incoming[0]; // es folgt die Abfrage, welcher Befehl angekommen ist if (befehl.equalsIgnoreCase("USER")) { String antwort = ""; if (!(isTransactionState() && isBenAuth()) && isAuthenticationState()) { antwort = user(incoming[1]); if (isBenAuth()) { benutzername = incoming[1]; } } else { antwort = "-ERR Please enter USER"; } // LOG.debug("Antwort: "+antwort); sendeAntwort(antwort); } else if (befehl.equalsIgnoreCase("PASS")) { String antwort = ""; if (!(isTransactionState()) && isBenAuth()) { antwort = pass(incoming[1]); password = incoming[1]; aktivesKonto = emailServer.sucheKonto(benutzername, password); } else { antwort = "-ERR Please enter PASS"; } sendeAntwort(antwort); } else if (befehl.equalsIgnoreCase("STAT")) { String antwort = stat(aktivesKonto); sendeAntwort(antwort); } else if (befehl.equalsIgnoreCase("LIST")) { if (incoming.length < 1) { // listet die Email mit der nr == attribut auf String antwort = list(Integer.parseInt(incoming[1]), aktivesKonto); sendeAntwort(antwort); } else { // Listet alle Emails auf String antwort = list(aktivesKonto); sendeAntwort(antwort); } } else if (befehl.equalsIgnoreCase("RETR")) { // holt die Email mit der nr == attribut vom Server String antwort = retr(Integer.parseInt(incoming[1]), aktivesKonto); sendeAntwort(antwort); } else if (befehl.equalsIgnoreCase("DELE")) { // loescht Emails vom Server. Die Email wird als geloescht markiert, // bleibt aber ungeloescht, bis zum naechsten update, oder zum // konformen // Ende einer Sitzung. Die zu loeschende Email wird durch das // attibut ref. String antwort = dele(Integer.parseInt(incoming[1]), aktivesKonto); sendeAntwort(antwort); } else if (befehl.equalsIgnoreCase("RSET")) { // setzt alle DELE-Befehle zurueck String antwort = rset(aktivesKonto); sendeAntwort(antwort); } else if (befehl.equalsIgnoreCase("QUIT")) { // beendet die Sitzung String antwort = quit(aktivesKonto); try { sendeAntwort(antwort); schliesseSocket(); } // hier kann ich nicht die Verbindungsexception werfen, das gibt // Fehler... catch (Exception e) { schliesseSocket(); } } else if (befehl.equalsIgnoreCase("NOOP")) { // provoziert lediglich eine Antwort vom Server String antwort = noop(); sendeAntwort(antwort); } } public void schliesseSocket() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), schliesseSocket()"); if (socket != null) { socket.schliessen(); socket = null; beenden(); } } /** * Hier wird der Nutzername ueberprueft. Dazu wird die Methode sucheBenutzer aufgerufen, die ueberprueft, ob es * einen solchen angemeldeten Benutzer gibt. Wenn das zutrifft, wird der boolean setBenutzernameIstTrue auf (true) * gesetzt und dem Client das mitgeteilt. * * @param benutzername * @param verbindungsId */ public String user(String benutzername) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), user(" + benutzername + ")"); String ergebnis = ""; if (sucheBenutzer(benutzername)) { setBenAuth(true); ergebnis = "+OK enter password"; } else { ergebnis = "-ERR user or password wrong"; } return ergebnis; } /** * Es wird ueberprueft, ob das Passwort richtig ist, fuer den zuvor uebertragenen Benutzernamen. * * @param passwort * @param verbindungsId */ public String pass(String passwort) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), pass(" + passwort + ")"); String ergebnis = ""; try { if (pruefePasswort(passwort)) { if (isBenAuth()) { setTransactionState(true); ergebnis = "+OK Mailbox locked and ready"; } } else { ergebnis = "-ERR user or password wrong"; } } catch (Exception e) { LOG.debug("", e); ergebnis = "-ERR user or password wrong"; } return ergebnis; } /** * Diese Methode liefert den Status der Mailbox. Es wird zunaechst die Fktn. des EmailServers aufgerufen, die die * Anzahl der Emails im Postfach zaehlt. Diese Ergebnisse werden in einem int- Array aufgefangen. Groesse und Anzahl * werden dann ausgelesen und in 2 int-Werten gespeichert. Der datenstring enthaelt die auszugebende Message. * Anschlie�end wird der Status durch die Fktn. emailServer.sendeAntwort an den Client uebertragen. * * @param verbindungsId */ public String stat(EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), stat(" + uebergebenesAktivesKonto + ")"); // Main.debug // .println("===========================================STAT - BenName: " // + uebergebenesAktivesKonto.getBenutzername() // + " Passwd: " + uebergebenesAktivesKonto.getPasswort()); String daten = ""; if (isTransactionState()) { try { int[] a = anzahlEmailsImPostfach(uebergebenesAktivesKonto); daten = "+OK " + a[1] + " " + a[0]; } catch (Exception e) { daten = "-ERR please try again"; } } return daten; } /** * Hier werden alle auf dem Server gespeicherten Emails aufgelistet. Zunaechst wird die Methode stat auf- gerufen, * die den Status ausgibt, und anschliessend werden aus dem String-Array die daten fuer alle emails (indexnr und * groesse in bytes) ausgelesen. Die werden dann in einer while-Schleife solange immer jede Email einzeln * ausgegeben, bis alle daten uebertragen wurden. * * @param verbindungsId */ public String list(EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), list(" + uebergebenesAktivesKonto + ")"); String ergebnis = ""; if (isTransactionState()) { try { // zunaechst wird der Status aufgefuehrt, dann folgt das // eigentliche listing... Da ist auch das +OK bei ergebnis = stat(aktivesKonto) + "\n"; String a = emailsAuflisten(uebergebenesAktivesKonto); String[] ergServer = a.split(" "); int i = 0; while (i < ergServer.length) { ergebnis = ergebnis + ergServer[i] + " " + ergServer[i + 1] + "\n"; i = i + 2; } } catch (Exception e) { LOG.debug("", e); ergebnis = "-ERR no such message"; } } return ergebnis; } /** * FUNKTIONIERT Diese Methode verfaehrt ebenso wie die "list", jedoch werden hier nur einmal Daten uebertragen, und * nicht in einer for-Schleife, und zwar fuer die email mit dem index i im String der zurueckgegebenen Emails. * Dafuer wird zunaechst die Methode emailsAuflisten des EmailServers aufgerufen, um alle Emails in einen langen * String zu speichern. Dann wird die Email mit dem index "int i" mit ihrer groesse an den Client gesendet. * * @param i * @param verbindungsId */ public String list(int i, EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), list(" + i + "," + uebergebenesAktivesKonto + ")"); String ergebnis = ""; if (isTransactionState()) { try { String a = emailsAuflisten(uebergebenesAktivesKonto); String[] ergServer = a.split(" "); int j = 0; while (j < ergServer.length) { // wenn der Index der Email (j) mit der gewuenschten // Abfragenummer (i) uebereinstimmt if (j == i) { ergebnis = "+OK " + ergServer[i] + " " + ergServer[i + 1]; } i = i + 2; } } catch (Exception e) { LOG.debug("", e); ergebnis = "-ERR no such message"; } } return ergebnis; } /** * FUNKTIONIERT Diese Methode dient zum abrufen der Emails vom Server, hiermit werden die Emails an den Client * geschickt. Wir bekommen immer nur genau eine email vom Server, eben jene mit Index i. Dieser Email Attribute * werden einzelnen Strings zugeordnet. Dieser wird zu einem einzig langen String verarbeitet, der sinn- voll * getrennt wird. Dieser lange String wird dann an den Client gesendet, und zuvor die Statusaus- gabe. Klappt das * nicht, wird ein Fehler geworfen. Es wird auf jeden Fall noch abgefragt, ob die Email bereits als zu loeschen * markiert wurde, wenn ja, dann darf sie nicht mehr abgesendet werden. * * @param i * @param verbindungsId */ public String retr(int i, EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), retr(" + i + "," + uebergebenesAktivesKonto + ")"); Email abgerufeneEmail = emailsAbrufen(i, uebergebenesAktivesKonto); String ergebnis = ""; if (isTransactionState()) { try { if (abgerufeneEmail.getDelete() == true || abgerufeneEmail.getNeu() == false) { ergebnis = "-ERR no such message"; } else { ergebnis = "+OK message follows " + "\n" + abgerufeneEmail.toString(); abgerufeneEmail.setNeu(false); } } catch (Exception e) { LOG.debug("", e); ergebnis = "-ERR no such message"; } } return ergebnis; } /** * FUNKTIONIERT Hiermit wird die Methode des EmailServers aufgerufen, dass deine Email, die der Client wuenscht, * geloescht wird. Wenn diese Methode erfolgreich ausgefuehrt werden konnte, dann wird die Antwort an den Client * uebermittelt. Sonst erfolgt eine Exception, mit der ent- sprechenden Antwort fuer den Client. * * @param i * @param verbindungsId */ public String dele(int i, EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), dele(" + i + "," + uebergebenesAktivesKonto + ")"); String ergebnis = ""; if (isTransactionState()) { try { if (emailsAlsGeloeschtMarkieren(i, uebergebenesAktivesKonto) == true) { ergebnis = "+OK message marked for delete"; } } catch (Exception e) { LOG.debug("", e); ergebnis = "-ERR no such message"; } } return ergebnis; } /** * FUNKTIONIERT In dieser Methode wird der Status der Emails, ob diese nach dem Abmelden geloescht werden sollen * oder nicht korrigiert. Hier wird das Nachrichtenkonto des angemeldeten Users ueber- prueft, ob sich darin * irgendwelche Mails befinden, die geloescht werden sollen (getDelete = true). Dieser Wert aller Emails in diesem * Postfach wird dann auf false gesetzt, sodass nach dem Abmelden keine Email geloescht wird. Dann wird dem Client * eine Nachricht ueber den erfolgreichen Vorgang uebermittelt. * * @param verbindungsId */ public String rset(EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), rset(" + uebergebenesAktivesKonto + ")"); String ergebnis = ""; if (isTransactionState()) { try { for (ListIterator iter = uebergebenesAktivesKonto.getNachrichten().listIterator(); iter .hasNext();) { Email email = (Email) iter.next(); email.setDelete(false); } ergebnis = "+OK"; } catch (Exception e) { LOG.debug("", e); // Der Server gibt dem Client, auch wenn nichts geaendert werden // konnte, keine negative // Antwort mit -ERR. } } return ergebnis; } /** * NACHTRAG 2008: Nicht wird die Verbindung zwischen Client und Server (das ja vom POP3Server uebernommen wird) * getrennt. Nur die Emails werden geloescht. In dieser Methode wird die Sitzung zwischen POP3Server und Client * geschlossen. Die als zu loeschen markierten Emails werden endgueltig beseitigt, und die Verbindung aus der Liste * des EmailServers entfernt. Anschliessend erfolgt eine Ausgabe des Servers und eine Anweisung an den Client, * selbst die Verbindung auch zu loeschen, wird uebermittelt. * * @param verbindungsId */ public String quit(EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), quit(" + uebergebenesAktivesKonto + ")"); String ergebnis = ""; if (isTransactionState()) { try { for (int idx = uebergebenesAktivesKonto.getNachrichten().size() - 1; idx >= 0; idx--) { if (((Email) uebergebenesAktivesKonto.getNachrichten().get(idx)).getDelete()) { uebergebenesAktivesKonto.getNachrichten().remove(idx); } } ergebnis = "+OK"; } catch (Exception e) { LOG.debug("EXCEPTION (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), quit: "); LOG.debug("", e); } } benutzername = ""; password = ""; emailServer.benachrichtigeBeobachter(); return ergebnis; } /** * 2008 immernoch? FUNKTIONIERT... aber VERBINDUNGS-TIMEOUT noop soll lediglich, vom Client aus gesendet, beim * EmailServer ein +OK provozieren, um zu sehen, ob man noch mit dem Server verbunden ist, und damit im Falle evtl. * Inaktivität kein autologoff gestartet wird. Beim Anstossen dieser Methode wird einfach versucht ein System.out. * zurueckzugeben, wenn wir noch angemeldet sind, ansonsten wirft er einen Fehler. * * @return boolean */ public String noop() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), noop()"); String ergebnis = ""; try { if (isTransactionState() == true) { // es muss das Verbindungstimeout resettet werden!!!Macht das // Carsten in den Sockets? ergebnis = "+OK"; } else { ergebnis = "-ERR unknown command"; } } catch (Exception e) { ergebnis = "-ERR unknown command"; LOG.debug("", e); } return ergebnis; } /** * FUNKTIONIERT In dieser Methode wird ueberprueft, ob in der LinkedList aller registrierten Nutzer dieses * EmailServers ein Konto existiert, das auf diesen Benutzernamen passt, also ob es ein Konto gibt, das diesen Namen * hat. * * @param benutzernamen * @return boolean */ public boolean sucheBenutzer(String benutzernamen) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), sucheBenutzer(" + benutzernamen + ")"); for (ListIterator iter = emailServer.getListeBenutzerkonten().listIterator(); iter.hasNext();) { EmailKonto konto = (EmailKonto) iter.next(); if ((konto.getBenutzername().equalsIgnoreCase(benutzernamen))) { return true; } } return false; } /** * FUNKTIONIERT diese Methode prueft das Passwort, dass von der TCP Schicht uebermittelt, vom Lauscher Thread * aufgefangen und uebergeben, vom Client gesendet wurde, und schaut, ob es in der vom EmailServer verwalteten * LinkedList aller auf dem Server registierten Benutzerkonten in genau dem Konto vorhanden ist, ueber dessen * Benutzernamen sich der Client auch ange- meldet hat oder will. * * @param passwort * @return boolean */ public boolean pruefePasswort(String passwort) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), pruefePasswort(" + passwort + ")"); boolean erfolg = false; EmailKonto nowKonto = emailServer.sucheKonto(benutzername); if (nowKonto != null && nowKonto.getPasswort().equals(passwort)) { erfolg = true; } return erfolg; } /** * FUNKTIONIERT In dieser Methode wird versucht, eine Email mit dem Index, den der USER angibt, als zu loeschen * markieren. Diese soll dann nicht mehr aufgefuehrt werden, und wird zum Schluss der Sitzung wirklich geloescht. * * @param i * @return boolean */ public boolean emailsAlsGeloeschtMarkieren(int i, EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), emailsAlsGeloeschtMarkieren(" + i + "," + uebergebenesAktivesKonto + ")"); List emails = uebergebenesAktivesKonto.getNachrichten(); try { if (i >= emails.size()) { return false; } emails.get(i).setDelete(true); emailServer.kontenSpeichern(); return true; } catch (Exception e) { LOG.debug("", e); emailServer.kontenSpeichern(); } return false; } /** * FUNKTIONIERT Hier wird zunaechst eine LL erstellt, die die gewuenschten Emails enthaelt und spaeter zurueck * gegeben wird. Anschliessend wird das gesuchte Konto mit Hilfe der Methode sucheKontoZuVerb er- mittelt. * Anschlie�end wird die LL mit der Liste aller Nachrichten dieses Kontos gefuellt. Sollen alle Emails abgerufen * werden (i == 0), so werden lediglich der Wert, ob diese Emails schon einmal abgerufen wurden (der in jeder Email * gespeichert ist (boolean isNeu)), auf false gesetzt und die LL emails kann einfach zurueckgegeben werden. Wird * aber eine bestimmte Email verlangt (i != 0), so wird die Email mit dem Index i, der beim Speichern in die LL ja * immer noch derselbe ist, wie der, als der Status oder aufgelistet wurde, zurueckgegeben. Dazu wird der Wert isNeu * auf false gesetzt, die LL emails geleert (.clear()) und dann die gewuenschte Email in der LL gespeichert als * einzigstes Element. * * @param verbindungsId * @return LinkedList */ public Email emailsAbrufen(int i, EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), emailsAbrufen(" + i + "," + uebergebenesAktivesKonto + ")"); List gespeicherteEmails = new LinkedList(); Email abgerufeneEmail = new Email(); try { gespeicherteEmails = uebergebenesAktivesKonto.getNachrichten(); abgerufeneEmail = gespeicherteEmails.get(i); } catch (Exception e) { LOG.debug("", e); emailServer.kontenSpeichern(); } return abgerufeneEmail; } /** * FUNKTIONIERT BESCHREIBUNG UEBERARBEITEN Hier werden die Emails der Reihe nach aufgelistet und w�hrenddessen * indiziert. Zunaechst wird ein Konto zu der (bestehenden?) Verbindung gesucht, und dann abgefragt, ob diese Email * auch nicht geloescht werden soll. Wenn nein, wird anschliessend die Groesse jeder Email einzeln ermittelt. Der * Index der Email im Konto (fortlaufend nummeriert ohne besonderes) und die L�nge der Email in bytes wird dann in * einem 2-dimensionalen Array gesichert. Hier liegt auch die Groesse des Postfaches. Das Array kann max. 1000 * verschiedene Emails erfassen. Das Array ist lediglich 2 Felder breit, fuer Index und Email-Groesse(bytes). * * @param verbindungsId * @return String */ public String emailsAuflisten(EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), emailsAuflisten(" + uebergebenesAktivesKonto + ")"); StringBuilder str = new StringBuilder(); try { int index = 0; for (Email email : uebergebenesAktivesKonto.getNachrichten()) { if (email.getDelete() == false) { // Postfachgroesse in Oktetten, hier in bytes String emailgroesse = email.getText() + email.getAbsender() + email.getBetreff() + email.getEmpfaenger() + email.getDateReceived(); str.append(index++); str.append(" "); str.append(emailgroesse.length()); str.append(" "); } } } catch (Exception e) { LOG.debug("", e); emailServer.kontenSpeichern(); } return str.toString(); } /** * FUNKTIONIERT Es wird die Anzahl aller Emails, die in einem EmailKonto gespeichert sind ausgegeben. Dazu wird * zunaechst geschaut, ob der User, der anfragt, mit dieser VerbindungsId ein EmailKonto hat. Dafuer wird in der * (temp.) Verbindung nach dem Username und Passwort gefragt, mit dem er sich angemeldet hat. Gibt es ein solches * Konto, das mit der Methode "sucheKontoZuVerbindung" gesucht wird, wird fuer jede Email in dem Postfach die * L�nge ihrer Bytes gemessen und aufaddiert zu einem langen String. Hat man dann die gesamte L�nge aller * Strings im Postfach, wird diese, und die gesamtanzahl aller Emails im Postfach in einem 2-dimensionalen int- * Array der Gr��e 1 zurueckgegeben. Vor der Erzeugung des Strings wird natuerlich noch abgefragt, ob die Email, * die man gerade betrachtet, nicht zum loeschen ist (getDelete). Wenn sie nicht geloescht werden soll, dann wird * sie dem String hinzugefuegt. * * @return int [][] */ public int[] anzahlEmailsImPostfach(EmailKonto uebergebenesAktivesKonto) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), anzahlEmailsImPostfach(" + uebergebenesAktivesKonto + ")"); String str = ""; int[] a = new int[2]; int i = 0; a[0] = 0; a[1] = 0; try { for (Email email : uebergebenesAktivesKonto.getNachrichten()) { if (email.getDelete() != true) { str = str + email.getText() + email.getAbsender() + email.getBetreff() + email.getEmpfaenger() + email.getDateReceived(); i++; } } a[0] = str.length(); a[1] = i; } catch (Exception e) { LOG.debug("", e); emailServer.kontenSpeichern(); } return a; } /** * FUNKTIONIERT Eine Methode zum Senden von Antworten an den Client, damit dieser weiß, ueber welche Verbindung die * Kommunikation jetzt laeuft. Dazu wird die Id der Verbindung, die in der Liste aller Verbindungen beim EmailServer * gespeichert sind, uebergeben, und die Daten, die dem Client uebermittelt werden sollen. Dafuer wird in der Liste * aller Verbindungen beim EmailServer zunaechst gesucht, ob die geforderte Ver- bindung mit dieser (eindeutigen) Id * existiert. Wenn ja uebernimmt man von ihr die benoetigten Daten, um die nachrichten an den Client zu senden. Die * daten sind einem konformen Schema entnommen. So werden z.B. fuer die Benachrichtigung eines Clients, dass eine * neue Verbindung aufgebaut wurde, der String wie folgt uebergeben: IPClient +"$"+ IPServer +"$"+ Port +"$"+ * IDVerbindung. */ public void sendeAntwort(String daten) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Mitarbeiter), sendeAntwort(" + daten + ")"); try { if (isTransactionState() || isBenAuth()) { emailServer.kontenSpeichern(); } } catch (Exception e) {} emailServer.benachrichtigeBeobachter(socket.holeZielIPAdresse() + "> " + daten); sendeNachricht(daten); } public String holeBenutzername() { return benutzername; } public void setzeBenutzername(String benutzername) { this.benutzername = benutzername; } public String holePassword() { return password; } public void setzePassword(String password) { this.password = password; } public boolean isTransactionState() { return transactionState; } public void setTransactionState(boolean transactionState) { this.transactionState = transactionState; } public boolean isBenAuth() { return benAuth; } public void setBenAuth(boolean benAuth) { this.benAuth = benAuth; } public boolean isAuthenticationState() { return authenticationState; } public void setAuthenticationState(boolean authenticationState) { this.authenticationState = authenticationState; } public EmailServer getEmailServer() { return emailServer; } public void setEmailServer(EmailServer emailServer) { this.emailServer = emailServer; } } filius-2.9.4+ds/src/main/java/filius/software/email/POP3Server.java000066400000000000000000000053701506574241500250640ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.TCPServerAnwendung; import filius.software.transportschicht.Socket; import filius.software.transportschicht.TCPSocket; /* * Auch der POP3Server wird neu gemacht, bzw. dem neuen EmailServer angepasst. Das heißt vor * allem auch, dass der ganze unnötige Ballast, ich habe mich in dem Programm verlaufen, * rausgeworfen wird, wie z.B. 2x einen EmailServer zu implementieren! */ public class POP3Server extends TCPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(POP3Server.class); private EmailServer emailServer; // Konstruktor(en) public POP3Server(int port, // Betriebssystem bs, EmailServer emailServer) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Server), constr: POP3Server(" + port + "," + emailServer + ")"); this.port = port; this.emailServer = emailServer; } public EmailServer holeEmailServer() { return emailServer; } // Methoden protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (POP3Server), neuerMitarbeiter(" + socket + ")"); POP3Mitarbeiter popMitarbeiter; popMitarbeiter = new POP3Mitarbeiter((TCPSocket) socket, this); popMitarbeiter.starten(); mitarbeiter.add(popMitarbeiter); } }filius-2.9.4+ds/src/main/java/filius/software/email/SMTPClient.java000066400000000000000000000351461506574241500251020ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.TimeOutException; import filius.exception.VerbindungsException; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.clientserver.ClientAnwendung; import filius.software.transportschicht.TCPSocket; public class SMTPClient extends ClientAnwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(SMTPClient.class); /** * Die Anwendung, die diesen SMTP-Client verwendet (das kann eine EmailAnwendung zum Versand einer erstellten * Nachricht oder ein EMailServer zur Weiterleitung einer empfangenen Nachricht sein) */ private Anwendung anwendung = null; // Konstruktoren public SMTPClient(Anwendung anwendung) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), constr: SMTPClient(" + anwendung + ")"); this.anwendung = anwendung; this.setSystemSoftware(anwendung.getSystemSoftware()); } // Methoden /** * Versenden einer Email Hier wird eine defaultServerIP übergeben, die anzeigt, diese Methode als Teil einer * Weiterleitung aufgerufen wird. Wenn hier NULL übergeben wird, dann wird diese Client-Mehtode vom SMTP-Mitarbeiter * als Teil der Weiterleitung aufgerufen. Ist eine DefaultServerIP mit über- geben, dann wird sie von der Email * Anwendung gesendet, und die hat ihren Standartmailserver in den Einstellungen, der somit dann übergeben werden * kann. * * Neben der Email, die eindeutig ist, werden dann noch alle Empfaenger übereben, die rcpts, die aus einem langen * String von Empfaenger, Cc, und Bcc bestehen, wenn sie von der Email- Anwendung aufgerufen werden, und aus einem * langen String von rcpts (NUR-NOCH-EMPFAENGER, die ausgesondert wurden, je nach dem, ob sie im Teil der * Weiterleitung des SMTP-Mitarbeiters schon abgespeichert wurden, oder eben nicht und deswegen weiter versendet * werden.) * *
    *
  • evtl. Aufloesen der ServerIP-Adresse (bei weiterleiten, sonst nicht)
  • *
  • initialisieren des Sockets (Verbindungsaufbau)
  • *
  • Versand der Email
  • *
  • Verbindungsabbau
  • *
*/ public void versendeEmail(String defaultServerIP, Email email, String absender, String rcpts) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), versendeEmail(" + defaultServerIP + "," + email + "," + absender + "," + rcpts + ")"); String[] zuSendenAn = rcpts.split(","); List recipientTo = new ArrayList(); for (String empfaengerAdresse : zuSendenAn) { if (empfaengerAdresse.trim().length() > 0) { recipientTo.add(new AddressEntry(empfaengerAdresse.trim()).getMailAddress()); } } List unknownRecipients = new ArrayList(); for (String empfaengerAdresse : recipientTo) { String serverAdresse; if (defaultServerIP == null) { serverAdresse = loeseURLauf(empfaengerAdresse); LOG.debug(getClass() + "\n\tServer Adresse bei Weiterleitung fuer Maildomain " + empfaengerAdresse + " ist: " + serverAdresse); } else { serverAdresse = defaultServerIP; } if (serverAdresse == null) { unknownRecipients.add(empfaengerAdresse); } else { Object[] args; args = new Object[2]; args[0] = serverAdresse; args[1] = new Integer(25); ausfuehren("initialisiereSocket", args); args = new Object[3]; args[0] = email; args[1] = absender; args[2] = empfaengerAdresse; ausfuehren("versenden", args); ausfuehren("schliesseSocket", null); } } if (!unknownRecipients.isEmpty() && anwendung instanceof EmailServer) { ((EmailServer) anwendung).sendUnknownReceiverResponse(email, absender, unknownRecipients); } } /** Diese Methode ist blockierend */ public void initialisiereSocket(String zielAdresse, Integer port) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), initialisiereSocket(" + zielAdresse + "," + port + ")"); anwendung.benachrichtigeBeobachter(EmailServer.LINE_SEPARATOR); try { socket = new TCPSocket(getSystemSoftware(), zielAdresse, port); socket.verbinden(); if (socket.istVerbunden()) { anwendung.benachrichtigeBeobachter( messages.getString("sw_smtpclient_msg1") + " " + socket.holeZielIPAdresse() + ":" + socket.holeZielPort() + " " + messages.getString("sw_smtpclient_msg2")); } } catch (Exception e) { LOG.debug("", e); socket = null; anwendung.benachrichtigeBeobachter(e); } } /** * Diese Methode ist blockierend. */ public void schliesseSocket() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schliesseSocket()"); if (socket != null) { socket.schliessen(); anwendung.benachrichtigeBeobachter( messages.getString("sw_smtpclient_msg1") + " " + socket.holeZielIPAdresse() + ":" + socket.holeZielPort() + " " + messages.getString("sw_smtpclient_msg3")); socket = null; } } private boolean schickeHelo() throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schickeHelo()"); String empfang; empfang = socket.empfangen(); if (empfang.startsWith("220")) { socket.senden("HELO " + this.getSystemSoftware().primaryIPAdresse()); empfang = socket.empfangen(); if (empfang.startsWith("250")) { return true; } else { throw new Exception(empfang); } } else { return false; } } /** * Mit dieser Methode wird eine Email versendet. Es wird eine Email entgegengenommen, und aus ihr Attribute * entnommen, die dann einzeln versendet werden nach SMTP-manier. Dazu werden verschiedene Methoden der Reihe nach * aufgerufen, die in vordefinierter Abfolge die einzelnen Elemente einer Email versenden und auf ein Response des * Servers warten. Nur bei erfolgreichem Response geht es weiter. * * @param email */ public boolean versenden(Email email, String absender, String rcpts) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), versenden(" + email + "," + absender + "," + rcpts + ")"); boolean erfolg = true; if (socket == null) return false; else { try { erfolg = schickeHelo(); if (erfolg) { erfolg = schickeMailFrom(absender); } if (erfolg) { erfolg = schickeRcptTo(rcpts); } if (erfolg) { erfolg = schickeDataBeginn(); } if (erfolg) { erfolg = schickeData(email); } if (erfolg) { schickeQuit(); } if (erfolg && anwendung instanceof EmailAnwendung) { ((EmailAnwendung) anwendung).addGesendeteNachricht(email); } anwendung.benachrichtigeBeobachter(messages.getString("sw_smtpclient_msg4") + " " + rcpts); anwendung.benachrichtigeBeobachter(); } catch (Exception e) { LOG.debug("", e); anwendung.benachrichtigeBeobachter(e); erfolg = false; } return erfolg; } } /** * Dient der Aufloesung der EmailAdressen-Domains auf den Empfaengerangaben zu einer Ip-Adresse, damit zum dortigen * MTA eine Verbindung aufgebaut werden kann. Es muss nur der Domainnamen eingegeben werden, der Socket loest sofort * auf die IP auf... * * @param url * @return */ public String loeseURLauf(String url) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), loeseURLauf(" + url + ")"); String[] teileDerEmail = url.split("@"); try { return getSystemSoftware().holeDNSClient().holeIPAdresseMailServer(teileDerEmail[1]); } catch (TimeOutException e) { LOG.debug("", e); } return null; } /** * Hier wird der MailFrom an den Empfaenger versendet bsp fuer MailFrom: "andre.asschoff@web.de", "carsten@gmx.de" * Dazu wird aus dem Absender der Email der getMailFrom() gebastelt * * @param email * @return * @throws Exception */ private boolean schickeMailFrom(String absender) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schickeMailFrom(" + absender + ")"); AddressEntry senderAddress = new AddressEntry(absender); if (senderAddress.getMailAddress().length() == 0 || EingabenUeberpruefung .isGueltig(senderAddress.getMailAddress(), EingabenUeberpruefung.musterEmailAdresse)) { socket.senden("MAIL FROM: <" + senderAddress.getMailAddress() + ">"); String empfangen = socket.empfangen(); if (empfangen.substring(0, 3).equals("250")) { return true; } else { throw new Exception(empfangen); } } else { return false; } } /** * Hier wird RcptTo und RcptToDomain versendet Es können auch mehrere Empfaenger angegeben werden, die durch ein * Komma voneinander getrennt sind. * * @param email * @return * @throws Exception */ private boolean schickeRcptTo(String rcpts) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schickeRcptTo(" + rcpts + ")"); String[] empfaenger = rcpts.split(","); for (int i = 0; i < empfaenger.length; i++) { if (EingabenUeberpruefung.isGueltig(empfaenger[i], EingabenUeberpruefung.musterEmailAdresse)) { socket.senden("RCPT TO:<" + empfaenger[i] + ">"); String empfangen2 = socket.empfangen(); if (!empfangen2.substring(0, 3).equals("250") && !empfangen2.substring(0, 3).equals("251")) { throw new Exception(empfangen2); } } else { return false; } } return true; } /** * Hier wird nur "DATA" gesendet, was den nachfolgende Email-Versand anzeigt. Da DATA laut RFC 821 nur einen * Intermediate Status andeutet, bliebe zu ueberlegen, ob nicht das anschliessende senden der Nachricht in dieser * Methode gleich mit aufgerufen wird... * * @return * @throws Exception */ private boolean schickeDataBeginn() throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schickeDataBeginn()"); socket.senden("DATA"); String empfangen3 = socket.empfangen(); if (empfangen3.substring(0, 3).equals("354")) { return true; } else { throw new Exception(empfangen3); } } /** * Hier wird der Datenteil der Email an sich versendet. * * @param email * @return * @throws Exception */ private boolean schickeData(Email email) throws Exception { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schickeData(" + email + ")"); if (email != null) { socket.senden(email.toString() + "\r\n.\r\n"); String empfangen34 = socket.empfangen(); if (empfangen34.startsWith("250")) { return true; } else { throw new Exception(empfangen34); } } else { return false; } } /** * Hiermit wird ein Quit gesendet, die zum Beenden der Uebertragung dient. * * @return * @throws TimeOutException * @throws VerbindungsException */ private boolean schickeQuit() throws VerbindungsException, TimeOutException { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPClient), schickeQuit()"); String empfangen; socket.senden("QUIT"); empfangen = socket.empfangen(); if (empfangen.startsWith("221")) { return true; } else { return false; } } } filius-2.9.4+ds/src/main/java/filius/software/email/SMTPMitarbeiter.java000066400000000000000000000231651506574241500261310ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.ServerMitarbeiter; import filius.software.transportschicht.TCPSocket; /** * Der Mitarbeiter dient dazu, den Datenaustausch mit einem Client durchzufuehren.
* Dazu durchlaeuft er folgende Zustaende: *
    *
  1. START: Empfang des Begruessungskommandos (HELO oder EHLO)
  2. *
  3. ENVELOPE: Empfang der 'Briefumschlags'-Informationen (Sender, Empfaenger etc)
  4. *
  5. DATA: Empfang der eigentlichen E-Mail mit Kopfdaten und Nutzdaten, die mit einem Zeilenumbruch, einem Punkt, * einem Zeilenumbruch beendet werden (<CR><LF>.<CR><LF>)
  6. *
  7. END: Der Datenaustausch wurde beendet. (Das ist der einzig gueltige Endzustand!)
  8. *
* Nachdem eine Nachricht ordnungsgemaess empfangen wurde, befindet sich der Mitarbeiter im END-Zustand. Das heisst, * dass dieser Mail-Server es nicht ermoeglicht mit einer Verbindung mehrere E-Mails zu uebertragen. * */ public class SMTPMitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(SMTPMitarbeiter.class); private static final int START = 0, ENVELOPE = 1, DATA = 2, END = 3; private EmailServer emailServer; private int zustand = START; private List rcptTo = new LinkedList(); private String mailFrom; private String quelltext = ""; private Email email = null; private EmailKonto empfaengerKonto = new EmailKonto(); public SMTPMitarbeiter(// Betriebssystem bs, TCPSocket socket, SMTPServer server) { super(server, socket); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPMitarbeiter), constr: SMTPMitarbeiter(" + socket + "," + server + ")"); this.socket = socket; this.emailServer = server.holeEmailServer(); emailServer.benachrichtigeBeobachter(EmailServer.LINE_SEPARATOR); senden(messages.getString("sw_smtpmitarbeiter_msg1") + " " + emailServer.getMailDomain()); } /** * In dieser Methode werden die ankommenden Befehle und Daten zur Uebertragung einer E-Mail verarbeitet. */ protected void verarbeiteNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPMitarbeiter), verarbeiteNachricht(" + nachricht + ")"); String[] tokens; String tmp; int pos, pos2; emailServer.benachrichtigeBeobachter(socket.holeZielIPAdresse() + "< " + nachricht); // Verarbeitung der vom Client ankommenden Daten if (nachricht.trim().equalsIgnoreCase("QUIT")) { senden(messages.getString("sw_smtpmitarbeiter_msg2")); socket.schliessen(); } else if (zustand == START) { pos = nachricht.indexOf(" "); if (pos > 0) tmp = nachricht.substring(0, pos).trim(); else tmp = nachricht.trim(); if (tmp.equalsIgnoreCase("HELO") || tmp.equalsIgnoreCase("EHLO")) { if (pos > 0) tmp = nachricht.substring(pos, nachricht.length()).trim(); else tmp = ""; senden("250 Hello " + tmp); zustand = ENVELOPE; } else if (tmp.equalsIgnoreCase("NOOP")) { senden(messages.getString("sw_smtpmitarbeiter_msg3")); } } else if (zustand == ENVELOPE) { pos = nachricht.indexOf(":"); if (pos > 0) tmp = nachricht.substring(0, pos).trim(); else tmp = nachricht.trim(); if (tmp.equalsIgnoreCase("MAIL FROM")) { pos = nachricht.indexOf("<"); pos2 = nachricht.indexOf(">"); if (pos > 0 && pos2 > pos) { mailFrom = nachricht.substring(pos + 1, pos2).trim(); senden(messages.getString("sw_smtpmitarbeiter_msg4")); } else { senden(messages.getString("sw_smtpmitarbeiter_msg5")); } } else if (tmp.equalsIgnoreCase("RCPT TO")) { pos = nachricht.indexOf("<"); pos2 = nachricht.indexOf(">"); if (pos > 0 && pos2 > pos) { rcptTo.add(nachricht.substring(pos + 1, pos2).trim()); senden(messages.getString("sw_smtpmitarbeiter_msg6")); } else { senden(messages.getString("sw_smtpmitarbeiter_msg7")); } } else if (tmp.equalsIgnoreCase("DATA")) { if (mailFrom != null && rcptTo.size() > 0) { senden(messages.getString("sw_smtpmitarbeiter_msg8")); zustand = DATA; } else { senden(messages.getString("sw_smtpmitarbeiter_msg9")); } } else if (tmp.equalsIgnoreCase("NOOP")) { senden("250 OK"); } } else if (zustand == DATA) { tokens = nachricht.split("\n"); if ((tokens.length > 0 && tokens[tokens.length - 1].trim().equals(".")) || (tokens.length > 1 && tokens[tokens.length - 2].trim().equals(".") && tokens[tokens.length - 2].trim().equals(""))) { quelltext = quelltext + nachricht.substring(0, nachricht.lastIndexOf(".")); email = new Email(quelltext); senden(messages.getString("sw_smtpmitarbeiter_msg10")); verarbeiteEmail(); zustand = END; } else { quelltext = quelltext + nachricht; } } else if (zustand == END) { senden(messages.getString("sw_smtpmitarbeiter_msg11")); zustand = ENVELOPE; } } /** * Nachdem eine E-Mail erfolgreich empfangen wurde, wird sie in dieser Methode in Abhaengigkeit der angegebenen * Empfaenger in einem lokalen Postfach abgelegt bzw. an einen neuen Empfaenger weiter geleitet. */ private void verarbeiteEmail() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPMitarbeiter), verarbeiteEmail()"); StringBuilder weitereEmpfaenger = new StringBuilder(); List unknownRecipients = new ArrayList(); for (String aktuellerEmpfaenger : rcptTo) { String[] temp = aktuellerEmpfaenger.split("@"); String benutzer = temp[0]; if (emailServer.pruefeAufSelbeDomain(aktuellerEmpfaenger)) { empfaengerKonto = emailServer.sucheKonto(benutzer); if (empfaengerKonto != null) { empfaengerKonto.getNachrichten().add(new Email(email.toString())); emailServer.benachrichtigeBeobachter(messages.getString("sw_smtpmitarbeiter_msg12") + " " + empfaengerKonto.getBenutzername() + " " + messages.getString("sw_smtpmitarbeiter_msg13")); } else { unknownRecipients.add(aktuellerEmpfaenger); } } else { weitereEmpfaenger.append(aktuellerEmpfaenger); weitereEmpfaenger.append(","); } } emailServer.kontenSpeichern(); if (weitereEmpfaenger.length() > 0) { emailServer.emailWeiterleiten(new Email(email.toString()), mailFrom, weitereEmpfaenger.toString()); } if (!unknownRecipients.isEmpty()) { emailServer.sendUnknownReceiverResponse(email, mailFrom, unknownRecipients); } } /** * Methode zum Senden von Nachrichten. Gleichzeitig wird der Beobachter des Mail-Servers ueber die verschickte * Nachricht informiert! */ private void senden(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPMitarbeiter), senden(" + nachricht + ")"); try { socket.senden(nachricht); emailServer.benachrichtigeBeobachter(socket.holeZielIPAdresse() + "> " + nachricht); } catch (Exception e) { LOG.debug("", e); emailServer.benachrichtigeBeobachter( messages.getString("sw_smtpmitarbeiter_msg14") + " " + socket.holeZielIPAdresse()); } } } filius-2.9.4+ds/src/main/java/filius/software/email/SMTPServer.java000066400000000000000000000054251506574241500251270ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.TCPServerAnwendung; import filius.software.transportschicht.Socket; import filius.software.transportschicht.TCPSocket; /** * * @author Andre Asschoff * */ public class SMTPServer extends TCPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(SMTPServer.class); private EmailServer emailServer; public SMTPServer(int port, EmailServer emailServer) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPServer), constr: SMTPServer(" + port + "," + emailServer + ")"); this.port = port; this.emailServer = emailServer; } public EmailServer getEmailServer() { return emailServer; } public void setEmailServer(EmailServer emailServer) { this.emailServer = emailServer; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public EmailServer holeEmailServer() { return emailServer; } @Override protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (SMTPServer), neuerMitarbeiter(" + socket + ")"); SMTPMitarbeiter smtpMitarbeiter; smtpMitarbeiter = new SMTPMitarbeiter((TCPSocket) socket, this); smtpMitarbeiter.starten(); mitarbeiter.add(smtpMitarbeiter); } } filius-2.9.4+ds/src/main/java/filius/software/firewall/000077500000000000000000000000001506574241500230225ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/firewall/Firewall.java000066400000000000000000000353661506574241500254470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.firewall; import java.util.LinkedList; import java.util.List; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.transportschicht.Segment; import filius.software.transportschicht.TcpSegment; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; import filius.software.vermittlungsschicht.VermittlungsProtokoll; /** * Die Firewall kann in zwei verschiedenen Modi betrieben werden. *

* Als Personal Firewall werden lediglich Port-Regeln ausgewertet. Eine Port-Regel spezifiziert zugelassene * TCP-/UDP-Ports und ob diese nur von IP-Adressen im lokalen Rechnernetz oder global kontaktiert werden koennen. *

*

* Wenn die Firewall in einem Gateway betrieben wird, gibt es vier verschiedene Regeltypen. Alle Regeln * spezifizieren - im Gegensatz zum Betrieb als Personal Firewall - Dateneinheiten, die nicht zugelassen werden. * Geprueft werden: *

    *
  1. Sender-IP-Adresse
  2. *
  3. Absender-IP-Adresse
  4. *
  5. TCP-/UDP-Port
  6. *
  7. ACK(=0)+SYN(=1)-Bit der TCP-Segmente (indiziert Initialisierung des Verbindungsaufbaus)
  8. *
*/ public class Firewall extends Anwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(Firewall.class); // firewall ruleset private Vector ruleset = new Vector(); private short defaultPolicy = FirewallRule.DROP; private boolean activated = true; private boolean dropICMP = false; private boolean filterSYNSegmentsOnly = true; private boolean filterUdp = true; private LinkedList firewallThreads = new LinkedList(); /** * startet die Anwendung Firewall. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Firewall), starten()"); super.starten(); initFirewallThreads(); } protected void initFirewallThreads() { for (NetzwerkInterface nic : getAllNetworkInterfaces()) { FirewallThread thread = new FirewallThread(this, nic, this.betriebssystem); addAndStartThread(thread); } } protected void addAndStartThread(FirewallThread thread) { thread.starten(); firewallThreads.add(thread); } /** * ruft die Methoden zum ordnungsgemäßen Stoppen aller existierenden Threads auf */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Firewall), beenden()"); super.beenden(); this.beendeFirewallThread(null); } private void beendeFirewallThread(NetzwerkInterface nic) { for (FirewallThread thread : this.firewallThreads) { if (nic == null) { thread.beenden(); } else if (nic == thread.getNetzwerkInterface()) { thread.beenden(); break; } } } /** * Method to check whether IP packet is allowed. * * @param ipPacket * @return whether this IP packet must be forwarded (true: forward IP packet; false: discard the IP packet) */ public boolean acceptIPPacket(IpPaket ipPacket) { boolean accept = true; if (isActivated()) { if (ipPacket.getProtocol() == IcmpPaket.ICMP_PROTOCOL) { accept = checkAcceptIcmp(ipPacket); } else if (ipPacket.getProtocol() == IpPaket.TCP) { accept = checkAcceptTCP(ipPacket); } else if (ipPacket.getProtocol() == IpPaket.UDP) { accept = checkAcceptUDP(ipPacket); } else { benachrichtigeBeobachter(messages.getString("sw_firewall_msg9") + " " + ((this.defaultPolicy == FirewallRule.ACCEPT) ? messages.getString("jfirewalldialog_msg33") : messages.getString("jfirewalldialog_msg34"))); accept = defaultPolicy == FirewallRule.ACCEPT; } } return accept; } /** * @param idx * following function assume to be human readable ID starting from 1; --> for internal processing reduce * by 1 */ public boolean moveUp(int idx) { if (idx <= ruleset.size() && idx > 1) { FirewallRule currRule = ruleset.get(idx - 1); ruleset.remove(idx - 1); ruleset.insertElementAt(currRule, idx - 2); return true; } return false; } boolean checkAcceptIcmp(IpPaket packet) { boolean accept = !(packet instanceof IcmpPaket && dropICMP); if (!accept) { benachrichtigeBeobachter(messages.getString("firewallthread_msg1") + " " + packet.getSender() + " -> " + packet.getEmpfaenger() + " (code: " + ((IcmpPaket) packet).getIcmpCode() + ", type: " + ((IcmpPaket) packet).getIcmpType() + ")"); } return accept; } boolean checkAcceptTCP(IpPaket packet) { boolean accept = true; if (packet.getProtocol() == IpPaket.TCP && isSegmentApplicable(packet)) { boolean foundRule = false; Segment segment = (Segment) packet.getSegment(); for (int i = 0; i < ruleset.size() && !foundRule; i++) { FirewallRule firewallRule = ruleset.get(i); boolean ruleToBeApplied = isProtocolApplicable(packet, firewallRule) && (isEndpointsApplicable(packet.getSender(), packet.getEmpfaenger(), segment.getZielPort(), firewallRule)); if (ruleToBeApplied) { // if rule matches to current packet, then notifyRuleApplication(i, firewallRule); accept = firewallRule.action == FirewallRule.ACCEPT; foundRule = true; } } if (!foundRule) { accept = defaultPolicy == FirewallRule.ACCEPT; } } return accept; } private boolean isEndpointsApplicable(String endpoint1IPAddress, String endpoint2IPAddress, int endpoint2Port, FirewallRule firewallRule) { boolean ruleToBeApplied = isSourceAddressApplicable(endpoint1IPAddress, firewallRule); ruleToBeApplied = ruleToBeApplied && isDestAddressApplicable(endpoint2IPAddress, firewallRule); ruleToBeApplied = ruleToBeApplied && isPortApplicable(endpoint2Port, firewallRule); return ruleToBeApplied; } private void notifyRuleApplication(int i, FirewallRule firewallRule) { benachrichtigeBeobachter(messages.getString("sw_firewall_msg8") + " #" + (i + 1) + " (" + firewallRule.toString(getAllNetworkInterfaces()) + ") -> " + ((firewallRule.action == FirewallRule.ACCEPT) ? messages.getString("jfirewalldialog_msg33") : messages.getString("jfirewalldialog_msg34"))); } private boolean isSegmentApplicable(IpPaket packet) { boolean isApplicable = false; if (packet.getProtocol() == IpPaket.TCP) { TcpSegment segment = (TcpSegment) packet.getSegment(); isApplicable = !filterSYNSegmentsOnly || segment.isSyn() && !segment.isAck(); } else if (packet.getProtocol() == IpPaket.UDP) { isApplicable = filterUdp; } return isApplicable; } boolean checkAcceptUDP(IpPaket packet) { boolean accept = true; if (packet.getProtocol() == IpPaket.UDP && isSegmentApplicable(packet)) { boolean foundRule = false; Segment segment = (Segment) packet.getSegment(); for (int i = 0; i < ruleset.size() && !foundRule; i++) { FirewallRule firewallRule = ruleset.get(i); // check if request is allowed boolean applicableOnRequest = isEndpointsApplicable(packet.getSender(), packet.getEmpfaenger(), segment.getZielPort(), firewallRule); // check if response is allowed (only if rule specifies port) boolean applicableOnResponse = FirewallRule.ALL_PORTS != firewallRule.port && isEndpointsApplicable( packet.getEmpfaenger(), packet.getSender(), segment.getQuellPort(), firewallRule); boolean ruleToBeApplied = isProtocolApplicable(packet, firewallRule) && (applicableOnRequest || applicableOnResponse); if (ruleToBeApplied) { // if rule matches to current packet, then notifyRuleApplication(i, firewallRule); accept = firewallRule.action == FirewallRule.ACCEPT; foundRule = true; } } if (!foundRule) { accept = defaultPolicy == FirewallRule.ACCEPT; } } return accept; } private boolean isPortApplicable(int port, FirewallRule firewallRule) { return firewallRule.port == FirewallRule.ALL_PORTS || port == firewallRule.port; } private boolean isProtocolApplicable(IpPaket packet, FirewallRule firewallRule) { return firewallRule.protocol == FirewallRule.ALL_PROTOCOLS || (packet.getProtocol() == (int) firewallRule.protocol); } private boolean isDestAddressApplicable(String ipAddress, FirewallRule firewallRule) { return firewallRule.destIP.isEmpty() || VermittlungsProtokoll.gleichesRechnernetz(ipAddress, firewallRule.destIP, firewallRule.destMask); } private boolean isSourceAddressApplicable(String ipAddress, FirewallRule firewallRule) { boolean ruleToBeApplied = false; if (firewallRule.srcIP.isEmpty()) { ruleToBeApplied = true; } else { if (firewallRule.srcIP.equals(FirewallRule.SAME_NETWORK)) { for (NetzwerkInterface iface : ((InternetKnoten) getSystemSoftware().getKnoten()) .getNetzwerkInterfaces()) { if (VermittlungsProtokoll.gleichesRechnernetz(ipAddress, iface.getIp(), iface.getSubnetzMaske())) { ruleToBeApplied = true; break; } } } else { ruleToBeApplied = VermittlungsProtokoll.gleichesRechnernetz(ipAddress, firewallRule.srcIP, firewallRule.srcMask); } } return ruleToBeApplied; } /** * @param idx * following function assume to be human readable ID starting from 1; --> for internal processing reduce * by 1 */ public boolean moveDown(int idx) { if (idx >= 0 && idx < ruleset.size()) { FirewallRule currRule = ruleset.get(idx - 1); ruleset.remove(idx - 1); ruleset.insertElementAt(currRule, idx); return true; } return false; } public void addRule() { ruleset.add(new FirewallRule()); } public void addRule(FirewallRule rule) { ruleset.add(rule); } public boolean updateRule(int idx, FirewallRule rule) { if (idx >= 0 && idx < ruleset.size()) { ruleset.set(idx, rule); } return true; } /** * entfernt eine Regel aus dem Regelkatalog */ public void deleteRule(int idx) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Firewall), entferneRegel(" + idx + ")"); if (idx >= 0 && idx < ruleset.size()) { ruleset.remove(idx); } } /** * Methode fuer den Zugriff auf das Betriebssystem, auf dem diese Anwendung laeuft. * * @param bs */ public void setSystemSoftware(InternetKnotenBetriebssystem bs) { super.setSystemSoftware(bs); } public Vector getRuleset() { return this.ruleset; } public void setRuleset(Vector rules) { this.ruleset = rules; } public void setFilterUdp(boolean filterUdp) { this.filterUdp = filterUdp; } public boolean getFilterUdp() { return filterUdp; } protected List getAllNetworkInterfaces() { InternetKnoten host = (InternetKnoten) this.getSystemSoftware().getKnoten(); return host.getNetzwerkInterfaces(); } public void setDefaultPolicy(short defPol) { defaultPolicy = defPol; } public short getDefaultPolicy() { return defaultPolicy; } public void setDropICMP(boolean selState) { dropICMP = selState; } public boolean getDropICMP() { return dropICMP; } /** * @deprecated use {@link #setFilterSYNSegmentsOnly(boolean)}; deprecated since 1.10.5; to ensure downward * compatibility this method is used to set the new attribute when old project files are loaded. */ @Deprecated public void setAllowRelatedPackets(boolean selState) { filterSYNSegmentsOnly = selState; } public void setFilterSYNSegmentsOnly(boolean selState) { filterSYNSegmentsOnly = selState; } public boolean getFilterSYNSegmentsOnly() { return filterSYNSegmentsOnly; } public void setActivated(boolean selState) { activated = selState; } public boolean isActivated() { return activated; } } filius-2.9.4+ds/src/main/java/filius/software/firewall/FirewallRule.java000066400000000000000000000132741506574241500262710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.firewall; import java.util.List; import org.apache.commons.lang3.StringUtils; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.software.vermittlungsschicht.VermittlungsProtokoll; public class FirewallRule { /** * fake "IP" address to represent "any IP from same network" NOTE: netmask is irrelevant for this "special IP" */ public static String SAME_NETWORK = "999.999.999.999"; // see /etc/protocols public static final short ALL_PROTOCOLS = -1; // do not limit to specific protocol public static final short ICMP = 1; public static final short TCP = 6; public static final short UDP = 17; // ports; see /etc/services public static final int ALL_PORTS = -1; // action, if rules matches public static final short DROP = 0; public static final short ACCEPT = 1; // attributes; direct access tolerable... ;-) public String srcIP = ""; public String srcMask = ""; public String destIP = ""; public String destMask = ""; public int port = FirewallRule.ALL_PORTS; public short protocol = FirewallRule.TCP; public short action = FirewallRule.DROP; // constructor public FirewallRule(String srcIP, String srcMask, String destIP, String destMask, int port, short protocol, short action) { this.srcIP = srcIP; this.srcMask = srcMask; this.destIP = destIP; this.destMask = destMask; this.port = port; this.protocol = protocol; this.action = action; } public FirewallRule() {} public boolean validateExistingAttributes() { boolean result = true; if (StringUtils.isNoneBlank(srcIP) && !EingabenUeberpruefung.isGueltig(srcIP, EingabenUeberpruefung.musterIpAdresse) && !SAME_NETWORK.equals(srcIP)) { result = false; } if (StringUtils.isNoneBlank(srcMask) && !EingabenUeberpruefung.isValidSubnetmask(srcMask)) { result = false; } if (StringUtils.isNoneBlank(destIP) && !EingabenUeberpruefung.isGueltig(destIP, EingabenUeberpruefung.musterIpAdresse)) { result = false; } if (StringUtils.isNoneBlank(destMask) && !EingabenUeberpruefung.isValidSubnetmask(destMask)) { result = false; } if (port != ALL_PORTS && !EingabenUeberpruefung.isGueltig(String.valueOf(port), EingabenUeberpruefung.musterPort)) { result = false; } if (protocol != TCP && protocol != ICMP && protocol != UDP && protocol != ALL_PROTOCOLS) { result = false; } if (action != DROP && action != ACCEPT) { return false; } return result; } public String toString() { return toString(null); } public String toString(List nics) { String ip = null; String mask = null; String result = ""; boolean sameNet = false; if (nics != null) { ip = nics.get(0).getIp(); mask = nics.get(0).getSubnetzMaske(); if (srcIP.equals(FirewallRule.SAME_NETWORK) && ip != null && mask != null) sameNet = true; } if (StringUtils.isEmpty(srcIP)) result += "*/"; else if (sameNet) result += VermittlungsProtokoll.getSubnetForIp(ip, mask) + "/" + mask + " -> "; else result += srcIP + "/"; if (sameNet) {} else if (StringUtils.isEmpty(srcMask)) result += "* -> "; else result += srcMask + " -> "; if (StringUtils.isEmpty(destIP)) result += "*/"; else result += destIP + "/"; if (StringUtils.isEmpty(destMask)) result += "*; "; else result += destMask + "; "; if (protocol >= 0) { if (protocol == FirewallRule.TCP) result += "TCP:"; else if (protocol == FirewallRule.UDP) result += "UDP:"; else result += "?:"; } else { result += "*:"; } if (port >= 0) result += port + " => "; else result += "* => "; if (action == FirewallRule.ACCEPT) result += "ACCEPT"; else if (action == FirewallRule.DROP) result += "DROP"; else result += action; return result; } } filius-2.9.4+ds/src/main/java/filius/software/firewall/FirewallThread.java000066400000000000000000000105021506574241500265600ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.firewall; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.ProtokollThread; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.SystemSoftware; import filius.software.vermittlungsschicht.IpPaket; /* * @author Weyer * Die Klasse schiebt sich zwischen die Ethernetschicht und die Vermittlungsschicht. Sie * tauscht den Ip-Pakete-Puffer aus, sodass sie nach Regeln selektieren kann, welche Pakete * als gueltig weitergeleitet werden */ public class FirewallThread extends ProtokollThread implements I18n { private static Logger LOG = LoggerFactory.getLogger(FirewallThread.class); private LinkedList ausgangsPuffer; protected SystemSoftware systemSoftware; private Firewall firewall; private NetzwerkInterface netzwerkInterface = null; public NetzwerkInterface getNetzwerkInterface() { return netzwerkInterface; } public FirewallThread(Firewall firewall, NetzwerkInterface nic, SystemSoftware systemSoftware) { super(new LinkedList()); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FirewallThread), constr: FirewallThread(" + firewall + ")"); this.firewall = firewall; this.netzwerkInterface = nic; this.systemSoftware = systemSoftware; } /* * tauscht den IP-Puffer zwischen Ethernetschicht und Vermittlungsschicht aus, und startet den Thread zur * Überwachung des Datenaustausches */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FirewallThread), starten()"); super.starten(); this.ausgangsPuffer = netzwerkInterface.getPort().holeEingangsPuffer(); LinkedList eingangsPuffer = holeEingangsPuffer(); netzwerkInterface.getPort().setzeEingangsPuffer(eingangsPuffer); } public void beenden() { super.beenden(); netzwerkInterface.getPort().setzeEingangsPuffer(this.ausgangsPuffer); } @Override protected void verarbeiteDatenEinheit(EthernetFrame frame) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FirewallThread), verarbeiteDatenEinheit(" + frame.toString() + ")"); Lauscher.getLauscher().addDatenEinheit(netzwerkInterface, systemSoftware, frame); if (!checkDiscardByFirewall(frame)) { forwardFrame(frame); } } protected void forwardFrame(EthernetFrame frame) { synchronized (ausgangsPuffer) { ausgangsPuffer.add(frame); ausgangsPuffer.notify(); } } protected boolean checkDiscardByFirewall(EthernetFrame frame) { return frame.getDaten() != null && frame.getDaten() instanceof IpPaket && !firewall.acceptIPPacket((IpPaket) frame.getDaten()); } } filius-2.9.4+ds/src/main/java/filius/software/firewall/FirewallWebKonfig.java000066400000000000000000000463131506574241500272350ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.firewall; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.www.WebServer; import filius.software.www.WebServerPlugIn; public class FirewallWebKonfig extends WebServerPlugIn implements I18n { private static Logger LOG = LoggerFactory.getLogger(FirewallWebKonfig.class); private WebServer webserver; private Firewall firewall; private String srcIP = ""; private String srcMask = ""; private String destIP = ""; private String destMask = ""; private short protocol = FirewallRule.ALL_PROTOCOLS; private int port = FirewallRule.ALL_PORTS; private String portStr = ""; private short action = FirewallRule.DROP; private String errMsg = ""; public void setFirewall(Firewall firewall) { this.firewall = firewall; } public Firewall getFirewall() { return firewall; } public void setWebserver(WebServer server) { this.webserver = server; } public WebServer getWebserver() { return webserver; } /** * Wird ueber das Interface WebServerPlugIn aufgerufen. Muss den gelieferten DatenString verarbeiten, die Firewall * bestuecken, und anschließend eine HTML-Seite zurueckliefern */ public String holeHtmlSeite(String postDaten) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebKonfig), holeHtmlSeite(" + postDaten + ")"); String seite = ""; if (postDaten != null && !postDaten.isEmpty()) { // firewallBestuecken(postDaten); // Dort wird die Methode // postStringZerlegen() ausgefuehrt this.processParameters(postDaten); // process parameters for new firewall format } // LOG.debug("FirewallWebKonfig: Seite liefern= \n"+seite); seite = konfigSeiteErstellen(); return seite; } /* * @author weyer liefert zu einem ausgeführten Submit-Befehl die einzelnen Stücke zurück */ private String[][] postStringZerlegen(String post) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebKonfig), postStringZerlegen(" + post + ")"); String[] submitTeile; String[] element, tmp; String[][] tupel; // LOG.debug("String mit submit in FirewallWebKonfig angekommen: "+post); // String zerlegen und überprüfen: try { submitTeile = URLDecoder.decode(post, "UTF-8").split("&"); } catch (UnsupportedEncodingException e) { submitTeile = post.split("&"); } // Die ersten 5 Einträge des Arrays sind immer gleich tupel = new String[submitTeile.length][2]; for (int i = 0; i < submitTeile.length; i++) { tmp = submitTeile[i].split("="); element = new String[] { "", "" }; for (int j = 0; j < tmp.length && j < element.length; j++) { element[j] = tmp[j].trim(); } tupel[i] = element; } return tupel; } /** * * @param postString * String of POST parameters submitted to web site */ private void processParameters(String postString) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebKonfig), processParameters(" + postString + ")"); // globally defined to make them usable in HTML creation method srcIP = ""; srcMask = ""; destIP = ""; destMask = ""; protocol = FirewallRule.ALL_PROTOCOLS; port = FirewallRule.ALL_PORTS; portStr = ""; action = FirewallRule.DROP; errMsg = ""; // -------------- int radioRule = -1; short defaultPolicy = FirewallRule.DROP; boolean cbActive = false; boolean cbDropIcmp = false; boolean cbOnlySyn = false; boolean doSaveState = false; boolean doSaveDefPol = false; boolean doMoveUp = false; boolean doMoveDown = false; boolean doDelRule = false; boolean doAddRule = false; if (webserver.getSystemSoftware() != null) { String[][] postContent = postStringZerlegen(postString); // copy parameter values to variables for (int i = 0; i < postContent.length; i++) { if (postContent[i][0].equals("btnSave")) { // button: save general settings doSaveState = true; } else if (postContent[i][0].equals("defPolSet")) { // button: set default policy doSaveDefPol = true; } else if (postContent[i][0].equals("moveup")) { // button: move rule up doMoveUp = true; } else if (postContent[i][0].equals("movedown")) { // button: move rule down doMoveDown = true; } else if (postContent[i][0].equals("delrule")) { // button: delete rule doDelRule = true; } else if (postContent[i][0].equals("addrule")) { // button: add rule doAddRule = true; } else if (postContent[i][0].equals("defaultPolicy")) { if (postContent[i][1].equals("accept")) defaultPolicy = FirewallRule.ACCEPT; else defaultPolicy = FirewallRule.DROP; } else if (postContent[i][0].equals("srcIP")) { srcIP = postContent[i][1]; if (!srcIP.isEmpty()) { if (!EingabenUeberpruefung.isGueltig(srcIP, EingabenUeberpruefung.musterIpAdresse)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg2") + "
  • "; if (srcMask.isEmpty() || !EingabenUeberpruefung.isValidSubnetmask(srcMask)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg3") + "
  • "; } } else if (postContent[i][0].equals("srcMask")) { srcMask = postContent[i][1]; if (!srcMask.isEmpty()) { if (!EingabenUeberpruefung.isValidSubnetmask(srcMask)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg3") + "
  • "; if (srcIP.isEmpty()) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg7") + "
  • "; } } else if (postContent[i][0].equals("destIP")) { destIP = postContent[i][1]; if (!destIP.isEmpty()) { if (!EingabenUeberpruefung.isGueltig(destIP, EingabenUeberpruefung.musterIpAdresse)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg4") + "
  • "; if (destMask.isEmpty() || !EingabenUeberpruefung.isValidSubnetmask(destMask)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg5") + "
  • "; } } else if (postContent[i][0].equals("destMask")) { destMask = postContent[i][1]; if (!destMask.isEmpty()) { if (!EingabenUeberpruefung.isValidSubnetmask(destMask)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg5") + "
  • "; if (destIP.isEmpty()) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg7") + "
  • "; } } else if (postContent[i][0].equals("protocol")) { if (postContent[i][1].equals("TCP")) protocol = FirewallRule.TCP; else if (postContent[i][1].equals("UDP")) protocol = FirewallRule.UDP; } else if (postContent[i][0].equals("port")) { portStr = postContent[i][1]; try { port = Integer.parseInt(portStr); if (port != FirewallRule.ALL_PORTS) if (!EingabenUeberpruefung.isGueltig(portStr, EingabenUeberpruefung.musterPort)) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg6") + "
  • "; } catch (Exception e) { if (!postContent[i][1].isEmpty()) errMsg += "
  • " + messages.getString("firewallwebkonfig_msg6") + "
  • "; } } else if (postContent[i][0].equals("action")) { if (postContent[i][1].equals("accept")) action = FirewallRule.ACCEPT; else action = FirewallRule.DROP; } else if (postContent[i][0].equals("radioRule")) { try { radioRule = Integer.parseInt(postContent[i][1]); } catch (Exception e) {} } else if (postContent[i][0].equals("firewallActivate")) { cbActive = true; } else if (postContent[i][0].equals("dropICMP")) { cbDropIcmp = true; } else if (postContent[i][0].equals("onlySYN")) { cbOnlySyn = true; } } // actions if (doSaveState) { firewall.setActivated(cbActive); firewall.setDropICMP(cbDropIcmp); firewall.setFilterSYNSegmentsOnly(cbOnlySyn); } else if (doSaveDefPol) { firewall.setDefaultPolicy(defaultPolicy); } else if (doMoveUp) { firewall.moveUp(radioRule); } else if (doMoveDown) { firewall.moveDown(radioRule); } else if (doDelRule) { firewall.deleteRule(radioRule - 1); } else if (doAddRule) { if (errMsg.isEmpty()) firewall.addRule(new FirewallRule(srcIP, srcMask, destIP, destMask, port, protocol, action)); else errMsg = "" + messages.getString("firewallwebkonfig_msg1") + "
      " + errMsg + "
    " + "

    "; } if (!doAddRule) errMsg = ""; // only use error message, if rule should have been added! } } /* * diese Seite erstellt den kompletten Quelltext für die konfig.html */ private String konfigSeiteErstellen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebKonfig), konfigSeiteErstellen()"); String html; // LOG.debug("FirewallWebKonfig: dynamische Generierung der HTML-konfig-Seite!"); if (firewall != null) { try { html = textDateiEinlesen("tmpl/firewall_konfig_webseite_" + Information.getInformation().getLocaleOrDefault().toString() + ".txt"); html = html.replaceAll(":action_pfad:", getPfad()); StringBuffer cbFwActivate = new StringBuffer(); cbFwActivate.append("\t\t"); html = html.replaceAll(":firewallActivate:", cbFwActivate.toString()); StringBuffer cbDropIcmp = new StringBuffer(); cbDropIcmp.append("\t\t"); html = html.replaceAll(":dropICMP:", cbDropIcmp.toString()); StringBuffer cbOnlySyn = new StringBuffer(); cbOnlySyn.append("\t\t"); html = html.replaceAll(":onlySYN:", cbOnlySyn.toString()); if (firewall.getDefaultPolicy() == FirewallRule.ACCEPT) { html = html.replaceAll(":defPolDropSelected:", ""); html = html.replaceAll(":defPolAcceptSelected:", " selected=\"selected\""); } else { html = html.replaceAll(":defPolAcceptSelected:", ""); html = html.replaceAll(":defPolDropSelected:", " selected=\"selected\""); } StringBuffer ruleTable = new StringBuffer(); Vector ruleset = firewall.getRuleset(); for (int i = 0; i < ruleset.size(); i++) { ruleTable.append(""); ruleTable.append(" "); ruleTable.append((i + 1) + ""); ruleTable.append("" + ruleset.get(i).srcIP + ""); ruleTable.append("" + ruleset.get(i).srcMask + ""); ruleTable.append("" + ruleset.get(i).destIP + ""); ruleTable.append("" + ruleset.get(i).destMask + ""); ruleTable.append(""); if (ruleset.get(i).protocol == FirewallRule.TCP) ruleTable.append("TCP"); else if (ruleset.get(i).protocol == FirewallRule.UDP) ruleTable.append("UDP"); ruleTable.append(""); ruleTable.append(""); if (ruleset.get(i).port >= 0) ruleTable.append(ruleset.get(i).port); ruleTable.append(""); ruleTable.append(""); if (ruleset.get(i).action == FirewallRule.ACCEPT) ruleTable.append(messages.getString("jfirewalldialog_msg33")); else ruleTable.append(messages.getString("jfirewalldialog_msg34")); ruleTable.append(""); ruleTable.append(""); } html = html.replaceAll(":ruleset:", ruleTable.toString()); if (errMsg.isEmpty()) { html = html.replaceAll(":errorMsg:", ""); html = html.replaceAll(":srcIP:", ""); html = html.replaceAll(":srcMask:", ""); html = html.replaceAll(":destIP:", ""); html = html.replaceAll(":destMask:", ""); html = html.replaceAll(":port:", ""); html = html.replaceAll(":optionProtAll:", ""); html = html.replaceAll(":optionProtTCP:", ""); html = html.replaceAll(":optionProtUDP:", ""); html = html.replaceAll(":optionActionDrop:", ""); html = html.replaceAll(":optionActionAccept:", ""); } else { html = html.replaceAll(":errorMsg:", errMsg); html = html.replaceAll(":srcIP:", srcIP); html = html.replaceAll(":srcMask:", srcMask); html = html.replaceAll(":destIP:", destIP); html = html.replaceAll(":destMask:", destMask); if (port > 0) html = html.replaceAll(":port:", portStr); else html = html.replaceAll(":port:", portStr); if (protocol == FirewallRule.ALL_PROTOCOLS) { html = html.replaceAll(":optionProtAll:", " selected"); html = html.replaceAll(":optionProtTCP:", ""); html = html.replaceAll(":optionProtUDP:", ""); } else if (protocol == FirewallRule.TCP) { html = html.replaceAll(":optionProtAll:", ""); html = html.replaceAll(":optionProtTCP:", " selected"); html = html.replaceAll(":optionProtUDP:", ""); } else if (protocol == FirewallRule.UDP) { html = html.replaceAll(":optionProtAll:", ""); html = html.replaceAll(":optionProtTCP:", ""); html = html.replaceAll(":optionProtUDP:", " selected"); } else { html = html.replaceAll(":optionProtAll:", ""); html = html.replaceAll(":optionProtTCP:", ""); html = html.replaceAll(":optionProtUDP:", ""); } if (action == FirewallRule.DROP) { html = html.replaceAll(":optionActionDrop:", " selected"); html = html.replaceAll(":optionActionAccept:", ""); } else if (action == FirewallRule.ACCEPT) { html = html.replaceAll(":optionActionDrop:", ""); html = html.replaceAll(":optionActionAccept:", " selected"); } } } catch (Exception f) { LOG.debug("", f); return null; } return html; } else { return null; } } } filius-2.9.4+ds/src/main/java/filius/software/firewall/FirewallWebLog.java000066400000000000000000000057471506574241500265470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.firewall; import java.util.Observable; import java.util.Observer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.software.www.WebServerPlugIn; /** * * @author Michell wird verwendet, zu einem fuer das Logfenster des WebServers, zum anderen fuer die Anzeige ueber * /log.html Daher muss die Klasse eine Referenz auf beide Anwendungen haben * */ public class FirewallWebLog extends WebServerPlugIn implements Observer, I18n { private static Logger LOG = LoggerFactory.getLogger(FirewallWebLog.class); private StringBuffer logDaten = new StringBuffer(); public void setFirewall(Firewall firewall) { firewall.hinzuBeobachter(this); } /** * Diese Methode muss die HTML-Seite mit den Log-Informationen zurück liefern */ public String holeHtmlSeite(String postDaten) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebLog), holehtmlSeite(" + postDaten + ")"); StringBuffer logSeite = new StringBuffer(); logSeite.append(messages.getString("sw_firewallweblog_msg1")); logSeite.append(messages.getString("sw_firewallweblog_msg2")); logSeite.append("\n\n" + logDaten + "\n\n"); logSeite.append("\t\n"); return logSeite.toString(); } public void update(Observable arg0, Object arg1) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebLog), update(" + arg0 + "," + arg1 + ")"); if (arg1 instanceof Object[]) { logDaten.append("
    \n").append(((Object[]) arg1)[0]); } else if (null != arg1) { logDaten.append("
    \n").append(arg1); } } } filius-2.9.4+ds/src/main/java/filius/software/lokal/000077500000000000000000000000001506574241500223175ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/lokal/FileExplorer.java000066400000000000000000000167321506574241500255730ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.lokal; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.ResourceUtil; import filius.software.Anwendung; import filius.software.system.Datei; public class FileExplorer extends Anwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(FileExplorer.class); // Betriebssystem betriebssystem; private HashMap fileTypeMap; public FileExplorer(// Betriebssystem betriebssystem ) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileExplorer), constr: FileExplorer()"); this.initFileTypeMap(); } public void beenden() { // TODO Auto-generated method stub LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileExplorer), beenden()"); } public void starten() { // TODO Auto-generated method stub LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileExplorer), starten()"); } /** * Diese Funktion importiert die ausgewaehlte Datei in das Betriebssystem des Rechners. Dabei wird die Datei Base64 * kodiert, um sie als String verarbeiten zu koennen! * * @param dateiname * @return */ public String addFile(String pfadname, String dateiname, DefaultMutableTreeNode ordner, String neuerName) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileExplorer), addFile(" + pfadname + "," + dateiname + "," + ordner + "," + neuerName + ")"); String ergebnis = messages.getString("sw_fileexplorer_msg1"); java.io.File file = new java.io.File(pfadname + dateiname); if (!file.exists() || file.length() > 150000) { // max filesize 150.000 // Bytes return ergebnis + "\n" + messages.getString("sw_fileexplorer_msg3"); } if (ordner.getUserObject().getClass().equals(Datei.class)) { LOG.debug("ERROR (" + this.hashCode() + "): Es können keine Dateien in Dateien angeleget werden!"); } else { if (neuerName.equals("")) neuerName = "noName"; String dateityp = this.getFileType(dateiname); Datei tempDatei; if (dateityp != null && dateityp.equals("text")) { String txtInhalt = ""; try { BufferedReader in = new BufferedReader(new FileReader(pfadname + dateiname)); String str; while ((str = in.readLine()) != null) { txtInhalt += str + "\r\n"; } in.close(); ergebnis = messages.getString("sw_fileexplorer_msg2"); } catch (IOException e) { LOG.debug("", e); } tempDatei = new Datei(neuerName, dateityp, txtInhalt); } else { tempDatei = new Datei(neuerName, dateityp, Base64.encodeFromFile(pfadname + dateiname)); tempDatei.setSize(file.length()); ergebnis = messages.getString("sw_fileexplorer_msg2"); } getSystemSoftware().getDateisystem().speicherDatei(ordner, tempDatei); } return ergebnis; } /** * Ermittelt an Hand der Dateiendung den Dateitypen, damit dieser im Dateikonstruktor gesetzt werden kann. Es werden * folgende Typen unterstuetzt und entsprechend gesetzt. Die Dateitypen werden in der Konfigurationsdate * filetypes.txt festgelegt. * * @param dateiname * Der zu ueberpruefende Dateiname * @return Typ der Datei als String */ public String getFileType(String dateiname) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileExplorer), getFileType(" + dateiname + ")"); String tmpType = ""; /* * Schritt 1: Die Datei wird in LowerCase konvertiert und der letzte Token wird gesucht */ dateiname = dateiname.toLowerCase(); StringTokenizer st = new StringTokenizer(dateiname, "."); // LOG.debug("Die Datei hat " + st.countTokens() + " Tokens!"); int counting = st.countTokens(); if (counting > 1) { for (int i = 1; i < counting; i++) { st.nextToken(); } String a = st.nextToken(); tmpType = fileTypeMap.get(a); } else { tmpType = "text"; } // LOG.debug("Der Dateityp ist: " + tmpType); return tmpType; } /** * Liest einmalig die definierten Filetypen ein, damit diese beim Importieren der Dateien passend zugewiesen werden * koennen. Wird einmalig im Konstruktor des FileImporters aufgerufen. * * @author Johannes Bade & Thomas Gerding * */ public void initFileTypeMap() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileExplorer), getFileTypeMap()"); fileTypeMap = new HashMap(); RandomAccessFile configFile; try { configFile = new RandomAccessFile(ResourceUtil.getResourcePath("config/filetypes.txt"), "r"); for (String line; (line = configFile.readLine()) != null;) { StringTokenizer stx = new StringTokenizer(line, ";"); String tmpType = stx.nextToken(); StringTokenizer sty = new StringTokenizer(stx.nextToken(), ","); while (sty.hasMoreElements()) { fileTypeMap.put(sty.nextToken(), tmpType); } } } catch (FileNotFoundException e) { LOG.debug("", e); } catch (IOException e) { LOG.debug("", e); } } } filius-2.9.4+ds/src/main/java/filius/software/lokal/FileImporter.java000066400000000000000000000163561506574241500255760ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.lokal; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.I18n; import filius.software.Anwendung; import filius.software.system.Datei; /** * Diese Klasse stellt Funktionen bereit um reale Dateien auf Rechnerrechner der Internetworking Anwendung zu * importieren. * * @author Thomas Gerding & Johannes Bade * */ public class FileImporter extends Anwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(FileImporter.class); private HashMap fileTypeMap; public FileImporter() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileImporter), constr: FileImporter()"); this.getFileTypeMap(); } public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileImporter), starten()"); } public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileImporter), beenden()"); } /** * Diese Funktion importiert die ausgewaehlte Datei in das Betriebssystem des Rechners. Dabei wird die Datei Base64 * kodiert, um sie als String verarbeiten zu koennen! * * @param dateiname * @return */ public String addFile(String pfadname, String dateiname, DefaultMutableTreeNode ordner, String neuerName) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileImporter), addFile(" + pfadname + "," + dateiname + "," + ordner + "," + neuerName + ")"); Datei tempDatei; String dateityp; String ergebnis = messages.getString("sw_fileimporter_msg1"); // LOG.debug("#################################################"); try { if (ordner.getUserObject().getClass().equals(Datei.class)) { // Main.debug // .println("Es können Keine Dateien in Dateien angeleget werden!"); } else { if (dateiname.equals("")) { dateityp = this.getFileType(dateiname); tempDatei = new Datei("noName", this.getFileType(dateiname), Base64.encodeFromFile(pfadname + dateiname)); if (dateityp.equals("text")) { tempDatei.setDateiInhalt(Base64.decodeToObject(tempDatei.getDateiInhalt()).toString()); } getSystemSoftware().getDateisystem().speicherDatei(ordner, tempDatei); } else { dateityp = this.getFileType(dateiname); tempDatei = new Datei(dateiname, dateityp, Base64.encodeFromFile(pfadname + dateiname)); if (dateityp.equals("text")) { tempDatei.setDateiInhalt(Base64.decodeToObject(tempDatei.getDateiInhalt()).toString()); } getSystemSoftware().getDateisystem().speicherDatei(ordner, tempDatei); } ergebnis = messages.getString("sw_fileimporter_msg2") + "\n\t" + pfadname + dateiname; } } catch (Exception e) {} return ergebnis; } /** * Ermittelt an Hand der Dateiendung den Dateitypen, damit dieser im Dateikonstruktor gesetzt werden kann. Es werden * folgende Typen unterstuetzt und entsprechend gesetzt. Die Dateitypen werden in der Konfigurationsdate * filetypes.txt festgelegt. * * @param dateiname * Der zu ueberpruefende Dateiname * @return Typ der Datei als String */ public String getFileType(String dateiname) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileImporter), getFileType(" + dateiname + ")"); String tmpType = ""; /* * Schritt 1: Die Datei wird in LowerCase konvertiert und der letzte Token wird gesucht */ dateiname = dateiname.toLowerCase(); StringTokenizer st = new StringTokenizer(dateiname, "."); // LOG.debug("Die Datei hat " + st.countTokens() + " Tokens!"); int counting = st.countTokens(); if (counting > 1) { for (int i = 1; i < counting; i++) { st.nextToken(); } String a = st.nextToken(); tmpType = fileTypeMap.get(a); } else { tmpType = "text"; } // LOG.debug("Der Dateityp ist: " + tmpType); return tmpType; } /** * Liest einmalig die definierten Filetypen ein, damit diese beim Importieren der Dateien passend zugewiesen werden * koennen. Wird einmalig im Konstruktor des FileImporters aufgerufen. * * @author Johannes Bade & Thomas Gerding * */ public void getFileTypeMap() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (FileImporter), getFileTypeMap()"); fileTypeMap = new HashMap(); RandomAccessFile configFile; try { configFile = new RandomAccessFile("config/filetypes.txt", "r"); for (String line; (line = configFile.readLine()) != null;) { StringTokenizer stx = new StringTokenizer(line, ";"); String tmpType = stx.nextToken(); StringTokenizer sty = new StringTokenizer(stx.nextToken(), ","); while (sty.hasMoreElements()) { fileTypeMap.put(sty.nextToken(), tmpType); } } } catch (FileNotFoundException e) { LOG.debug("", e); } catch (IOException e) { LOG.debug("", e); } // LOG.debug(fileTypeMap); } } filius-2.9.4+ds/src/main/java/filius/software/lokal/ImageViewer.java000066400000000000000000000040351506574241500253700ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.lokal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.Anwendung; public class ImageViewer extends Anwendung { private static Logger LOG = LoggerFactory.getLogger(ImageViewer.class); public ImageViewer() // Betriebssystem betriebssystem) { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ImageWriter), constr: ImageWriter()"); } public void starten() { LOG.debug( "INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ImageWriter), starten()"); } public void beenden() { LOG.debug( "INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ImageWriter), beenden()"); } } filius-2.9.4+ds/src/main/java/filius/software/lokal/Terminal.java000066400000000000000000001342541506574241500247460ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.lokal; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.TimeOutException; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.clientserver.ClientAnwendung; import filius.software.dns.DNSNachricht; import filius.software.dns.Resolver; import filius.software.dns.ResourceRecord; import filius.software.netzzugangsschicht.Ethernet; import filius.software.system.Betriebssystem; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.transportschicht.ServerSocket; import filius.software.transportschicht.Socket; import filius.software.transportschicht.SocketSchnittstelle; import filius.software.transportschicht.TransportProtokoll; import filius.software.vermittlungsschicht.ARP; import filius.software.vermittlungsschicht.ArpPaket; import filius.software.vermittlungsschicht.IP; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.Route; import filius.software.vermittlungsschicht.RouteNotFoundException; import filius.software.vermittlungsschicht.VermittlungsProtokoll; /** * Diese Klasse soll eine Art Eingabeaufforderung oder Unix-Shell darstellen, in der zumindest rudimentaere Befehle wie * dir/ls/rename etc. moeglich sein sollen. Auerdem soll hierin auch der Start von bestimmten Serveranwendungen und * netcat moeglich sein. * * @author Thomas Gerding & Johannes Bade * */ public class Terminal extends ClientAnwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(Terminal.class); private Map usageInfo; // Betriebssystem betriebssystem; boolean abfrageVar; private DefaultMutableTreeNode aktuellerOrdner; private boolean interrupted = false; public void initUsageInfo() { if (usageInfo == null) { usageInfo = new HashMap<>(); usageInfo.put("arp", messages.getString("sw_terminal_arp") + "\n" + messages.getString("sw_terminal_usage_arp")); usageInfo.put("arpsend", messages.getString("sw_terminal_arpsend") + "\n" + messages.getString("sw_terminal_usage_arpsend")); usageInfo.put("cat", messages.getString("sw_terminal_cat") + "\n" + messages.getString("sw_terminal_usage_cat")); usageInfo.put("cd", messages.getString("sw_terminal_cd") + "\n" + messages.getString("sw_terminal_usage_cd")); usageInfo.put("copy", messages.getString("sw_terminal_copy") + "\n" + messages.getString("sw_terminal_usage_copy")); usageInfo.put("cp", messages.getString("sw_terminal_copy") + "\n" + messages.getString("sw_terminal_usage_copy")); usageInfo.put("del", messages.getString("sw_terminal_del") + "\n" + messages.getString("sw_terminal_usage_del")); usageInfo.put("dir", messages.getString("sw_terminal_dir") + "\n" + messages.getString("sw_terminal_usage_dir")); usageInfo.put("help", messages.getString("sw_terminal_help") + "\n" + messages.getString("sw_terminal_usage_help")); usageInfo.put("host", messages.getString("sw_terminal_host") + "\n" + messages.getString("sw_terminal_usage_host")); usageInfo.put("ipconfig", messages.getString("sw_terminal_ipconfig") + "\n" + messages.getString("sw_terminal_usage_ipconfig")); usageInfo.put("ls", messages.getString("sw_terminal_dir") + "\n" + messages.getString("sw_terminal_usage_dir")); usageInfo.put("mkdir", messages.getString("sw_terminal_mkdir") + "\n" + messages.getString("sw_terminal_usage_mkdir")); usageInfo.put("move", messages.getString("sw_terminal_move") + "\n" + messages.getString("sw_terminal_usage_move")); usageInfo.put("mv", messages.getString("sw_terminal_move") + "\n" + messages.getString("sw_terminal_usage_move")); usageInfo.put("netstat", messages.getString("sw_terminal_netstat") + "\n" + messages.getString("sw_terminal_usage_netstat")); usageInfo.put("nslookup", messages.getString("sw_terminal_nslookup") + "\n" + messages.getString("sw_terminal_usage_nslookup")); usageInfo.put("ping", messages.getString("sw_terminal_ping") + "\n" + messages.getString("sw_terminal_usage_ping")); usageInfo.put("pwd", messages.getString("sw_terminal_pwd") + "\n" + messages.getString("sw_terminal_usage_pwd")); usageInfo.put("rm", messages.getString("sw_terminal_del") + "\n" + messages.getString("sw_terminal_usage_del")); usageInfo.put("route", messages.getString("sw_terminal_route") + "\n" + messages.getString("sw_terminal_usage_route")); usageInfo.put("tcpdump", messages.getString("sw_terminal_tcpdump") + "\n" + messages.getString("sw_terminal_usage_tcpdump")); usageInfo.put("touch", messages.getString("sw_terminal_touch") + "\n" + messages.getString("sw_terminal_usage_touch")); usageInfo.put("traceroute", messages.getString("sw_terminal_traceroute") + "\n" + messages.getString("sw_terminal_usage_traceroute")); usageInfo.put("type", messages.getString("sw_terminal_cat") + "\n" + messages.getString("sw_terminal_usage_cat")); } } public void setSystemSoftware(InternetKnotenBetriebssystem bs) { super.setSystemSoftware(bs); this.aktuellerOrdner = getSystemSoftware().getDateisystem().getRoot(); } /** * Diese Funktion bildet "move" bzw. "rename" ab und erlaubt es eine bestimmte Datei umzubenennen. * * @param alterName * Der bisherige Dateiname * @param neuerName * Der gewnschte neue Dateiname * @return Gibt eine Meldung ueber den Erfolg oder Misserfolg des Umbenennens zurck. * @author Thomas Gerding & Johannes Bade */ public String move(String[] args) { return mv(args); } public String mv(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Terminal), mv("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 2)) { return usage("mv"); } if (pureCopy(args)) { // positive case, everything worked fine this.getSystemSoftware().getDateisystem() .deleteFile(filius.software.system.Dateisystem.absoluterPfad(getAktuellerOrdner()) + Dateisystem.FILE_SEPARATOR + args[0]); benachrichtigeBeobachter(messages.getString("sw_terminal_msg35")); return messages.getString("sw_terminal_msg35"); } else { benachrichtigeBeobachter(messages.getString("sw_terminal_msg36")); return messages.getString("sw_terminal_msg36"); } // negative case, something wrong } /** * delete file */ public String rm(String[] args) { return del(args); } public String del(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Terminal), del("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 1)) { return usage("del"); } if (this.getSystemSoftware().getDateisystem() .deleteFile(filius.software.system.Dateisystem.absoluterPfad(getAktuellerOrdner()) + Dateisystem.FILE_SEPARATOR + args[0])) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg37")); return messages.getString("sw_terminal_msg37"); } else { benachrichtigeBeobachter(messages.getString("sw_terminal_msg38")); return messages.getString("sw_terminal_msg38"); } } /** * Kopiert eine Datei * * @param Parameter * Array (String) * @return */ // // common functionality for move and copy... private boolean pureCopy(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Terminal), pureCopy("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); this.getSystemSoftware().getDateisystem().printTree(); String srcString = args[0]; if (srcString.length() > 0 && srcString.substring(0, 1).equals(Dateisystem.FILE_SEPARATOR)) { // 'pfad' // is // absolute // path! srcString = Dateisystem.evaluatePathString(srcString); } else { srcString = Dateisystem .evaluatePathString(filius.software.system.Dateisystem.absoluterPfad(getAktuellerOrdner()) + Dateisystem.FILE_SEPARATOR + srcString); } String destString = args[1]; if (destString.length() > 0 && destString.substring(0, 1).equals(Dateisystem.FILE_SEPARATOR)) { // 'pfad' // is // absolute // path! destString = Dateisystem.evaluatePathString(destString); } else { destString = Dateisystem .evaluatePathString(filius.software.system.Dateisystem.absoluterPfad(getAktuellerOrdner()) + Dateisystem.FILE_SEPARATOR + destString); } String destDir = Dateisystem.getDirectory(destString); String destFile = Dateisystem.getBasename(destString); // LOG.debug("pureCopy: source '"+srcDir+"'-'"+srcFile+"', destination // '"+destDir+"'-'"+destFile+"'"); Datei sfile = this.getSystemSoftware().getDateisystem().holeDatei(srcString); if (sfile == null) return false; Datei dfile = new Datei(destFile, sfile.getDateiTyp(), sfile.getDateiInhalt()); return this.getSystemSoftware().getDateisystem().speicherDatei(destDir, dfile); } // individual functionality for copy only public String copy(String[] args) { return cp(args); } public String cp(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (Terminal), cp("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 2)) { return usage("cp"); } if (pureCopy(args)) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg33")); return messages.getString("sw_terminal_msg33"); } // positive case, everything worked fine else { benachrichtigeBeobachter(messages.getString("sw_terminal_msg34")); return messages.getString("sw_terminal_msg34"); } // negative case, something wrong } /* */ public String ipconfig(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), ipconfig("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 0)) { return usage("ipconfig"); } Betriebssystem bs = (Betriebssystem) getSystemSoftware(); String ausgabe = ""; ausgabe += messages.getString("sw_terminal_msg4") + " " + bs.primaryIPAdresse() + "\n"; ausgabe += messages.getString("sw_terminal_msg5") + " " + bs.primarySubnetMask() + "\n"; ausgabe += messages.getString("sw_terminal_msg6666") + " " + bs.primaryMACAddress() + "\n"; ausgabe += messages.getString("sw_terminal_msg6") + " " + bs.getStandardGateway() + "\n"; ausgabe += messages.getString("sw_terminal_msg27") + " " + bs.getDNSServer() + "\n"; benachrichtigeBeobachter(ausgabe); return ausgabe; } /* Entspricht route print unter windows */ public String route(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), route("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 0)) { return usage("route"); } String ausgabe = messages.getString("sw_terminal_msg7"); LinkedList routingTabelle = getSystemSoftware().getWeiterleitungstabelle().holeTabelle(); ListIterator it = routingTabelle.listIterator(); while (it.hasNext()) { String[] eintrag = (String[]) it.next(); ausgabe += "| "; for (int i = 0; i < eintrag.length; i++) { ausgabe += eintrag[i] + stringFuellen(15 - eintrag[i].length(), " ") + " | "; } ausgabe += "\n"; } benachrichtigeBeobachter(ausgabe); return ausgabe; } /** * Diese Funktion bietet Aehnliches wie "ls" oder "dir" auf der normalen Eingabeaufforderung. Es gibt eine Liste * aller Dateien des Rechners und deren Groesse zurueck. * * @return Gibt die Liste der vorhandenen Dateien (und Verzeichnisse) in einem formatierten String zurueck, der * direkt ausgegeben werden kann. * * @author Thomas Gerding & Johannes Bade * @param Parameter * Array (String) */ public String ls(String[] args) { return dir(args); } public String dir(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), dir("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 0, 1)) { return usage("dir"); } List liste; String currPath; if (args[0].isEmpty()) { liste = getSystemSoftware().getDateisystem().listeVerzeichnis(aktuellerOrdner); currPath = Dateisystem.absoluterPfad(aktuellerOrdner); } else { if (args[0].length() > 0 && args[0].substring(0, 1).equals(Dateisystem.FILE_SEPARATOR)) { liste = getSystemSoftware().getDateisystem() .listeVerzeichnis(getSystemSoftware().getDateisystem().verzeichnisKnoten(args[0])); currPath = Dateisystem.evaluatePathString(args[0]); } else { liste = getSystemSoftware().getDateisystem() .listeVerzeichnis(Dateisystem.verzeichnisKnoten(aktuellerOrdner, args[0])); currPath = Dateisystem.evaluatePathString( Dateisystem.absoluterPfad(aktuellerOrdner) + Dateisystem.FILE_SEPARATOR + args[0]); } } if (liste == null || liste.size() == 0) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg8")); return messages.getString("sw_terminal_msg8"); } else { StringBuffer inhalt = new StringBuffer(); inhalt.append(messages.getString("sw_terminal_msg9") + " " + currPath + ":"); int anzahlVerzeichnisse = 0; int anzahlDateien = 0; for (Object tmp : liste) { inhalt.append("\n"); // Fall Datei: if (tmp instanceof Datei) { anzahlDateien++; Datei tmpDatei = (Datei) tmp; int leerzeichen = 40 - tmpDatei.getName().length(); inhalt.append(tmpDatei.getName() + stringFuellen(leerzeichen, ".") + tmpDatei.holeGroesse()); } // Fall Ordner: else { anzahlVerzeichnisse++; inhalt.append("[" + tmp + "]"); } } inhalt.append("\n"); inhalt.append(messages.getString("sw_terminal_msg10") + anzahlVerzeichnisse); inhalt.append(messages.getString("sw_terminal_msg11") + anzahlDateien); benachrichtigeBeobachter(inhalt.toString()); return inhalt.toString(); } } /** * * touch * */ public String touch(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), touch("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 1)) { return usage("touch"); } String ergebnis = messages.getString("sw_terminal_msg12"); String absPath; if (args[0].length() > 0 && args[0].substring(0, 1).equals(Dateisystem.FILE_SEPARATOR)) { absPath = Dateisystem.evaluatePathString(args[0]); } else { absPath = Dateisystem.evaluatePathString( Dateisystem.absoluterPfad(aktuellerOrdner) + Dateisystem.FILE_SEPARATOR + args[0]); } String filePath = Dateisystem.getDirectory(absPath); String dateiName = Dateisystem.getBasename(absPath); if (!dateiName.equals("")) { if (!getSystemSoftware().getDateisystem().dateiVorhanden(filePath, dateiName)) { getSystemSoftware().getDateisystem().speicherDatei(filePath, new Datei(dateiName, "text/txt", "")); ergebnis = messages.getString("sw_terminal_msg13"); } else { ergebnis = messages.getString("sw_terminal_msg14"); } } else { ergebnis = messages.getString("sw_terminal_msg15"); } benachrichtigeBeobachter(ergebnis); return ergebnis; } /** * * mkdir * */ public String mkdir(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), mkdir("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 1)) { return usage("mkdir"); } String ergebnis = messages.getString("sw_terminal_msg16"); String absPath; if (args[0].length() > 0 && args[0].substring(0, 1).equals(Dateisystem.FILE_SEPARATOR)) { absPath = Dateisystem.evaluatePathString(args[0]); } else { absPath = Dateisystem.evaluatePathString( Dateisystem.absoluterPfad(aktuellerOrdner) + Dateisystem.FILE_SEPARATOR + args[0]); } String filePath = Dateisystem.getDirectory(absPath); String dateiName = Dateisystem.getBasename(absPath); if (!dateiName.equals("")) { if (!getSystemSoftware().getDateisystem().dateiVorhanden(filePath, dateiName) && getSystemSoftware().getDateisystem().erstelleVerzeichnis(filePath, dateiName)) { ergebnis = messages.getString("sw_terminal_msg17"); } else { ergebnis = messages.getString("sw_terminal_msg18"); } } else { ergebnis = messages.getString("sw_terminal_msg19"); } benachrichtigeBeobachter(ergebnis); return ergebnis; } /** * * cd * */ public String cd(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), cd("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); String ergebnis = ""; if (!numParams(args, 0, 1)) { return usage("cd"); } if (numParams(args, 1)) { DefaultMutableTreeNode newDir; if (args[0].charAt(0) == '/') { newDir = getSystemSoftware().getDateisystem().changeDirectory(args[0]); } else { newDir = getSystemSoftware().getDateisystem() .changeDirectory(Dateisystem.absoluterPfad(aktuellerOrdner), args[0]); } if (newDir != null) { aktuellerOrdner = newDir; } else { ergebnis = messages.getString("sw_terminal_msg20"); } } else { ergebnis = Dateisystem.absoluterPfad(aktuellerOrdner); } benachrichtigeBeobachter(ergebnis); return ergebnis; } // Unix Tool 'pwd': print working directory public String pwd(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), pwd("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); if (!numParams(args, 0)) { return usage("pwd"); } String ergebnis = Dateisystem.absoluterPfad(aktuellerOrdner); benachrichtigeBeobachter(ergebnis); return ergebnis; } public String netstat(String[] args) { TransportProtokoll transport; StringBuffer ergebnis = new StringBuffer(); String protocol; ergebnis.append(messages.getString("sw_terminal_msg49")); ergebnis.append("--------------------------------------------------------------------------\n"); transport = this.getSystemSoftware().holeTcp(); protocol = "TCP"; processSockets(ergebnis, transport, protocol); transport = this.getSystemSoftware().holeUdp(); protocol = "UDP"; processSockets(ergebnis, transport, protocol); benachrichtigeBeobachter(ergebnis); return ergebnis.toString(); } private void processSockets(StringBuffer ergebnis, TransportProtokoll transport, String protocol) { for (SocketSchnittstelle socket : transport.holeAktiveSockets()) { for (SocketInformation info : getSocketInfo(socket)) { ergebnis.append(String.format("| %-7s ", protocol)); ergebnis.append(String.format("| %15s:%-5s ", info.localHost, info.localPort)); ergebnis.append(String.format("| %15s:%-5s ", info.remoteHost, info.remotePort)); ergebnis.append(String.format("| %-12s |\n", info.state)); } } } private class SocketInformation { String localHost; String localPort; String remoteHost; String remotePort; String state; } private List getSocketInfo(SocketSchnittstelle socket) { List infoList = new ArrayList<>(); if (socket instanceof Socket) { infoList.add(getSocketInfo((Socket) socket)); } else if (socket instanceof ServerSocket) { ServerSocket temp = (ServerSocket) socket; infoList.add(getSocketInfo(temp)); for (String port : temp.getSockets().keySet()) { infoList.add(getSocketInfo(temp.getSockets().get(port))); } } return infoList; } private SocketInformation getSocketInfo(ServerSocket serverSocket) { SocketInformation info = new SocketInformation(); info.remoteHost = "-"; info.remotePort = "-"; info.localHost = "0.0.0.0"; info.localPort = String.valueOf(serverSocket.getLocalPort()); info.state = "LISTEN"; return info; } private SocketInformation getSocketInfo(Socket socket) { SocketInformation info = new SocketInformation(); info.remoteHost = socket.holeZielIPAdresse(); info.remotePort = String.valueOf(socket.holeZielPort()); try { Route routingEntry = ((InternetKnotenBetriebssystem) this.getSystemSoftware()) .determineRoute(info.remoteHost); info.localHost = routingEntry.getInterfaceIpAddress(); } catch (RouteNotFoundException e) { info.localHost = ""; } info.localPort = String.valueOf(socket.holeLokalenPort()); info.state = socket.getStateAsString(); return info; } /** * * test * */ public String test(String[] args) { String ergebnis = messages.getString("sw_terminal_msg23"); if (this.getSystemSoftware().getDateisystem().speicherDatei(aktuellerOrdner, new Datei("test.txt", "txt", "blaaa"))) { ergebnis = messages.getString("sw_terminal_msg24"); } benachrichtigeBeobachter(ergebnis); return ergebnis; } /** * * help command to list all available commands implemented in this terminal application * */ public String help(String[] args) { return args.length >= 1 ? usage(args[0]) : usage(null); } private String usage(String cmd) { initUsageInfo(); if (usageInfo.containsKey(cmd)) { benachrichtigeBeobachter(usageInfo.get(cmd) + "\n"); } else { benachrichtigeBeobachter(messages.getString("sw_terminal_msg25")); } return messages.getString("sw_terminal_msg25"); } public String type(String[] args) { return cat(args); } public String cat(String[] args) { StringBuilder result = new StringBuilder(); if (args == null || args.length < 1 || args[0] == null || "".equals(args[0])) { usage("cat"); } else { Datei file = getSystemSoftware().getDateisystem().holeDatei(this.aktuellerOrdner, args[0]); if (null != file) { result.append(file.getDateiInhalt()); } else { result.append(messages.getString("sw_terminal_msg54")); } } benachrichtigeBeobachter(result.toString()); return result.toString(); } public String arp(String[] args) { StringBuilder ergebnis = new StringBuilder(); ARP arp = getSystemSoftware().holeARP(); String filterAddress = null; if (numParams(args, 2) && EingabenUeberpruefung.isValidIpAddress(args[1])) { if ("-d".contentEquals(args[0])) { arp.removeARPTableEntry(args[1]); } else if ("-a".contentEquals(args[0])) { filterAddress = args[1]; } } else if (numParams(args, 1) && "-d".contentEquals(args[0])) { arp.resetArpTable(); } ergebnis.append(messages.getString("sw_terminal_msg50")); ergebnis.append("----------------------------------------\n"); Map arpTable = null == filterAddress ? arp.holeARPTabelle() : arp.holeARPTabelle(filterAddress); for (String ipAddress : arpTable.keySet()) { ergebnis.append(String.format("| %-15s ", ipAddress)); ergebnis.append(String.format("| %-17s |\n", arpTable.get(ipAddress))); } ergebnis.append("----------------------------------------\n"); benachrichtigeBeobachter(ergebnis.toString()); return ergebnis.toString(); } /** * * 'host' command to resolve URL to an IP address using the client's DNS server entry * */ public String host(String[] args) { benachrichtigeBeobachter(Boolean.TRUE); if (!numParams(args, 1)) { usage("host"); } else { Resolver res = getSystemSoftware().holeDNSClient(); try { DNSNachricht result = res.resolveA(args[0]); int resultCount = 0; for (ResourceRecord rr : result.holeResourceRecords()) { if (ResourceRecord.ADDRESS.equals(rr.getType())) { benachrichtigeBeobachter(rr.getDomainname() + " " + messages.getString("sw_terminal_msg28") + " " + rr.getRdata() + "\n"); resultCount++; } } result = res.resolveMX(args[0]); for (ResourceRecord rr : result.holeResourceRecords()) { if (ResourceRecord.MAIL_EXCHANGE.equals(rr.getType())) { benachrichtigeBeobachter(rr.getDomainname() + " " + messages.getString("sw_terminal_msg58") + " " + rr.getRdata() + "\n"); resultCount++; } } if (resultCount == 0) { LOG.debug("Terminal 'host': result is empty!"); benachrichtigeBeobachter(messages.getString("sw_terminal_msg30")); } } catch (TimeOutException e) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg31")); } catch (Exception e) { LOG.debug("", e); benachrichtigeBeobachter(messages.getString("sw_terminal_msg29")); } } benachrichtigeBeobachter(Boolean.FALSE); return null; } public String nslookup(String[] args) { if (!numParams(args, 1)) { return usage("nslookup"); } Betriebssystem bs = (Betriebssystem) getSystemSoftware(); Resolver res = bs.holeDNSClient(); if (res == null) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg30")); return messages.getString("sw_terminal_msg30"); } benachrichtigeBeobachter(Boolean.TRUE); try { DNSNachricht response = res.resolveA(args[0], bs.getDNSServer()); String serverAddress = response.isLocal() ? IP.LOCALHOST : bs.getDNSServer(); benachrichtigeBeobachter("Server: " + serverAddress + "\n"); benachrichtigeBeobachter("Address: " + serverAddress + "\n"); benachrichtigeBeobachter("\nNon-authoritative Answer:\n"); for (ResourceRecord rr : response.holeAntwortResourceRecords()) { benachrichtigeBeobachter("Name: " + rr.getDomainname() + "\n"); benachrichtigeBeobachter("Address: " + rr.getRdata() + "\n"); } } catch (TimeOutException e) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg31") + "\n"); } catch (Exception e) { LOG.debug("", e); benachrichtigeBeobachter(messages.getString("sw_terminal_msg29") + "\n"); } benachrichtigeBeobachter(Boolean.FALSE); return null; } /** * 'ping' command to check connectivity via ICMP echo request/reply */ public String ping(String[] args) { LOG.debug("ping with params: {}", args.toString()); String target = null; boolean enableBroadcast = false; if (numParams(args, 1)) { target = args[0]; } else if (numParams(args, 2)) { target = args[1]; enableBroadcast = true; } else { return usage("ping"); } Resolver res = getSystemSoftware().holeDNSClient(); if (res == null) { LOG.debug("ERROR (" + this.hashCode() + "): Terminal 'host': Resolver is null!"); benachrichtigeBeobachter(messages.getString("sw_terminal_msg30")); return messages.getString("sw_terminal_msg30"); } // first: resolve host name String targetIp; try { targetIp = IP.ipCheck(target); if (targetIp == null) { // args[0] is not an IP address targetIp = res.holeIPAdresse(target); } if (targetIp == null) { // args[0] could also not be resolved LOG.debug("ERROR (" + this.hashCode() + "): Terminal 'host': result is null!"); benachrichtigeBeobachter(messages.getString("sw_terminal_msg30")); return messages.getString("sw_terminal_msg30"); } } catch (TimeOutException e) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg31") + " (DNS)"); return messages.getString("sw_terminal_msg31" + " (DNS)"); } catch (Exception e) { LOG.debug("", e); benachrichtigeBeobachter(messages.getString("sw_terminal_msg29")); return messages.getString("sw_terminal_msg29"); } try { Route route = ((InternetKnotenBetriebssystem) getSystemSoftware()).determineRoute(targetIp); if (!enableBroadcast && VermittlungsProtokoll.isBroadcast(targetIp, route.getInterfaceIpAddress(), route.getNetMask())) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg53")); return messages.getString("sw_terminal_msg53"); } } catch (RouteNotFoundException e1) { benachrichtigeBeobachter(messages.getString("sw_terminal_msg52")); return messages.getString("sw_terminal_msg52"); } // second: send several ICMP echo requests long timeStart, timeDiff; // inform about a multiple data transmission to the observer benachrichtigeBeobachter(Boolean.TRUE); benachrichtigeBeobachter("PING " + target + " (" + targetIp + ")"); int receivedReplies = 0; int num; int loopNumber = Information.isPosixCommandLineToolBehaviour() ? 10 : 4; for (num = 0; !interrupted && num < loopNumber; num++) { try { timeStart = System.currentTimeMillis(); IcmpPaket pingResponse = getSystemSoftware().holeICMP().startSinglePing(targetIp, num + 1); timeDiff = 1000 - (System.currentTimeMillis() - timeStart); if (pingResponse.getTtl() >= 0) { benachrichtigeBeobachter("\nFrom " + pingResponse.getSender() + ": icmp_seq=" + (num + 1) + " ttl=" + pingResponse.getTtl() + " time=" + (System.currentTimeMillis() - timeStart) + "ms"); receivedReplies++; } if (timeDiff > 0) { try { LOG.debug("Terminal waits for " + timeDiff + " ms"); Thread.sleep(timeDiff); } catch (InterruptedException e) {} } } catch (java.util.concurrent.TimeoutException e) { benachrichtigeBeobachter( "\nFrom " + args[0] + " (" + targetIp + "): icmp_seq=" + (num + 1) + " -- Timeout!"); } catch (Exception e) { LOG.debug("", e); } } // print statistics benachrichtigeBeobachter("\n--- " + args[0] + " " + messages.getString("sw_terminal_msg45") + " ---\n" + num + " " + messages.getString("sw_terminal_msg46") + ", " + receivedReplies + " " + messages.getString("sw_terminal_msg47") + ", " + ((int) Math.round((1 - (((double) receivedReplies) / ((double) num))) * 100)) + "% " + messages.getString("sw_terminal_msg48") + "\n"); benachrichtigeBeobachter(Boolean.FALSE); return ""; } /** * 'tcpdump' simple tool to show data exchange */ public String tcpdump(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), tcpdump("); for (int i = 0; i < args.length; i++) { LOG.debug(i + "='" + args[i] + "' "); } LOG.debug(")"); benachrichtigeBeobachter(Boolean.TRUE); benachrichtigeBeobachter(messages.getString("sw_terminal_msg55")); Lauscher lauscher = Lauscher.getLauscher(); String localMacAddress = ((Betriebssystem) getSystemSoftware()).primaryMACAddress(); int offset = lauscher.getOffsetByTimestamp(localMacAddress, System.currentTimeMillis()); while (!interrupted) { Object[][] data = lauscher.getDaten(localMacAddress, true, offset); for (int i = 0; i < data.length; i++) { Object[] packetData = data[i]; int currentFrameSerialNumber = Integer.parseInt(packetData[0].toString()); int nextFrameSerialNumber = data.length > i + 1 ? Integer.parseInt(data[i + 1][0].toString()) : Integer.MAX_VALUE; if (currentFrameSerialNumber < nextFrameSerialNumber) { String message = packetData[1] + " " + packetData[4] + " " + packetData[2] + " > " + packetData[3] + " " + packetData[6]; benachrichtigeBeobachter("\n " + message.trim().replace("\n", " ")); offset++; } } try { Thread.sleep(100); } catch (InterruptedException e) {} } benachrichtigeBeobachter(Boolean.FALSE); return ""; } public String arpsend(String[] args) { String targetIP = IP.CURRENT_NETWORK; String targetMAC = Ethernet.ETHERNET_BROADCAST; String senderIP = null; Betriebssystem os = (Betriebssystem) getSystemSoftware(); if (numParams(args, 2)) { if (EingabenUeberpruefung.isGueltig(args[1], EingabenUeberpruefung.musterIpAdresse)) { targetIP = args[1]; targetMAC = os.holeARP().holeARPTabellenEintrag(targetIP, 2, true); } if (EingabenUeberpruefung.isGueltig(args[0], EingabenUeberpruefung.musterIpAdresse)) { senderIP = args[0]; } } if (null != targetMAC && null != senderIP) { ArpPaket arpPacket = os.holeARP().sendArpReply(os.primaryMACAddress(), senderIP, targetMAC, targetIP, null); benachrichtigeBeobachter(" >>> " + arpPacket); } else { usage("arpsend"); } return null; } /** * 'traceroute' prints the route packets take to the network host (using ICMP Echo Request and ICMP Time Exceeded) */ public String traceroute(String[] args) { if (!numParams(args, 1)) { usage("traceroute"); return null; } int maxHops = 20; // 1.: Hostnamen auflösen String destIP = IP.ipCheck(args[0]); if (destIP == null) { filius.software.dns.Resolver res = getSystemSoftware().holeDNSClient(); try { destIP = res.holeIPAdresse(args[0]); } catch (TimeOutException e) { benachrichtigeBeobachter(messages.getString("sw_terminal_trace_msg2")); return null; } } if (destIP == null) { benachrichtigeBeobachter(messages.getString("sw_terminal_trace_msg3")); return null; } benachrichtigeBeobachter(Boolean.TRUE); if (destIP.equals(args[0])) { benachrichtigeBeobachter( String.format(messages.getString("sw_terminal_trace_msg4") + "\n", args[0], maxHops)); } else { benachrichtigeBeobachter( String.format(messages.getString("sw_terminal_trace_msg5") + "\n", args[0], destIP, maxHops)); } // 2.: Pings senden und gucken, was alles zurueckkommt IcmpPaket recv = null; int seqNr = 42 * 23; int fehler = 0; int ttl; for (ttl = 1; ttl <= maxHops && !interrupted; ttl++) { benachrichtigeBeobachter(" " + ttl + " "); for (int i = 0; i < 3 && !interrupted; i++) { seqNr++; recv = getSystemSoftware().holeICMP().sendProbe(destIP, ttl, seqNr); if (recv != null && recv.getSeqNr() == seqNr) { fehler = 0; break; } fehler++; benachrichtigeBeobachter("* "); } if (fehler == 0) { benachrichtigeBeobachter(recv.getSender()); if (recv.getIcmpType() != 11) { break; } } else if (fehler > 5) { break; } benachrichtigeBeobachter("\n"); } benachrichtigeBeobachter(Boolean.FALSE); if (ttl >= maxHops) { benachrichtigeBeobachter( "\n\n" + String.format(messages.getString("sw_terminal_trace_msg6"), args[0], maxHops)); } else if (interrupted) { benachrichtigeBeobachter("\n\n" + messages.getString("sw_terminal_trace_msg7")); } else if (recv != null && recv.getIcmpType() == 3) { switch (recv.getIcmpCode()) { case 0: benachrichtigeBeobachter( "\n\n" + String.format(messages.getString("sw_terminal_trace_msg8"), recv.getSender())); break; case 1: benachrichtigeBeobachter( "\n\n" + String.format(messages.getString("sw_terminal_trace_msg9"), recv.getSender())); break; default: benachrichtigeBeobachter( "\n\n" + String.format(messages.getString("sw_terminal_trace_msg10"), recv.getSender())); break; } } else if (fehler == 0) { if (ttl == 1) { benachrichtigeBeobachter( "\n\n" + String.format(messages.getString("sw_terminal_trace_msg11"), args[0])); } else { benachrichtigeBeobachter( "\n\n" + String.format(messages.getString("sw_terminal_trace_msg12"), args[0], ttl)); } } else { benachrichtigeBeobachter("\n\n" + messages.getString("sw_terminal_trace_msg13")); } return null; } public void setInterrupt(boolean val) { this.interrupted = val; } public void beenden() { setInterrupt(true); super.beenden(); } public void terminalEingabeAuswerten(String enteredCommand, String[] enteredParameters) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), terminalEingabeAuswerten(" + enteredCommand + "," + enteredParameters + ")"); Object[] args = new Object[1]; args[0] = enteredParameters; try { // test, whether method exists; if not, exception will be evaluated this.getClass().getDeclaredMethod(enteredCommand, enteredParameters.getClass()); setInterrupt(false); ausfuehren(enteredCommand, args); } catch (NoSuchMethodException e) { benachrichtigeBeobachter(messages.getString("terminal_msg2") + "\n" + messages.getString("terminal_msg3")); } catch (Exception e) { LOG.debug("", e); } } public DefaultMutableTreeNode getAktuellerOrdner() { return aktuellerOrdner; } public void setAktuellerOrdner(DefaultMutableTreeNode aktuellerOrdner) { this.aktuellerOrdner = aktuellerOrdner; } /** * * @author Hannes Johannes Bade & Thomas Gerding * * fuellt einen String mit Leerzeichen auf (bis zur länge a) * * @param a * @param fueller * @return */ private String stringFuellen(int a, String fueller) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), stringFuellen(" + a + "," + fueller + ")"); String tmp = ""; for (int i = 0; i < a; i++) { tmp = tmp + fueller; } return tmp; } /** * method to check for correct number of parameters */ private int countParams(String[] args) { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (Terminal), countParams(" + args + ")"); int count = 0; for (int i = 0; i < args.length; i++) { if (!args[i].isEmpty()) { count++; } else return count; // return on first empty entry } return count; } private boolean numParams(String[] args, int exactNum) { return (exactNum == countParams(args)); } private boolean numParams(String[] args, int minNum, int maxNum) { int count = countParams(args); return ((count >= minNum) && (count <= maxNum)); } } filius-2.9.4+ds/src/main/java/filius/software/lokal/TextEditor.java000066400000000000000000000036011506574241500252550ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.lokal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.Anwendung; /** * * * @author Thomas Gerding & Johannes Bade * */ public class TextEditor extends Anwendung { private static Logger LOG = LoggerFactory.getLogger(TextEditor.class); // Betriebssystem betriebssystem; boolean abfrageVar; public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (TextEditor), beenden()"); } public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ", " + this.getId() + ") " + getClass() + " (TextEditor), starten()"); } } filius-2.9.4+ds/src/main/java/filius/software/nat/000077500000000000000000000000001506574241500217775ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/nat/InetAddress.java000066400000000000000000000053761506574241500250620ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.nat; import java.util.Objects; import org.apache.commons.lang3.StringUtils; public class InetAddress { private String ipAddress; private int port; private int protocol; public InetAddress(String ipAddress, int port, int protocol) { super(); this.ipAddress = ipAddress; this.port = port; this.protocol = protocol; } public String getIpAddress() { return ipAddress; } public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getProtocol() { return protocol; } public void setProtocol(int protocol) { this.protocol = protocol; } @Override public boolean equals(Object other) { boolean result = true; if (null == other) { result = false; } else if (!(other instanceof InetAddress)) { result = false; } else if (!StringUtils.equals(ipAddress, ((InetAddress) other).ipAddress)) { return false; } else if (port != ((InetAddress) other).port) { return false; } else if (protocol != ((InetAddress) other).protocol) { return false; } return result; } @Override public int hashCode() { return Objects.hash(ipAddress, port, protocol); } @Override public String toString() { return ipAddress + ":" + port; } } filius-2.9.4+ds/src/main/java/filius/software/nat/NatEntry.java000066400000000000000000000015141506574241500244070ustar00rootroot00000000000000package filius.software.nat; import java.util.Date; /** * Diese Klasse stellt die Einträge für die NAT-Tabelle da */ public class NatEntry { private InetAddress address; private NatType natType; private Date lastUpdate; public InetAddress getInetAddress() { return address; } public void setInetAddress(InetAddress address) { this.address = address; } public Date getLastUpdate() { return lastUpdate; } public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } public NatType getNatType() { return natType; } public void setNatType(NatType natType) { this.natType = natType; } @Override public String toString() { return address + " (" + lastUpdate + "; " + natType + ")"; // neu } }filius-2.9.4+ds/src/main/java/filius/software/nat/NatGateway.java000066400000000000000000000122511506574241500247070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.nat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Gateway; import filius.rahmenprogramm.I18n; import filius.software.firewall.Firewall; import filius.software.system.GatewayFirmware; import filius.software.vermittlungsschicht.IpPaket; public class NatGateway extends Firewall implements I18n { static final int PSEUDO_PORT_ICMP = 0; private static Logger LOG = LoggerFactory.getLogger(NatGateway.class); private NetworkAddressTranslationTable natTable = new NetworkAddressTranslationTable(); @Override protected void initFirewallThreads() { Gateway gateway = (Gateway) getSystemSoftware().getKnoten(); addAndStartThread(new NatGatewayLANThread(this, gateway.holeLANInterface(), gateway.holeWANInterface(), this.betriebssystem)); addAndStartThread(new NatGatewayWANThread(this, gateway.holeWANInterface(), gateway.holeLANInterface(), this.betriebssystem)); LOG.debug("Threads for WAN and LAN nic are started on {}", gateway.getName()); } public void insertNewConnection(int protocol, String lanIpAddress, int lanPort, String wanIpAddress, int wanPort) { InetAddress lanAddress = new InetAddress(lanIpAddress, lanPort, protocol); Gateway gateway = (Gateway) getSystemSoftware().getKnoten(); if (!natTable.hasConnection(lanAddress)) { int port = PSEUDO_PORT_ICMP; if (protocol == IpPaket.TCP) { port = getSystemSoftware().holeTcp().reserviereFreienPort(); } else if (protocol == IpPaket.UDP) { port = getSystemSoftware().holeUdp().reserviereFreienPort(); } LOG.debug("New connection in NAT table: protocol={}, port={}, address={}", protocol, port, lanAddress); natTable.addDynamic(port, wanIpAddress, protocol, lanAddress, NatType.DynamicEntry); natTable.print(); ((GatewayFirmware) gateway.getSystemSoftware()).fireNATPropertyChange(); } } public void replaceSource(IpPaket packet) { Gateway gateway = (Gateway) getSystemSoftware().getKnoten(); if (packet.getProtocol() == IpPaket.TCP || packet.getProtocol() == IpPaket.UDP) { InetAddress lanAddress = new InetAddress(packet.getSender(), packet.getSegment().getQuellPort(), packet.getProtocol()); int port = natTable.findPort(lanAddress); packet.getSegment().setQuellPort(port); } packet.setSender(gateway.holeWANInterface().getIp()); } public void replaceDestination(IpPaket packet) { int protocol = packet.getProtocol(); int port = (protocol == IpPaket.TCP || protocol == IpPaket.UDP) ? packet.getSegment().getZielPort() : PSEUDO_PORT_ICMP; String sender = (protocol == IpPaket.TCP || protocol == IpPaket.UDP) ? packet.getSender() : ""; InetAddress dest = natTable.find(port, sender, protocol); Gateway gateway = (Gateway) getSystemSoftware().getKnoten(); ((GatewayFirmware) gateway.getSystemSoftware()).fireNATPropertyChange(); if (dest != null) { packet.setEmpfaenger(dest.getIpAddress()); if (packet.getProtocol() == IpPaket.TCP || packet.getProtocol() == IpPaket.UDP) { packet.getSegment().setZielPort(dest.getPort()); } } } public NetworkAddressTranslationTable getNATTable() { return natTable; } public boolean eintragExistiert(IpPaket packet) { int protocol = packet.getProtocol(); int port = (protocol == IpPaket.TCP || protocol == IpPaket.UDP) ? packet.getSegment().getZielPort() : PSEUDO_PORT_ICMP; String sender = (protocol == IpPaket.TCP || protocol == IpPaket.UDP) ? packet.getSender() : ""; return (natTable.find(port, sender, protocol)) == null? false: true; } } filius-2.9.4+ds/src/main/java/filius/software/nat/NatGatewayLANThread.java000066400000000000000000000127721506574241500264020ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.nat; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.firewall.FirewallThread; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.SystemSoftware; import filius.software.transportschicht.TcpSegment; import filius.software.transportschicht.UdpSegment; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; import filius.software.vermittlungsschicht.VermittlungsProtokoll; public class NatGatewayLANThread extends FirewallThread implements I18n { private NatGateway natGateway; private NetzwerkInterface lanNic; private NetzwerkInterface wanNic; /** Init thread for the LAN network interface */ public NatGatewayLANThread(NatGateway natGateway, NetzwerkInterface nic, NetzwerkInterface wanNic, SystemSoftware systemSoftware) { super(natGateway, nic, systemSoftware); this.lanNic = nic; this.wanNic = wanNic; this.natGateway = natGateway; } @Override protected void verarbeiteDatenEinheit(EthernetFrame frame) { Lauscher.getLauscher().addDatenEinheit(lanNic, systemSoftware, frame); if (!checkDiscardByFirewall(frame) && !checkTTLExceeded(frame)) { updateNatTable(frame); EthernetFrame modifiedFrame = modifyOutgoingFrame(frame); forwardFrame(modifiedFrame); } } boolean checkTTLExceeded(EthernetFrame frame) { boolean exceeded = false; if (frame.getDaten() instanceof IpPaket) { IpPaket packet = (IpPaket) frame.getDaten(); if (packet.getTtl() <= 1 && isOutgoingPacket(packet)) { int seqNo = 0; if (packet instanceof IcmpPaket) { seqNo = ((IcmpPaket) packet).getSeqNr(); } natGateway.getSystemSoftware().holeICMP().sendeTimeExceededReply(packet.getSender(), seqNo); exceeded = true; } } return exceeded; } protected EthernetFrame modifyOutgoingFrame(EthernetFrame frame) { EthernetFrame modifiedFrame = frame.clone(); if (modifiedFrame.getDaten() instanceof IpPaket && !((IpPaket) modifiedFrame.getDaten()).getEmpfaenger().equals(wanNic.getIp())) { IpPaket packet = (IpPaket) modifiedFrame.getDaten(); if (isOutgoingPacket(packet)) { natGateway.replaceSource(packet); } } return modifiedFrame; } protected void updateNatTable(EthernetFrame frame) { if (frame.getDaten() instanceof IpPaket) { IpPaket packet = (IpPaket) frame.getDaten(); if (isOutgoingPacket(packet)) { if (packet.getSegment() instanceof TcpSegment) { TcpSegment tcpSegment = (TcpSegment) packet.getSegment(); if (tcpSegment.isSyn() && !tcpSegment.isAck()) { natGateway.insertNewConnection(packet.getProtocol(), packet.getSender(), tcpSegment.getQuellPort(), packet.getEmpfaenger(), tcpSegment.getZielPort()); } } else if (packet.getSegment() instanceof UdpSegment) { UdpSegment udpSegment = (UdpSegment) packet.getSegment(); natGateway.insertNewConnection(packet.getProtocol(), packet.getSender(), udpSegment.getQuellPort(), packet.getEmpfaenger(), udpSegment.getZielPort()); } else if (packet.getProtocol() == IcmpPaket.ICMP_PROTOCOL) { natGateway.insertNewConnection(packet.getProtocol(), packet.getSender(), NatGateway.PSEUDO_PORT_ICMP, packet.getEmpfaenger(), NatGateway.PSEUDO_PORT_ICMP); } } } } protected boolean isOutgoingPacket(IpPaket packet) { return !VermittlungsProtokoll.gleichesRechnernetz(packet.getEmpfaenger(), lanNic.getIp(), lanNic.getSubnetzMaske()) && !VermittlungsProtokoll.isBroadcast(packet.getEmpfaenger(), packet.getSender(), lanNic.getSubnetzMaske()) && !packet.getEmpfaenger().equals(wanNic.getIp()); } } filius-2.9.4+ds/src/main/java/filius/software/nat/NatGatewayWANThread.java000066400000000000000000000100041506574241500263770ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.nat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.firewall.FirewallThread; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.SystemSoftware; import filius.software.vermittlungsschicht.IpPaket; import filius.software.vermittlungsschicht.VermittlungsProtokoll; public class NatGatewayWANThread extends FirewallThread implements I18n { private static Logger LOG = LoggerFactory.getLogger(NatGatewayWANThread.class); private NatGateway natGateway; private NetzwerkInterface lanNic; private NetzwerkInterface wanNic; /** Init thread for the WAN network interface */ public NatGatewayWANThread(NatGateway natGateway, NetzwerkInterface nic, NetzwerkInterface lanNic, SystemSoftware systemSoftware) { super(natGateway, nic, systemSoftware); this.natGateway = natGateway; this.wanNic = nic; this.lanNic = lanNic; } @Override protected void verarbeiteDatenEinheit(EthernetFrame frame) { Lauscher.getLauscher().addDatenEinheit(wanNic, systemSoftware, frame); boolean applyNATForwarding = true; if (frame.getDaten() instanceof IpPaket) { IpPaket packet = (IpPaket) frame.getDaten(); applyNATForwarding = natGateway.eintragExistiert(packet); } if (checkToLANAddress(frame)) { LOG.debug("Discarded frame addressed to LAN address but received on WAN port: {}", frame); } else if (checkDiscardByFirewall(frame)) { LOG.debug("Discarded frame by firewall: {}", frame); } else { if (applyNATForwarding) { forwardFrame(modifyIncomingFrame(frame)); } else { forwardFrame(frame); } } } private EthernetFrame modifyIncomingFrame(EthernetFrame frame) { EthernetFrame modifiedFrame = frame.clone(); if (modifiedFrame.getDaten() instanceof IpPaket) { IpPaket packet = (IpPaket) modifiedFrame.getDaten(); if (packet.getEmpfaenger().contentEquals(wanNic.getIp())) { natGateway.replaceDestination(packet); } } return modifiedFrame; } private boolean checkToLANAddress(EthernetFrame frame) { boolean toLANAddress = false; if (null != lanNic && frame.getDaten() instanceof IpPaket) { IpPaket packet = (IpPaket) frame.getDaten(); if (VermittlungsProtokoll.gleichesRechnernetz(packet.getEmpfaenger(), lanNic.getIp(), lanNic.getSubnetzMaske())) { toLANAddress = true; } } return toLANAddress; } } filius-2.9.4+ds/src/main/java/filius/software/nat/NatType.java000066400000000000000000000001521506574241500242240ustar00rootroot00000000000000package filius.software.nat; public enum NatType { StaticEntry, DynamicEntry, DynamicEnryFromStatic } filius-2.9.4+ds/src/main/java/filius/software/nat/NetworkAddressTranslationTable.java000066400000000000000000000126711506574241500307770ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.nat; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.vermittlungsschicht.IcmpPaket; public class NetworkAddressTranslationTable { private static Logger LOG = LoggerFactory.getLogger(NetworkAddressTranslationTable.class); private ConcurrentHashMap dynamicNATTable = new ConcurrentHashMap<>(); private Map staticNATTable = new HashMap<>(); public Map getStaticNATTable() { return Collections.unmodifiableMap(staticNATTable); } public ConcurrentHashMap getDynamicNATTable() { return dynamicNATTable; } public void setStaticNATTable(Map staticNATTable) { this.staticNATTable = new HashMap(staticNATTable); } public void resetStaticNATTable() { this.staticNATTable.clear(); } void addDynamic(int port, String wanAddress, int protocol, InetAddress lanAddress, NatType natType) { if (lanAddress != null) { NatEntry entry = new NatEntry(); entry.setInetAddress(lanAddress); entry.setNatType(natType); entry.setLastUpdate(new Date()); dynamicNATTable.put(new PortProtocolPair((protocol==IcmpPaket.ICMP_PROTOCOL?"":wanAddress), port, protocol), entry); } } public void addStatic(int port, int protocol, InetAddress lanAddress) { if (lanAddress != null) { NatEntry entry = new NatEntry(); entry.setInetAddress(lanAddress); entry.setNatType(NatType.StaticEntry); staticNATTable.put(new PortProtocolPair("", port, protocol), entry); } } public InetAddress find(int port, String address, int protocol) { PortProtocolPair lookup = new PortProtocolPair(address, port, protocol); NatEntry natEntry = dynamicNATTable.get(lookup); InetAddress IpAddress = null; if (natEntry == null) { lookup = new PortProtocolPair("", port, protocol); natEntry = staticNATTable.get(lookup); if (natEntry != null) { IpAddress = natEntry.getInetAddress(); addDynamic(port, address, protocol, IpAddress, NatType.DynamicEnryFromStatic); } } else { natEntry.setLastUpdate(new Date()); IpAddress = natEntry.getInetAddress(); } return IpAddress; } public boolean hasConnection(InetAddress lanAddress) { boolean exists = false; for (PortProtocolPair key : dynamicNATTable.keySet()) { if (dynamicNATTable.get(key).getInetAddress().equals(lanAddress)) { exists = true; break; } } if (!exists) { for (PortProtocolPair key : staticNATTable.keySet()) { if (staticNATTable.get(key).getInetAddress().equals(lanAddress)) { exists = true; break; } } } return exists; } public int findPort(InetAddress lanAddress) { int port = 0; for (PortProtocolPair key : dynamicNATTable.keySet()) { if (dynamicNATTable.get(key).getInetAddress().equals(lanAddress)) { port = key.getPort(); break; } } if (port == 0) { for (PortProtocolPair key : staticNATTable.keySet()) { if (staticNATTable.get(key).getInetAddress().equals(lanAddress)) { port = key.getPort(); break; } } } return port; } public void print() { for (Entry natEntry : staticNATTable.entrySet()) { LOG.debug("{} -> {}", natEntry.getKey(), natEntry.getValue()); } for (Entry entry : dynamicNATTable.entrySet()) { LOG.debug("{} -> {}", entry.getKey(), entry.getValue()); } } } filius-2.9.4+ds/src/main/java/filius/software/nat/PortProtocolPair.java000066400000000000000000000054061506574241500261310ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.nat; import java.util.Objects; import org.apache.commons.lang3.StringUtils; public class PortProtocolPair { private int port; private int protocol; private String address; public PortProtocolPair(String address, int port, int protocol) { super(); this.port = port; this.address = address; this.protocol = protocol; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getProtocol() { return protocol; } public void setProtocol(int protocol) { this.protocol = protocol; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public boolean equals(Object other) { boolean result = true; if (null == other) { result = false; } else if (!(other instanceof PortProtocolPair)) { result = false; } else if (port != ((PortProtocolPair) other).port) { return false; } else if (!StringUtils.equals(address, ((PortProtocolPair) other).address)) { result = false; } else if (protocol != ((PortProtocolPair) other).protocol) { return false; } return result; } @Override public int hashCode() { return Objects.hash(port, protocol); } @Override public String toString() { return "prot=" + protocol + " / " + port; } } filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/000077500000000000000000000000001506574241500251425ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/Ethernet.java000066400000000000000000000167201506574241500275710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.netzzugangsschicht; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.ProtocolDataUnit; import filius.software.Protokoll; import filius.software.system.SystemSoftware; import filius.software.vermittlungsschicht.ArpPaket; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; /** Diese Klasse implementiert die Netzzugangsschicht */ public class Ethernet extends Protokoll { private static Logger LOG = LoggerFactory.getLogger(Ethernet.class); public static final String ETHERNET_BROADCAST = "FF:FF:FF:FF:FF:FF"; /** * Liste der Threads fuer die Ueberwachung der Netzwerkkarten */ private LinkedList threads = new LinkedList(); /** Puffer fuer eingehende ARP-Pakete */ private LinkedList arpPakete = new LinkedList(); /** Puffer fuer eingehende IP-Pakete */ private LinkedList ipPakete = new LinkedList(); /** Puffer fuer eingehende ICMP-Pakete */ private LinkedList icmpPakete = new LinkedList(); /** Konstruktor zur Initialisierung der Systemsoftware */ public Ethernet(SystemSoftware systemSoftware) { super(systemSoftware); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Ethernet), constr: Ethernet(" + systemSoftware + ")"); } /** Methode fuer den Zugriff auf den Puffer mit ARP-Paketen */ public LinkedList holeARPPuffer() { return arpPakete; } /** Methode fuer den Zugriff auf den Puffer mit IP-Paketen */ public LinkedList holeIPPuffer() { return ipPakete; } /** Methode fuer den Zugriff auf den Puffer mit ICMP-Paketen */ public LinkedList holeICMPPuffer() { return icmpPakete; } /** Methode fuer den Zugriff auf den Puffer mit IP-Paketen */ public void setzeIPPuffer(LinkedList puffer) { ipPakete = puffer; } /** * Sendet Pakete als Ethernet-Frame weiter. Zuerst wird dazu ueberprueft, ob die Ziel-MAC-Adresse eine eigene * Netzwerkkarte adressiert. Wenn dies nicht der Fall ist, wird der Frame ueber die Netzwerkkarte verschickt, die * durch die Quell-MAC-Adresse spezifiziert wird. * * @param useNic * the network interface controller with which the PDU must be sent */ public void senden(ProtocolDataUnit daten, String startMAC, String zielMAC, String typ, NetzwerkInterface useNic) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Ethernet), senden(" + daten + "," + startMAC + "," + zielMAC + "," + typ + ")"); EthernetFrame ethernetFrame = new EthernetFrame(daten, startMAC, zielMAC, typ); List senderNICs = new ArrayList<>(); if (null != useNic) { LOG.trace("Ethernet: send via specific nic {}", useNic); senderNICs.add(useNic); } // I do not understand the purpose of this code. It should never be used! if (senderNICs.isEmpty()) { for (NetzwerkInterface nic : ((InternetKnoten) holeSystemSoftware().getKnoten()).getNetzwerkInterfaces()) { if (nic.getMac().equalsIgnoreCase(zielMAC)) { LOG.error("Ethernet: send via nic selected by target MAC ({})", zielMAC); senderNICs.add(nic); } } } if (senderNICs.isEmpty()) { for (NetzwerkInterface nic : ((InternetKnoten) holeSystemSoftware().getKnoten()).getNetzwerkInterfaces()) { if (nic.getMac().equalsIgnoreCase(startMAC)) { senderNICs.add(nic); } } } for (NetzwerkInterface nic : senderNICs) { synchronized (nic.getPort().holeAusgangsPuffer()) { nic.getPort().holeAusgangsPuffer().add(ethernetFrame); nic.getPort().holeAusgangsPuffer().notify(); } Lauscher.getLauscher().addDatenEinheit(nic, holeSystemSoftware(), ethernetFrame); } } public LinkedList getEthernetThreads() { return threads; } /** * Hier wird zu jeder Netzwerkkarte ein Thread zur Ueberwachung des Eingangspuffers gestartet. */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Ethernet), starten()"); InternetKnoten knoten; EthernetThread interfaceBeobachter; // ensure that there are no pdu's left from last run! if (arpPakete.size() > 0) { LOG.debug("Clear ARP buffer. Still data left from last run."); arpPakete.clear(); } if (icmpPakete.size() > 0) { LOG.debug("Clear ICMP buffer. Still data left from last run."); icmpPakete.clear(); } if (ipPakete.size() > 0) { LOG.debug("Clear IP buffer. Still data left from last run."); ipPakete.clear(); } if (holeSystemSoftware().getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) holeSystemSoftware().getKnoten(); for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { interfaceBeobachter = new EthernetThread(this, nic, holeSystemSoftware()); interfaceBeobachter.starten(); try { threads.add(interfaceBeobachter); } catch (Exception e) { LOG.debug("", e); } } } } /** * beendet alle laufenden EthernetThreads zur Ueberwachung der Eingangspuffer der Netzwerkkarten */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Ethernet), beenden()"); EthernetThread interfaceBeobachter; for (int x = 0; x < threads.size(); x++) { interfaceBeobachter = (EthernetThread) threads.get(x); interfaceBeobachter.beenden(); } } } filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/EthernetFrame.java000066400000000000000000000170011506574241500305350ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.netzzugangsschicht; import java.util.HashSet; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.ProtocolDataUnit; import filius.software.vermittlungsschicht.ArpPaket; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; /** * Diese Klasse implementiert einen Frame auf der Netzzugangsschicht. */ public class EthernetFrame extends ProtocolDataUnit { private static Logger LOG = LoggerFactory.getLogger(EthernetFrame.class); private static final long serialVersionUID = 1L; /** Protokolltypen der Vermittlungsschicht */ public static final String IP = "0x800", ARP = "0x806"; /** Die Ziel-Adresse des Frames */ private String zielMacAdresse; /** Die MAC-Adresse, von dem sendenden Rechner */ private String quellMacAdresse; /** Typ des uebergeordneten Protokolls (ARP oder IP) */ private String typ; /** die Nutzdaten */ private ProtocolDataUnit daten; private Set readByLauscherForMac = new HashSet<>(); /** Konstruktor zur Initialisierung der Attribute des Frames */ public EthernetFrame(ProtocolDataUnit daten, String quellMacAdresse, String zielMacAdresse, String typ) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (EthernetFrame), constr: EthernetFrame(" + daten + "," + quellMacAdresse + "," + zielMacAdresse + "," + typ + ")"); this.zielMacAdresse = zielMacAdresse; this.quellMacAdresse = quellMacAdresse; this.typ = typ; this.daten = daten; } public EthernetFrame clone() { ProtocolDataUnit clonedData; if (daten instanceof IcmpPaket) { clonedData = ((IcmpPaket) daten).clone(); } else if (daten instanceof IpPaket) { clonedData = ((IpPaket) daten).clone(); } else { clonedData = daten; } EthernetFrame clonedFrame = new EthernetFrame(clonedData, quellMacAdresse, zielMacAdresse, typ); clonedFrame.readByLauscherForMac = readByLauscherForMac; clonedFrame.setRcvNic(getRcvNic()); return clonedFrame; } public void setReadByLauscherForMac(String mac) { readByLauscherForMac.add(mac); } public boolean isReadByLauscherForMac(String mac) { return readByLauscherForMac.contains(mac); } /** Zugriff auf die Daten, die mit dem Frame verschickt werden */ public ProtocolDataUnit getDaten() { return daten; } /** Zugriff auf die Absender-MAC-Adresse */ public String getQuellMacAdresse() { return quellMacAdresse; } /** Zugriff auf den Protokolltyp. Zulaessig sind ARP und IP */ public String getTyp() { return typ; } /** Methode fuer den Zugriff auf die Ziel-MAC-Adresse */ public String getZielMacAdresse() { return zielMacAdresse; } public String toString() { return "[" + "src=" + quellMacAdresse + ", " + "dest=" + zielMacAdresse + ", " + "type=" + typ + (daten != null ? " | " + daten : "") + "]"; } /** * Checks if the whole content (which consists of {@code zielMacAdresse}, {@code quellMacAdresse} and {@code daten}) * of this frame is equal to the frame in the argument. * * @param frame * frame to be compared to this frame * @return true if zielMacAdresse, quellMacAdresse and daten are equal * @author Christoph Irniger */ public boolean sameContent(EthernetFrame frame) { if (frame == this) { return true; } // check header if (zielMacAdresse.equals(frame.getZielMacAdresse()) && quellMacAdresse.equals(frame.getQuellMacAdresse()) && typ.equals(frame.getTyp())) { Object argumentData = frame.getDaten(); // check payload if (daten instanceof IpPaket && argumentData instanceof IpPaket) { IpPaket argumentPacket = (IpPaket) argumentData; IpPaket thisPacket = (IpPaket) daten; return thisPacket.getIdentification() == argumentPacket.getIdentification() && thisPacket.getTtl() == argumentPacket.getTtl(); } else if (daten instanceof ArpPaket && argumentData instanceof ArpPaket) { ArpPaket thisArpPacket = (ArpPaket) daten; ArpPaket argumentArpPacket = (ArpPaket) frame.getDaten(); return thisArpPacket.getArpPacketNumber() == argumentArpPacket.getArpPacketNumber(); } else { return false; } } else { return false; } } /** * If this frame and the frame in the first argument both encapsule an IP packet, this method checks if the * packets have the same content and if the IP packet inside this frame has a specific TTL. * * @param frame * the frame whose payload should be compared to this payload * @param ttl * the desired TTL of this frame in the first argument * @return true if the frame in the first argument has the same payload and the specified TTL * @author Christoph Irniger */ public boolean samePayload(EthernetFrame frame, int ttl) { Object framePayload = frame.getDaten(); // if both frames are of type IP if (daten instanceof IpPaket && framePayload instanceof IpPaket) { if (!(daten instanceof IcmpPaket) && !(framePayload instanceof IcmpPaket)) { IpPaket thisIpPacket = (IpPaket) daten; IpPaket argumentIpPacket = (IpPaket) framePayload; return thisIpPacket.getIdentification() == argumentIpPacket.getIdentification() && thisIpPacket.getTtl() == ttl; } else if (daten instanceof IcmpPaket && framePayload instanceof IcmpPaket) { IcmpPaket thisIcmpPacket = (IcmpPaket) daten; IcmpPaket argumentIcmpPacket = (IcmpPaket) framePayload; return thisIcmpPacket.getIdentification() == argumentIcmpPacket.getIdentification() && thisIcmpPacket.getTtl() == ttl; } else { return false; } } else { return false; } } } filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/EthernetThread.java000066400000000000000000000110401506574241500307070ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of Informatics and E-Learning" by a students' * project group: members (2006-2007): André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, Nadja Haßler, * Ernst Johannes Klebert, Michell Weyer supervisors: Stefan Freischlad (maintainer until 2009), Peer Stechert Project * is maintained since 2010 by Christian Eibl and Stefan Freischlad Filius 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 of the License, or (at your option) version 3. ** ** Filius 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 Filius. If not, see * . */ package filius.software.netzzugangsschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.nachrichten.Lauscher; import filius.software.ProtokollThread; import filius.software.system.SystemSoftware; import filius.software.vermittlungsschicht.ArpPaket; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; /** * Diese Klasse ueberwacht die Eingangspuffer von Netzwerkkarten. */ public class EthernetThread extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(EthernetThread.class); /** die Netzwerkkarte, deren Anschluss ueberwacht wird */ private NetzwerkInterface netzwerkInterface; /** die Systemsoftware (Betriebssystem), die auf dem Knoten läuft */ private SystemSoftware systemSoftware; /** die Ethernet-Schicht */ private Ethernet ethernet; /** * Der Konstruktor zur Initialisierung des zu ueberwachenden Puffers und der Ethernet-Schicht und der Netzwerkkarte */ public EthernetThread(Ethernet ethernet, NetzwerkInterface nic, SystemSoftware systemSoftware) { super(nic.getPort().holeEingangsPuffer()); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EthernetThread), constr: EthernetThread(" + ethernet + "," + nic + ")"); this.ethernet = ethernet; this.netzwerkInterface = nic; this.systemSoftware = systemSoftware; } /** * Hier werden die Nutzdaten des ankommenden Frames entweder in den Puffer fuer IP-Pakete oder fuer ARP-Pakete * geschrieben. */ protected void verarbeiteDatenEinheit(EthernetFrame etp) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (EthernetThread), verarbeiteDateneinheit(" + etp.toString() + ")"); // record receipt (independent of further processing) Lauscher.getLauscher().addDatenEinheit(netzwerkInterface, systemSoftware, etp); // only process in case of correct MAC address, i.e., this packet is // addressed for this NIC (or broadcast) // otherwise stop processing: if (!etp.getZielMacAdresse().equalsIgnoreCase("FF:FF:FF:FF:FF:FF") // broadcast && !etp.getZielMacAdresse().equals(this.netzwerkInterface.getMac())) { return; } etp.getDaten().setRcvNic(netzwerkInterface); if (etp.getTyp().equals(EthernetFrame.IP)) { IpPaket ipPacket = (IpPaket) etp.getDaten(); if (ipPacket.getProtocol() == IcmpPaket.ICMP_PROTOCOL) { synchronized (ethernet.holeICMPPuffer()) { ethernet.holeICMPPuffer().add((IcmpPaket) ipPacket); ethernet.holeICMPPuffer().notifyAll(); } } else { synchronized (ethernet.holeIPPuffer()) { ethernet.holeIPPuffer().add(ipPacket); ethernet.holeIPPuffer().notify(); } } } else if (etp.getTyp().equals(EthernetFrame.ARP)) { synchronized (ethernet.holeARPPuffer()) { ethernet.holeARPPuffer().add((ArpPaket) etp.getDaten()); ethernet.holeARPPuffer().notify(); } } else { LOG.info("Paket konnte nicht zugeordnet werden"); } } } filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/ModemEmpfaenger.java000066400000000000000000000105641506574241500310460ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.netzzugangsschicht; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Modem; import filius.software.ProtokollThread; import filius.software.system.ModemFirmware; /** * Diese Klasse setzt die Beobachtung eines TCP/IP-Sockets um, die ein Modem mit einem zweiten Modem verbindet und * leitet die Frames an das angeschlossene virtuelle Rechnernetz weiter. */ public class ModemEmpfaenger extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(ModemEmpfaenger.class); /** * Die Modem-Firmware zur Verbindung von Rechnernetzen ueber eine TCP/IP-Verbindung */ private ModemFirmware firmware; /** * Der Stream zum Empfang der Frames ueber den TCP/IP-Socket */ private InputStream in; /** * Konstruktor zur Initialisierung der Firmware und des Sockets. Der Konstruktor der Oberklasse wird nicht * aufgerufen. */ public ModemEmpfaenger(ModemFirmware firmware, InputStream in) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ModemAnschlussBeobachterExtern), constr: ModemAnschlussBeobachterExtern(" + firmware + "," + in + ")"); this.firmware = firmware; this.in = in; } /** * Die run()-Methode der Oberklasse muss ueberschrieben werden, weil hier kein Puffer als LinkedList sondern ein * Socket bzw. Stream ueberwacht wird. */ public void run() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ModemAnschlussBeobachterExtern), run()"); ObjectInputStream in = null; try { in = new ObjectInputStream(this.in); } catch (IOException e) { LOG.debug("", e); } while (running) { try { EthernetFrame object = (EthernetFrame) in.readObject(); verarbeiteDatenEinheit(object); } catch (Exception e) { LOG.debug("", e); if (running) { firmware.verbindungZuruecksetzen(); } } } } /** * Hier werden ankommende Frames von einem Modem an das angeschlossene virtuelle Rechnernetz weitergegeben. */ protected void verarbeiteDatenEinheit(EthernetFrame datenEinheit) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ModemAnschlussBeobachterExtern), verarbeiteDatenEinheit(" + datenEinheit.toString() + ")"); if (firmware.isStarted()) { synchronized (((Modem) firmware.getKnoten()).getErstenAnschluss().holeAusgangsPuffer()) { ((Modem) firmware.getKnoten()).getErstenAnschluss().holeAusgangsPuffer().add(datenEinheit); ((Modem) firmware.getKnoten()).getErstenAnschluss().holeAusgangsPuffer().notify(); } } } public void starten() { super.starten(); } public void beenden() { super.beenden(); } } filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/ModemSender.java000066400000000000000000000064511506574241500302150ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.netzzugangsschicht; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Modem; import filius.software.ProtokollThread; import filius.software.system.ModemFirmware; /** * Diese Klasse dient dazu, die Verbindung zu einer zweiten Instanz eines Modems ueber eine TCP/IP-Verbindung * herzustellen. Hiermit wird die Verbindung zu dem virtuellen Rechnernetz ueberwacht. */ public class ModemSender extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(ModemSender.class); /** * Der Stream zur Uebertragung der Frames ueber den TCP/IP-Socket */ private ObjectOutputStream out; /** * Der Konstruktor, in dem der zu beobachtendende Puffer an den Konstruktor der Oberklasse weitergibt und die * Firmware und Socket initialisiert. */ public ModemSender(ModemFirmware firmware, OutputStream out) { super(((Modem) firmware.getKnoten()).getErstenAnschluss().holeEingangsPuffer()); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ModemAnschlussBeobachterIntern), constr: ModemAnschlussBeobachterIntern(" + firmware + "," + out + ")"); try { this.out = new ObjectOutputStream(out); } catch (IOException e) { LOG.debug("", e); } } /** * Methode zur Verarbeitung von Frames aus dem angeschlossenen virtuellen Rechnernetzes.
    * Hier werden eingehende Frames lediglich in den Stream geschrieben. */ protected void verarbeiteDatenEinheit(EthernetFrame datenEinheit) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ModemAnschlussBeobachterIntern), verarbeiteDatenEinheit(" + datenEinheit.toString() + ")"); try { out.writeObject(datenEinheit); out.flush(); } catch (IOException e) { LOG.debug("", e); } } } filius-2.9.4+ds/src/main/java/filius/software/netzzugangsschicht/SwitchPortBeobachter.java000066400000000000000000000110741506574241500320750ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.netzzugangsschicht; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.Port; import filius.hardware.knoten.Switch; import filius.software.ProtokollThread; import filius.software.system.SwitchFirmware; /** * Der Switch-Portbeobachter dient dazu, eingehende Frames an einem Switch-Anschluss abzuholen und an den richtigen * Anschluss weiterzuleiten. */ public class SwitchPortBeobachter extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(SwitchPortBeobachter.class); /** Die Switch-Firmware */ private SwitchFirmware switchFirmware; /** Der Switch-Anschluss, der ueberwacht wird */ private Port anschluss; // private boolean threadRunning = false; /** * Der Konstruktor initialisiert den Eingangspuffer durch Aufruf des Konstruktors der Oberklasse und initialisert * die Switch-Firmware und den zu ueberwachenden Anschluss. */ public SwitchPortBeobachter(SwitchFirmware switchFirmware, Port anschluss) { super(anschluss.holeEingangsPuffer()); LOG.trace("SwitchPortBeobachter, constr: SwitchPortBeobachter(" + switchFirmware + "," + anschluss + ")"); this.switchFirmware = switchFirmware; this.anschluss = anschluss; } /** * Methode zur Weiterleitung der Frames zu dem richtigen Anschluss des Switch. *
      *
    1. Dazu wird zunaechst geprueft, ob der Frame bereits weitergeleitet wurde. Wenn es eine Wiederholung ist, die * durch einen Zyklus im Netzwerk entstanden sein kann, wird der Frame verworfen.
    2. *
    3. Dann wird der Frame zu der von der Firmware verwalteten Liste der weitergeleiteten Frames hinzugefuegt.
    4. *
    5. Die SAT-Tabelle wird ergaenzt.
    6. *
    7. Der Frame wird an einen Anschluss weitergeleitet, wenn fuer die Ziel-MAC-Adresse ein Eintrag in der SAT * existiert.
    8. *
    9. Wenn kein SAT-Eintrag existiert wird der Frame als Broadcast an alle Anschluesse des Switch * weitergeleitet.
    10. *
    */ public void verarbeiteDatenEinheit(EthernetFrame etp) { LOG.trace("(SwitchPortBeobachter), verarbeiteDatenEinheit(" + etp.toString() + ")"); if (!switchFirmware.holeDurchgelaufeneFrames().contains(etp)) { switchFirmware.holeDurchgelaufeneFrames().add(etp); Date letztes_update = new Date(); switchFirmware.hinzuSatEintrag(etp.getQuellMacAdresse(), anschluss, letztes_update); Port zielAnschluss = switchFirmware.holeAnschlussFuerMAC(etp.getZielMacAdresse()); if (zielAnschluss != null) { synchronized (zielAnschluss.holeAusgangsPuffer()) { zielAnschluss.holeAusgangsPuffer().add(etp); zielAnschluss.holeAusgangsPuffer().notify(); } } else { for (Port aktiverAnschluss : ((Switch) switchFirmware.getKnoten()).getAnschluesse()) { if (!aktiverAnschluss.isPortFrei() && (aktiverAnschluss != anschluss)) { synchronized (aktiverAnschluss.holeAusgangsPuffer()) { aktiverAnschluss.holeAusgangsPuffer().add(etp); aktiverAnschluss.holeAusgangsPuffer().notify(); } } } } } } }filius-2.9.4+ds/src/main/java/filius/software/rip/000077500000000000000000000000001506574241500220075ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/rip/RIPBeacon.java000066400000000000000000000070031506574241500244140ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import java.util.Random; import filius.exception.VerbindungsException; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.software.clientserver.ClientAnwendung; import filius.software.system.VermittlungsrechnerBetriebssystem; import filius.software.transportschicht.UDPSocket; /** * * @author pyropeter * @author stefanf */ public class RIPBeacon extends ClientAnwendung { private Random rand; public void starten() { super.starten(); rand = new Random(); ausfuehren("announce", null); } public void announce() { VermittlungsrechnerBetriebssystem bs = (VermittlungsrechnerBetriebssystem) getSystemSoftware(); RIPTable table = bs.getRIPTable(); UDPSocket sock; try { sock = new UDPSocket(bs, "255.255.255.255", 520, 521); sock.verbinden(); } catch (VerbindungsException e) { return; } while (running) { synchronized (table) { while (table.getNextBeacon() - RIPUtil.getTime() > 0) { try { table.wait(table.getNextBeacon() - RIPUtil.getTime()); } catch (InterruptedException e) {} } table.check(); broadcast(sock, bs, table); table.setNextBeacon(RIPUtil.getTime() + (int) (RIPTable.INTERVAL * (rand.nextFloat() / 3 + 0.84))); } } sock.beenden(); } public void broadcast(UDPSocket sock, VermittlungsrechnerBetriebssystem bs, RIPTable table) { InternetKnoten knoten = (InternetKnoten) bs.getKnoten(); RIPMessage msg; for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { msg = new RIPMessage(nic.getIp(), bs.primaryIPAdresse(), RIPTable.INFINITY, RIPTable.TIMEOUT); for (RIPRoute route : table.routes) { // split horizon: if (nic.getIp().equals(route.getInterfaceIpAddress())) { continue; } msg.addRoute(new RIPMessageRoute(route.getNetAddress(), route.getNetMask(), route.hops)); } sock.bind(nic.getIp()); sock.senden(msg.toString()); } } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPMessage.java000066400000000000000000000062431506574241500246160ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import java.util.LinkedList; import filius.software.vermittlungsschicht.IP; /** * * @author pyropeter * */ public class RIPMessage { public String ip; public String publicIp; public int infinity; public int timeout; public LinkedList routes; public RIPMessage(String ip, String publicIp, int infinity, int timeout) { this.ip = ip; this.publicIp = publicIp; this.infinity = infinity; this.timeout = timeout; routes = new LinkedList(); } public RIPMessage(String msg) throws IllegalArgumentException { String[] lines = msg.split("\n"); try { ip = IP.ipCheck(lines[0]); publicIp = IP.ipCheck(lines[1]); infinity = Integer.parseInt(lines[2]); timeout = Integer.parseInt(lines[3]); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(); } if (ip == null || publicIp == null || timeout <= 0 || infinity <= 0) { throw new IllegalArgumentException(); } routes = new LinkedList(); for (int i = 4; i < lines.length; i++) { routes.add(new RIPMessageRoute(lines[i])); } } public String toString() { String res = ""; res += ip + "\n"; res += publicIp + "\n"; res += infinity + "\n"; res += timeout; for (RIPMessageRoute route : routes) { res += "\n" + route.toString(); } return res; } public void addRoute(RIPMessageRoute newRoute) { for (RIPMessageRoute route : routes) { if (route.ip.equals(newRoute.ip) && route.mask.equals(newRoute.mask)) { if (newRoute.hops < route.hops) { route.hops = newRoute.hops; } return; } } routes.add(newRoute); } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPMessageRoute.java000066400000000000000000000041671506574241500256400ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import filius.software.vermittlungsschicht.IP; /** * * @author pyropeter * */ public class RIPMessageRoute { public String ip; public String mask; public int hops; public RIPMessageRoute(String ip, String mask, int hops) { this.ip = ip; this.mask = mask; this.hops = hops; } public RIPMessageRoute(String msg) throws IllegalArgumentException { String[] fields = msg.split(" "); try { ip = IP.ipCheck(fields[0]); mask = IP.ipCheck(fields[1]); hops = Integer.parseInt(fields[2]); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(); } if (ip == null || mask == null || hops < 0) { throw new IllegalArgumentException(); } } public String toString() { return ip + " " + mask + " " + hops; } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPRoute.java000066400000000000000000000045231506574241500243270ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import filius.software.vermittlungsschicht.Route; /** * * @author pyropeter * @author stefanf * */ public class RIPRoute extends Route { public long expires; public long created; public int hops; public String hopPublicIp; // hint for system administrator public RIPRoute(int timeout, String netAddr, String netMask, String nextHop, String hopPublicIp, String nic, int hops) { super(netAddr, netMask, nextHop, nic); this.created = RIPUtil.getTime(); refresh(timeout); this.hopPublicIp = hopPublicIp; this.hops = hops; } public void refresh(int timeout) { if (timeout > 0) { this.expires = RIPUtil.getTime() + timeout; } else { this.expires = 0; } } public boolean isExpired() { return (this.expires > 0) && (this.expires < RIPUtil.getTime()); } public void setGateway(String gateway) { this.gateway = gateway; } public void setInterfaceIpAddress(String interfaceIpAddress) { this.interfaceIpAddress = interfaceIpAddress; } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPServer.java000066400000000000000000000033621506574241500244770ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import filius.software.clientserver.UDPServerAnwendung; import filius.software.transportschicht.Socket; /** * * @author pyropeter * */ public class RIPServer extends UDPServerAnwendung { public RIPServer() { super(); port = 520; } protected void neuerMitarbeiter(Socket socket) { RIPServerMitarbeiter ripMitarbeiter; ripMitarbeiter = new RIPServerMitarbeiter(this, socket); ripMitarbeiter.starten(); mitarbeiter.add(ripMitarbeiter); } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPServerMitarbeiter.java000066400000000000000000000107541506574241500266720ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.software.clientserver.ServerMitarbeiter; import filius.software.system.VermittlungsrechnerBetriebssystem; import filius.software.transportschicht.Socket; import filius.software.vermittlungsschicht.IP; /** * * @author pyropeter * @author stefanf * */ public class RIPServerMitarbeiter extends ServerMitarbeiter { private RIPTable table; private VermittlungsrechnerBetriebssystem bs; private InternetKnoten knoten; public RIPServerMitarbeiter(RIPServer server, Socket socket) { super(server, socket); bs = (VermittlungsrechnerBetriebssystem) server.getSystemSoftware(); knoten = (InternetKnoten) bs.getKnoten(); table = bs.getRIPTable(); } protected void verarbeiteNachricht(String nachricht) { RIPMessage msg; try { msg = new RIPMessage(nachricht); } catch (IllegalArgumentException e) { return; } // find the interface that (probably) received the message String nicIp = findeInterfaceIp(msg.ip); if (nicIp == null || nicIp.equals(msg.ip)) { return; } RIPRoute route; int hops; synchronized (table) { for (RIPMessageRoute entry : msg.routes) { if (entry.hops >= msg.infinity || entry.hops + 1 >= RIPTable.INFINITY) { hops = RIPTable.INFINITY; } else { hops = entry.hops + 1; } route = table.search(entry.ip, entry.mask); if (route != null) { // route exists, just update if (!route.getGateway().equals(msg.ip) && route.hops <= hops) { continue; } if (route.hops > hops) { // found a shorter route route.setGateway(msg.ip); route.hopPublicIp = msg.publicIp; route.setInterfaceIpAddress(nicIp); } else if (route.hops < hops) { // the old route just got worse. this has to be // flushed to other routers immediately table.setNextBeacon(0); } route.hops = hops; route.refresh(msg.timeout); } else { // route is unknown, create it if (hops < RIPTable.INFINITY) { route = new RIPRoute(msg.timeout, entry.ip, entry.mask, msg.ip, msg.publicIp, nicIp, hops); table.addRoute(route); table.setNextBeacon(0); } } } table.notifyAll(); } } String findeInterfaceIp(String ipStr) { long ip = IP.inetAton(ipStr); for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { long netMask = IP.inetAton(nic.getSubnetzMaske()); long netAddr = IP.inetAton(nic.getIp()) & netMask; if ((ip & netMask) == netAddr) { return nic.getIp(); } } return null; } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPTable.java000066400000000000000000000065751506574241500242710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import java.util.LinkedList; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IP; /** * * @author pyropeter * @author stefanf * */ public class RIPTable { public static final int INFINITY = 16; /** * refresh interval in millis. the refresh is used to update the routing table in case of changes of the network * configuration. the only change that can occur during simulation mode is because of firewall re-configuration. */ public static final int INTERVAL = 1000 * 30; public static final int TIMEOUT = INTERVAL * 5 / 2; public LinkedList routes; private long nextBeacon; private InternetKnotenBetriebssystem bs; public RIPTable(InternetKnotenBetriebssystem bs) { this.bs = bs; reset(); } public void reset() { this.routes = new LinkedList(); this.setNextBeacon(RIPUtil.getTime() + 1000); } public void addRoute(RIPRoute route) { routes.add(route); } public void addLocalRoutes(InternetKnoten knoten) { long netMask, netAddr; for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { netMask = IP.inetAton(nic.getSubnetzMaske()); netAddr = IP.inetAton(nic.getIp()) & netMask; addRoute(new RIPRoute(0, IP.inetNtoa(netAddr), IP.inetNtoa(netMask), nic.getIp(), bs.primaryIPAdresse(), nic.getIp(), 0)); } } public RIPRoute search(String net, String mask) { for (RIPRoute route : routes) { if (route.getNetAddress().equals(net) && route.getNetMask().equals(mask)) { return route; } } return null; } public void check() { for (RIPRoute route : routes) { if (route.isExpired()) { route.hops = INFINITY; } } } public long getNextBeacon() { return nextBeacon; } public void setNextBeacon(long nextBeacon) { this.nextBeacon = nextBeacon; } } filius-2.9.4+ds/src/main/java/filius/software/rip/RIPUtil.java000066400000000000000000000027071506574241500241500ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.rip; import java.util.Calendar; /** * * @author pyropeter * */ public class RIPUtil { public RIPUtil() {} public static long getTime() { return Calendar.getInstance().getTimeInMillis(); } } filius-2.9.4+ds/src/main/java/filius/software/system/000077500000000000000000000000001506574241500225415ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/system/Betriebssystem.java000066400000000000000000000057771506574241500264300ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.rip.RIPTable; /** * Diese Klasse stellt die Funktionalitaet eines Betriebssystems fuer Hosts (d. h. Rechner und Notebooks) zur * Verfuegung. Spezifisch ist die Moeglichkeit, einen DHCP-Server zu installieren und die Konfiguration der * Netzwerkkarten mit DHCP durchzufuehren. Die weitere Funktionalitaet wird von der Oberklasse * (InternetKnotenBetriebssystem) zur Verfuegung gestellt. * */ @SuppressWarnings("serial") public class Betriebssystem extends InternetKnotenBetriebssystem { private static Logger LOG = LoggerFactory.getLogger(Betriebssystem.class); private String ssid; public String getSsid() { return ssid; } public void setSsid(String ssid) { this.ssid = ssid; } @Override public boolean isRipEnabled() { return false; } @Override public RIPTable getRIPTable() { return null; } /** * Starten der Threads. Der DHCP-Client wird hier gestartet, wenn die Konfiguration mit DHCP aktiviert ist. Der * DHCP-Server wird hier auch gestartet. */ @Override public synchronized void starten() { LOG.trace("(Betriebssystem), starten()"); super.starten(); } /** * Aufruf erfolgt beim Wechsel vom Aktions- zum Entwurfsmodus. Die entsprechende Methode der Oberklasse wird * aufgerufen und der DHCP-Server und -Client beendet. */ public void beenden() { LOG.trace("(Betriebssystem), beenden()"); super.beenden(); } public void nicWireless(boolean wireless) { primaryNetworkInterface().setWireless(wireless); } @Override public boolean wireless() { return primaryNetworkInterface().isWireless(); } } filius-2.9.4+ds/src/main/java/filius/software/system/Datei.java000066400000000000000000000125371506574241500244420ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.io.Serializable; import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.Base64; /** * Dateien sind Objekte, die vom Betriebssystem verwaltet werden. Jede Datei hat einen Dateinamen (der eindeutig sein * sollte, einen Datei Typ (ähnlich einem mime/type) und natürlich den Dateiinhalt selber. * * @author Nadja & Thomas Gerding * */ public class Datei extends Observable implements Serializable { private static Logger LOG = LoggerFactory.getLogger(Datei.class); private static final long serialVersionUID = 1L; /** Typ der Datei, z.B. mp3, doc, txt,... */ private String dateiTyp; /** Inhalt der Datei als String (ggf. Base64-kodiert) */ private String dateiInhalt; /** Der Dateiname (ist auch der Rueckgabewert von toString()) */ private String name; // Dateiname // real size of file without having enforced Base64 encoding private long decodedSize = -1; /** * Erzeugt ein neues Objekt vom Typ Datei. Dieser Konstruktor wird benoetigt, damit die Klasse den Anforderungen an * JavaBeans entspricht! * */ public Datei() { } /** * Erzeugt ein neues Objekt vom Typ Datei * * @param name * der Bezeichner der Datei * @param typ * ein Dateittyp * @param dateiInhalt * der eigentliche Inhalt. Binaerdateien sollten Base64-kodiert gespeichert werden. */ public Datei(String name, String typ, String dateiInhalt) { this.dateiInhalt = dateiInhalt; this.name = name; this.dateiTyp = typ; } public void setSize(long size) { this.decodedSize = size; } /** * Liefert die Groesse der Datei zurueck * * @return die Laenge des Strings dateiInhalt */ public long holeGroesse() { if (this.getDateiInhalt() == null) return 0; if (this.dateiTyp != null && this.dateiTyp.equals("text")) return this.getDateiInhalt().length(); else { try { if (decodedSize < 0) setSize(Base64.decode(this.getDateiInhalt()).length); return decodedSize; } catch (Exception e) { // current file does not seem to be Base64 encoded (that's why // an error occurred... hopefully) LOG.debug("File {} is not base64 encoded. File size is its character number.", this.name); if (decodedSize < 0) return this.getDateiInhalt().length(); else return decodedSize; } } } /** * Liefert den Dateiinhalt so zurueck, wie er gespeichert wurde. D. h., dass kodierte Dateien auch kodiert * ausgegeben werden. * * @return der Dateiinhalt als String */ public String getDateiInhalt() { return dateiInhalt; } public void setDateiInhalt(String dateiInhalt) { this.dateiInhalt = dateiInhalt; this.setChanged(); this.notifyObservers(); } /** * Methode fuer den Zugriff auf Base64-kodierte Dateien. * * @return Gibt den Base64-dekodierten Dateiinhalt als String zurück. * @see setzeDateiInhaltDecoded */ public String holeDateiInhaltDecoded() { return (String) Base64.decodeToObject(this.getDateiInhalt()); } /** * Methode fuer den Zugriff auf Base64-kodierte Dateien. * * @param dateiInhalt * einen "binaeren" Dateiinhalt, der Base64-kodiert gespeichert werden soll */ public void setzeDateiInhaltDecoded(String dateiInhalt) { this.dateiInhalt = Base64.encodeObject(dateiInhalt); } public String getDateiTyp() { return dateiTyp; } public void setDateiTyp(String dateiTyp) { this.dateiTyp = dateiTyp; } public String getName() { return name; } public void setName(String name) { this.name = name; this.setChanged(); this.notifyObservers(); } public String toString() { return name; } } filius-2.9.4+ds/src/main/java/filius/software/system/Dateisystem.java000066400000000000000000000620751506574241500257110ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.io.Serializable; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Die Klasse Dateisystem dient dazu, die Funktionalitaet des Dateisystems eines Betriebssystems nachzubilden. Das * Dateisystem ist als Baum strukturiert. Daher werden die einzelnen Verzeichnisse und Dateien als Knoten gespeichert. *
    * Dateien werden als Objekte der Klasse Datei uebergeben, die als Inhalt sowohl einfache Textdateien wie auch * Base64-kodierte binaere Dateien ermoeglicht.
    * Datei wird hier als generischer Begriff fuer eine Datei und ein Verzeichnis verwendet. * * @see javax.swing.tree.DefaultMutableTree * @see filius.software.system.Datei */ @SuppressWarnings("serial") public class Dateisystem implements Serializable { private static Logger LOG = LoggerFactory.getLogger(Dateisystem.class); /** * Trennzeichen zwischen Verzeichnissen (und Datei) in einer Pfadangabe. */ public static final String FILE_SEPARATOR = "/"; /** * Das Attribut root stellt den Wurzelknoten dar. Im Betriebssystem Linux entspricht es dem Einhaengepunkt (Mount * point) "/". */ private DefaultMutableTreeNode root; /** * Diese Klasse muss fuer die persistente Speicherung einer Filius-Projektdatei den Anforderungen einer JavaBean * genuegen. Daher ist der Paramterlose Konstruktor wichtig! */ public Dateisystem() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), constr: Dateisystem()"); root = new DefaultMutableTreeNode("root"); } /** * @deprecated Still used for persistency in project files up to version 1.10. This method is replaced by getRoot() */ @Deprecated public DefaultMutableTreeNode getArbeitsVerzeichnis() { return root; } /** * @deprecated Still used for persistency in project files up to version 1.10. This method is replaced by setRoot() */ @Deprecated public void setArbeitsVerzeichnis(DefaultMutableTreeNode arbeitsVerzeichnis) { this.root = arbeitsVerzeichnis; } // print entire tree, starting from root node public void printTree() { printSubtree("", root); } private void printSubtree(String indent, DefaultMutableTreeNode tmpRoot) { DefaultMutableTreeNode node; if (tmpRoot.getUserObject() instanceof Datei) { // LOG.debug(tmpRoot.getUserObject().toString()); } else { LOG.debug(indent + "--" + "[" + tmpRoot.getUserObject().toString() + "]"); } indent = indent + " |"; for (Enumeration e = tmpRoot.children(); e.hasMoreElements();) { node = (DefaultMutableTreeNode) e.nextElement(); printSubtree(indent, node); } } /** * Diese Methode prueft, ob eine Datei (bzw. ein Verzeichnis) in einem konkreten Verzeichnis bereits vorhanden ist. * Unterverzeichnisse werden nicht rekursiv durchsucht! * * @param verzeichnis * Das Verzeichnis, das auf einen Dateinamen geprueft wird (keine Pfadangabe!). * @param dateiName * Der Name einer Datei oder eines Verzeichnisses, nach dem gesucht wird. * @return Rueckgabewert ist, ob eine Datei oder ein Verzeichnis mit dem Bezeichner "dateiName" im Verzeichnis * "verzeichnis" vorhanden ist. */ public boolean dateiVorhanden(DefaultMutableTreeNode verzeichnis, String dateiName) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), dateiVorhanden(" + verzeichnis + "," + dateiName + ")"); if (verzeichnis == null) { return false; } else { for (Enumeration e = verzeichnis.children(); e.hasMoreElements();) { DefaultMutableTreeNode enode = (DefaultMutableTreeNode) e.nextElement(); if (enode.getUserObject().toString().equalsIgnoreCase(dateiName)) { return true; } } } return false; } /** * Diese Methode prueft, ob eine Datei (bzw. ein Verzeichnis) in einem konkreten Verzeichnis bereits vorhanden ist. * Unterverzeichnisse werden nicht rekursiv durchsucht! * * @param verzeichnis * der absolute Pfad zu dem Verzeichnis, das auf einen Dateinamen geprueft wird. * @param dateiName * Der Name einer Datei oder eines Verzeichnisses, nach dem gesucht wird. * @return Rueckgabewert ist, ob eine Datei oder ein Verzeichnis mit dem Bezeichner "dateiName" im Verzeichnis * "verzeichnis" vorhanden ist. */ public boolean dateiVorhanden(String verzeichnis, String dateiName) { return dateiVorhanden(verzeichnisKnoten(verzeichnis), dateiName); } /** * Gibt einen String zurueck, der den Pfad zu des als Parameter angegebenen Knotens darstellt (z.B. * /ordner/unterordner). * * @param node * Der Knoten im Verzeichnisbaum kann ein Verzeichnis oder eine Datei repraesentieren * @return der Dateipfad als String. Verzeichnisse werden durch den "File-Separator" getrennt. * * @see FILE_SEPARATOR */ public static String absoluterPfad(DefaultMutableTreeNode node) { StringBuffer buffer = new StringBuffer(); for (Object pathElement : ArrayUtils.remove(node.getUserObjectPath(), 0)) { buffer.append(FILE_SEPARATOR).append(pathElement); } if (buffer.length() == 0) { buffer.append(FILE_SEPARATOR); } return buffer.toString(); } public String holeRootPfad() { return absoluterPfad(root); } /** * Gibt die Knoten des Verzeichnisbaums zurueck, der unter dem angegeben Pfad zu finden ist * * @param pfad * der absolute (!) Verzeichnis- oder Dateipfad als String * @return der Knoten im Verzeichnisbaum, der durch durch den uebergebenen Pfad bezeichnet wird * @see absoluterPfad(DefaultMutableTreeNode) */ public DefaultMutableTreeNode verzeichnisKnoten(String pfad) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", verzeichnisKnoten(" + pfad + ")"); pfad = stripRoot(evaluatePathString(pfad)); if (pfad.equals(FILE_SEPARATOR) || pfad.isEmpty()) { return root; } Enumeration enumeration; DefaultMutableTreeNode node; enumeration = root.preorderEnumeration(); while (enumeration.hasMoreElements()) { node = (DefaultMutableTreeNode) enumeration.nextElement(); // LOG.debug("verzeichnisKnoten:\n\t'"+pfad+"' =?= '"+absoluterPfad(node)+"'"); if (pfad.equalsIgnoreCase(absoluterPfad(node))) { return node; } } return null; } /** * Gibt die Knoten des Verzeichnisbaums zurueck, der unter dem angegeben Pfad zu finden ist * * @param verzeichnis * Verzeichnisknoten, in dem nach einem Kinderknoten gesucht werden soll * @param pfad * der absolute Verzeichnis- oder Dateipfad als String * @return der Knoten im Verzeichnisbaum, der durch durch den uebergebenen Pfad bezeichnet wird * @see absoluterPfad(DefaultMutableTreeNode) */ public static DefaultMutableTreeNode verzeichnisKnoten(DefaultMutableTreeNode verzeichnis, String pfad) { LOG.trace("INVOKED (static) filius.software.system.Dateisystem, verzeichnisKnoten(" + verzeichnis + "," + pfad + ")"); DefaultMutableTreeNode node; String absolutePath; if (pfad.length() > 0 && pfad.substring(0, 1).equals(FILE_SEPARATOR)) { absolutePath = evaluatePathString(pfad); } else { absolutePath = evaluatePathString(absoluterPfad(verzeichnis) + FILE_SEPARATOR + pfad); } Enumeration enumeration = verzeichnis.preorderEnumeration(); while (enumeration.hasMoreElements()) { node = (DefaultMutableTreeNode) enumeration.nextElement(); if (absolutePath.equalsIgnoreCase(absoluterPfad(node))) { return node; } } return null; } /** * delete file from filesystem * */ public boolean deleteFile(String absolutePath) { LOG.debug( "INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), deleteFile(" + absolutePath + ")"); DefaultMutableTreeNode node = verzeichnisKnoten(absolutePath); if (node != null) { node.removeFromParent(); return true; } else { return false; } } /** * Mit dieser Methode wird eine Referenz auf eine Datei geholt. * * @param dateiPfad * der absolute Pfad zu der Datei * @return die Datei wenn vorhanden, sonst "null" */ public Datei holeDatei(String dateiPfad) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), holeDatei(" + dateiPfad + ")"); DefaultMutableTreeNode node; node = verzeichnisKnoten(dateiPfad); if (node != null && (node.getUserObject() instanceof Datei)) { // LOG.debug("("+this.hashCode()+") "+getClass()+", holeDatei: return='"+(Datei) // node.getUserObject()+"'"); return (Datei) node.getUserObject(); } else { // LOG.debug("("+this.hashCode()+") "+getClass()+", holeDatei: return="); return null; } } /** * Mit dieser Methode wird eine Referenz auf eine Datei geholt. * * @param verzeichnis * ein Oberverzeichnis der zu holenden Datei * @param dateiPfad * der relative Pfad der Datei im Bezug zu verzeichnis * @return die Datei wenn vorhanden, sonst "null" */ public Datei holeDatei(DefaultMutableTreeNode verzeichnis, String dateiPfad) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), holeDatei(" + verzeichnis + "," + dateiPfad + ")"); String absoluterDateiPfad; absoluterDateiPfad = absoluterPfad(verzeichnis) + FILE_SEPARATOR + dateiPfad; return holeDatei(absoluterDateiPfad); } /** * Mit dieser Methode wird eine Referenz auf eine Datei geholt. * * @param verzeichnisPfad * der absolute Pfad zu einem Oberverzeichnis der zu holenden Datei * @param dateiPfad * der relative Pfad der Datei im Bezug zu verzeichnisPfad * @return die Datei wenn vorhanden, sonst "null" */ public Datei holeDatei(String verzeichnisPfad, String dateiPfad) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), holeDatei(" + verzeichnisPfad + "," + dateiPfad + ")"); String absoluterDateiPfad; absoluterDateiPfad = verzeichnisPfad + FILE_SEPARATOR + dateiPfad; return holeDatei(absoluterDateiPfad); } /** * Sucht in einem Verzeichnis nach Dateien die dem Suchbegriff aehneln. * * * @param suchVerzeichnis * Verzeichni, in dem nach Dateien gesucht wird, die dem Suchstring entsprechen. * @param suchString * ein String nach dem in Dateinnamen gesucht wird * @return eine Liste von Dateien, in deren Dateiname der Suchstring enthalten ist */ public List dateiSuche(String suchVerzeichnis, String suchString) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Dateisystem), dateiSuche(" + suchVerzeichnis + "," + suchString + ")"); List dateien = new LinkedList(); for (Enumeration e = verzeichnisKnoten(suchVerzeichnis).children(); e.hasMoreElements();) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement(); if (node.getUserObject() instanceof Datei) { Datei tmpDatei = (Datei) node.getUserObject(); if (tmpDatei.getName().toLowerCase().matches("(.+)?" + suchString.toLowerCase() + "(.+)?")) { dateien.add(tmpDatei); } } } return dateien; } /** * Methode zum speichern einer Datei. Existiert die Datei in dem angegebenen Verzeichnis bereits, wird sie * ueberschrieben! Existiert der Knoten im Verzeichnisbaum noch nicht, wird er angelegt. * * @param verzeichnisPfad * absoluter Pfad des Verzeichnisses, in dem die Datei gespeichert werden soll * @param datei * der Dateiname der zu speichernden Datei * @return ob das Speichern erfolgreich war */ public boolean speicherDatei(String verzeichnisPfad, Datei datei) { LOG.debug("speicherDatei(" + verzeichnisPfad + "," + datei + ")"); DefaultMutableTreeNode node = null; node = verzeichnisKnoten(verzeichnisPfad); if (node != null) { if (!dateiVorhanden(node, datei.getName())) { DefaultMutableTreeNode dateiNode = new DefaultMutableTreeNode(datei); node.add(dateiNode); } else { node = verzeichnisKnoten(verzeichnisPfad + FILE_SEPARATOR + datei.getName()); Datei file = (Datei) node.getUserObject(); file.setDateiInhalt(datei.getDateiInhalt()); file.setDateiTyp(datei.getDateiTyp()); file.setSize(datei.holeGroesse()); } return true; } else { LOG.debug("Datei " + datei + " konnte nicht gespeichert werden, " + "weil Verzeichnis " + verzeichnisPfad + " nicht existiert."); return false; } } /** * Methode zum speichern einer Datei. Existiert die Datei in dem angegebenen Verzeichnis bereits, wird sie * ueberschrieben! Existiert der Knoten im Verzeichnisbaum noch nicht, wird er angelegt.
    * Diese Methode verwendet speicherDatei(String, String). * * @param verzeichnis * Verzeichnis, in dem die Datei gespeichert werden soll * @param datei * der Dateiname der zu speichernden Datei * @return ob das Speichern erfolgreich war */ public boolean speicherDatei(DefaultMutableTreeNode verzeichnis, Datei datei) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", speicherDatei(" + verzeichnis + "," + datei + ")"); return speicherDatei(absoluterPfad(verzeichnis), datei); } /** * Diese Methode legt ein neues Verzeichnis an. Wenn das Verzeichnis bereits existiert, passiert nichts. * * @param verzeichnisPfad * absoluter Pfad zu dem Verzeichnis, in dem das neue Verzeichnis erstellt werden soll * @param neuesVerzeichnis * der Name des neu zu erstellenden Verzeichnisses * @return ob das Verzeichnis nach Ausfuehrung dieser Methode existiert. D. h. wenn es bereits vorher vorhanden war, * wird auch "true" zurueck gegeben. */ public boolean erstelleVerzeichnis(String verzeichnisPfad, String neuesVerzeichnis) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", erstelleVerzeichnis(" + verzeichnisPfad + "," + neuesVerzeichnis + ")"); DefaultMutableTreeNode node; DefaultMutableTreeNode neuerNode = null; String absPath; if (neuesVerzeichnis.length() > 0 && neuesVerzeichnis.substring(0, 1).equals(FILE_SEPARATOR)) { absPath = evaluatePathString(neuesVerzeichnis); } else { absPath = evaluatePathString(verzeichnisPfad + FILE_SEPARATOR + neuesVerzeichnis); } verzeichnisPfad = getDirectory(absPath); neuesVerzeichnis = getBasename(absPath); node = verzeichnisKnoten(verzeichnisPfad); if (node != null) { if (dateiVorhanden(node, neuesVerzeichnis)) { LOG.debug( "WARNING (" + this.hashCode() + "): Verzeichnis " + neuesVerzeichnis + " wurde nicht erzeugt, " + "weil es im Verzeichnis " + verzeichnisPfad + " bereits existiert."); } else { neuerNode = new DefaultMutableTreeNode(neuesVerzeichnis); node.add(neuerNode); // LOG.debug("("+this.hashCode()+"): Verzeichnis " // + neuesVerzeichnis + " wurde erstellt."); } return true; } else { return false; } } /** * Diese Methode legt ein neues Verzeichnis an. Wenn das Verzeichnis bereits existiert, passiert nichts. * * @param verzeichnis * Verzeichnis, in dem das neue Verzeichnis erstellt werden soll * @param neuesVerzeichnis * der Name des neu zu erstellenden Verzeichnisses * @return ob das Verzeichnis nach Ausfuehrung dieser Methode existiert. D. h. wenn es bereits vorher vorhanden war, * wird auch "true" zurueck gegeben. */ public boolean erstelleVerzeichnis(DefaultMutableTreeNode verzeichnis, String neuesVerzeichnis) { return erstelleVerzeichnis(absoluterPfad(verzeichnis), neuesVerzeichnis); } /** * Methode zum abrufen einer Liste aller Dateien in einem Verzeichnis. * * @param node * das Verzeichnis, in dem die Dateien gespeichert sind * @return eine Liste der Dateien */ public List holeDateien(DefaultMutableTreeNode node) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", holeDateien(" + node + ")"); List liste = new LinkedList(); if (node == null) { return null; } else { for (Enumeration e = node.children(); e.hasMoreElements();) { DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement(); if (n.getUserObject() instanceof Datei) { Datei dat = (Datei) n.getUserObject(); liste.add(dat); } } return liste; } } /** * Methode, um den Inhalt eines Verzeichnisses zu erhalten. * * @param verzeichnis * das Verzeichnis, dessen Inhalt zurueckgegeben werden soll * @return wenn das Verzeichnis existiert, wird eine Liste mit allen Objekten der Kinderknoten zurueckgeliefert. Die * Liste enthaelt Datei-Objekte fuer Dateien und Strings fuer Verzeichnisse. Wenn das Verzeichnis nicht * existiert wird null zurueckgeliefert. */ public List listeVerzeichnis(DefaultMutableTreeNode verzeichnis) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + ", listeVerzeichnis(" + verzeichnis + ")"); LinkedList liste = new LinkedList(); if (verzeichnis == null) { return null; } else { Enumeration enumeration = verzeichnis.children(); while (enumeration.hasMoreElements()) { DefaultMutableTreeNode tmpNode = (DefaultMutableTreeNode) enumeration.nextElement(); liste.addLast(tmpNode.getUserObject()); } return liste; } } public DefaultMutableTreeNode getRoot() { return root; } public void setRoot(DefaultMutableTreeNode root) { this.root = root; } public void fixDirectory(DefaultMutableTreeNode root) { if (root.getAllowsChildren()) { for (int i = 0; i < root.getChildCount(); i++) { fixDirectory((DefaultMutableTreeNode) root.getChildAt(i)); } } if (root.getUserObject() == null) { root.setUserObject("restored-" + System.currentTimeMillis()); } else if (root.getUserObject() instanceof Datei && ((Datei) root.getUserObject()).getName().isEmpty()) { ((Datei) root.getUserObject()).setName("restored-" + System.currentTimeMillis()); } } // change current working directory public DefaultMutableTreeNode changeDirectory(String path) { DefaultMutableTreeNode newDir = verzeichnisKnoten(toAbsolutePath(path)); return newDir; } public DefaultMutableTreeNode changeDirectory(String currDir, String relPath) { return changeDirectory(currDir + Dateisystem.FILE_SEPARATOR + relPath); } /** * bunch of path calculation and assignment functions for nodes in the tree */ // evaluate '.' and '..' as special directories public static String evaluatePathString(String path) { LOG.trace("INVOKED (static) filius.software.system.Dateisystem, evaluatePathString(" + path + ")"); String result = ""; StringTokenizer tk = new StringTokenizer(path, Dateisystem.FILE_SEPARATOR); String[] pathElements = new String[tk.countTokens()]; int currIndex = -1; String currString; while (tk.hasMoreTokens()) { currString = tk.nextToken(); if (currString.equals("..")) { currIndex--; } else if (!currString.equals(".") && !currString.equals("")) { currIndex++; pathElements[currIndex] = currString; } } for (int i = 0; i <= currIndex; i++) { // NOTE: if currIndex<0, e.g. // because of multiple '..' // elements, then empty path will // be returned! result += pathElements[i]; if (i < currIndex) result += Dateisystem.FILE_SEPARATOR; } if (currIndex >= 0 && path.substring(0, 1).equals(FILE_SEPARATOR)) result = FILE_SEPARATOR + result; // add leading slash if it was // present before // LOG.debug(" \tevaluatePathString, result="+result); return result; } // strip root node denomination, e.g. 'root', from actual (absolute!) path // String representation private static String stripRoot(String path) { LOG.trace("INVOKED (static) filius.software.system.Dateisystem, stripRoot(" + path + ")"); if (path.indexOf(Dateisystem.FILE_SEPARATOR) >= 0) { return path.substring(path.indexOf(Dateisystem.FILE_SEPARATOR)); } else { return path; } } // get directory part in absolute file pathname public static String getDirectory(String path) { LOG.trace("INVOKED (static) filius.software.system.Dateisystem, getDirectory(" + path + ")"); if (path.lastIndexOf(Dateisystem.FILE_SEPARATOR) >= 0) return path.substring(0, path.lastIndexOf(Dateisystem.FILE_SEPARATOR)); else return ""; } // get filename part in absolute file pathname public static String getBasename(String path) { LOG.trace("INVOKED (static) filius.software.system.Dateisystem, getBasename(" + path + ")"); if (path.lastIndexOf(Dateisystem.FILE_SEPARATOR) >= 0) return path.substring(path.lastIndexOf(Dateisystem.FILE_SEPARATOR) + 1); else return path; } public String toAbsolutePath(String path) { return toAbsolutePath(root, path); } /** * Define the absolute path as string. But it may not exist! */ public String toAbsolutePath(DefaultMutableTreeNode currentDir, String path) { String absolutePath; if (StringUtils.startsWith(path, FILE_SEPARATOR)) { absolutePath = path; } else { absolutePath = absoluterPfad(currentDir) + FILE_SEPARATOR + path; } return absolutePath; } } filius-2.9.4+ds/src/main/java/filius/software/system/GatewayFirmware.java000066400000000000000000000310021506574241500264760ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.beans.PropertyChangeEvent; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.Verbindung; import filius.hardware.knoten.Gateway; import filius.hardware.knoten.Knoten; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.ResourceUtil; import filius.software.firewall.Firewall; import filius.software.firewall.FirewallRule; import filius.software.firewall.FirewallWebKonfig; import filius.software.firewall.FirewallWebLog; import filius.software.nat.InetAddress; import filius.software.nat.NatEntry; import filius.software.nat.NatGateway; import filius.software.nat.NetworkAddressTranslationTable; import filius.software.nat.PortProtocolPair; import filius.software.rip.RIPTable; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; import filius.software.www.WebServer; /** * The Home Router supports the following functions: *
  • DHCP server on the LAN port *
  • IP configuration with DHCP on the WAN port *
  • Firewall *
  • Webserver for basic administration */ @SuppressWarnings("serial") public class GatewayFirmware extends InternetKnotenBetriebssystem { private static Logger LOG = LoggerFactory.getLogger(GatewayFirmware.class); private long retentionTime = 300000; private boolean natMethodChanged = false; private LinkedList staticNAT = null; public long getRetentionTime() { return retentionTime * Verbindung.holeVerzoegerungsFaktor(); } public void setNatMethodChanged(boolean b) { natMethodChanged = b; } public void setStaticNAT(LinkedList staticNAT) { this.staticNAT = new LinkedList(staticNAT); } public LinkedList getStaticNAT() { return this.staticNAT; } /** Konstruktor mit Initialisierung von Firewall und Webserver */ public GatewayFirmware() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (VermittlungsrechnerBetriebssystem), constr: VermittlungsrechnerBetriebssystem()"); setIpForwardingEnabled(true); initialisiereAnwendungen(); } public void setKnoten(Knoten gateway) { super.setKnoten(gateway); getDHCPServer().bindAddress(List.of(((Gateway) gateway).holeLANInterface())); } /** The IP configuration with DHCP is supported on WAN port only. */ @Override public String dhcpEnabledMACAddress() { return ((Gateway) getKnoten()).holeWANInterface().getMac(); } @Override public NetzwerkInterface primaryNetworkInterface() { return ((Gateway) getKnoten()).holeLANInterface(); } /** * Methode zur initialisierung der Firewall und des Web-Servers mit den Erweiterungen fuer den Zugriff auf die * Firewall ueber eine Web-Schnittstelle */ private void initialisiereAnwendungen() { FirewallWebLog weblog; FirewallWebKonfig webkonfig; WebServer server = null; Firewall firewall = null; staticNAT = new LinkedList(); // Installation von Firewall und Webserver installApp("filius.software.nat.NatGateway"); installApp("filius.software.www.WebServer"); firewall = this.holeFirewall(); server = this.holeWebServer(); firewall.setDefaultPolicy(FirewallRule.DROP); firewall.setActivated(false); // Erweiterung des Webservers fuer die Anzeige der // Log-Eintraege der Firewall weblog = new FirewallWebLog(); weblog.setFirewall(firewall); weblog.setPfad("log"); server.setzePlugIn(weblog); // Erweiterung des Webservers fuer die Konfiguration // der Firewall webkonfig = new FirewallWebKonfig(); webkonfig.setWebserver(server); webkonfig.setFirewall(firewall); webkonfig.setPfad("konfig"); server.setzePlugIn(webkonfig); server.erzeugeIndexDatei(ResourceUtil .getResourcePath("tmpl/gateway_index_" + Information.getInformation().getLocaleOrDefault() + ".txt")); } /** * Starten des Webservers * * @see filius.software.system.InternetKnotenBetriebssystem.starten() */ public void starten() { super.starten(); holeWebServer().setAktiv(true); Timer timer = new Timer(); NetworkAddressTranslationTable nat = ((NatGateway) this.holeFirewall()).getNATTable(); nat.resetStaticNATTable(); if (!staticNAT.isEmpty()) { for (int i = 0; i < staticNAT.size(); i++) { String[] entry = staticNAT.get(i); if (validateData(entry)) { int protocol; switch (entry[0].toUpperCase()) { case "TCP": protocol = IpPaket.TCP; break; case "UDP": protocol = IpPaket.UDP; break; default: protocol = Integer.parseInt(entry[0]); } InetAddress lanAddress = new InetAddress(entry[2], Integer.parseInt(entry[3]), protocol); nat.addStatic(Integer.parseInt(entry[1]), protocol, lanAddress); } } } fireNATPropertyChange(); Map dynNat = nat.getDynamicNATTable(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { if (!isStarted()) { timer.cancel(); } else if (!dynNat.isEmpty()) { checkNAT(); } } }, 1000, 1000); if (natMethodChanged) { firePropertyChanged(new PropertyChangeEvent(this, "nat_method", null, null)); natMethodChanged = false; } } private Boolean validateData(String[] entry) { for (int i = 0; i < entry.length; i++) { Pattern pattern = null; switch (i) { case 0: pattern = EingabenUeberpruefung.musterProtocol; break; case 1: pattern = EingabenUeberpruefung.musterPort; break; case 2: pattern = EingabenUeberpruefung.musterIpAdresse; break; case 3: pattern = EingabenUeberpruefung.musterPort; break; } if (!EingabenUeberpruefung.isGueltig(entry[i], pattern)) { return false; } } return true; } public void beenden() { super.beenden(); } /** * Methode fuer den Zugriff auf die Firewall. Dieser Zugriff ist nicht JavaBean-konform, weil die Speicherung der * Firewall als eine Anwendung durch die Oberklasse erfolgt. */ public Firewall holeFirewall() { Firewall firewall = (Firewall) holeSoftware("filius.software.firewall.Firewall"); if (null == firewall) { firewall = (NatGateway) holeSoftware("filius.software.nat.NatGateway"); } return firewall; } /** * Methode fuer den Zugriff auf den Webserver Dieser Zugriff ist nicht JavaBean-konform, weil die Speicherung des * Webservers als eine Anwendung durch die Oberklasse erfolgt. */ public WebServer holeWebServer() { return (WebServer) holeSoftware("filius.software.www.WebServer"); } @Override public RIPTable getRIPTable() { return null; } @Override public boolean isRipEnabled() { return false; } public Vector> holeNAT() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (GatewayFirmware), holeNAT()"); NetworkAddressTranslationTable nat = null; nat = ((NatGateway) this.holeFirewall()).getNATTable(); Vector> eintraege = new Vector>(); Vector eintrag; for (PortProtocolPair key : nat.getStaticNATTable().keySet()) { eintrag = new Vector(); eintrag = createEintrag(key.getProtocol(), "*", key.getPort(), nat.getStaticNATTable().get(key).getInetAddress().getIpAddress(), nat.getStaticNATTable().get(key).getInetAddress().getPort(), null); eintraege.add(eintrag); } for (PortProtocolPair key : nat.getDynamicNATTable().keySet()) { eintrag = new Vector(); eintrag = createEintrag(key.getProtocol(),(key.getProtocol() == IcmpPaket.ICMP_PROTOCOL?"*":key.getAddress()), key.getPort(), nat.getDynamicNATTable().get(key).getInetAddress().getIpAddress(), nat.getDynamicNATTable().get(key).getInetAddress().getPort(), nat.getDynamicNATTable().get(key).getLastUpdate()); eintraege.add(eintrag); } return eintraege; } private Vector createEintrag(int prot, String wanAddress, int mappedPort, String lanAddress, int lanPort, Date update) { Vector eintrag = new Vector(); String ausgabe; SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); switch (prot) { case IpPaket.TCP: ausgabe = "TCP"; break; case IpPaket.UDP: ausgabe = "UDP"; break; case IcmpPaket.ICMP_PROTOCOL: ausgabe = "ICMP"; break; default: ausgabe = "" + prot; } eintrag.add(ausgabe); eintrag.add("" + mappedPort); eintrag.add(wanAddress); eintrag.add(lanAddress); eintrag.add("" + lanPort); if (update == null) { eintrag.add("-"); } else { eintrag.add(formatter.format(update)); } return eintrag; } /** * Methode zum Löschen der dynamischen Einträge der NAT-Tabelle */ public void loescheNAT() { Map nat = ((NatGateway) this.holeFirewall()).getNATTable().getDynamicNATTable(); nat.clear(); firePropertyChanged(new PropertyChangeEvent(this, "nat_entry", null, null)); } /** * Methode zum Überprüfen der NAT */ public void checkNAT() { Date jetzt = new Date(); ConcurrentHashMap nat = ((NatGateway) this.holeFirewall()).getNATTable() .getDynamicNATTable(); nat.forEach((portProtocolPair, natEntry) -> { if (jetzt.getTime() - natEntry.getLastUpdate().getTime() >= getRetentionTime()) { nat.remove(portProtocolPair); firePropertyChanged(new PropertyChangeEvent(this, "nat_entry", null, null)); } ; }); } public void fireNATPropertyChange() { firePropertyChanged(new PropertyChangeEvent(this, "nat_entry", null, null)); } } filius-2.9.4+ds/src/main/java/filius/software/system/InternetKnotenBetriebssystem.java000066400000000000000000000610621506574241500313050ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.EingabenUeberpruefung; import filius.rahmenprogramm.FiliusClassLoader; import filius.rahmenprogramm.Information; import filius.software.Anwendung; import filius.software.dhcp.DHCPClient; import filius.software.dhcp.DHCPServer; import filius.software.dns.Resolver; import filius.software.netzzugangsschicht.Ethernet; import filius.software.rip.RIPTable; import filius.software.transportschicht.TCP; import filius.software.transportschicht.UDP; import filius.software.vermittlungsschicht.ARP; import filius.software.vermittlungsschicht.ICMP; import filius.software.vermittlungsschicht.IP; import filius.software.vermittlungsschicht.Route; import filius.software.vermittlungsschicht.RouteNotFoundException; import filius.software.vermittlungsschicht.Weiterleitungstabelle; /** * Diese Klasse implementiert die Funktionalitaet eines Betriebssystems für Internetknoten. Dass heisst, das * Betriebssystem unterstuetzt den gesamten Protokollstapel, der fuer den Betrieb von Internetanwendungen benoetigt * wird.
    * Ausserdem stellt diese Klasse eine Schnittstelle fuer den Zugriff auf *
      *
    1. die erste Netzwerkkarte,
    2. *
    3. den DNS-Client (Resolver)
    4. *
    * zur Verfuegung. (als Entwurfsmuster Fassade) */ @SuppressWarnings("serial") public abstract class InternetKnotenBetriebssystem extends SystemSoftware { private static Logger LOG = LoggerFactory.getLogger(InternetKnotenBetriebssystem.class); /** Das lokale Dateisystem eines Rechners */ private Dateisystem dateisystem; /** der DHCP-Server, der aktiviert und deaktiviert werden kann */ private DHCPServer dhcpServer; /** ob die Konfiguration der Netzwerkkarte mit DHCP erfolgt */ private boolean dhcpKonfiguration; /** * der DHCP-Client, der zur Konfiguration der Netzwerkkarte genutzt wird, wenn die Konfiguration mit DHCP erfolgen * soll * * @see dhcpKonfiguration */ private DHCPClient dhcpClient; /** * Die installierten Anwendungen. Sie werden mit dem Anwendungsnamen als Schluessel in einer HashMap gespeichert. */ private HashMap installierteAnwendung; /** * Whether IP forwarding is enabled, i.e. if received ip pakets for another destination are sent again toward its * destination. */ private boolean ipForwardingEnabled; /** * Mit Hilfe des DNS-Client werden Rechneradressen, die als Domainname uebergeben werden aufgeloest. Ausserdem wird * er benutzt, um jegliche Anfragen an den DNS-Server zu stellen. */ private Resolver dnsclient; /** Die Transportschicht wird durch TCP und UDP implementiert. */ private TCP tcp; /** Die Transportschicht wird durch TCP und UDP implementiert. */ private UDP udp; /** * Die Vermittlungsschicht wird durch das Address Resolution Protocol (ARP) und das Internetprotokoll implementiert. * Dafür stehen die Klassen ARP und Vermittlung. */ private ARP arpVermittlung; /** * Die Vermittlungsschicht wird durch das Address Resolution Protocol (ARP) und das Internetprotokoll implementiert. * Dafür stehen die Klassen ARP und Vermittlung. */ private IP vermittlung; private ICMP icmpVermittlung; /** * Die Weiterleitungstabelle enthaelt neben Standardeintraegen ggfs. auch durch den Anwender hinzugefuegte * Eintraege. Diese zusaetzliche Funktionalitaet wird derzeit nur durch den Vermittlungsrechner genutzt. Generell * wird die Entscheidung, ueber welche Netzwerkkarte Daten versendet werden, auf Grundlage der Weiterleitungstabelle * getroffen. Sie kann nicht der Vermittlungsschicht zugeordnet werden, weil sie mit einem Projekt persistent * gespeichert werden muss. */ private Weiterleitungstabelle weiterleitungstabelle; /** * Die Netzzugangsschicht wird durch das Ethernet-Protokoll implementiert. Die zugehoerigen Threads werden vom * Betriebssystem gestartet und beendet. */ private Ethernet ethernet; /** * Konstruktor fuer das Betriebssystem eines Internetknotens. Hier werden *
      *
    • die Schichten initialisiert,
    • *
    • die installierten Anwendungen zurueck gesetzt,
    • *
    • das Dateisystem initialisiert,
    • *
    • der DNS-Client erzeugt.
    • *
    */ public InternetKnotenBetriebssystem() { super(); LOG.trace("(InternetKnotenBetriebssystem), constr: InternetKnotenBetriebssystem()"); installierteAnwendung = new HashMap(); weiterleitungstabelle = new Weiterleitungstabelle(); weiterleitungstabelle.setInternetKnotenBetriebssystem(this); arpVermittlung = new ARP(this); vermittlung = new IP(this); icmpVermittlung = new ICMP(this); ethernet = new Ethernet(this); tcp = new TCP(this); udp = new UDP(this); dateisystem = new Dateisystem(); dnsclient = new Resolver(); dnsclient.setSystemSoftware(this); dhcpServer = new DHCPServer(); dhcpServer.setSystemSoftware(this); LOG.trace("InternetKnotenBetriebssystem initialized"); } /** * Zum beenden der Protokoll-Threads und der Anwendungs-Threads. * * @see filius.software.system.SystemSoftware.beenden() */ public void beenden() { super.beenden(); LOG.trace("(InternetKnotenBetriebssystem), beenden()"); // Die einzelnen Protokoll-Threads werden beginnend // mit der untersten Schicht beendet. // Netzzugangsschicht ethernet.beenden(); // Vermittlungsschicht arpVermittlung.beenden(); vermittlung.beenden(); icmpVermittlung.beenden(); // Transportschicht tcp.beenden(); udp.beenden(); dnsclient.beenden(); dhcpServer.beenden(); if (dhcpClient != null) { dhcpClient.beenden(); } for (Anwendung anwendung : installierteAnwendung.values()) { anwendung.beenden(); } } /** Methode zum Zugriff auf den DHCP-Server. */ public DHCPServer getDHCPServer() { return dhcpServer; } /** Methode zum Zugriff auf den DHCP-Server. */ public void setDHCPServer(DHCPServer dhcpServer) { this.dhcpServer = dhcpServer; } /** * Methode zum starten der Protokoll-Threads und der Anwendungen. * * @see filius.software.system.SystemSoftware.starten() */ @Override public synchronized void starten() { super.starten(); LOG.trace("(InternetKnotenBetriebssystem), starten()"); dateisystem.fixDirectory(dateisystem.getRoot()); // Die Protokoll-Threads der einzelnen Schichten werden // beginnend mit der untersten Schicht gestartet. ethernet.starten(); arpVermittlung.starten(); vermittlung.starten(); icmpVermittlung.starten(); tcp.starten(); udp.starten(); if (dhcpServer.isAktiv()) { dhcpServer.starten(); } if (isDHCPKonfiguration()) { dhcpClient = new DHCPClient(); dhcpClient.setSystemSoftware(this); dhcpClient.starten(); } for (Anwendung anwendung : installierteAnwendung.values()) { if (anwendung != null) { anwendung.starten(); } } } /** Ob die Konfiguration der Netzwerkkarte mit DHCP erfolgt */ public boolean isDHCPKonfiguration() { return dhcpKonfiguration; } /** Ob die Konfiguration der Netzwerkkarte mit DHCP erfolgt */ public void setDHCPKonfiguration(boolean dhcp) { this.dhcpKonfiguration = dhcp; } /** Methode fuer den Zugriff auf den DNS-Resolver */ public Resolver holeDNSClient() { return dnsclient; } /** * Methode fuer den Zugriff auf das Transport Control Protocol (TCP). */ public TCP holeTcp() { return tcp; } /** * Methode fuer den Zugriff auf das User Datagram Protocol (UDP). */ public UDP holeUdp() { return udp; } /** * Methode fuer den Zugriff auf das Address Resolution Protocol (ARP). */ public ARP holeARP() { return arpVermittlung; } /** * Methode fuer den Zugriff auf das Internet Control Message Protocol (ICMP). */ public ICMP holeICMP() { return icmpVermittlung; } /** * Methode fuer den Zugriff auf das Internet Protocol (IP). */ public IP holeIP() { return vermittlung; } /** Methode fuer den Zugriff auf das Ethernet-Protokoll */ public Ethernet holeEthernet() { return ethernet; } /** * Methode fuer den Zugriff auf das Dateisystem Diese Methode wird fuer das Speichern des Dateisystems in einer * Projektdatei benoetigt. (JavaBean- konformer Zugriff erforderlich) */ public Dateisystem getDateisystem() { return dateisystem; } /** * Methode, um das Dateisystem zu setzen. Diese Methode wird fuer das Speichern des Dateisystems in einer * Projektdatei benoetigt. (JavaBean- konformer Zugriff erforderlich) * * @param dateisystem */ public void setDateisystem(Dateisystem dateisystem) { this.dateisystem = dateisystem; } public abstract RIPTable getRIPTable(); public abstract boolean isRipEnabled(); public boolean isIpForwardingEnabled() { return ipForwardingEnabled; } public void setIpForwardingEnabled(boolean ipForwardingEnabled) { this.ipForwardingEnabled = ipForwardingEnabled; } /** * Methode fuer den Zugriff auf die Hash-Map zur Verwaltung der installierten Anwendungen. Diese Methode wird * benoetigt, um den Anforderungen an JavaBeans gerecht zu werden. */ public void setInstallierteAnwendungen(HashMap anwendungen) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), setInstallierteAnwendungen()"); this.installierteAnwendung = anwendungen; } /** * Methode zur Ausgabe auf der aktuell installierten Anwendungen auf der Standardausgabe */ private void printInstallierteAnwendungen() { LOG.debug("\tInternetKnotenBetriebssystem: installierte Anwendungen:"); for (String app : installierteAnwendung.keySet()) { LOG.debug("\t - {}", app); } LOG.debug("\t ges: {}", installierteAnwendung); } /** * Methode fuer den Zugriff auf die Hash-Map zur Verwaltung der installierten Anwendungen. Diese Methode wird * benoetigt, um den Anforderungen an JavaBeans gerecht zu werden. */ public HashMap getInstallierteAnwendungen() { return installierteAnwendung; } /** * Methode fuer den Zugriff auf eine bereits installierte Anwendung. * * @param anwendungsklasse * Klasse der Anwendung * @return das Programm / die Anwendung */ public Anwendung holeSoftware(String anwendungsklasse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), holeSoftware(" + anwendungsklasse + ")"); Anwendung anwendung; if (anwendungsklasse == null) return null; anwendung = (Anwendung) installierteAnwendung.get(anwendungsklasse); if (anwendung == null) { return null; } else { return anwendung; } } /** * Methode zum Entfernen einer installierten Anwendung. * * @param awKlasse * Klasse der zu entfernenden Anwendung * @return ob eine Anwendung entfernt wurde */ public boolean entferneSoftware(String awKlasse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), entferneSoftware(" + awKlasse + ")"); printInstallierteAnwendungen(); // DEBUG boolean entfernt = false; Iterator it = installierteAnwendung.entrySet().iterator(); while (it.hasNext() && !entfernt) { if (awKlasse.equals((String) ((Entry) it.next()).getKey())) { it.remove(); entfernt = true; } } return entfernt; } public boolean installAppIfAvailable(String klassenname) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), installiereSoftware(" + klassenname + ")"); printInstallierteAnwendungen(); // DEBUG boolean erfolg = false; if (checkAlreadyInstalled(klassenname)) { LOG.info("App {} could not be installed because app is already installed.", klassenname); } else if (!checkAppAvailable(klassenname)) { LOG.info("{} could not be installed because app is not in list of available software.", klassenname); } else if (installApp(klassenname)) { LOG.info("App {} installed.", klassenname); erfolg = true; } else { LOG.info("App {} could not be installed. An error occurred.", klassenname); } return erfolg; } private boolean checkAlreadyInstalled(String klassenname) { return holeSoftware(klassenname) != null; } private boolean checkAppAvailable(String klassenname) { boolean available = false; try { for (Map app : Information.getInformation().ladeProgrammListe()) { if (klassenname.equals(app.get("Klasse"))) { available = true; } } } catch (Exception e) { LOG.debug("list of applications could not be read.", e); } return available; } public boolean installApp(String klassenname) { boolean erfolg = true; try { Class cl = Class.forName(klassenname, true, FiliusClassLoader.getInstance(Thread.currentThread().getContextClassLoader())); Anwendung neueAnwendung = (Anwendung) cl.getConstructor().newInstance(); neueAnwendung.setSystemSoftware(this); installierteAnwendung.put(klassenname, neueAnwendung); } catch (Exception e) { LOG.debug("App could not be instantiated. Probably because class could not be found.", e); erfolg = false; } return erfolg; } public boolean deinstalliereAnwendung(String anwendungsName) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), deinstalliereAnwendung(" + anwendungsName + ")"); printInstallierteAnwendungen(); // DEBUG Anwendung anwendung; if (anwendungsName == null) return false; anwendung = (Anwendung) installierteAnwendung.get(anwendungsName); if (anwendung == null) { return false; } else { installierteAnwendung.remove(anwendung.holeAnwendungsName()); return true; } } /** * Methode zur Abfrage aller aktuell installierter Anwendungen * * @return ein Array der Anwendungsnamen */ public Anwendung[] holeArrayInstallierteSoftware() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), holeArrayInstallierteSoftware()"); Anwendung[] anwendungen; Iterator it = installierteAnwendung.entrySet().iterator(); anwendungen = new Anwendung[installierteAnwendung.size()]; for (int i = 0; it.hasNext() && i < anwendungen.length; i++) { anwendungen[i] = (Anwendung) ((Entry) it.next()).getValue(); } // printInstallierteAnwendungen(); return anwendungen; } /** * Methode fuer den JavaBean-konformen Zugriff auf die Weiterleitungstabelle. Diese Methode wird aber ausserdem von * der Implementierung der Vermittlungsschicht verwendet. */ public Weiterleitungstabelle getWeiterleitungstabelle() { return weiterleitungstabelle; } public Route determineRoute(String ipAddress) throws RouteNotFoundException { return weiterleitungstabelle.holeWeiterleitungsEintrag(ipAddress); } /** * Methode fuer den JavaBean-konformen Zugriff auf die Weiterleitungstabelle. */ public void setWeiterleitungstabelle(Weiterleitungstabelle tabelle) { this.weiterleitungstabelle = tabelle; } /** * Methode fuer den Zugriff auf die IP-Adresse des Standard-Gateways, aller Netzwerkkarten. * * @return IP-Adresse der einzigen Netzwerkkarte als String */ public String getStandardGateway() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), getStandardGateway()"); InternetKnoten knoten; NetzwerkInterface nic; if (getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) getKnoten(); if (knoten.getNetzwerkInterfaces().size() > 0) { nic = (NetzwerkInterface) knoten.getNetzwerkInterfaces().get(0); return nic.getGateway(); } } return null; } /** * Methode zum Einstellen des Standard-Gateways fuer die Netwerkkarten. Das ist eine Methode des Entwurfsmusters * Fassade. * * @param gateway * IP-Adresse der Netzwerkkarten als String */ public void setStandardGateway(String gateway) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), setStandardGateway(" + gateway + ")"); InternetKnoten knoten; NetzwerkInterface nic; Iterator it; gateway = (gateway != null && gateway.trim().equals("")) ? gateway.trim() : IP.ipCheck(gateway); if (gateway != null && EingabenUeberpruefung.isGueltig(gateway, EingabenUeberpruefung.musterIpAdresseAuchLeer) && getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) getKnoten(); it = knoten.getNetzwerkInterfaces().listIterator(); while (it.hasNext()) { nic = (NetzwerkInterface) it.next(); nic.setGateway(gateway); } } } /** * Methode zum Einstellen der IP-Adresse fuer die einzige Netwerkkarte. Das ist eine Methode des Entwurfsmusters * Fassade */ public void setzeIPAdresse(String ip) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), setzeIPAdresse(" + ip + ")"); InternetKnoten knoten; ip = IP.ipCheck(ip); if (ip != null && EingabenUeberpruefung.isGueltig(ip, EingabenUeberpruefung.musterIpAdresse) && getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) getKnoten(); ((NetzwerkInterface) knoten.getNetzwerkInterfaces().get(0)).setIp(ip); } } /** * Methode fuer den Zugriff auf die IP-Adresse der einzigen Netwerkkarte. Das ist eine Methode des Entwurfsmusters * Fassade */ public String primaryIPAdresse() { NetzwerkInterface nic = primaryNetworkInterface(); return null == nic ? null : nic.getIp(); } public NetzwerkInterface primaryNetworkInterface() { NetzwerkInterface nic = null; if (getKnoten() instanceof InternetKnoten) { InternetKnoten knoten = (InternetKnoten) getKnoten(); nic = knoten.getNetzwerkInterfaces().get(0); } return nic; } /** * Methode fuer den Zugriff auf die MAC-Adresse der einzigen Netwerkkarte. Das ist eine Methode des Entwurfsmusters * Fassade */ public String primaryMACAddress() { NetzwerkInterface nic = primaryNetworkInterface(); return null == nic ? null : nic.getMac(); } public List allNetworkInterfaces() { List nicList = Collections.emptyList(); if (getKnoten() instanceof InternetKnoten) { InternetKnoten knoten = (InternetKnoten) getKnoten(); nicList = knoten.getNetzwerkInterfaces(); } return nicList; } public String dhcpEnabledMACAddress() { return primaryMACAddress(); } /** * Methode fuer den Zugriff auf die IP-Adresse des DNS-Servers der aller Netzwerkkarten. Das ist eine Methode des * Entwurfsmusters Fassade */ public String getDNSServer() { LOG.trace( "INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), getDNSServer()"); InternetKnoten knoten; if (getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) getKnoten(); if (knoten.getNetzwerkInterfaces().size() > 0) { NetzwerkInterface nic = (NetzwerkInterface) knoten.getNetzwerkInterfaces().get(0); return nic.getDns(); } } return null; } /** * Methode fuer den Zugriff auf die IP-Adresse des DNS-Servers der aller Netzwerkkarten. Das ist eine Methode des * Entwurfsmusters Fassade */ public void setDNSServer(String dns) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), setDNSServer(" + dns + ")"); InternetKnoten knoten; NetzwerkInterface nic; Iterator it; dns = (dns != null && dns.trim().equals("")) ? dns.trim() : IP.ipCheck(dns); if (dns != null && EingabenUeberpruefung.isGueltig(dns, EingabenUeberpruefung.musterIpAdresseAuchLeer) && getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) getKnoten(); it = knoten.getNetzwerkInterfaces().listIterator(); while (it.hasNext()) { nic = (NetzwerkInterface) it.next(); nic.setDns(dns); } } } /** * Methode fuer den Zugriff auf die Netzmaske der einzigen Netzwerkkarte. Das ist eine Methode des Entwurfsmusters * Fassade */ public void setzeNetzmaske(String mask) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (InternetKnotenBetriebssystem), setzeNetzmaske(" + mask + ")"); InternetKnoten knoten; mask = IP.ipCheck(mask); if (mask != null && EingabenUeberpruefung.isGueltig(mask, EingabenUeberpruefung.musterSubNetz) && getKnoten() instanceof InternetKnoten) { knoten = (InternetKnoten) getKnoten(); ((NetzwerkInterface) knoten.getNetzwerkInterfaces().get(0)).setSubnetzMaske(mask); // LOG.debug("\t" // + ((NetzwerkInterface) knoten.getNetzwerkInterfaces() // .getFirst()).getSubnetzMaske()); } } /** * Methode fuer den Zugriff auf die Netzmaske der einzigen Netzwerkkarte. Das ist eine Methode des Entwurfsmusters * Fassade */ public String primarySubnetMask() { NetzwerkInterface nic = primaryNetworkInterface(); return null == nic ? null : nic.getSubnetzMaske(); } } filius-2.9.4+ds/src/main/java/filius/software/system/ModemFirmware.java000066400000000000000000000236471506574241500261560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Modem; import filius.rahmenprogramm.I18n; import filius.software.netzzugangsschicht.ModemEmpfaenger; import filius.software.netzzugangsschicht.ModemSender; /** *

    * Diese Klasse implementiert die Systemfunktionalitaet des Modems. Dies dient dazu, Rechnernetze miteinander zu * verbinden, die auch auf verschiedenen realen Rechnern laufen koennen. *

    *

    * Das Modem unterscheidet dazu zwei Modi: Im Server-Modus wird eine Verbindungsanfrage eines zweiten Modems angenommen. * Im Client-Modus wird der Verbindungsaufbau zu einem Modem im Server-Modus initiiert. *

    *

    * Zur Verbindung zweier Modems wird eine reale TCP/IP-Verbindung aufgebaut. Das ermöglicht, dass Modems verschiedener * Filius-Prozesse Daten austauschen. Die verbundenen Modems tauschen darueber alle Daten aus, die sie aus dem jeweils * angeschlossenen virtuellen Rechnernetz empfangen. *

    *

    * Damit kann jedoch die Situation auftreten, dass sich ein Modem im Entwurfsmodus und sich das zweite im Aktionsmodus * befindet. Daten, die ein Modem empfaengt, waehrend es sich im Entwurfsmodus befindet, werden verworfen. *

    */ @SuppressWarnings("serial") public class ModemFirmware extends SystemSoftware implements Runnable, I18n { private static Logger LOG = LoggerFactory.getLogger(ModemFirmware.class); /** * Das Modem kann in zwei verschiedenen Modi betrieben werden. Als Server wartet es auf Verbindungswuensche und als * Client baut es die Verbindung zu einem anderen Modem im Server-Modus auf. */ public static final int SERVER = 1, CLIENT = 2; /** * Der tatsaechliche TCP-Port, der geoeffnet wird oder zu dem die Verbindung aufgebaut wird. D. h. es wird immer nur * der Port des Modems im Server-Modus festgelegt. */ private int port = 12345; /** * Die IP-Adresse des anderen Rechners, auf dem das Server-Modem laeuft. D. h. dieses Attribut wird nur im * Client-Modus verwendet. */ private String ipAdresse = "localhost"; private ServerSocket serverSocket; private Socket socket = null; /** Hier kommen die Daten vom anderen Modem an */ private ModemEmpfaenger empfaenger = null; /** * Hier kommen die Daten des verbundenen (eigenen) Rechnernetzes an. */ private ModemSender sender = null; /** * Der Modus, in dem das Modem betrieben wird. Das Modem kann in zwei verschiedenen Modi betrieben werden. Als * Server wartet es auf Verbindungswuensche und als Client baut es die Verbindung zu einem anderen Modem im * Server-Modus auf. */ private int mode = CLIENT; /** * Diese Methode dient dazu, ein Modem zu starten, dass im Server-Modus betrieben wird. Damit wird der TCP-Port * geoeffnet und eingehende Verbindungsanfragen koennen entgegen genommen werden. Das Warten auf eingehende * Verbindungen und die Ueberwachung des Socket-Status erfolgt in einem neuen Thread! */ public synchronized void starteServer() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ModemFirmware), starteServer()"); (new Thread(this)).start(); } /** * Mit diesr Methode wird das Modem im Client-Modus gestartet. Das heisst, dass eine TCP/IP-Verbindung zu einem * anderen Modem im Server-Modus hergestellt wird. Ausserdem wird der Thread zur Ueberwachung des Socket-Status * gestartet. */ public void starteClient() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ModemFirmware), starteClient()"); try { socket = new Socket(ipAdresse, port); aktiviereModemVerbindung(); } catch (UnknownHostException e) { LOG.debug("", e); benachrichtigeBeobacher(messages.getString("modemfirmware_msg1")); ((Modem) getKnoten()).setzeModemVerbindungAktiv(false); } catch (IOException e) { LOG.debug("", e); benachrichtigeBeobacher(messages.getString("modemfirmware_msg2")); ((Modem) getKnoten()).setzeModemVerbindungAktiv(false); } catch (InterruptedException e) { LOG.debug("", e); benachrichtigeBeobacher(null); ((Modem) getKnoten()).setzeModemVerbindungAktiv(false); } } private synchronized void aktiviereModemVerbindung() throws IOException, InterruptedException { OutputStream out = null; InputStream in = null; while (!socket.isConnected()) { Thread.sleep(100); } benachrichtigeBeobacher(null); in = socket.getInputStream(); out = socket.getOutputStream(); ((Modem) getKnoten()).setzeModemVerbindungAktiv(true); if (in != null && out != null) { leerePortPuffer(); empfaenger = new ModemEmpfaenger(this, in); sender = new ModemSender(this, out); empfaenger.starten(); sender.starten(); } } private synchronized void deaktiviereModemVerbindung() { if (empfaenger != null) { empfaenger.beenden(); empfaenger = null; } if (sender != null) { sender.beenden(); sender = null; } } /** * Mit dieser Methode wird der Modus bestimmt, in dem das Modem laeuft. Wenn der Modus gewaechselt wird, werden * eventuell bestehende Verbindungen abgebrochen. * * @param mode * der neue Modus (SERVER oder CLIENT) */ public void setMode(int mode) { trennen(); this.mode = mode; } public int getMode() { return mode; } public boolean istServerBereit() { return serverSocket != null && serverSocket.isBound(); } /** * Diese Methode wird durch ein Ereignis von der GUI aufgerufen (d. h. durch Benutzereingaben) oder durch den Thread * ausgeloest, der den Socket ueberwacht oder beim Wechsel des Modus aufgerufen.
    * Wenn noch Verbindungen bestehen werden diese abgebaut. */ public void trennen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ModemFirmware), trennen()"); deaktiviereModemVerbindung(); if (mode == SERVER && serverSocket != null) { try { serverSocket.close(); } catch (IOException e) {} serverSocket = null; } if (socket != null) { try { socket.close(); } catch (IOException e) { LOG.debug("", e); } socket = null; } benachrichtigeBeobacher(null); ((Modem) getKnoten()).setzeModemVerbindungAktiv(false); } public void verbindungZuruecksetzen() { trennen(); if (mode == SERVER) { starteServer(); } } /** * Mit dieser Methode werden gegebenenfalls noch nicht leere Puffer der Modemanschluesse vor dem Start der * Datenweiterleitung geleert. */ private void leerePortPuffer() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ModemFirmware), leerePortPuffer()"); synchronized (((Modem) getKnoten()).getErstenAnschluss().holeEingangsPuffer()) { ((Modem) getKnoten()).getErstenAnschluss().holeEingangsPuffer().clear(); } } /** * Dieser Thread wird ausschliesslich fuer den Verbindungsaufbau im Server-Modus genutzt! Das Modem im Server-Modus * wartet auf eingehende Verbindungen. Es wird aber nur eine Verbindungsanfrage angenommen. Um nicht den gesamten * Programmablauf zu unterbrechen, erfolgt das Warten in einem eigenen Thread. */ public void run() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ModemFirmware), run()"); try { serverSocket = new ServerSocket(port); benachrichtigeBeobacher(null); socket = serverSocket.accept(); aktiviereModemVerbindung(); serverSocket.close(); } catch (Exception e) { LOG.debug("EXCEPTION (" + this.hashCode() + "): Modemverbindung beendet."); ((Modem) getKnoten()).setzeModemVerbindungAktiv(false); } finally { benachrichtigeBeobacher(null); } } public String getIpAdresse() { return ipAdresse; } public void setIpAdresse(String ipAdresse) { this.ipAdresse = ipAdresse; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } } filius-2.9.4+ds/src/main/java/filius/software/system/SwitchFirmware.java000066400000000000000000000216251506574241500263500ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.beans.PropertyChangeEvent; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import java.util.LinkedList; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.Port; import filius.hardware.knoten.Switch; import filius.rahmenprogramm.I18n; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.netzzugangsschicht.SwitchPortBeobachter; /** * Diese Klasse erweitert den Wert der HashMap für die SAT um einen Eintrag für das letzte Update. */ class satEntry { private Port port; private Date letztes_Update; public Port getPort() { return port; } public Date holeLetztesUpdate() { return letztes_Update; } public void setPort(Port port) { this.port = port; } public void hinzuLetztesUpdate(Date letztes_Update) { this.letztes_Update = letztes_Update; } } /** * Diese Klasse stellt die Funktionalitaet des Switches zur Verfuegung. Wichtiges Element ist die Source Address Table * (SAT). Der Switch operiert nur auf der Netzzugangsschicht, auf der MAC-Adressen verwendet werden. */ public class SwitchFirmware extends SystemSoftware implements I18n { private static Logger LOG = LoggerFactory.getLogger(SwitchFirmware.class); private static final long serialVersionUID = 1L; /** * Die Source Address Tabel (SAT), in der die MAC-Adressen den physischen Anschluessen des Switch zugeordnet werden */ private ConcurrentHashMap sat = new ConcurrentHashMap(); /** * Liste der Anschlussbeobachter. Sie implementieren die Netzzugangsschicht. */ private LinkedList switchBeobachter; /** * Hier werden bereits weitergeleitete Frames gespeichert. Wird ein Frame wiederholt verschickt, beispielsweise * wegen einer Verbindung, die zwei Anschluesse kurzschliesst, wird der Frame verworfen. * * @see filius.software.netzzugangsschicht.SwitchPortBeobachter */ private LinkedList durchgelaufeneFrames = new LinkedList(); private String ssid = UUID.randomUUID().toString().substring(0, 6); private long retentionTime = 300000; public long getRetentionTime() { return retentionTime; } public void setRetentionTime(long retentionTime) { this.retentionTime = retentionTime; } /** * Hier wird die Netzzugangsschicht des Switch initialisiert und gestartet. Ausserdem wird die SAT zurueckgesetzt. */ public void starten() { super.starten(); LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SwitchFirmware), starten()"); SwitchPortBeobachter anschlussBeobachter; sat = new ConcurrentHashMap(); switchBeobachter = new LinkedList(); for (Port anschluss : ((Switch) getKnoten()).getAnschluesse()) { anschlussBeobachter = new SwitchPortBeobachter(this, anschluss); anschlussBeobachter.starten(); switchBeobachter.add(anschlussBeobachter); } firePropertyChanged(new PropertyChangeEvent(this, "sat_entry", null, null)); Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { if (!isStarted()) { timer.cancel(); } else if (!sat.isEmpty()) { checkSAT(); } } }, 1000, 1000); } /** Hier wird die Netzzugangsschicht des Switch gestoppt. */ public void beenden() { super.beenden(); LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SwitchFirmware), beenden()"); for (SwitchPortBeobachter anschlussBeobachter : switchBeobachter) { anschlussBeobachter.beenden(); } } /** Diese Methode wird genutzt, um die SAT abzurufen. */ public Vector> holeSAT() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SwitchFirmware), holeSAT()"); Vector> eintraege = new Vector>(); Vector eintrag; String ausgabe; SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); for (String elem : sat.keySet()) { Port anschluss = (Port) sat.get(elem).getPort(); ausgabe = messages.getString("sw_switchfirmware_msg1") + " " + (((Switch) getKnoten()).getAnschluesse().indexOf(anschluss) + 1); eintrag = new Vector(); eintrag.add(elem.toUpperCase()); eintrag.add(ausgabe); eintrag.add(formatter.format(sat.get(elem).holeLetztesUpdate())); eintraege.add(eintrag); } return eintraege; } /** * Methode zum erzeugen eines neuen Eintrags in der SAT. Wenn bereits ein Eintrag zu der uebergebenen MAC-Adresse * vorliegt, wird der alte Eintrag aktualisiert. * * @param macAdresse * die MAC-Adresse des entfernten Anschlusses * @param anschluss * der Anschluss des Switch, der mit dem entfernten Anschluss verbunden ist */ public void hinzuSatEintrag(String macAdresse, Port anschluss, Date letztes_Update) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SwitchFirmware), hinzuSatEintrag(" + macAdresse + "," + anschluss + "," + letztes_Update + ")"); satEntry eintrag = new satEntry(); eintrag.setPort(anschluss); eintrag.hinzuLetztesUpdate(letztes_Update); sat.put(macAdresse, eintrag); firePropertyChanged(new PropertyChangeEvent(this, "sat_entry", null, anschluss)); } /** * Mit dieser Methode wird der Anschluss ausgewaehlt, der die Verbindung zum Anschuss mit der uebergebenen * MAC-Adresse herstellt. Dazu wird die SAT verwendet. * * @param macAdresse * die Zieladresse eines Frames nach der in der SAT gesucht werden soll * @return der Anschluss zur MAC oder null, wenn kein passender Eintrag existiert */ public Port holeAnschlussFuerMAC(String macAdresse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SwitchFirmware), holeAnschlussFuerMAC(" + macAdresse + ")"); if (sat.containsKey(macAdresse)) { return (Port) sat.get(macAdresse).getPort(); } else { return null; } } /** * Methode zum Zugriff auf die bereits durchgelaufenen Frames. Diese wird dazu genutzt um Fehler durch Zyklen zu * vermeiden. * * @return Liste der bereits weitergeleiteten Frames. */ public LinkedList holeDurchgelaufeneFrames() { return durchgelaufeneFrames; } public void setSSID(String ssid) { this.ssid = ssid; } public String getSSID() { return ssid; } /** * Methode zum Löschen der gesamten SAT */ public void loescheSAT() { sat.clear(); firePropertyChanged(new PropertyChangeEvent(this, "sat_entry", null, null)); } /** * Methode zum Überprüfen der SAT */ public void checkSAT() { Date jetzt = new Date(); sat.forEach((mac, eintrag) -> { if (jetzt.getTime() - eintrag.holeLetztesUpdate().getTime() >= getRetentionTime()) { sat.remove(mac); firePropertyChanged(new PropertyChangeEvent(this, "sat_entry", null, null)); } ; }); } } filius-2.9.4+ds/src/main/java/filius/software/system/SystemSoftware.java000066400000000000000000000107751506574241500264150ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.Serializable; import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.Knoten; /** * Die Klasse SystemSoftware umfasst die grundlegenden Funktionen einer Systemsoftware, die auf allen Stationen * (Endsysteme und Uebertragungseinheiten) zur Verfuegung stehen muss.
    * Die Klasse ist abstrakt und wird fuer die verschiedenen Stationen unterschiedlich implementiert. */ @SuppressWarnings("serial") public abstract class SystemSoftware extends Observable implements Serializable { private static Logger LOG = LoggerFactory.getLogger(SystemSoftware.class); /** Die Hardware, auf der diese Systemsoftware laeuft. */ private Knoten hardware; private boolean started; /** * Diese Methode wird beim Wechsel vom Konfigurationsmodus (zum Aufbau des Rechnernetzes und Konfiguration der * Komponenten) zum Aktionsmodus (mit der Moeglichkeit den Datenaustausch zu simulieren) ausgefuehrt!
    * In den implementierenden Unterklassen sollen an dieser Stelle alle Threads zur Simulation des virtuellen * Netzwerks gestartet werden. */ public void starten() { started = true; } /** * Diese Methode wird beim Wechsel vom Aktionsmodus (mit der Moeglichkeit den Datenaustausch zu simulieren) zum * Konfigurationsmodus (zum Aufbau des Rechnernetzes und Konfiguration der Komponenten) ausgefuehrt!
    * In den implementierenden Unterklassen sollen an dieser Stelle alle Threads zur Simulation des virtuellen * Netzwerks angehalten werden. */ public void beenden() { started = false; } public boolean isStarted() { return started; } /** * The {@link PropertyChangeSupport} is the new approach for implementing the observable pattern because * {@link Observable} is deprecated since Java 9. Do not use both mechanisms! */ private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); public void addPropertyChangeListener(PropertyChangeListener listener) { this.pcs.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { this.pcs.removePropertyChangeListener(listener); } protected void firePropertyChanged(PropertyChangeEvent event) { this.pcs.firePropertyChange(event); } public Knoten getKnoten() { return hardware; } public void setKnoten(Knoten hardware) { this.hardware = hardware; // LOG.debug("SystemSoftware ("+this.hashCode()+") now is connected to Knoten // ("+hardware.hashCode()+")"); } /** * Statusnachrichten werden damit an die Beobachter weitergeleitet. * * @deprecated Use firePropertyChanged based on the {@link PropertyChangeSupport} mechanism instead. */ public void benachrichtigeBeobacher(Object o) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (SystemSoftware), benachrichtigeBeobachter(" + o + ")"); setChanged(); notifyObservers(o); } public boolean wireless() { return false; } } filius-2.9.4+ds/src/main/java/filius/software/system/VermittlungsrechnerBetriebssystem.java000066400000000000000000000152761506574241500324160ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.system; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.knoten.InternetKnoten; import filius.hardware.knoten.Knoten; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.ResourceUtil; import filius.software.firewall.Firewall; import filius.software.firewall.FirewallRule; import filius.software.firewall.FirewallWebKonfig; import filius.software.firewall.FirewallWebLog; import filius.software.rip.RIPBeacon; import filius.software.rip.RIPServer; import filius.software.rip.RIPTable; import filius.software.vermittlungsschicht.VermittlungWeb; import filius.software.www.WebServer; /** * Diese Klasse stellt die Funktionalitaet eines Betriebssystems fuer Vermittlungsrechner zur Verfuegung. Spezifisch ist * die automatische Installation einer Firewall und eines Webservers mit einer Erweiterung zur Konfiguration der * Firewall. Die weitere Funktionalitaet wird von der Oberklasse (InternetKnotenBetriebssystem) zur Verfuegung gestellt. */ public class VermittlungsrechnerBetriebssystem extends InternetKnotenBetriebssystem { private static Logger LOG = LoggerFactory.getLogger(VermittlungsrechnerBetriebssystem.class); private static final long serialVersionUID = 1L; private boolean ripEnabled; private RIPTable riptable; private RIPBeacon ripbeacon; private RIPServer ripserver; /** Konstruktor mit Initialisierung von Firewall und Webserver */ public VermittlungsrechnerBetriebssystem() { super(); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (VermittlungsrechnerBetriebssystem), constr: VermittlungsrechnerBetriebssystem()"); setIpForwardingEnabled(true); initialisiereAnwendungen(); } public void setKnoten(Knoten vermittlungsrechner) { super.setKnoten(vermittlungsrechner); } /** * Methode zur initialisierung der Firewall und des Web-Servers mit den Erweiterungen fuer den Zugriff auf die * Firewall ueber eine Web-Schnittstelle */ private void initialisiereAnwendungen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (VermittlungsrechnerBetriebssystem), initialisiereFirewallUndWebserver()"); FirewallWebLog weblog; FirewallWebKonfig webkonfig; WebServer server = null; Firewall firewall = null; // Installation von Firewall und Webserver installApp("filius.software.firewall.Firewall"); installApp("filius.software.www.WebServer"); firewall = this.holeFirewall(); server = this.holeWebServer(); firewall.setDefaultPolicy(FirewallRule.DROP); firewall.setActivated(false); // Erweiterung des Webservers fuer die Anzeige der // Log-Eintraege der Firewall weblog = new FirewallWebLog(); weblog.setFirewall(firewall); weblog.setPfad("log"); server.setzePlugIn(weblog); // Erweiterung des Webservers fuer die Konfiguration // der Firewall webkonfig = new FirewallWebKonfig(); webkonfig.setWebserver(server); webkonfig.setFirewall(firewall); webkonfig.setPfad("konfig"); server.setzePlugIn(webkonfig); server.erzeugeIndexDatei(ResourceUtil.getResourcePath( "tmpl/vermittlung_index_" + Information.getInformation().getLocaleOrDefault() + ".txt")); // ------------- RIP ------------------ riptable = new RIPTable(this); ripserver = new RIPServer(); ripserver.setSystemSoftware(this); ripbeacon = new RIPBeacon(); ripbeacon.setSystemSoftware(this); VermittlungWeb ripweb = new VermittlungWeb(this); ripweb.setPfad("routes"); server.setzePlugIn(ripweb); } /** * Starten des Webservers * * @see filius.software.system.InternetKnotenBetriebssystem.starten() */ public void starten() { LOG.debug( "INVOKED (" + this.hashCode() + ") " + getClass() + " (VermittlungsrechnerBetriebssystem), starten()"); super.starten(); // Startet den Web-Server holeWebServer().setAktiv(true); if (ripEnabled) { riptable.reset(); riptable.addLocalRoutes((InternetKnoten) this.getKnoten()); ripserver.starten(); ripserver.setAktiv(true); ripbeacon.starten(); } } public void beenden() { super.beenden(); if (ripEnabled) { ripbeacon.beenden(); ripserver.beenden(); } } @Override public RIPTable getRIPTable() { if (ripEnabled) { return riptable; } else { return null; } } @Override public boolean isRipEnabled() { return ripEnabled; } public void setRipEnabled(boolean state) { ripEnabled = state; } /** * Methode fuer den Zugriff auf die Firewall. Dieser Zugriff ist nicht JavaBean-konform, weil die Speicherung der * Firewall als eine Anwendung durch die Oberklasse erfolgt. */ public Firewall holeFirewall() { return (Firewall) holeSoftware("filius.software.firewall.Firewall"); } /** * Methode fuer den Zugriff auf den Webserver Dieser Zugriff ist nicht JavaBean-konform, weil die Speicherung des * Webservers als eine Anwendung durch die Oberklasse erfolgt. */ public WebServer holeWebServer() { return (WebServer) holeSoftware("filius.software.www.WebServer"); } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/000077500000000000000000000000001506574241500246175ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/transportschicht/Segment.java000066400000000000000000000040721506574241500270670ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import filius.software.ProtocolDataUnit; @SuppressWarnings("serial") public class Segment extends ProtocolDataUnit { private int quellPort; private int zielPort; private int pruefSumme; private String daten; public String getDaten() { return daten; } public void setDaten(String daten) { this.daten = daten; } public int getPruefSumme() { return pruefSumme; } public void setPruefSumme(int pruefSumme) { this.pruefSumme = pruefSumme; } public int getQuellPort() { return quellPort; } public void setQuellPort(int quellPort) { this.quellPort = quellPort; } public int getZielPort() { return zielPort; } public void setZielPort(int zielPort) { this.zielPort = zielPort; } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/ServerSocket.java000066400000000000000000000242031506574241500301020ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import java.util.Collections; import java.util.Hashtable; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.ServerSocketException; import filius.exception.TimeOutException; import filius.exception.VerbindungsException; import filius.hardware.NetzwerkInterface; import filius.rahmenprogramm.I18n; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; /** * Der ServerSocket wird von Server-Anwendungen genutzt, die eine TCP-Verbindung fuer den Datenaustausch nutzen. Der * Server-Socket verwaltet dazu eine Liste mit TCP-Sockets, die im Passiv-Modus gestartet werden. D. h., dass ein * solcher Socket auf eingehende Verbindungsanfragen wartet.
    * Der Server-Socket leitet ankommende Segmente an Hand von entfernter IP-Adresse und entferntem TCP-Port an den * richtigen lokalen TCP-Socket weiter. * * @author carsten */ public class ServerSocket implements SocketSchnittstelle, I18n { private static Logger LOG = LoggerFactory.getLogger(ServerSocket.class); /** * Tabelle zur Verwaltung der TCP-Sockets. Der Key in der Tabelle besteht aus einem String, der durch zusammensetzen * von Sender-IP-Adresse und Sender-TCP-Port besteht (Bsp.: 192.168.0.1:1100). Als Value ist der Socket abgelegt. */ private Hashtable socketListe = new Hashtable(); /** * Ein aktuell erstellter Socket, der auf eine eingehende Verbindungsanfrage wartet. Dieser Socket ist noch nicht in * die Socketliste eingetragen, weil die IP-Adresse und der TCP-Port des entfernten Sockets vor Eintreffen der * Anfrage fuer einen Verbindungsaufbau noch nicht bekannt sind. */ private Socket aktuellerSocket; /** Das Betriebssystem */ private InternetKnotenBetriebssystem betriebssystem; /** Das Transport Control Protocol (TCP) oder User Datagram Protocol (UDP) */ private TransportProtokoll protokoll; /** * der lokale TCP-Port, an dem Verbindungsanfragen durch diesen Socket angenommen werden. */ private int lokalerPort; /** bind nic as proxy for the bound IP address. if this list is empty, assume all nic / ip addresses. */ private List boundNICs = Collections.emptyList(); /** * Konstruktor fuer einen Server-Socket. Hier werden die lokalen Attribute initialisiert. Ausserdem wird der lokale * TCP-Port fuer diesen Socket reserviert. Wenn das nicht moeglich ist, weil er schon belegt ist, wird eine * Exception ausgeloest. * * @author carsten * @param betriebssystem * @param lokalerPort * - Lokaler Port, auf dem der Server laufen soll. Bsp.: http-Anwendungen zumeist auf Port 80 * @param bind */ public ServerSocket(InternetKnotenBetriebssystem betriebssystem, int lokalerPort, int transportProtokoll, List bind) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ServerSocket), constr: ServerSocket(" + betriebssystem + "," + lokalerPort + "," + transportProtokoll + ")"); this.betriebssystem = betriebssystem; this.lokalerPort = lokalerPort; this.boundNICs = bind; if (transportProtokoll == IpPaket.TCP) { protokoll = betriebssystem.holeTcp(); } else { protokoll = betriebssystem.holeUdp(); } } public int getLocalPort() { return this.lokalerPort; } public Hashtable getSockets() { return socketListe; } /** * Methode zum Eintragen eines neuen Sockets in die Socket-Liste */ public void eintragenSocket(Socket socket) { String ziel = socket.holeZielIPAdresse() + ":" + socket.holeZielPort(); socketListe.put(ziel, socket); LOG.debug("[port={}] socket for remote client {} added.", lokalerPort, ziel); } /** * Methode zum Austragen eines geschlossenen Sockets aus der Liste der Sockets. Wenn der letzte Socket ausgetragen * wurde und kein Socket auf einen Verbindungsaufbau wartet, dann ist der Server-Socket damit beendet. Der Port wird * dann freigegeben. */ public void austragenSocket(Socket socket) { String ziel = socket.holeZielIPAdresse() + ":" + socket.holeZielPort(); socketListe.remove(ziel); LOG.debug("[port={}] socket for remote client {} removed.", lokalerPort, ziel); if (socketListe.isEmpty() && aktuellerSocket == null) { protokoll.gibPortFrei(lokalerPort); } } /** * Mit dieser Methode wird ein neuer 'lauschender' TCP-Socket erzeugt und zurueck gegeben. Diese Methode * blockiert den Thread, bis eine Verbindung zu dem Socket aufgebaut wurde! Der Eintrag in die Socketliste * wird vom TCP-Socket nach erfolgreichem Verbindungsaufbau initiiert. * * @throws ServerSocketException * - Diese Exception wird geworfen, wenn auf dem angeforderten Port schon eine Anwendung laeuft. */ public synchronized Socket oeffnen() throws VerbindungsException, ServerSocketException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ServerSocket), oeffnen()"); // Falls schon eine Anwendung auf dem vorgeschlagenen Port laeuft // wird eine Exception ausgeloest if (!protokoll.reservierePort(lokalerPort, this)) { throw new ServerSocketException(messages.getString("sw_serversocket_msg1") + " " + lokalerPort + " " + messages.getString("sw_serversocket_msg2")); } if (protokoll instanceof TCP) { aktuellerSocket = new TCPSocket(betriebssystem, lokalerPort); } else { aktuellerSocket = new UDPSocket(betriebssystem, lokalerPort); } try { aktuellerSocket.verbinden(); } catch (TimeOutException e) { aktuellerSocket = null; LOG.debug("[port={}] unexpected timeout exception while listening for incoming connection.", lokalerPort, e); } if (aktuellerSocket != null && aktuellerSocket.istVerbunden()) { return aktuellerSocket; } else { return null; } } /** * Ankommende Segmente werden von dieser Methode an den richtigen Socket weitergegeben. Wenn kein Scoket aus der * Socketliste zur entfernten IP-Adresse und dem entfernten TCP-Port passt, wird das Segment an den * 'aktuellerSocket' weitergegeben. Dieser Socket wurde gerade erst erzeugt und wartet auf eingehende * Verbindungsanfragen. */ @Override public void hinzufuegen(String startIp, int startPort, Segment segment) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ServerSocket), hinzufuegen(" + startIp + "," + startPort + "," + segment + ")"); boolean isForBoundIPAddress = boundNICs.isEmpty(); for (NetzwerkInterface netzwerkInterface : boundNICs) { if (segment.getRcvNic().getIp().equals(netzwerkInterface.getIp())) { isForBoundIPAddress = true; } } String start = startIp + ":" + startPort; if (!isForBoundIPAddress) { LOG.debug("discard segment '{}' since it is not for bound ip address of this socket '{}'", segment, boundNICs); } else if (socketListe.containsKey(start)) { Socket socket = (Socket) socketListe.get(start); socket.hinzufuegen(startIp, startPort, segment); } else if (aktuellerSocket != null) { aktuellerSocket.hinzufuegen(startIp, startPort, segment); } } /** * Methode zum Schliessen des Server-Sockets. Das heisst, dass keine Verbindungsanfragen mehr angenommen werden. Die * von dem Server-Socket verwalteten Sockets werden dadurch nicht geschlossen!
    * Der reservierte Port wird deshalb auch erst dann freigegeben, wenn der letzte der verwalteten Sockets geschlossen * wurde.
    * Diese Methode blockiert nicht, weil der aktuelle Socket immer ein Socket ist, der sich im Zustand LISTEN * befindet. Wenn ein Socket in diesem Zustand geschlossen wird, blockiert dieser nicht! */ public void schliessen() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ServerSocket), schliessen()"); if (aktuellerSocket != null) { aktuellerSocket.schliessen(); protokoll.gibPortFrei(lokalerPort); } } /** * Diese Methode wird beim Wechsel vom Aktions- zum Entwurfsmodus aufgerufen, damit moeglicherweise blockierte * Threads beendet werden koennen.
    * Diese Methode ist nicht blockierend! */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ServerSocket), beenden()"); if (aktuellerSocket != null) aktuellerSocket.beenden(); } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/Socket.java000066400000000000000000000254761506574241500267300ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.SocketException; import filius.exception.TimeOutException; import filius.exception.VerbindungsException; import filius.rahmenprogramm.I18n; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IP; import filius.software.vermittlungsschicht.IpPaket; /** * Dieser Klasse ist Oberklasse von UDPSocket und TCPSocket. Da die Funktionalitaet nicht als eigener Thread * implementiert ist, kann immer nur entweder gesendet oder empfangen werden! * */ public abstract class Socket implements SocketSchnittstelle, I18n { private static Logger LOG = LoggerFactory.getLogger(Socket.class); /** * Konstanten zur Spezifizierung des Modus des Sockets */ protected static final int AKTIV = 1, PASSIV = 2; /** * Ob der Socket im Passiv- oder Aktiv-Modus betrieben wird. AKTIV bedeutet, dass der Verbindungsaufbau zu einem * anderen Socket initiiert wird, PASSIV bedeutet, dass der Socket auf eingehende Verbindungsanfragen wartet. Dieses * Attribut sollte also mit einer der Konstanten AKTIV / PASSIV initialisiert werden! */ protected int modus; /** Das mit dem Socket verbundene Transport-Protokoll */ protected TransportProtokoll protokoll; /** der lokal belegte Port */ protected int lokalerPort = -1; protected String quellIp; /** * die IP-Adresse des Knotens, auf dem der entfernte Socket bereitgestellt wird */ protected String zielIp; /** TCP-/UDP-Port auf dem entfernten Rechner */ protected int zielPort; /** * Konstruktor zur Initialisierung eines Client-Sockets. Dazu wird das mit dem Socket verbundene Transport-Protokoll * initialisiert.
    * Der Konstruktor ist nicht blockierend. * * @param betriebssystem * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public Socket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort, int transportProtokoll) throws VerbindungsException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), constr: Socket(" + betriebssystem + "," + zielAdresse + "," + zielPort + "," + transportProtokoll + ")"); String ip; modus = AKTIV; if (transportProtokoll == IpPaket.TCP) protokoll = betriebssystem.holeTcp(); else protokoll = betriebssystem.holeUdp(); ip = ipCheck(zielAdresse); if (ip != null) { this.zielIp = ip; } else { try { this.zielIp = betriebssystem.holeDNSClient().holeIPAdresse(zielAdresse); } catch (TimeOutException e) { LOG.debug("could not resolve destination IP address for new socket to " + zielAdresse, e); } } if (zielIp != null) { this.zielPort = zielPort; } else { throw new VerbindungsException(messages.getString("sw_socket_msg1")); } } /** * Konstruktor zur Initialisierung eines Client-Sockets. Dazu wird der Konstruktor verwendet, der keinen lokalen * Port uebergeben bekommt.
    * Der Konstruktor ist nicht blockierend. * * @param betriebssystem * @param zielAdresse * @param zielPort * @param lokalerPort * ein bestimmter lokaler Port, der beim Betriebssystem reserviert werden soll. Dieser Parameter wird nur * dann verwendet, wenn der Wert groesser 0 ist. * @throws VerbindungsException */ public Socket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort, int transportProtokoll, int lokalerPort) throws VerbindungsException { this(betriebssystem, zielAdresse, zielPort, transportProtokoll); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (Socket), constr: Socket(" + betriebssystem + "," + zielAdresse + "," + zielPort + "," + transportProtokoll + "," + lokalerPort + ")"); this.lokalerPort = lokalerPort; } /** * Hier wird das Attribut protokoll initialisiert.
    * Der Konstruktor ist nicht blockierend. * * @param betriebssystem * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public Socket(InternetKnotenBetriebssystem betriebssystem, int lokalerPort, int transportProtokoll) throws VerbindungsException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), constr: Socket(" + betriebssystem + "," + lokalerPort + "," + transportProtokoll + ")"); modus = PASSIV; if (transportProtokoll == IpPaket.TCP) { protokoll = betriebssystem.holeTcp(); } else { protokoll = betriebssystem.holeUdp(); } this.lokalerPort = lokalerPort; } /** * leerer Konstruktor wird von Unterklassen benoetigt */ public Socket() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), constr: Socket()"); } /** Zum Versenden einer Nachricht ueber den Socket */ public abstract void senden(String nachricht) throws VerbindungsException, TimeOutException; /** * Zum Empfangen einer Nachricht ueber den Socket */ public abstract String empfangen() throws VerbindungsException, TimeOutException; /** * Zum Empfangen einer Nachricht ueber den Socket * * @param timeoutMillis */ public abstract String empfangen(long timeoutMillis) throws VerbindungsException, TimeOutException; /** * Test, ob der uebergebene String eine gueltige IP-Adresse ist. Zurueckgegeben wird die IP-Adresse ohne * ueberfluessige Nullen. * * @param ip * die zu pruefende IP-Adresse * @return */ protected String ipCheck(String ip) { return IP.ipCheck(ip); } /** * Methode zum Versenden eines Segments an die Ziel-IP-Adresse mit Hilfe des Transport-Protokolls * * @param segment * das zu sendende Segment */ protected void sende(Segment segment) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), sende(" + segment + ")"); protokoll.senden(zielIp, quellIp, segment); } protected void sende(String zielIp, String quellIp, Segment segment) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), sende(" + segment + ")"); protokoll.senden(zielIp, quellIp, segment); } /** * Methode zum Schliessen eines Sockets. Damit ist gegebenenfalls auch verbunden, dass der Port freigegeben wird. * */ public abstract void schliessen(); public abstract void verbinden() throws VerbindungsException, TimeOutException; public abstract boolean istVerbunden(); /** * Methode zum Eintragen eines Ports. Wenn es Teil eines Server-Sockets ist, wird der Start- und Ziel-Port * * @throws SocketException * */ protected void eintragenPort() throws SocketException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), eintragenPort()"); SocketSchnittstelle socket = null; if (lokalerPort == -1) { lokalerPort = protokoll.reserviereFreienPort(this); LOG.debug("[port={}] new client port registered.", lokalerPort); } else { if (protokoll.isUsed(lokalerPort)) { socket = protokoll.holeSocket(lokalerPort); } if (socket != null && socket instanceof ServerSocket) { ServerSocket serverSocket = (ServerSocket) socket; serverSocket.eintragenSocket(this); LOG.debug("[port={}] new server port registered for remote endpoint {}:{}.", lokalerPort, holeZielIPAdresse(), holeZielPort()); } else if (!protokoll.reservierePort(lokalerPort, this)) { throw new SocketException(); } } } /** * Methode zum austragen des Ports des Sockets. Entweder wird der Port im Betriebssystem freigegeben (modus == * AKTIV) oder der Socket wird in der Liste, die durch einen Server-Socket verwaltet wird, entfernt (modus == * PASSIV). */ protected void austragenPort() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), austragenPort()"); SocketSchnittstelle socket = null; try { socket = protokoll.holeSocket(lokalerPort); } catch (SocketException e) {} if (socket != null && socket instanceof ServerSocket) { ServerSocket serverSocket = (ServerSocket) socket; serverSocket.austragenSocket(this); } else if (socket != null) { protokoll.gibPortFrei(lokalerPort); } } /** * Methode fuer den Zugriff auf die IP-Adresse des entfernten Sockets */ public String holeZielIPAdresse() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), holeZielIPAdresse()"); return zielIp; } /** * Methode fuer den Zugriff auf den TCP-Port des entfernten Sockets */ public int holeZielPort() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Socket), holeZielPort()"); return zielPort; } public int holeLokalenPort() { return lokalerPort; } public void bind(String quellIp) { this.quellIp = quellIp; } public abstract String getStateAsString(); } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/SocketSchnittstelle.java000066400000000000000000000045021506574241500314610ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; /** * Die SocketSchnittstelle beschreibt die Methoden, die von UDP- und TCP-Socket (auch Server-Socket) bereitgestellt * werden. Das Transportprotokoll verwendet die Methode hinzufuegen() zur Weitergabe von eintreffenden Segmenten, die * dort dem Ziel-Socket zugeordnet werden. */ public interface SocketSchnittstelle { /** * Methode zur Weitergabe von Segmenten an den Socket, der die Schnittstelle zur Anwendung darstellt. * * @param startIp * IP-Adresse von der das Segment eingetroffen ist * @param startPort * Port von der das Segment verschickt wurde * @param segment * das eingetroffene Segment */ public void hinzufuegen(String startIp, int startPort, Segment segment); /** Methode zum schliessen eines Sockets. */ public void schliessen(); /** * Methode zum Beenden eines Sockets beim Wechsel vom Aktions- in den Entwurfsmodus. Der Zustand des Sockets danach * ist nicht bestimmt. Er sollte daher nicht weiter verwendet werden! */ public void beenden(); } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/TCP.java000066400000000000000000000036531506574241500261170ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; /** * * @author carsten Demux ist die Schnittstelle zwischen der TCP und der IP-Schicht */ public class TCP extends TransportProtokoll { private static Logger LOG = LoggerFactory.getLogger(TCP.class); /** * @author carsten * @param betriebssystem */ public TCP(InternetKnotenBetriebssystem betriebssystem) { super(betriebssystem, IpPaket.TCP); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (TCP), constr: TCP(" + betriebssystem + ")"); } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/TCPSocket.java000066400000000000000000001133621506574241500272670ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import java.util.LinkedList; import java.util.ListIterator; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.SocketException; import filius.exception.TimeOutException; import filius.exception.VerbindungsException; import filius.hardware.Verbindung; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; /** *

    * Dieser Socket implementiert die Schnittstelle fuer den Ende-zu-Ende-Datenaustausch mit dem Transport Control * Protocol. Dazu werden insbesondere drei Funktionen unterschieden: *

      *
    1. Verbindungsaufbau
    2. *
    3. Senden und
    4. *
    5. Empfangen von Nachrichten
    6. *
    7. Verbindungsabbau
    8. *
    * Es wurden Vereinfachungen hinsichtlich Wartezeiten, Wiederholung von Datenuebertragungen und aehnliches vorgenommen. *

    * Die vier genannten Funktionen werden jeweils durch eine Methode implementiert. Diese Methoden blockieren die * Ausfuehrung eines Threads. Sie stellen die Schnittstelle zur Anwendungsschicht dar.
    *

    * Die Verwendung des TCP-Sockets funktioniert foldendermassen: *

      *
    1. Der Aufruf des Konstruktors initialisiert die notwendigen Attribute fuer eine Verbindung.
    2. *
    3. Der Verbindungsaufbau erfolgt durch Aufruf der Methode verbinden(). Diese Methode blockiert so lange, bis * der Verbindungsaufbau erfolgreich abgeschlossen werden konnte oder eine Ausnahme ausgeloest wurde. Der * Verbindungsaufbau erfolgt mit einem Three-Way-Handshake.
    4. *
    5. Mit Aufruf der Methode istVerbunden() kann der Verbindungsstatus abgerufen werden. Diese Methode prueft, * ob der Zustand des Sockets ESTABLISHED ist.
    6. *
    7. Wenn eine Verbindung erfolgreich aufgebaut werden konnte, ist es moeglich Nachrichten mit der Methode * sende(String) an den entfernten Socket zu verschicken. Diese Methode blockiert, bis die Nachricht erfolgreich * uebertragen werden konnte oder eine Ausnahme ausgeloest wurde. Die Uebertragung erfolgt mit einem * Stop-And-Wait-Algorithmus.
    8. *
    9. Ebenso kann nach efolgtem Verbindungsaufbau mit Aufruf der Methode empfangen() auf eine eingehende * Nachricht gewartet werden. Es wird ein String zurueck gegeben, wenn die Nachricht vollstaendig empfangen wurde.
    10. *
    11. Der Verbindungsabbau wird durch Aufruf der Methode schliessen() initiiert. Allerdings erfolgt der * Verbindungsabbau synchron. D. h., der Verbindungsabbau muss von beiden Seiten initiiert werden. Diese Methode * blockiert so lange, bis der Verbindungsabbau beendet wurde. Allerdings gibt es einen Timeout, nach dem der Socket * auch ohne Antwort der Gegenseite geschlossen wird.
    12. *
    13. Beim Wechsel vom Aktions- in den Entwurfsmodus muss die Methode beenden() aufgerufen werden, damit der * Socket geschlossen wird und alle Threads, die noch in einer Methode blockiert werden, wieder freigegeben werden. Der * Socket ist anschliessend geschlossen. Es wird jedoch kein ordentlicher Verbindungsabbau durchgefuehrt.
    14. *
    *

    * * Der TCP-Socket befindet sich immer in einem der folgenden Zustaende: *
      *
    • 1 - CLOSED
    • *
    • 2 - LISTEN
    • *
    • 3 - SYN_RCVD
    • *
    • 4 - SYN_SENT
    • *
    • 5 - ESTABLISHED
    • *
    • 6 - CLOSE_WAIT
    • *
    • 7 - LAST_ACK
    • *
    • 8 - FIN_WAIT_1
    • *
    • 9 - FIN_WAIT_2
    • *
    • 10 - CLOSING
    • *
    • 11 - TIME_WAIT
    • *
    * * Uebergaenge zwischen den Zustaenden werden durch folgende Ereignisse ausgeloest: *
      *
    1. aktiv oeffnen
    2. *
    3. passiv oeffnen
    4. *
    5. schliessen
    6. *
    7. senden
    8. *
    9. timeout
    10. *
    11. FIN
    12. *
    13. ACK+FIN
    14. *
    15. ACK
    16. *
    17. SYN+ACK
    18. *
    * *

    * Oeffnen eines Server-Sockets: Das Kommando 'passiv oeffnen' ist das Ereignis, das den Uebergang vom Zustand 1 * (CLOSED) zu 2 (LISTEN) ausloest.
    * Mit dem Empfangen eines SYN-Segments wird der Uebergang zum Zustand 3 ausgeloest und zugleich ein SYN+ACK Segment * verschickt.
    * Nach dem Empfang eines ACK-Segments wechselt der Server-Socket in den Zustand 5 (ESTABLISHED). *

    * *

    * Oeffnen eines Client-Sockets: Das Kommando 'aktiv oeffnen' loest den Uebergang zum Zustand 4 aus. Zugleich wird ein * SYN-Segment gesendet.
    * Mit dem Empfang eines SYN+ACK-Segments geht der Socket in den Zustand 5 (ESTABLISHED) ueber und sendet zugleich noch * ein ACK-Segment. *

    * *

    * Initiieren des Verbindungsabbaus: Mit dem Kommando 'schliessen' wird der Verbindungsabbau gestartet. Zugleich wird * ein FIN-Segment gesendet und der Socket wechselt zum Zustand 8 (FIN_WAIT_1).
    * Mit dem Empfang eines ACK+FIN-Segments erfolgt der Uebergang zum Zustand 11 und zugleich wird ein ACK-Segment * versendet.
    * Nach einem Timeout, geht der Socket in den Zustand 1 (CLOSED) ueber. *

    * *

    * Reaktion auf Verbindungsabbau: Erhaelt der Socket ein FIN-Segment, wechselt er aus Zustand 5 in Zustand 6 * (CLOSE_WAIT) und sendet zugleich ein ACK-Segment.
    * Durch das Kommando 'schliessen' geht der Socket in den Zustand 7 ueber und sendet zugleich ein FIN-Segment
    * Mit dem Empfang eines ACK-Segments geht der Socket in Zustand 1 (CLOSED) ueber. *

    */ public class TCPSocket extends Socket implements Runnable { private static Logger LOG = LoggerFactory.getLogger(TCPSocket.class); /** Die Zustaende, die ein Socket einnehmen kann */ protected static final int CLOSED = 1, LISTEN = 2, SYN_RCVD = 3, SYN_SENT = 4, ESTABLISHED = 5, CLOSE_WAIT = 6, LAST_ACK = 7, FIN_WAIT_1 = 8, FIN_WAIT_2 = 9, CLOSING = 10, TIME_WAIT = 11; /** * Der aktuelle Zustand des Sockets. Zustandsuebergaenge werden in den Methoden verbinden(), schliessen() und * hinzufuegen() ausgeloest. */ private int zustand = CLOSED; private boolean timeout; private boolean stopThread; private boolean closeSocket; /** * Anzahl der maximalen Sendeversuche, in Fehlersituationen, d. h., dass ein Segment nicht bestaetigt wurde. */ protected static final int MAX_SENDEVERSUCHE = 3; /** Maximum Segment Size (MSS) */ protected final static int MSS = 1460; /** Puffer fuer eingegangene Segmente. */ private LinkedList puffer = new LinkedList<>(); private LinkedList receivedPayload = new LinkedList<>(); private static long synInitValue = 1l; /** * dieses Attribut ist immer die Sequenznummer des als naechstes zu sendenden Segments. Die Sequenznummer wird * waehrend des Veringudngsaufbaus erhoeht, wenn das SYN-Flag gesetzt ist und wenn die Verbindung hergestellt ist, * wenn in dem Segment Nutzdaten verschickt werden. */ private long nextSendSequenceNumber = synInitValue++ % (long) Math.pow(2, 32) * 1_000_000l; private long lastSentSequenceNumber; /** * dieses Attribut ist immer die zuletzt bestaetigte entfernte Sequenznummer
    */ private long remoteSequenceNumber = 0; /** * Konstruktor ruft den Konstruktor der Oberklasse auf. Ausserdem wird das Attribut protokoll mit dem TCP * initialisiert.
    * Der Konstruktor ist nicht blockierend. * * @param betriebssystem * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public TCPSocket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort) throws VerbindungsException { super(betriebssystem, zielAdresse, zielPort, IpPaket.TCP); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (TCPSocket), constr: TCPSocket(" + betriebssystem + "," + zielAdresse + "," + zielPort + ")"); } /** * Konstruktor ruft den Konstruktor der Oberklasse auf. Ausserdem wird das Attribut protokoll mit dem TCP * initialisiert.
    * Der Konstruktor ist nicht blockierend. * * @param betriebssystem * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public TCPSocket(InternetKnotenBetriebssystem betriebssystem, int lokalerPort) throws VerbindungsException { super(betriebssystem, lokalerPort, IpPaket.TCP); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (TCPSocket), constr: TCPSocket(" + betriebssystem + "," + lokalerPort + ")"); } /** * Mit dieser Methode werden die Standardfelder des Kopfteils eines Segments gefuellt und dann wird es versendet. *
    * Das heisst, dass *
      *
    • lokaler Port und
    • *
    • entfernter Port *
    * gesetzt werden. * * @param repeat */ private void sendeSegment(TcpSegment segment, boolean repeat) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), sendeSegment(" + segment + ")"); segment.setQuellPort(lokalerPort); segment.setZielPort(zielPort); if (repeat) { segment.setSeqNummer(lastSentSequenceNumber); } else { segment.setSeqNummer(nextSendSequenceNumber); nextSendSequenceNumber = nextSequenceNumber(segment); } lastSentSequenceNumber = segment.getSeqNummer(); protokoll.senden(zielIp, segment); } /** * Methode zur Initialisierung des Verbindungsaufbaus und die Sequenznummer wird mit einer Zufallszahl * initialisiert. Ausserdem wird der Inhalt des Puffers geloescht. Abhaengig davon, ob der Socket-Modus AKTIV oder * PASSIV ist erfolgen verschiedene Zustandsuebergaenge.
    * Diese Methode blockiert, bis der Verbindungsaufbau erfolgreich abgeschlossen ist, oder eine * VerbindungsException ausgeloest wurde.
    * Die Methode kann nicht zweimal gleichzeitig aufgerufen werden. Der zweite Aufruf wartet so lange mit der * Ausfuehrung, bis der erste Aufruf komplett abgeschlossen und der Monitor dieser Klasse wieder frei gegeben wird. *
      *
    1. passiver Modus: (Oeffnen eines Server-Sockets) Der Aufruf dieser Methode im PASSIV-Modus loest automatisch * den Uebergang vom Zustand CLOSED zu LISTEN aus.
      * Mit dem Empfangen eines SYN-Segments wird der Uebergang zum Zustand SYN_RCVD ausgeloest, die * Acknowledge-Sequenznummer initialisiert und ein SYN+ACK Segment verschickt.
      * Nach dem Empfang eines ACK-Segments wechselt der Server-Socket in den Zustand ESTABLISHED.
    2. *
    3. aktiver Modus: (Oeffnen eines Client-Sockets) Der Aufruf dieser Methode loest den Versand eines SYN-Segments * und damit den Uebergang zum Zustand SYN_SENT aus.
      * Mit dem Empfang eines SYN+ACK-Segments geht der Socket in den Zustand ESTABLISHED ueber, initialisiert die * Acknowledge Sequenznummer und sendet zugleich noch ein ACK-Segment.
    4. *
    * * @throws VerbindungsException * wenn ein Fehler auftritt bzw. ein unerwartetes Segment waehrend des Verbindungsaufbaus empfangen * wird. * @throws TimeOutException * wenn eine Antwort nicht innerhalb der maximalen Round-Trip-Time empfangen wird. */ public synchronized void verbinden() throws VerbindungsException, TimeOutException { LOG.debug("initiate new tcp socket connection"); stopThread = false; closeSocket = false; new Thread(this).start(); while (zustand != ESTABLISHED && !closeSocket && !stopThread) { try { Thread.sleep(100); } catch (InterruptedException e) {} } if (closeSocket) { if (timeout) { throw new TimeOutException(messages.getString("sw_tcpsocket_msg2")); } else if (modus == AKTIV) { throw new VerbindungsException(messages.getString("sw_tcpsocket_msg3")); } } else if (zustand == ESTABLISHED) { LOG.debug("[port={}] connection established.", lokalerPort); } } protected void connect() { puffer.clear(); if (modus == PASSIV) { connectServerMode(); } else { connectClientMode(); } } protected void connectServerMode() { zustand = LISTEN; LOG.debug("INFO (" + this.hashCode() + "): verbinden() [passiver Modus], Socket: " + this.toString()); while (!closeSocket && !stopThread && zustand == LISTEN) { synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(); } catch (InterruptedException e) {} } else break; } } long sendezeit = Long.MAX_VALUE; for (int i = 0; !closeSocket && !stopThread && i <= MAX_SENDEVERSUCHE && zustand != ESTABLISHED; i++) { synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(defaultTimeout()); } catch (InterruptedException e) {} } } if (puffer.size() >= 1) { sendezeit = System.currentTimeMillis(); TcpSegment segment = (TcpSegment) puffer.removeFirst(); if (zustand == LISTEN && segment.isSyn()) { // Initialisierung der zunaechst zu sendenden // ACK-Nummer anhand der empfangenen // Sequenznummer remoteSequenceNumber = nextSequenceNumber(segment); zustand = SYN_RCVD; TcpSegment tmp = new TcpSegment(); tmp.setSyn(true); sendeAck(segment, tmp); } else if (zustand == SYN_RCVD && segment.isAck()) { try { eintragenPort(); zustand = ESTABLISHED; } catch (SocketException e) { LOG.debug("Port for new socket could not be registered.", e); closeSocket = true; } } else { closeSocket = true; } } else if (System.currentTimeMillis() - sendezeit > defaultTimeout()) { timeout = true; closeSocket = true; } } } protected void connectClientMode() { try { eintragenPort(); LOG.debug("INFO (" + this.hashCode() + "): verbinden() [aktiver Modus], Socket: " + this.toString()); for (int i = 0; !closeSocket && !stopThread && zustand != ESTABLISHED && i < MAX_SENDEVERSUCHE; i++) { TcpSegment tmp = new TcpSegment(); tmp.setSyn(true); sendeSegment(tmp, i > 0); zustand = SYN_SENT; synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(defaultTimeout()); } catch (InterruptedException e) {} } } if (!closeSocket && !stopThread && puffer.size() >= 1) { TcpSegment segment = (TcpSegment) puffer.removeFirst(); if (zustand == SYN_SENT && segment.isAck() && segment.isSyn()) { // Initialisierung der zunaechst zu sendenden // ACK-Nummer anhand der empfangenen // Sequenznummer remoteSequenceNumber = nextSequenceNumber(segment); zustand = ESTABLISHED; sendeAck(segment, null); } else { beenden(); } } else if (zustand != CLOSED) { timeout = true; closeSocket = true; } } if (zustand != ESTABLISHED) { closeSocket = true; } } catch (SocketException e1) { closeSocket = true; LOG.debug("Port for socket could not be registered.", e1); } } /** * Diese Methode erstellt die entsprechende Anzahl von Segmenten zur Uebertragung einer Nachricht und gibt diese in * einer Liste zurueck. Das letzte Segment wird mit dem Flag 'Ende' markiert. * * @author carsten * @param daten * - Datenstring, der in den Segmenten als Nutzdaten uebertragen werden soll * @return - Gibt eine Liste mit den erstellten TcpSegmenten zurueck. */ protected LinkedList erstelleSegmente(String daten) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), erstelleSegment(" + daten + ")"); LinkedList segmenteListe; int paketeAnzahl; TcpSegment segment; paketeAnzahl = Math.max(1, (int) Math.ceil((float) daten.length() / (float) MSS)); segmenteListe = new LinkedList(); for (int i = 1; i <= paketeAnzahl; i++) { segment = new TcpSegment(); if (daten.length() < MSS) { segment.setDaten(daten); } else { segment.setDaten(daten.substring(0, MSS)); daten = daten.substring(MSS); } if (i == paketeAnzahl) { segment.setPush(true); } segmenteListe.add(segment); } return segmenteListe; } /** * Mit dieser Methode wird eine Nachricht auf Segmente aufgeteilt und versendet. Bevor das naechste Segment * verschickt wird, blockiert die Methode bis zur Bestaetigung mit einem ACK-Segment. Es wird also ein * Stop-and-Wait-Algorithmus umgesetzt.
    * Diese Methode ist synchronized, weil sonst die durchgaengig aufsteigende Sequenznummer der Segmente nicht * gewaehrleistet wird. * * @throws VerbindungsException * wenn beim Aufruf keine Verbindung hergestellt ist oder waehrend der Uebertragung die Verbindung * beendet wird. * @throws TimeOutException * wenn eine Bestaetigung nicht rechtzeitig eintrifft */ public synchronized void senden(String nachricht) throws VerbindungsException, TimeOutException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), senden(" + nachricht + ")"); TcpSegment segment; boolean bestaetigt = true; long versendeZeitpunkt = Long.MAX_VALUE; long rtt; if (zustand != ESTABLISHED) { LOG.debug("EXCEPTION: " + getClass() + " (" + this.hashCode() + "); zustand=" + zustand); beenden(); throw new VerbindungsException(messages.getString("sw_tcpsocket_msg6")); } LinkedList liste = erstelleSegmente(nachricht); // Die erstellten Segmente werden verschickt // und auf die Bestaetigung gewartet, bevor das // naechste Segment verschickt wird. ListIterator it = liste.listIterator(); while (it.hasNext() && !closeSocket && !stopThread && zustand == ESTABLISHED) { bestaetigt = false; segment = (TcpSegment) it.next(); // Versand eines einzelnen Segments mit dem // Stop-and-Wait-Algorithmus // Wenn diese Schleife beendet ist, muss das Segment entweder // verschickt und der Empfang bestaetigt worden sein und damit // auch die Sequenznummer inkrementiert sein oder der Versand // war nicht erfolgreich for (int i = 0; !closeSocket && !stopThread && i < MAX_SENDEVERSUCHE && !bestaetigt; i++) { if (zustand != ESTABLISHED) { // Wenn die Verbindung zwischenzeitlich unterbrochen // wurde, wird eine Verbindungsexception ausgeloest. closeSocket = true; throw new VerbindungsException(messages.getString("sw_tcpsocket_msg7")); } sendeSegment(segment, i > 0); versendeZeitpunkt = System.currentTimeMillis(); // In dieser Schleife werden alle eingehenden // Segmente geprueft, ob sie das ACK-Segment und // damit die Bestaetigung fuer das versendete // Segment darstellen. Alle anderen Segmente // werden verworfen. do { synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(defaultTimeout()); } catch (InterruptedException e) { LOG.debug("", e); } } else if (!bestaetigt) { if (puffer.getFirst().isAck()) { TcpSegment antwort = puffer.removeFirst(); if (antwort.getAckNummer() == nextSendSequenceNumber) { bestaetigt = true; } } } } rtt = System.currentTimeMillis() - versendeZeitpunkt; } while (!bestaetigt && (rtt < defaultTimeout()) && zustand == ESTABLISHED && !closeSocket && !stopThread); } if (!bestaetigt && zustand != CLOSED && !stopThread) { LOG.debug("[port={}] message '{}' could not be transferred. socket will be closed.", lokalerPort, nachricht); schliessen(); throw new TimeOutException(messages.getString("sw_tcpsocket_msg8")); } } } /** * Beim Aufruf dieser Methode werden die eingehenden TCP-Segmente zu einer Nachricht zusammen gefuegt und wenn das * Ende der Nachricht erreicht ist, wird diese zurueck gegeben. Das Ende einer Nachricht wird hier mit dem Flag * 'Ende' gekennzeichnet.
    * Diese Methode ist blockierend. * * @return gibt den zusammengesetzten empfangenen Datenstring zurueck. Wenn die Verbindung vor Eingang einer * Nachricht geschlossen wurde, wird null zurueck gegeben. * @throws VerbindungsException * - wird geworfen, wenn beim Aufruf keine Verbindung hergestellt ist * @throws TimeOutException * - wird geworfen, wenn die entfernte Anwendung nicht mehr reagiert oder Verbindung unterbrochen wurde. */ public String empfangen() throws VerbindungsException, TimeOutException { return empfangen(0); } protected int defaultTimeout() { return MAX_SENDEVERSUCHE * Verbindung.holeRTT(); } /** * Beim Aufruf dieser Methode werden die eingehenden TCP-Segmente zu einer Nachricht zusammen gefuegt und wenn das * Ende der Nachricht erreicht ist, wird diese zurueck gegeben. Das Ende einer Nachricht wird hier mit dem Flag * 'Ende' gekennzeichnet.
    * Diese Methode ist blockierend. * * @return gibt den zusammengesetzten empfangenen Datenstring zurueck. Wenn die Verbindung vor Eingang einer * Nachricht geschlossen wurde, wird null zurueck gegeben. * @throws VerbindungsException * - wird geworfen, wenn beim Aufruf keine Verbindung hergestellt ist * @throws TimeOutException * - wird geworfen, wenn die entfernte Anwendung nicht mehr reagiert oder Verbindung unterbrochen wurde. */ public String empfangen(long timeoutMillis) throws VerbindungsException, TimeOutException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), empfangen()"); if (zustand != ESTABLISHED) { throw new VerbindungsException(messages.getString("sw_tcpsocket_msg9")); } long startTime = System.currentTimeMillis(); synchronized (receivedPayload) { if (receivedPayload.size() < 1) { try { receivedPayload.wait(timeoutMillis); } catch (InterruptedException e) {} } } long stopTime = System.currentTimeMillis(); String data = null; if (zustand == ESTABLISHED && !receivedPayload.isEmpty()) { synchronized (receivedPayload) { data = receivedPayload.removeFirst(); } } else if (stopTime - startTime >= timeoutMillis) { throw new TimeOutException(messages.getString("sw_tcpsocket_msg10")); } return data; } protected void listen() { StringBuffer nachricht = new StringBuffer(); while (!closeSocket && !stopThread && zustand == ESTABLISHED) { TcpSegment segment = null; synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(Verbindung.holeRTT()); } catch (InterruptedException e) {} } else { segment = (TcpSegment) puffer.getFirst(); } } // waehrend des Empfangs werden keine ACK-Segmente // verarbeitet. Diese werden nur beim Versenden von // Nachrichten genutzt. Daher wartet diese Methode dann // auf das naechste eintreffende Segment. if (!closeSocket && !stopThread && zustand == ESTABLISHED && null != segment && !segment.isAck()) { synchronized (puffer) { puffer.remove(segment); puffer.notifyAll(); } // ist das Segment schon bestaetigt worden? long ack = nextSequenceNumber(segment); if (ack <= remoteSequenceNumber) { sendeAck(segment, null); } else if (ack > remoteSequenceNumber) { sendeAck(segment, null); remoteSequenceNumber = ack; nachricht.append(segment.getDaten()); } if (segment.isPush()) { synchronized (receivedPayload) { receivedPayload.add(nachricht.toString()); nachricht = new StringBuffer(); receivedPayload.notifyAll(); } } } } synchronized (receivedPayload) { receivedPayload.notifyAll(); } LOG.debug("[port={}] stop listening for incoming data.", lokalerPort); } /** * Mit dieser Methode wird ein Verbindungsabbau mit dem FIN-Flag eingeleitet. Diese Methode darf nicht * blockieren, wenn sich der Socket im Zustand LISTEN befindet!
    * Das Verhalten zum Schliessen des Sockets ist vom aktuellen Zustand abhaengig: *
      *
    1. LISTEN: Der Zustand wird einfach auf CLOSED gesetzt, weiter passiert nichts.
    2. *
    3. ESTABLISHED: Dann wird ein FIN-Segment verschickt und in den Zustand FIN_WAIT_1 gewechselt.
      * Mit dem Empfang eines ACK-Segments erfolgt der Zustandsuebergang zu FIN_WAIT_2 ohne eine Aktion.
      * Nach dem Empfang eines FIN-Segments wird ein ACK-Segment versendet und in den Zustand TIME_WAIT gewechselt. *
      * und zugleich wird ein ACK-Segment versendet.
      * Nach einem Timeout, geht der Socket in den Zustand CLOSED ueber.
      * Eine weitere Moeglichkeit ist der Uebergang vom Zustand FIN_WAIT_1 zu TIME_WAIT bei Empfang eines * FIN+ACK-Segments. Dann wird ein ACK zurueckgeschickt.
      * Die dritte Moeglichkeit ist, das im Zustand FIN_WAIT_1 ein FIN-Segment empfangen wird. Dann wird ein ACK-Segment * verschickt und in den Zustand CLOSING gewechselt. Mit dem Empfang eines ACK-Segments erfolgt der Uebergang in den * Zustand TIME_WAIT.
    4. *
    5. SYN_RCVD: Verhalten wie in Zustand ESTABLISHED.
    6. *
    7. CLOSE_WAIT: Wenn der entfernte Socket zuerst den Verbindungsabbau eingeleitet hat, befindet sich dieser * Socket bereits im Zustand CLOSE_WAIT. Auch in diesem Fall wird ein FIN-Segment versendet. Damit erfolgt der * Uebergang zum Zustand LAST_ACK. Wenn das FIN-Segment bestaetigt erfolgt der Uebergang in den Zustand CLOSED ohne * weitere Aktionen.
    8. *
    * * Initiieren des Verbindungsabbaus: Mit dem Kommando 'schliessen' wird der Verbindungsabbau gestartet. Diese * Methode blockiert so lange, bis der Endzustand CLOSED erreicht wurde! Wenn beim Datenaustausch zum * Verbindungsabbau etwas nicht funktioniert, wird der Socket einseitig geschlossen */ public void schliessen() { LOG.debug("close tcp socket"); closeSocket = true; synchronized (puffer) { puffer.notifyAll(); } } public void run() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), run()"); LOG.debug("start socket connection mode: {}", modus == PASSIV ? "server" : "client"); connect(); LOG.debug("[port={}] start listening for incoming messages", lokalerPort); listen(); LOG.debug("[port={}] close socket", lokalerPort); close(); LOG.debug("[port={}] port closed", lokalerPort); austragenPort(); } protected void close() { if (!stopThread && zustand != LISTEN && zustand != CLOSED) { TcpSegment tmp = new TcpSegment(); tmp.setFin(true); switch (zustand) { case ESTABLISHED: case SYN_RCVD: sendeSegment(tmp, false); zustand = FIN_WAIT_1; break; case SYN_SENT: zustand = CLOSED; synchronized (puffer) { puffer.notifyAll(); } break; case CLOSE_WAIT: sendeSegment(tmp, false); zustand = LAST_ACK; break; } for (int i = 0; !stopThread && zustand != CLOSED && i < 5; i++) { synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(defaultTimeout()); } catch (InterruptedException e) {} } if (zustand == TIME_WAIT) { puffer.clear(); zustand = CLOSED; } else if (puffer.size() >= 1) { tmp = (TcpSegment) puffer.removeFirst(); switch (zustand) { case LAST_ACK: if (tmp.isAck()) zustand = CLOSED; break; case CLOSING: if (tmp.isAck()) zustand = TIME_WAIT; break; case FIN_WAIT_2: if (tmp.isFin()) { sendeAck(tmp, null); zustand = TIME_WAIT; } break; case FIN_WAIT_1: if (tmp.isAck() && tmp.isFin()) { sendeAck(tmp, null); zustand = TIME_WAIT; } else if (tmp.isAck()) { zustand = FIN_WAIT_2; } else if (tmp.isFin()) { sendeAck(tmp, null); zustand = CLOSING; } break; } } } } } } /** * Methode zum Senden der Bestaetigung eines TCP-Segments. Dazu wird anhand des Attibuts ackNummer zunaechst * geprueft, ob das Segment bestaetigt werden kann. Dann wird die Sequenznummer des uebergebenen Segments um eins * erhoeht und in das Acknowledge-Feld des Kopfteils im neuen Acknowledge-Segment geschrieben. Es werden also *
      *
    • das ACK-Flag und
    • *
    • die Acknowledge-Sequenznummer initialisiert
    • *
    * Zur weiteren Bearbeitung wird das zu sendende Segment an die Methode sendeSegment(TcpSegment) weitergegeben. * * @param empfangSegment * das empfangene Segment, dessen Empfang bestaetigt werden soll. * @param sendeSegment * das zu sendende Segment (kann bereits vorab initialisiert werden), wenn dieser Parameter 'null' ist, * wird ein neues Segment erstellt. */ private void sendeAck(TcpSegment empfangSegment, TcpSegment sendeSegment) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), sendeAck(" + empfangSegment + "," + sendeSegment + ")"); if (sendeSegment == null) { sendeSegment = new TcpSegment(); } sendeSegment.setAck(true); sendeSegment.setAckNummer(nextSequenceNumber(empfangSegment)); sendeSegment(sendeSegment, false); } static long nextSequenceNumber(TcpSegment segment) { long nextNumber = segment.getSeqNummer(); if (segment.isSyn() || segment.isFin()) { nextNumber++; } nextNumber += StringUtils.length(segment.getDaten()); nextNumber %= 4_294_967_296l; return nextNumber; } /** * In dieser Methode werden die eingehenden Segmente in den Puffer eingefuegt. Ausserdem wird der Zustandsuebergang * von ESTABLISHED nach CLOSE_WAIT ausgeloest und die Methode zum Schliessen des Sockets beim Empfang eines * FIN-Segments aufgerufen. */ @Override public void hinzufuegen(String startIp, int startPort, Segment segment) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), hinzufuegen(" + startIp + "," + startPort + "," + segment + ")"); TcpSegment tcpSegment = (TcpSegment) segment; if (zustand == LISTEN) { zielPort = startPort; zielIp = startIp; } if (zustand == ESTABLISHED && tcpSegment.isFin()) { sendeAck(tcpSegment, null); zustand = CLOSE_WAIT; synchronized (puffer) { puffer.notifyAll(); } } else { synchronized (puffer) { puffer.addLast(tcpSegment); puffer.notifyAll(); } } } /** * Diese Methode wird beim Wechsel vom Aktions- zum Entwurfsmodus aufgerufen, damit moeglicherweise blockierte * Threads beendet werden koennen. Hier wird der Zustand auf CLOSED gesetzt und Threads, die auf den Puffer warten, * aufgeweckt. Es findet kein ordentlicher Verbindungsaufbau statt! Der Zustand des Sockets ist anschliessend * unbestimmt. Daher sollte dieser Socket nicht weiter verwendet werden.
    * Die Methode ist nicht blockierend! */ public void beenden() { LOG.debug("stop tcp socket thread"); stopThread = true; synchronized (puffer) { puffer.notifyAll(); } } /** * Zur Abfrage, ob eine Verbindung aufgebaut ist.
    * Diese Methode ist nicht blockierend. * * @return ob der aktuelle Zustand ESTABLISHED ist */ public boolean istVerbunden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), istVerbunden(), port: " + this.holeLokalenPort()); return (zustand == ESTABLISHED); } /* workaround function for more ugly exception provoking original code */ public boolean isSortOfConnected() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TCPSocket), isSortOfConnected(), port: " + this.holeLokalenPort()); if (zustand >= 5 && zustand <= 11) { return true; } else { return false; } } public String getStateAsString() { switch (this.zustand) { case CLOSED: return "CLOSED"; case LISTEN: return "LISTEN"; case SYN_RCVD: return "SYN_RCVD"; case SYN_SENT: return "SYN_SENT"; case ESTABLISHED: return "ESTABLISHED"; case CLOSE_WAIT: return "CLOSE_WAIT"; case LAST_ACK: return "LAST_ACK"; case FIN_WAIT_1: return "FIN_WAIT_1"; case FIN_WAIT_2: return "FIN_WAIT_2"; case CLOSING: return "CLOSING"; case TIME_WAIT: return "TIME_WAIT"; default: return ""; } } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/TcpSegment.java000066400000000000000000000070461506574241500275420ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import java.io.Serializable; /** * * @author carsten * */ public class TcpSegment extends Segment implements Serializable { private static final long serialVersionUID = 1L; private long seqNummer, ackNummer; private int dataOffset; private int reservedField = 0; private boolean urg = false; private boolean ack = false; private boolean psh = false; private boolean rst = false; private boolean syn = false; private boolean fin = false; private int window; private int urgentPointer; public TcpSegment() {} public boolean isAck() { return ack; } public void setAck(boolean ack) { this.ack = ack; } public long getAckNummer() { return ackNummer; } public void setAckNummer(long ackNummer) { this.ackNummer = ackNummer; } public int getDataOffset() { return dataOffset; } public void setDataOffset(int dataOffset) { this.dataOffset = dataOffset; } public boolean isFin() { return fin; } public void setFin(boolean fin) { this.fin = fin; } public boolean isPush() { return psh; } public void setPush(boolean psh) { this.psh = psh; } public int getReservedField() { return reservedField; } public void setReservedField(int reservedField) { this.reservedField = reservedField; } public boolean isRst() { return rst; } public void setRst(boolean rst) { this.rst = rst; } public long getSeqNummer() { return seqNummer; } public void setSeqNummer(long seqNummer) { this.seqNummer = seqNummer; } public boolean isSyn() { return syn; } public void setSyn(boolean syn) { this.syn = syn; } public boolean isUrg() { return urg; } public void setUrg(boolean urg) { this.urg = urg; } public boolean isPsh() { return psh; } public int getUrgentPointer() { return urgentPointer; } public void setUrgentPointer(int urgentPointer) { this.urgentPointer = urgentPointer; } public int getWindow() { return window; } public void setWindow(int window) { this.window = window; } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/TransportProtokoll.java000066400000000000000000000217111506574241500313660ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import java.lang.Thread.State; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.SocketException; import filius.rahmenprogramm.I18n; import filius.software.Protokoll; import filius.software.system.InternetKnotenBetriebssystem; public abstract class TransportProtokoll extends Protokoll implements I18n, Runnable { private static Logger LOG = LoggerFactory.getLogger(TransportProtokoll.class); private static final int PORT_UNTERE_GRENZE = 1024; private static final int PORT_OBERE_GRENZE = 65535; protected static final int TTL = 64; private int typ; /** IP-Adresse und Segment, das in einem neuen Thread verschickt wird */ private LinkedList segmentListe = new LinkedList(); private Map portTabelle = new HashMap(); private TransportProtokollThread thread; /** * Dieser Thread fuehrt die run()-Methode dieser Klasse aus und wird dazu verwendet, Segmente zu verschicken. */ private Thread sendeThread = null; /** * In diesem Attribut wird gespeichert, ob der Thread zum Versenden von Segmenten laeuft. */ private boolean running = false; /** * @author carsten * @param betriebssystem */ public TransportProtokoll(InternetKnotenBetriebssystem betriebssystem, int typ) { super(betriebssystem); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), constr: TransportProtokoll(" + betriebssystem + "," + typ + ")"); this.typ = typ; } public List holeAktiveSockets() { List sockets = new ArrayList<>(); for (SocketSchnittstelle socket : portTabelle.values()) { if (null != socket) { sockets.add(socket); } } return sockets; } public int holeTyp() { return typ; } public int reserviereFreienPort() { return reserviereFreienPort(null); } public int reserviereFreienPort(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), reserviereFreienPort(" + socket + ")"); // Freien Port suchen boolean portGefunden = false; int freienPort; do { freienPort = sucheFreienPort(); if (!portTabelle.containsKey(freienPort)) { portGefunden = true; } } while (!portGefunden); reservierePort(freienPort, socket); return freienPort; } public SocketSchnittstelle holeSocket(int port) throws SocketException { LOG.trace( "INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), holeSocket(" + port + ")"); if (port == -1) { throw new SocketException(messages.getString("sw_transportprotokoll_msg3")); } if (!portTabelle.containsKey(port)) { throw new SocketException(messages.getString("sw_transportprotokoll_msg1") + " " + port + " " + messages.getString("sw_transportprotokoll_msg2")); } return (SocketSchnittstelle) portTabelle.get(port); } public boolean isUsed(int port) { return portTabelle.containsKey(port); } private int sucheFreienPort() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), sucheFreienPort()"); int spanne = PORT_OBERE_GRENZE - PORT_UNTERE_GRENZE; Random random = new Random(); int zufallsZahl = Math.abs(random.nextInt()); int zahl = (zufallsZahl) % spanne; return (PORT_UNTERE_GRENZE + zahl); } public boolean reservierePort(int port, SocketSchnittstelle socket) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), reservierePort(" + port + "," + socket + ")"); boolean portIsSubscribed = false; synchronized (portTabelle) { if (portTabelle.containsKey(port) && !portTabelle.get(port).equals(socket)) { LOG.debug("Port " + port + " cannot be subscribed for. It is already in use."); } else if (portTabelle.containsKey(port) && portTabelle.get(port).equals(socket)) { LOG.debug("Port {} already subscribed for the socket. Nothing to do.", port); portIsSubscribed = true; } else { LOG.debug("Port {} is subscribed for.", port); portTabelle.put(port, socket); portIsSubscribed = true; } } return portIsSubscribed; } public boolean gibPortFrei(int port) { LOG.debug("remove port {} from list of registered ports.", port); synchronized (portTabelle) { if (portTabelle.containsKey(port)) { portTabelle.remove(port); return true; } else { return false; } } } /** * @param zielIp * - Ip des Empfaengers * @param protokoll * - Protokollnummer des Protokolls, auf dass aufgesetzt wird. * @param segment * - Segment mit Daten zur IP-Schicht */ protected void senden(String zielIp, Object segment) { senden(zielIp, null, segment); } protected void senden(String zielIp, String quellIp, Object segment) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), senden(" + zielIp + "," + segment + ")"); synchronized (segmentListe) { segmentListe.addLast((new Object[] { zielIp, quellIp, segment })); segmentListe.notifyAll(); } } public void run() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), run()"); InternetKnotenBetriebssystem bs; Object[] temp; while (running) { synchronized (segmentListe) { if (segmentListe.size() < 1) { try { segmentListe.wait(); } catch (InterruptedException e1) {} } if (segmentListe.size() > 0) { temp = (Object[]) segmentListe.removeFirst(); bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); bs.holeIP().senden((String) temp[0], (String) temp[1], holeTyp(), TTL, (Segment) temp[2]); } } } } public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), starten()"); // ensure that there are no pdu's left from last run! segmentListe.clear(); thread = new TransportProtokollThread(this); thread.starten(); if (!running) { running = true; if (sendeThread == null || (!sendeThread.getState().equals(State.WAITING) && !sendeThread.getState().equals(State.BLOCKED))) { sendeThread = new Thread(this); sendeThread.start(); } } } public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (TransportProtokoll), beenden()"); thread.beenden(); running = false; if (sendeThread != null && (sendeThread.getState().equals(State.WAITING) || sendeThread.getState().equals(State.BLOCKED))) { sendeThread.interrupt(); } portTabelle.clear(); } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/TransportProtokollThread.java000066400000000000000000000056511506574241500325230ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.SocketException; import filius.software.ProtokollThread; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; public class TransportProtokollThread extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(TransportProtokollThread.class); private TransportProtokoll protokoll; public TransportProtokollThread(TransportProtokoll protokoll) { super(((InternetKnotenBetriebssystem) protokoll.holeSystemSoftware()).holeIP() .holePaketListe(protokoll.holeTyp())); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (TransportProtokollThread), constr: TransportProtokollThread(" + protokoll + ")"); this.protokoll = protokoll; } protected void verarbeiteDatenEinheit(IpPaket paket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (TransportProtokollThread), verarbeiteDatenEinheit(" + paket.toString() + ")"); Segment segment = (Segment) paket.getSegment(); segment.setRcvNic(paket.getRcvNic()); try { SocketSchnittstelle socket = protokoll.holeSocket(segment.getZielPort()); socket.hinzufuegen(paket.getSender(), segment.getQuellPort(), segment); } catch (SocketException e) { if (!paket.getEmpfaenger().equals("255.255.255.255") && !paket.getEmpfaenger().equals("0.0.0.0")) LOG.debug("", e); } } }filius-2.9.4+ds/src/main/java/filius/software/transportschicht/UDP.java000066400000000000000000000034101506574241500261100ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; public class UDP extends TransportProtokoll { private static Logger LOG = LoggerFactory.getLogger(UDP.class); public UDP(InternetKnotenBetriebssystem betriebssystem) { super(betriebssystem, IpPaket.UDP); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (UDP), constr: UDP(" + betriebssystem + ")"); } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/UDPSocket.java000066400000000000000000000223741506574241500272730ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.SocketException; import filius.exception.VerbindungsException; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; /** * Der UDP-Socket stellt die Schnittstelle zum Versenden von Segmenten zur Verfuegung, ohne eine Verbindung * herzustellen. Ausserdem wird jede Nachricht in genau einem Segment uebertragen. */ public class UDPSocket extends Socket { private static Logger LOG = LoggerFactory.getLogger(UDPSocket.class); /** Liste eingehender Segmente */ private LinkedList puffer = new LinkedList(); /** * Ob der Socket verbunden ist, d. h. Aufruf von verbinden() war erfolgreich und Socket wurde noch nicht durch * schliessen() geschlossen. */ private boolean verbunden; private boolean stopListening; /** * Konstruktor zur Initialisierung des Sockets. Dazu wird das mit dem Socket verbundene Transport-Protokoll * initialisiert und ein beliebiger Port wird reserviert. * * @param betriebssystem * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public UDPSocket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort) throws VerbindungsException { super(betriebssystem, zielAdresse, zielPort, IpPaket.UDP); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (UDPSocket), constr: UDPSocket(" + betriebssystem + "," + zielAdresse + "," + zielPort + ")"); } /** * Konstruktor zur Initialisierung des Sockets. Dazu wird das mit dem Socket verbundene Transport-Protokoll * initialisiert und ein bestimmter Port wird reserviert. * * @param betriebssystem * @param zielAdresse * @param zielPort * @param lokalerPort * ein bestimmter lokaler Port, der beim Betriebssystem reserviert werden soll. Dieser Parameter wird nur * dann verwendet, wenn der Wert groesser 0 ist. * @throws VerbindungsException */ public UDPSocket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort, int lokalerPort) throws VerbindungsException { super(betriebssystem, zielAdresse, zielPort, IpPaket.UDP, lokalerPort); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (UDPSocket), constr: UDPSocket(" + betriebssystem + "," + zielAdresse + "," + zielPort + "," + lokalerPort + ")"); } /** * Konstruktor zur Initialisierung eines Sockets mit Belegung eines bestimmten lokalen Ports aber ohne eine feste * Ziel-IP-Adresse. Diese wird beim Empfang eines Segments bestimmt. * * @param betriebssystem * @param lokalerPort * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public UDPSocket(InternetKnotenBetriebssystem betriebssystem, int lokalerPort) throws VerbindungsException { super(betriebssystem, lokalerPort, IpPaket.UDP); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (UDPSocket), constr: UDPSocket(" + betriebssystem + "," + lokalerPort + ")"); } /** * Pakete fuer diesen Socket werden ueber diese Funktion in den Puffer geschrieben. * * @author carsten */ @Override public void hinzufuegen(String startIp, int startPort, Segment segment) { LOG.trace("UDP data received from " + startIp + ":" + startPort + ", data: " + segment); zielIp = startIp; zielPort = startPort; synchronized (puffer) { puffer.add((UdpSegment) segment); puffer.notifyAll(); } } /** * Methode zum Empfang einer Nachricht (entspricht in dieser Implementierung genau einem UDP-Segment) mit einem * Timeout. Wenn der Timeout vor eintreffen eines Datagramms auftritt, ist der Rueckgabewert 'null'. * * @return gibt den empfangenen Datenstring zurueck. */ public synchronized String empfangen(long millis) { LOG.trace("UDP receive on port " + lokalerPort + " (timeout: " + millis + ")"); UdpSegment segment; synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(millis); } catch (InterruptedException e) { LOG.debug("UDP socket interrupted"); } } if (puffer.size() >= 1) { segment = (UdpSegment) puffer.removeFirst(); return segment.getDaten(); } else { LOG.debug("no UDP data received (local port {})", lokalerPort); return null; } } } /** * Methode zum Empfang einer Nachricht (entspricht in dieser Implementierung genau einem UDP-Segment) * * @return gibt den empfangenen Datenstring zurueck. */ public synchronized String empfangen() { LOG.trace("(UDPSocket), empfangen()"); return empfangen(0); } /** Methode zum Senden einer Nachricht ueber UDP */ public synchronized void senden(String nachricht) { LOG.trace("(UDPSocket), senden(" + nachricht + ")"); UdpSegment segment; segment = new UdpSegment(); segment.setDaten(nachricht); segment.setQuellPort(lokalerPort); segment.setZielPort(zielPort); super.sende(segment); } /** Methode zum senden eines Broadcast Datagramms mit UDP */ public synchronized void sendeBroadcast(String nachricht) { sendeBroadcast(null, nachricht); } public synchronized void sendeBroadcast(String quellIp, String nachricht) { LOG.trace("(UDPSocket), sendeBroadcast(" + nachricht + ")"); UdpSegment segment; segment = new UdpSegment(); segment.setDaten(nachricht); segment.setQuellPort(lokalerPort); segment.setZielPort(zielPort); protokoll.senden("255.255.255.255", quellIp, segment); } /** * Methode zum Schliessen eines Sockets. Der Port wird wieder freigegeben! */ public void schliessen() { LOG.trace("(UDPSocket), schliessen()"); stopListening = true; synchronized (puffer) { puffer.notifyAll(); } if (verbunden) { austragenPort(); verbunden = false; } } /** * Methode zum unterbrechen eines Methodenaufrufs zum empfangen eines Datagramms. Diese Methode wird beim Wechsel * vom Aktions- zum Entwurfsmodus aufgerufen. * */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (UDPSocket), beenden()"); stopListening = true; synchronized (puffer) { puffer.notifyAll(); } } public void verbinden() { LOG.debug("[port={}] start listening UDP", lokalerPort); if (!verbunden) { if (modus == PASSIV) { synchronized (puffer) { while (puffer.size() <= 0 && !stopListening) { try { puffer.wait(100); } catch (InterruptedException e) {} } } } if (!stopListening) { try { eintragenPort(); verbunden = true; } catch (SocketException e) { LOG.debug("EXCEPTION (" + this.hashCode() + "): verbinden() NICHT erfolgreich", e); } } } LOG.debug("[port={}] stop listening UDP", lokalerPort); } public boolean istVerbunden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (UDPSocket), istVerbunden()"); return verbunden; } public String getStateAsString() { if (this.istVerbunden()) { return "CONNECTED"; } else { return "DISCONNECTED"; } } } filius-2.9.4+ds/src/main/java/filius/software/transportschicht/UdpSegment.java000066400000000000000000000030711506574241500275360ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.transportschicht; import java.io.Serializable; public class UdpSegment extends Segment implements Serializable { private static final long serialVersionUID = 1L; private int laenge; public int getLaenge() { return laenge; } public void setLaenge(int laenge) { this.laenge = laenge; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/000077500000000000000000000000001506574241500253265ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/ARP.java000066400000000000000000000254741506574241500266270ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.Verbindung; import filius.hardware.knoten.InternetKnoten; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.system.SystemSoftware; /** * In dieser Klasse ist das Address Resolution Protocol (ARP) implementiert. Insbesondere wird hier die ARP-Tabelle mit * Eintraegen, die aus einer IP-Adresse und einem Paar aus MAC-Adresse und Zeitpunkt der Eintragerstellung besteht. */ public class ARP extends VermittlungsProtokoll { private static Logger LOG = LoggerFactory.getLogger(ARP.class); /** * Die ARP-Tabelle als Hashtabelle. Als Schluessel wird die IP-Adresse verwendet. Der zugehoerige Wert ist ein * String-Array mit der gesuchten MAC-Adresse und dem Zeitpunkt, zu dem der Eintrag vorgenommen wurde. */ private HashMap arpTabelle = new HashMap(); /** * Der Thread zur Ueberwachung des Puffers mit eingehenden ARP-Paketen */ private ARPThread thread; /** * Standard-Konstruktor zur Initialisierung der zugehoerigen Systemsoftware * * @param systemAnwendung */ public ARP(SystemSoftware systemAnwendung) { super(systemAnwendung); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (ARP), constr: ARP(" + systemAnwendung + ")"); } public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ARP), starten()"); resetArpTable(); thread = new ARPThread(this); thread.starten(); } public void resetArpTable() { synchronized (arpTabelle) { arpTabelle = new HashMap(); hinzuARPTabellenEintrag("255.255.255.255", "FF:FF:FF:FF:FF:FF"); } } public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ARP), beenden()"); if (thread != null) thread.beenden(); } /** * Fuegt eine Zeile zur ARP Tabelle hinzu. Dabei werden IP Adresse und MAC-Adresse uebergeben * * @author Thomas Gerding * * @param ipAdresse * @param macAdresse */ public void hinzuARPTabellenEintrag(String ipAdresse, String macAdresse) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ARP), hinzuARPTabellenEintrag(" + ipAdresse + "," + macAdresse + ")"); String tmpTime = "" + System.currentTimeMillis(); String[] tmpString = { macAdresse, tmpTime }; synchronized (arpTabelle) { arpTabelle.put(ipAdresse, tmpString); arpTabelle.notify(); } } public void removeARPTableEntry(String ipAddress) { synchronized (arpTabelle) { arpTabelle.remove(ipAddress); arpTabelle.notify(); } } public String toString() { StringBuilder builder = new StringBuilder(); for (Entry entry : arpTabelle.entrySet()) { builder.append("\t").append(entry.getKey()).append(" \t ").append(entry.getValue()[0]).append("\n"); } return builder.toString(); } public Map holeARPTabelle(String address) { Map table = new HashMap(); String[] entry = arpTabelle.get(address); if (null != entry) { table.put(address, entry[0]); } return table; } public Map holeARPTabelle() { Map table = new HashMap(); for (String ipAddress : arpTabelle.keySet()) { table.put(ipAddress, arpTabelle.get(ipAddress)[0]); } return table; } /** * Liefert die MAC Adresse zu einer IP Adresse aus der ARP Tabelle zurueck. Wenn kein passender Eintrag vorhanden * ist, wird eine Broadcast-Anfrage verschickt und auf eingehende Antworten gewartet. Wenn nach einem Timeout ein * passender Eintrag vorliegt, wird dieser zurueck gegeben. Andernfalls wird null zurueck gegeben. * * @author Thomas Gerding * @param maxRetries * @param selectNicByAddress * @param ipAdresse * @return MAC Adresse, zu der die IP Adresse gehoert, oder null, wenn keine MAC-Adresse bestimmt werden konnte */ public String holeARPTabellenEintrag(String zielIp, int maxRetries, boolean selectNicByAddress) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ARP), holeARPTabellenEintrag(" + zielIp + ")"); if (zielIp.equals("127.0.0.1")) { return ((InternetKnotenBetriebssystem) holeSystemSoftware()).primaryMACAddress(); } if (holeSystemSoftware() instanceof InternetKnotenBetriebssystem) { if (zielIp.equals(((InternetKnotenBetriebssystem) holeSystemSoftware()).primaryIPAdresse())) { return ((InternetKnotenBetriebssystem) holeSystemSoftware()).primaryMACAddress(); } } // Eintrag in ARP-Tabelle fuer gesuchte IP-Adresse? String[] arpEntry = arpTabelle.get(zielIp); if (arpEntry != null) { return ((String[]) arpEntry)[0]; } else { // ARP-Broadcast und warte auf Antwort for (int i = 0; arpEntry == null && i < maxRetries; i++) { if (!sendeARPBroadcast(zielIp, selectNicByAddress)) { LOG.debug("No ARP request sent. No applicable NIC."); } else { LOG.debug("Send ARP query for " + (i + 1) + ". time."); synchronized (arpTabelle) { try { arpTabelle.wait(Verbindung.holeRTT()); } catch (InterruptedException e) { LOG.debug("EXCEPTION (" + this.hashCode() + "): keine Anwort auf ARP-Broadcast fuer IP-Adresse " + zielIp + " eingegangen!", e); } } } // Abfrage in ARP-Tabelle nach Broadcast arpEntry = arpTabelle.get(zielIp); } if (arpEntry != null) { return ((String[]) arpEntry)[0]; } } LOG.info("kein ARP-Tabellen-Eintrag fuer " + zielIp); return null; } /** * Hilfsmethode zum Versenden einer ARP-Anfrage * * @param selectNicByAddress */ private boolean sendeARPBroadcast(String suchIp, boolean selectNicByAddress) { NetzwerkInterface nic; if (selectNicByAddress) { nic = getBroadcastNic(suchIp); } else { nic = getDefaultNic(); } boolean requestSent = false; if (nic != null) { sendArpRequest(nic.getMac(), nic.getIp(), "FF:FF:FF:FF:FF:FF", suchIp, null); requestSent = true; } return requestSent; } public ArpPaket sendArpRequest(String senderMAC, String senderIP, String targetMAC, String lookupOrTargetIP, NetzwerkInterface useNic) { ArpPaket arpPaket = new ArpPaket(); arpPaket.setOperation(ArpPaket.REQUEST); arpPaket.setProtokollTyp(EthernetFrame.IP); arpPaket.setTargetIP(lookupOrTargetIP); arpPaket.setTargetMAC(targetMAC); arpPaket.setSenderIP(senderIP); arpPaket.setSenderMAC(senderMAC); ((InternetKnotenBetriebssystem) holeSystemSoftware()).holeEthernet().senden(arpPaket, senderMAC, arpPaket.getTargetMAC(), EthernetFrame.ARP, null); return arpPaket; } public ArpPaket sendArpReply(String senderMAC, String senderIP, String targetMAC, String targetIP, NetzwerkInterface useNic) { ArpPaket antwortArp = new ArpPaket(); antwortArp.setOperation(ArpPaket.REPLY); antwortArp.setProtokollTyp(EthernetFrame.IP); antwortArp.setSenderIP(senderIP); antwortArp.setSenderMAC(senderMAC); if (targetIP.equalsIgnoreCase("0.0.0.0")) { antwortArp.setTargetIP("255.255.255.255"); antwortArp.setTargetMAC("ff:ff:ff:ff:ff:ff"); } else { antwortArp.setTargetIP(targetIP); antwortArp.setTargetMAC(targetMAC); } ((InternetKnotenBetriebssystem) holeSystemSoftware()).holeEthernet().senden(antwortArp, senderMAC, antwortArp.getTargetMAC(), EthernetFrame.ARP, useNic); return antwortArp; } NetzwerkInterface getBroadcastNic(String zielStr) { long netAddr, maskAddr, zielAddr = IP.inetAton(zielStr); long bestMask = -1; NetzwerkInterface bestNic = null; for (NetzwerkInterface nic : ((InternetKnoten) holeSystemSoftware().getKnoten()).getNetzwerkInterfaces()) { maskAddr = IP.inetAton(nic.getSubnetzMaske()); if (maskAddr <= bestMask) { continue; } netAddr = IP.inetAton(nic.getIp()) & maskAddr; if (netAddr == (maskAddr & zielAddr)) { bestMask = maskAddr; bestNic = nic; } } return bestNic; } NetzwerkInterface getDefaultNic() { // If the IP address is not yet initialized, no NIC could be selected. So use the first NIC (required for DHCP) return ((InternetKnoten) holeSystemSoftware().getKnoten()).getNetzwerkInterfaces().get(0); } public ARPThread getARPThread() { return thread; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/ARPThread.java000066400000000000000000000103361506574241500277460ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of Informatics and E-Learning" by a students' * project group: members (2006-2007): André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, Nadja Haßler, * Ernst Johannes Klebert, Michell Weyer supervisors: Stefan Freischlad (maintainer until 2009), Peer Stechert Project * is maintained since 2010 by Christian Eibl and Stefan Freischlad Filius 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 of the License, or (at your option) version 3. ** ** Filius 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 Filius. If not, see * . */ package filius.software.vermittlungsschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.software.ProtokollThread; import filius.software.system.InternetKnotenBetriebssystem; /** * Klasse zur Ueberwachung des Puffers fuer eingehende ARP-Pakete * */ public class ARPThread extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(ARPThread.class); /** * die Implementierung des Address Resolution Protocols mit der Verwaltung der ARP-Eintraege */ private ARP vermittlung; /** * Konstruktor zur Initialisierung des zu ueberwachenden Puffers und der ARP-Implementierung */ public ARPThread(ARP vermittlung) { super(((InternetKnotenBetriebssystem) vermittlung.holeSystemSoftware()).holeEthernet().holeARPPuffer()); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ARPThread), constr: ARPThread(" + vermittlung + ")"); this.vermittlung = vermittlung; } /** * Methode zur Verarbeitung eingehender ARP-Pakete
    * Aus jedem ARP-Paket wird ein neuer Eintrag fuer die ARP-Tabelle erzeugt (unabhaengig davon, ob es eine Anfrage * oder eine Antwort ist). Wenn die Anfrage eine eigene IP-Adresse betrifft, wird ein Antwort-Paket verschickt. */ protected void verarbeiteDatenEinheit(ArpPaket arpPaket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ARPThread), verarbeiteDatenEinheit(" + arpPaket.toString() + ")"); InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) vermittlung.holeSystemSoftware(); NetzwerkInterface nic = arpPaket.getRcvNic(); if (nic != null && !VermittlungsProtokoll.getSubnetForIp(nic.getIp(), nic.getSubnetzMaske()) .equals(arpPaket.getSenderIP())) { // Aus jedem ARP-Paket, mit dem eine eigene IP-Adresse abgefragt wird oder das eine Antwort ist, wird ein // neuer ARP-Eintrag erzeugt if (arpPaket.getTargetIP().equals(nic.getIp()) || arpPaket.getOperation() == ArpPaket.REPLY && !arpPaket.getSenderIP().equalsIgnoreCase("0.0.0.0")) { LOG.debug("ARP data received, will insert data for sender: {}", arpPaket); vermittlung.hinzuARPTabellenEintrag(arpPaket.getSenderIP(), arpPaket.getSenderMAC()); } // wenn die Anfrage eine Anfrage fuer eine eigene // IP-Adresse ist, wird eine Antwort verschickt if (arpPaket.getTargetMAC().equalsIgnoreCase("ff:ff:ff:ff:ff:ff") && arpPaket.getTargetIP().equalsIgnoreCase(nic.getIp())) { bs.holeARP().sendArpReply(nic.getMac(), nic.getIp(), arpPaket.getSenderMAC(), arpPaket.getSenderIP(), nic); } else { LOG.debug("ARP: do not respond on NIC '{}' for ARP request ({})", nic.getIp(), arpPaket); } } else { LOG.debug("ARP packet discarded ({})", arpPaket); } } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/ArpPaket.java000066400000000000000000000074351506574241500277110ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import filius.software.ProtocolDataUnit; /** Diese Klasse umfasst die Attribute eines ARP-Pakets */ public class ArpPaket extends ProtocolDataUnit { public static int REQUEST = 1, REPLY = 2; private static final long serialVersionUID = 1L; /** counter for the arp packet number */ private static long arpPacketNumberCounter; /** Protokoll-Typ der Vermittlungsschicht */ private String protokollTyp; /** Operation: 1 - request, 2 - reply */ private int operation = 1; /** MAC-Adresse des sendenden Knotens */ private String senderMAC; /** IP-Adresse des sendenden Knotens */ private String senderIP; /** MAC-Adresse des Zielknotens (i.d.R. Broadcast) */ private String targetMAC; /** * Ziel-IP-Adresse bzw. die Adresse des Knotens, zu dem die MAC-Adresse gesucht wird */ private String targetIP; /** ARP packet number (does not exist in reality) */ private final long arpPacketNumber; public ArpPaket() { this.arpPacketNumber = getNextArpPacketCounter(); } /** * Gets the next ARP packet number and increases global counter by 1. * * @return next arp packet number */ private static synchronized long getNextArpPacketCounter() { return ++arpPacketNumberCounter; } public String getProtokollTyp() { return protokollTyp; } public void setProtokollTyp(String protokollTyp) { this.protokollTyp = protokollTyp; } public String getSenderIP() { return senderIP; } public void setSenderIP(String quellIp) { this.senderIP = quellIp; } public String getSenderMAC() { return senderMAC; } public void setSenderMAC(String quellMacAdresse) { this.senderMAC = quellMacAdresse; } public String getTargetIP() { return targetIP; } public void setTargetIP(String zielIp) { this.targetIP = zielIp; } public String getTargetMAC() { return targetMAC; } public void setTargetMAC(String zielMacAdresse) { this.targetMAC = zielMacAdresse; } public long getArpPacketNumber() { return arpPacketNumber; } public String toString() { return "[op=" + (operation == REQUEST ? "REQUEST" : "REPLY") + ", sender=" + senderMAC + "|" + senderIP + ", target=" + targetMAC + "|" + targetIP + "]"; } public int getOperation() { return operation; } public void setOperation(int operation) { this.operation = operation; } }filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/ICMP.java000066400000000000000000000261031506574241500267230ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import static filius.software.netzzugangsschicht.Ethernet.ETHERNET_BROADCAST; import java.util.LinkedList; import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.VerbindungsException; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.system.SystemSoftware; /** * This class implements the ICMP protocol -- at least for echo request/response. */ public class ICMP extends VermittlungsProtokoll implements I18n { private static Logger LOG = LoggerFactory.getLogger(ICMP.class); public static final int TYPE_ECHO_REPLY = 0; public static final int TYPE_ECHO_REQUEST = 8; public static final int TYPE_DESTINATION_UNREACHABLE = 3; public static final int TYPE_TIME_EXCEEDED = 11; public static final int CODE_ECHO = 0; public static final int CODE_DEST_NETWORK_UNREACHABLE = 0; public static final int CODE_DEST_HOST_UNREACHABLE = 1; public static final int CODE_TTL_EXPIRED = 0; private ICMPThread thread; /** * Standard-Konstruktor zur Initialisierung der zugehoerigen Systemsoftware * * @param systemAnwendung */ public ICMP(SystemSoftware systemAnwendung) { super(systemAnwendung); LOG.debug( "INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (ICMP), constr: ICMP(" + systemAnwendung + ")"); } public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ICMP), starten()"); thread = new ICMPThread(this); thread.starten(); } public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ICMP), beenden()"); if (thread != null) thread.beenden(); } private void placeLocalICMPPacket(IcmpPaket icmpPacket) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (ICMP), placeLocalICMPPacket(" + icmpPacket.toString() + ")"); LinkedList icmpPakete = ((InternetKnotenBetriebssystem) holeSystemSoftware()).holeEthernet() .holeICMPPuffer(); synchronized (icmpPakete) { icmpPakete.add(icmpPacket); icmpPakete.notify(); } } /** Hilfsmethode zum Versenden eines ICMP Echo Requests */ public void sendEchoRequest(String destIp, int seqNr) { sendeICMP(TYPE_ECHO_REQUEST, CODE_ECHO, seqNr, null, destIp); } /** Hilfsmethode zum Versenden eines ICMP Echo Reply */ public void sendEchoReply(IcmpPaket rcvPacket) { sendeICMP(TYPE_ECHO_REPLY, CODE_ECHO, rcvPacket.getSeqNr(), null, rcvPacket.getSender()); } public void sendeICMP(int typ, int code, String zielIP) { sendeICMP(typ, code, 0, null, zielIP); } public void sendeICMP(int typ, int code, int seqNr, String quellIP, String zielIP) { sendeICMP(typ, code, 64, seqNr, -1, quellIP, zielIP); } private void sendeICMP(int typ, int code, int ttl, int seqNr, long identification, String quellIP, String zielIP) { IcmpPaket icmpPaket = new IcmpPaket(identification); icmpPaket.setEmpfaenger(zielIP); icmpPaket.setIcmpType(typ); icmpPaket.setIcmpCode(code); icmpPaket.setSeqNr(seqNr); icmpPaket.setTtl(ttl); try { InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); Route route = bs.determineRoute(zielIP); if (quellIP == null) { icmpPaket.setSender(route.getInterfaceIpAddress()); } else { icmpPaket.setSender(quellIP); } dispatch(icmpPaket, zielIP, route); } catch (RouteNotFoundException e) { // Es wurde keine Route gefunden, ueber die das Paket versendet // werden konnte. // Falls das weiterzuleitende Paket ein ICMP Echo Request ist, // muss ein ICMP Destination Unreachable: Network Unreachable (3/0) // zurueckgesendet werden. Andere ICMP-Paket muessen verworfen // werden. if (icmpPaket.isEchoRequest()) { sendeICMP(TYPE_DESTINATION_UNREACHABLE, CODE_DEST_NETWORK_UNREACHABLE, seqNr, null, quellIP); } } } /** * Hilfsmethode zum versenden eines Unicast-Pakets. Hier wird unterschieden, ob sich die Ziel-IP-Adresse im lokalen * Rechnernetz befindet oder ueber das Gateway verschickt werden muss. * * @param paket * das zu versendende IP-Paket * @param zielIp * @param route * TODO * @throws RouteNotFoundException * @throws VerbindungsException */ private void dispatch(IcmpPaket paket, String zielIp, Route route) throws RouteNotFoundException { NetzwerkInterface nic = ((InternetKnoten) holeSystemSoftware().getKnoten()) .getNetzwerkInterfaceByIp(route.getInterfaceIpAddress()); if (isBroadcast(zielIp, route.getInterfaceIpAddress(), nic.getSubnetzMaske())) { sendBroadcast(paket, zielIp, nic.getMac(), nic); } else if (gleichesRechnernetz(zielIp, route.getInterfaceIpAddress(), nic.getSubnetzMaske())) { sendUnicastToNextHop(paket, paket.getEmpfaenger(), nic.getMac(), nic); } else { sendUnicastToNextHop(paket, route.getGateway(), nic.getMac(), nic); } } /** * Hilfsmethode zum Versenden eines Unicast-Pakets im lokalen Rechnernetz. * * @param paket * das zu versendende IP-Paket * @param ziel * die Ziel-IP * @param macOfNicToUse * @param useNic * TODO */ private void sendUnicastToNextHop(IcmpPaket paket, String ziel, String macOfNicToUse, NetzwerkInterface useNic) { InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); String zielMacAdresse = bs.holeARP().holeARPTabellenEintrag(ziel, 2, true); if (this.isLocalAddress(ziel)) { placeLocalICMPPacket(paket); } else if (zielMacAdresse != null) { // MAC-Adresse konnte bestimmt werden bs.holeEthernet().senden(paket, macOfNicToUse, zielMacAdresse, EthernetFrame.IP, useNic); } else { // Es konnte keine MAC-Adresse bestimmt werden. // Falls das weiterzuleitende Paket ein ICMP Echo Request ist, // muss ein ICMP Destination Unreachable: Host Unreachable (3/1) // zurueckgesendet werden. Andere ICMP-Paket muessen verworfen // werden. if (paket.isEchoRequest()) { sendeICMP(TYPE_DESTINATION_UNREACHABLE, CODE_DEST_HOST_UNREACHABLE, paket.getSeqNr(), null, paket.getSender()); } } } private void sendBroadcast(IcmpPaket paket, String ziel, String macOfNicToUse, NetzwerkInterface useNic) { paket.setTtl(1); InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); bs.holeEthernet().senden(paket, macOfNicToUse, ETHERNET_BROADCAST, EthernetFrame.IP, useNic); } /** * Methode zum Weiterleiten eines ICMP-Pakets. Zunaechst wird geprueft, ob das Feld Time-to-Live-Feld (TTL) noch * nicht abgelaufen ist (d. h. TTL groesser 0). Wenn diese Bedingung erfuellt ist, wird zunaechst geprueft, ob es * sich um einen Broadcast handelt. Ein solches Paket wird nicht weitergeleitet sondern nur an die Transportschicht * weitergegeben. Sonst wird die Weiterleitungstabelle nach einem passenden Eintrag abgefragt. Anhand des * zurueckgegebenen Eintrags wird geprueft, ob das Paket fuer den eigenen Rechner ist oder ob ein Unicast-Paket * verschickt werden muss. * * @param icmpPaket * das zu versendende ICMP-Paket */ public void weiterleitenPaket(IcmpPaket icmpPaket) { if (icmpPaket.getTtl() <= 1) { // TTL ist abgelaufen. // (wird in ICMPThread.verarbeiteDatenEinheit() // dekrementiert, bevor diese Funktion aufgerufen // wird) // ICMP Timeout Expired In Transit (11/0) zuruecksenden: sendeTimeExceededReply(icmpPaket.getSender(), icmpPaket.getSeqNr()); } else { // TTL ist nicht abgelaufen. // Paket weiterleiten: sendeICMP(icmpPaket.getIcmpType(), icmpPaket.getIcmpCode(), icmpPaket.getTtl() - 1, icmpPaket.getSeqNr(), icmpPaket.getIdentification(), icmpPaket.getSender(), icmpPaket.getEmpfaenger()); } } public void sendeTimeExceededReply(String replyRecipient, int seqNo) { sendeICMP(TYPE_TIME_EXCEEDED, CODE_TTL_EXPIRED, seqNo, null, replyRecipient); } /** method to actually send a ping and compute the pong event */ public IcmpPaket startSinglePing(String destIp, int seqNr) throws TimeoutException { thread.resetIcmpResponseQueue(); sendEchoRequest(destIp, seqNr); IcmpPaket response = thread.waitForIcmpResponse(); if (response == null) { throw new TimeoutException("Destination Host Unreachable"); } else { if (!(seqNr == response.getSeqNr() && response.isEchoResponse())) { throw new TimeoutException("Destination Host Unreachable"); } } return response; } /** method to send a traceroute probe (realized as ping) */ public IcmpPaket sendProbe(String destIp, int ttl, int seqNr) { thread.resetIcmpResponseQueue(); sendeICMP(ICMP.TYPE_ECHO_REQUEST, ICMP.CODE_ECHO, ttl, seqNr, -1, null, destIp); return thread.waitForIcmpResponse(); } public ICMPThread getICMPThread() { return thread; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/ICMPThread.java000066400000000000000000000072751506574241500300640ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.Verbindung; import filius.software.ProtokollThread; import filius.software.system.InternetKnotenBetriebssystem; /** * Klasse zur Ueberwachung des Puffers fuer eingehende ICMP-Pakete * */ public class ICMPThread extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(ICMPThread.class); private ICMP vermittlung; /** der von dem Thread zu ueberwachende Puffer */ private LinkedList rcvdPackets; public ICMPThread(ICMP vermittlung) { super(((InternetKnotenBetriebssystem) vermittlung.holeSystemSoftware()).holeEthernet().holeICMPPuffer()); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ICMPThread), constr: ICMPThread(" + vermittlung + ")"); this.rcvdPackets = new LinkedList(); this.vermittlung = vermittlung; } /** * Methode zur Verarbeitung eingehender ICMP-Pakete
    */ protected void verarbeiteDatenEinheit(IcmpPaket icmpPaket) { if (vermittlung.isLocalAddress(icmpPaket.getEmpfaenger()) || vermittlung.isNetworkLocalBroadcast(icmpPaket.getEmpfaenger(), icmpPaket.getSender())) { if (icmpPaket.isEchoRequest()) { vermittlung.sendEchoReply(icmpPaket); } else { addIcmpResponse(icmpPaket); } } else if (vermittlung.isBroadcast(icmpPaket.getEmpfaenger())) { LOG.info("discard icmp broadcast for another network ({})", icmpPaket); } else { vermittlung.weiterleitenPaket(icmpPaket); } } private void addIcmpResponse(IcmpPaket icmpPaket) { synchronized (rcvdPackets) { rcvdPackets.add(icmpPaket); rcvdPackets.notifyAll(); } } public void resetIcmpResponseQueue() { synchronized (rcvdPackets) { rcvdPackets.clear(); rcvdPackets.notifyAll(); } } public IcmpPaket waitForIcmpResponse() { IcmpPaket response = null; synchronized (rcvdPackets) { try { rcvdPackets.wait(Verbindung.holeRTT()); } catch (InterruptedException e) {} if (rcvdPackets.size() > 0) { response = rcvdPackets.removeFirst(); } } return response; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/IP.java000066400000000000000000000334161506574241500265100ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import static filius.software.netzzugangsschicht.Ethernet.ETHERNET_BROADCAST; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.VerbindungsException; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.software.netzzugangsschicht.EthernetFrame; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.transportschicht.Segment; import filius.software.transportschicht.TcpSegment; import filius.software.transportschicht.UdpSegment; /** * Diese Klasse implementiert die Funktionalitaet des Internet-Protokolls. Das heisst, dass Pakete richtig * weitergeleitet werden und eingehende Segmente an die Transportschicht weitergeleitet werden. */ public class IP extends VermittlungsProtokoll implements I18n { private static final Pattern REGEX_CLIENT_MAC_ADDRESS_IN_DHCP = Pattern.compile("chaddr=(.{17})"); private static Logger LOG = LoggerFactory.getLogger(IP.class); public static final String CURRENT_NETWORK = "0.0.0.0"; /** String-Konstante fuer die IP-Adresse Localhost (127.0.0.1) */ public static final String LOCALHOST = "127.0.0.1"; /** Puffer fuer eingehende IP-Pakete fuer TCP */ private LinkedList ipPaketListeTCP = new LinkedList(); /** Puffer fuer eingehende IP-Pakete fuer UDP */ private LinkedList ipPaketListeUDP = new LinkedList(); /** * Der Thread zur Ueberwachung des IP-Pakete-Puffers der Ethernet-Schicht */ private IPThread thread; /** * Konstruktor zur Initialisierung der Systemsoftware * * @param systemsoftware */ public IP(InternetKnotenBetriebssystem systemsoftware) { super(systemsoftware); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (IP), constr: IP(" + systemsoftware + ")"); } public boolean isIPForwardingEnabled() { return ((InternetKnotenBetriebssystem) holeSystemSoftware()).isIpForwardingEnabled(); } public static long inetAton(String ipStr) { long ipAddr = 0; int octet; StringTokenizer ipToken = new StringTokenizer(ipStr, "."); try { for (int i = 0; i < 4; i++) { try { octet = Integer.parseInt(ipToken.nextToken()); } catch (java.lang.NumberFormatException e) { return -1; } if (0 > octet || octet > 255) { return -1; } ipAddr += octet; if (i < 3) { ipAddr <<= 8; } } } catch (NoSuchElementException e) { return -1; } if (ipToken.hasMoreTokens()) { return -1; } return ipAddr; } public static String inetNtoa(long ipAddr) { String ipStr = ""; ipStr = "." + (ipAddr & 255); ipAddr >>= 8; ipStr = "." + (ipAddr & 255) + ipStr; ipAddr >>= 8; ipStr = "." + (ipAddr & 255) + ipStr; ipAddr >>= 8; ipStr = (ipAddr & 255) + ipStr; return ipStr; } public static String ipCheck(String ip) { long ipAddr = inetAton(ip); if (ipAddr == -1) { return null; } return inetNtoa(ipAddr); } /** Hilfsmethode zum Versenden eines Broadcast-Pakets */ private void sendeBroadcast(IpPaket ipPaket) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (IP), sendeBroadcast(" + ipPaket.toString() + ")"); InternetKnoten knoten = (InternetKnoten) holeSystemSoftware().getKnoten(); for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { sendBroadcastOverNic(ipPaket, nic); } } private void sendBroadcastOverNic(IpPaket ipPaket, NetzwerkInterface useNic) { // Damit Broadcast-Pakete nicht in Zyklen gesendet werden, // wird das Feld Time-to-Live (TTL) auf 1 gesetzt. Damit // wird es von keinem Knoten weitergeschickt ipPaket.setTtl(1); String macAddressFilter = null; if (ipPaket.getSegment().getDaten().startsWith("DHCPDISCOVER") || ipPaket.getSegment().getDaten().startsWith("DHCPREQUEST")) { Matcher dhcpMACMatcher = REGEX_CLIENT_MAC_ADDRESS_IN_DHCP.matcher(ipPaket.getSegment().getDaten()); if (dhcpMACMatcher.find()) { macAddressFilter = dhcpMACMatcher.group(1); } } InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); if (CURRENT_NETWORK.equals(ipPaket.getSender()) && (null == macAddressFilter || useNic.getMac().equalsIgnoreCase(macAddressFilter)) || gleichesRechnernetz(ipPaket.getSender(), useNic.getIp(), useNic.getSubnetzMaske())) { bs.holeEthernet().senden(ipPaket, useNic.getMac(), ETHERNET_BROADCAST, EthernetFrame.IP, useNic); } } /** * Hilfsmethode fuer Pakete, die fuer den eigenen Knoten bestimmt sind * * @param segment * das zu verarbeitende Segment */ void benachrichtigeTransportschicht(IpPaket paket) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (IP), benachrichtigeTransportschicht(" + paket.toString() + ")"); if (paket.getSegment() instanceof TcpSegment) { synchronized (ipPaketListeTCP) { ipPaketListeTCP.add(paket); ipPaketListeTCP.notify(); } } else if (paket.getSegment() instanceof UdpSegment) { synchronized (ipPaketListeUDP) { ipPaketListeUDP.add(paket); ipPaketListeUDP.notify(); } } } /** * Hilfsmethode zum versenden eines Unicast-Pakets. Hier wird unterschieden, ob sich die Ziel-IP-Adresse im lokalen * Rechnernetz befindet oder ueber das Gateway verschickt werden muss. * * @param paket * das zu versendende IP-Paket * @throws RouteNotFoundException */ private void sendeUnicast(IpPaket paket, Route route) throws RouteNotFoundException { NetzwerkInterface nic = ((InternetKnoten) holeSystemSoftware().getKnoten()) .getNetzwerkInterfaceByIp(route.getInterfaceIpAddress()); String netzmaske = nic.getSubnetzMaske(); if (gleichesRechnernetz(paket.getEmpfaenger(), route.getInterfaceIpAddress(), netzmaske)) { // adressierter Knoten befindet sich im lokalen Rechnernetz sendeUnicastLokal(paket, paket.getEmpfaenger(), nic); } else { // adressierter Knoten ist ueber Gateway zu erreichen sendeUnicastLokal(paket, route.getGateway(), nic); } } private void sendeUnicastLokal(IpPaket paket, String ziel, NetzwerkInterface useNic) { InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); String zielMacAdresse = bs.holeARP().holeARPTabellenEintrag(ziel, 2, true); if (zielMacAdresse != null) { // MAC-Adresse konnte bestimmt werden bs.holeEthernet().senden(paket, useNic.getMac(), zielMacAdresse, EthernetFrame.IP, useNic); } else { // Es konnte keine MAC-Adresse bestimmt werden. // Es muss ein ICMP Destination Unreachable: Host Unreachable // (3/1) zurueckgesendet werden: bs.holeICMP().sendeICMP(ICMP.TYPE_DESTINATION_UNREACHABLE, ICMP.CODE_DEST_HOST_UNREACHABLE, paket.getSender()); } } /** * Methode zum Versenden eines IP-Pakets. Zunaechst wird die Sender-IP-Adresse an Hand der Weiterleitungstabelle * bestimmt (die Schnittstelle, ueber die das Paket versendet wird). Dann wird das Paket erzeugt und schliesslich an * die Methode weiterleitenPaket() uebergeben. * * @param zielIp * Gibt die Ziel-IP-Adresse an * @param quellIp * @param protokoll * Der Parameter Protokoll gibt die Protokollnummer an. Dabei steht die Nummer 6 fuer das Protokoll TCP * @param ttl * @param segment * - Enthaellt das erzeugte Segment mit den Nutzdaten. * @throws VerbindungsException */ public void senden(String zielIp, String quellIp, int protokoll, int ttl, Segment segment) { IpPaket paket = new IpPaket(protokoll, -1); paket.setEmpfaenger(zielIp); paket.setTtl(ttl); paket.setSegment(segment); if (this.isLocalAddress(zielIp)) { // Paket ist an diesen Rechner gerichtet paket.setSender(LOCALHOST); benachrichtigeTransportschicht(paket); } else if (zielIp.equals("255.255.255.255")) { if (quellIp == null) { quellIp = ((InternetKnotenBetriebssystem) holeSystemSoftware()).primaryIPAdresse(); } paket.setSender(quellIp); sendeBroadcast(paket); } else { try { InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); Route route = bs.determineRoute(paket.getEmpfaenger()); paket.setSender(route.getInterfaceIpAddress()); sendeUnicast(paket, route); } catch (RouteNotFoundException e) {} } } /** * Methode zum Weiterleiten eines IP-Pakets. Zunaechst wird geprueft, ob das Feld Time-to-Live-Feld (TTL) noch nicht * abgelaufen ist (d. h. TTL groesser 0). Wenn diese Bedingung erfuellt ist, wird die Weiterleitungstabelle nach * einem passenden Eintrag abgefragt und entsprechend verschickt. * * @param ipPaket * das zu versendende IP-Paket */ public void weiterleitenPaket(IpPaket paket) { InternetKnotenBetriebssystem bs = (InternetKnotenBetriebssystem) holeSystemSoftware(); if (paket.getTtl() <= 1) { bs.holeICMP().sendeICMP(ICMP.TYPE_TIME_EXCEEDED, ICMP.CODE_TTL_EXPIRED, paket.getSender()); } else { try { Route route = bs.determineRoute(paket.getEmpfaenger()); IpPaket packetClone = paket.clone(); packetClone.decrementTtl(); sendeUnicast(packetClone, route); } catch (RouteNotFoundException e) { bs.holeICMP().sendeICMP(ICMP.TYPE_DESTINATION_UNREACHABLE, ICMP.CODE_DEST_NETWORK_UNREACHABLE, paket.getSender()); bs.benachrichtigeBeobacher(messages.getString("sw_ip_msg4") + " \"" + bs.getKnoten().getName() + "\"!\n" + messages.getString("sw_ip_msg5") + " " + paket.getEmpfaenger() + " " + messages.getString("sw_ip_msg6")); } } } /** * Methode fuer den Zugriff auf den Puffer fuer eingehende Segmente * * @param protokollTyp * der Protokolltyp UDP oder TCP (Konstanten der Klasse TransportProtokoll) * @return die Liste mit Segmenten fuer UDP- oder TCP-Segmente */ public LinkedList holePaketListe(int protokollTyp) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (IP), holePaketListe(" + protokollTyp + ")"); if (protokollTyp == IpPaket.TCP) { return ipPaketListeTCP; } else if (protokollTyp == IpPaket.UDP) { return ipPaketListeUDP; } return null; } /** * Hier wird der Thread zur Ueberwachung des Puffers fuer eingehende IP-Pakete der Netzzugangsschicht */ public void starten() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (IP), starten()"); // ensure that there are no pdu's left from last run! if (ipPaketListeTCP.size() > 0) { LOG.debug("Clear TCP buffer. Still data left from last run."); ipPaketListeTCP.clear(); } if (ipPaketListeUDP.size() > 0) { LOG.debug("Clear UDP buffer. Still data left from last run."); ipPaketListeUDP.clear(); } thread = new IPThread(this); thread.starten(); } /** Der Thread zur Ueberwachung des IP-Pakete-Puffers */ public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (IP), beenden()"); thread.beenden(); } public IPThread getIPThread() { return thread; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/IPThread.java000066400000000000000000000056731506574241500276440ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.ProtokollThread; import filius.software.system.InternetKnotenBetriebssystem; /** * Thread zur Ueberwachung des IP-Puffers, in den von der Ethernet-Schicht eingehende IP-Pakete geschrieben werden */ public class IPThread extends ProtokollThread { private static Logger LOG = LoggerFactory.getLogger(IPThread.class); /** Die IPsschicht */ private IP vermittlung; /** * Konstruktor zur Initialisierung des zu ueberwachenden Puffers und der IPsschicht. */ public IPThread(IP vermittlung) { super(((InternetKnotenBetriebssystem) vermittlung.holeSystemSoftware()).holeEthernet().holeIPPuffer()); LOG.trace("INVOKED-2 (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (IPThread), constr: IPThread(" + vermittlung + ")"); this.vermittlung = vermittlung; } /** * In dieser Methode wird zunaechst das Feld Time-to-Live (TTL) des eingehenden Pakets dekrementiert. Anschliessend * wird es an die Methode weiterleitenPaket() des IP uebergeben. Dort werden Pakete, die fuer diesen Rechner * bestimmt sind, an die Transportschicht weiter gegeben und Pakete an andere Rechner weitergeleitet. */ protected void verarbeiteDatenEinheit(IpPaket datenEinheit) { IpPaket ipPaket = datenEinheit.clone(); if (vermittlung.isLocalAddress(ipPaket.getEmpfaenger()) || ipPaket.getEmpfaenger().equals("255.255.255.255")) { vermittlung.benachrichtigeTransportschicht(ipPaket); } else if (vermittlung.isIPForwardingEnabled()) { vermittlung.weiterleitenPaket(ipPaket); } } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/IcmpPaket.java000066400000000000000000000066621506574241500300600ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; /** Diese Klasse umfasst die Attribute eines ICMP-Pakets */ @SuppressWarnings("serial") public class IcmpPaket extends IpPaket { public static final int ICMP_PROTOCOL = 1; /** * sequence number of Echo Request packet: The sequence number can be used by the client to associate each echo * request with its reply. */ private int seqNr; /** * 0: ICMP Echo Reply (pong)
    * 3: see {@link #icmpType}
    * 8: ICMP Echo Request (ping)
    * 11: ICMP Time Exeeded (poof)
    * else: ICMP unknown */ private int icmpType; /** * 0: "ICMP Network Unreachable
    * 1: ICMP Host Unreachable
    * other: ICMP Destination Unreachable */ private int icmpCode; public IcmpPaket(long identification) { super(ICMP_PROTOCOL, identification); } @Override public IcmpPaket clone() { IcmpPaket clone = new IcmpPaket(getIdentification()); copyIpPacketAttributes(clone); copyIcmpAttributes(clone); return clone; } private void copyIcmpAttributes(IcmpPaket clone) { clone.seqNr = seqNr; clone.icmpType = icmpType; clone.icmpCode = icmpCode; } public void setIcmpType(int type) { this.icmpType = type; } public void setIcmpCode(int code) { this.icmpCode = code; } public int getIcmpType() { return icmpType; } public int getIcmpCode() { return icmpCode; } public boolean isEchoResponse() { return icmpType == ICMP.TYPE_ECHO_REPLY && icmpCode == ICMP.CODE_ECHO; } public boolean isEchoRequest() { return icmpType == ICMP.TYPE_ECHO_REQUEST && icmpCode == ICMP.CODE_ECHO; } public int getSeqNr() { return this.seqNr; } public void setSeqNr(int seqNr) { this.seqNr = seqNr; } public String toString() { return "[" + "sender=" + getSender() + "; " + "recipient=" + getEmpfaenger() + "; " + "ttl=" + getTtl() + "; " + "seqNr=" + seqNr + "; " + "icmpType=" + icmpType + "; " + "icmpCode=" + icmpCode + ", " + "identification=" + getIdentification() + "]"; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/IpPaket.java000066400000000000000000000074641506574241500275410ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import filius.software.ProtocolDataUnit; import filius.software.transportschicht.Segment; /** * Diese Klasse umfasst die Attribute bzw. Felder eines IP-Pakets */ @SuppressWarnings("serial") public class IpPaket extends ProtocolDataUnit implements Cloneable { public static final int UDP = 17, TCP = 6; /** counter of the identification number of the IP packet */ private static long identificationCounter; /** IP-Adresse des sendenden Knotens */ private String sender; private String empfaenger; /** Time-to-Live */ private int ttl; private final int protocol; /** identification number of the IP packet */ private final long identification; private Segment data; public IpPaket(int protocol, long identification) { this.protocol = protocol; // only use identification argument if it is not negative this.identification = identification < 0 ? getNextIdentCounter() : identification; } /** * Gets the next identification number and increases global counter by 1. * * @return next identification number */ private static synchronized long getNextIdentCounter() { return ++identificationCounter; } @Override public IpPaket clone() { IpPaket clone = new IpPaket(protocol, identification); clone.setRcvNic(getRcvNic()); copyIpPacketAttributes(clone); return clone; } void copyIpPacketAttributes(IpPaket clone) { clone.ttl = ttl; clone.empfaenger = empfaenger; clone.sender = sender; clone.data = data; } public String getEmpfaenger() { return empfaenger; } public void setEmpfaenger(String empfaenger) { this.empfaenger = empfaenger; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public int getProtocol() { return protocol; } public int getTtl() { return ttl; } public void setTtl(int ttl) { this.ttl = ttl; } public long getIdentification() { return identification; } public Segment getSegment() { return data; } public void setSegment(Segment data) { this.data = data; } public String toString() { return "[" + "ttl=" + ttl + ", " + "protocol=" + protocol + ", " + "empfaenger=" + empfaenger + ", " + "sender=" + sender + ", " + "identification=" + identification + "]"; } public void decrementTtl() { ttl--; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/Route.java000066400000000000000000000045471506574241500273010ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; /** * @author stefan * */ public class Route { private String netAddress; private String netMask; protected String gateway; protected String interfaceIpAddress; public String getNetAddress() { return netAddress; } public String getNetMask() { return netMask; } public String getGateway() { return gateway; } public String getInterfaceIpAddress() { return interfaceIpAddress; } public Route(String netAddress, String netMask, String gateway, String interfaceIpAddress) { this.netAddress = netAddress; this.netMask = netMask; this.gateway = gateway; this.interfaceIpAddress = interfaceIpAddress; } public Route(String[] routingInfo) { if (routingInfo.length == 4) { netAddress = routingInfo[0]; netMask = routingInfo[1]; gateway = routingInfo[2]; interfaceIpAddress = routingInfo[3]; } else if (routingInfo.length == 2) { gateway = routingInfo[0]; interfaceIpAddress = routingInfo[1]; } } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/RouteNotFoundException.java000066400000000000000000000026221506574241500326250ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; /** * @author stefan * */ public class RouteNotFoundException extends Exception { private static final long serialVersionUID = 1L; } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/VermittlungWeb.java000066400000000000000000000140501506574241500311470ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import java.io.IOException; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.software.rip.RIPRoute; import filius.software.rip.RIPTable; import filius.software.rip.RIPUtil; import filius.software.system.VermittlungsrechnerBetriebssystem; import filius.software.www.WebServerPlugIn; /** * * @author pyropeter * @author stefanf * */ public class VermittlungWeb extends WebServerPlugIn implements I18n { private VermittlungsrechnerBetriebssystem bs; public VermittlungWeb(VermittlungsrechnerBetriebssystem bs) { super(); this.bs = bs; } public String holeHtmlSeite(String postDaten) { RIPTable table = bs.getRIPTable(); String html = null; if (table != null) { html = createRoutingTablePage(table); } else { html = createForwardingTablePage(bs.getWeiterleitungstabelle()); } return html; } private String createForwardingTablePage(Weiterleitungstabelle weiterleitungstabelle) { StringBuffer forwardingEntries = new StringBuffer(); for (String[] forwardingEntry : weiterleitungstabelle.holeTabelle()) { forwardingEntries.append(forwardingEntryToHtml(forwardingEntry)); } String html = null; try { html = textDateiEinlesen("tmpl/routing_" + Information.getInformation().getLocaleOrDefault() + ".html"); html = html.replaceAll(":title:", messages.getString("sw_vermittlungweb_msg5")); html = html.replaceAll(":routing_entries:", forwardingEntries.toString()); html = html.replaceAll(":hint:", messages.getString("sw_vermittlungweb_msg4")); } catch (IOException e) { System.err.println("routing table template could not be read."); e.printStackTrace(); } return html; } private String forwardingEntryToHtml(String[] forwardingEntry) { String html = ""; html += "" + forwardingEntry[0] + ""; html += "" + forwardingEntry[1] + ""; boolean targetIsDirectlyConnected = forwardingEntry[2].equals(forwardingEntry[3]); boolean isLocalhost = forwardingEntry[3].equals("127.0.0.1"); if (targetIsDirectlyConnected || isLocalhost) { html += " 0 "; } else { html += " ? "; } html += "" + messages.getString("sw_vermittlungweb_msg3") + ""; html += "" + forwardingEntry[2] + ""; html += " - "; boolean isDefaultGateway = forwardingEntry[1].equals("0.0.0.0"); if (targetIsDirectlyConnected || isLocalhost) { return "" + html + ""; } else if (isDefaultGateway) { return "" + html + ""; } else { return "" + html + ""; } } private String createRoutingTablePage(RIPTable table) { StringBuffer routingEntries = new StringBuffer(); synchronized (table) { for (RIPRoute route : table.routes) { routingEntries.append(routeToHtml(route)); } } String html = null; try { html = textDateiEinlesen("tmpl/routing_" + Information.getInformation().getLocaleOrDefault() + ".html"); html = html.replaceAll(":title:", messages.getString("sw_vermittlungweb_msg6")); html = html.replaceAll(":routing_entries:", routingEntries.toString()); html = html.replaceAll(":hint:", messages.getString("sw_vermittlungweb_msg1")); } catch (IOException e) { System.err.println("routing table template could not be read."); e.printStackTrace(); } return html; } private String routeToHtml(RIPRoute route) { String html = ""; html += "" + route.getNetAddress() + ""; html += "" + route.getNetMask() + ""; html += "" + route.hops + ""; if (route.expires == 0) { html += "" + messages.getString("sw_vermittlungweb_msg3") + ""; } else { long gueltig = (route.expires - RIPUtil.getTime()) / 1000; html += "" + gueltig + ""; } html += "" + route.getGateway() + ""; html += "" + route.hopPublicIp + ""; if (route.hops == 0) { return "" + html + ""; } else if (route.hops == 16) { return "" + html + ""; } else { return "" + html + ""; } } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/VermittlungsProtokoll.java000066400000000000000000000145551506574241500326140ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import java.util.List; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.software.Protokoll; import filius.software.system.SystemSoftware; /** Oberklasse von ARP und IP mit Hilfsmethoden */ public abstract class VermittlungsProtokoll extends Protokoll { private static Logger LOG = LoggerFactory.getLogger(VermittlungsProtokoll.class); /** Standard-Konstruktor der Oberklasse Protokoll */ public VermittlungsProtokoll(SystemSoftware systemSoftware) { super(systemSoftware); LOG.trace("INVOKED-2 (" + this.hashCode() + ") " + getClass() + " (VermittlungsProtokoll), constr: VermittlungsProtokoll(" + systemSoftware + ")"); } /** * Methode zum pruefen, ob sich zwei IP-Adressen im gleichen Rechnernetz befinden. Dazu wird die Netzmaske * benoetigt: *
      *
    1. Bitweise ODER-Verknuepfung der ersten IP-Adresse und der Netzmaske
    2. *
    3. Bitweise ODER-Verknuepfung der zweiten IP-Adresse und der Netzmaske
    4. *
    5. Vergleich der zwei Netz-IDs, die in den vorangegangenen Schritten berechnet wurden
    6. *
    * * @param adresseEins * erste IP-Adresse als String * @param adresseZwei * zweite IP-Adresse als String * @param netzmaske * Netzmaske als String * @return ob die Netz-IDs der zwei Adressen uebereinstimmen */ public static boolean gleichesRechnernetz(String adresseEins, String adresseZwei, String netzmaske) { LOG.trace("INVOKED (static) filius.software.vermittlungsschicht.VermittlungsProtokoll, gleichesRechnernetz(" + adresseEins + "," + adresseZwei + "," + netzmaske + ")"); int addressOneAsInt = ipAddressToInt(adresseEins); int addressTwoAsInt = ipAddressToInt(adresseZwei); int netmaskAsInt = ipAddressToInt(netzmaske); return (addressOneAsInt & netmaskAsInt) == (addressTwoAsInt & netmaskAsInt); } static int ipAddressToInt(String address) { int addressAsInt = 0; StringTokenizer tokenizer = new StringTokenizer(address, "."); while (tokenizer.hasMoreTokens()) { addressAsInt = (addressAsInt << 8) + Integer.parseInt(tokenizer.nextToken()); } return addressAsInt; } public static boolean isBroadcast(String zielIpAdresse, String sendeIpAdresse, String netzmaske) { int addressAsInt = ipAddressToInt(zielIpAdresse); int netmaskAsInt = ipAddressToInt(netzmaske); boolean isGenericBroadcast = addressAsInt == 0xffffffff; boolean isNetworkBroadcast = (addressAsInt & ~netmaskAsInt) == (0xffffffff & ~netmaskAsInt) && gleichesRechnernetz(zielIpAdresse, sendeIpAdresse, netzmaske); return isGenericBroadcast || isNetworkBroadcast; } /* * Determine subnet address for given IP and netmask. */ public static String getSubnetForIp(String ip, String mask) { LOG.trace("INVOKED (static) filius.software.vermittlungsschicht.VermittlungsProtokoll, getSubnetForIp(" + ip + "," + mask + ")"); int[] a1, m; int[] res = new int[4]; StringTokenizer tokenizer; tokenizer = new StringTokenizer(ip, "."); a1 = new int[4]; for (int i = 0; i < a1.length && tokenizer.hasMoreTokens(); i++) { a1[i] = Integer.parseInt(tokenizer.nextToken()); } tokenizer = new StringTokenizer(mask, "."); m = new int[4]; for (int i = 0; i < m.length && tokenizer.hasMoreTokens(); i++) { m[i] = Integer.parseInt(tokenizer.nextToken()); } for (int i = 0; i < 4; i++) { res[i] = a1[i] & m[i]; } return res[0] + "." + res[1] + "." + res[2] + "." + res[3]; } public boolean isLocalAddress(String ip) { if (gleichesRechnernetz(ip, "127.0.0.0", "255.0.0.0")) { return true; } InternetKnoten knoten = (InternetKnoten) holeSystemSoftware().getKnoten(); for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { if (ip.equals(nic.getIp())) { return true; } } return false; } public boolean isBroadcast(String zielIp) { List nics = ((InternetKnoten) this.holeSystemSoftware().getKnoten()).getNetzwerkInterfaces(); for (NetzwerkInterface nic : nics) { if (isBroadcast(zielIp, nic.getIp(), nic.getSubnetzMaske())) { return true; } } return false; } public boolean isNetworkLocalBroadcast(String zielIp, String senderIP) { List nics = ((InternetKnoten) this.holeSystemSoftware().getKnoten()).getNetzwerkInterfaces(); for (NetzwerkInterface nic : nics) { if (gleichesRechnernetz(senderIP, nic.getIp(), nic.getSubnetzMaske()) && isBroadcast(zielIp, nic.getIp(), nic.getSubnetzMaske())) { return true; } } return false; } } filius-2.9.4+ds/src/main/java/filius/software/vermittlungsschicht/Weiterleitungstabelle.java000066400000000000000000000317251506574241500325440ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.vermittlungsschicht; import java.util.LinkedList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.rahmenprogramm.I18n; import filius.software.rip.RIPRoute; import filius.software.rip.RIPTable; import filius.software.system.InternetKnotenBetriebssystem; /** * Mit dieser Klasse wird die Weiterleitungstabelle implementiert. Es werden manuell erstellte Eintraege und aus der * IP-Konfiguration der Netzwerkkarten des Knotens automatisch erzeugte Eintraege unterschieden. Gespeichert werden nur * manuelle Eintraege. Ausserdem werden bei jeder Abfrage die automatischen Standard-Eintraege erzeugt. */ public class Weiterleitungstabelle implements I18n { private static Logger LOG = LoggerFactory.getLogger(Weiterleitungstabelle.class); /** * Die Tabelle mit den manuellen Eintraegen der Weiterleitungstabelle. Sie werden als String-Arrays in einer Liste * verwaltet. Ein Eintrag besteht aus folgenden Elementen: *
      *
    1. Netz-ID der Zieladresse als IP-Adresse
    2. *
    3. Netzmaske zur Berechnung der Netz-ID aus dem ersten Wert
    4. *
    5. Das Standard-Gateway, ueber die die Ziel-IP-Adresse erreicht wird, wenn sie sich nicht im gleichen * Rechnernetz wie der eigene Rechner befindet
    6. *
    7. Die IP-Adresse der Netzwerkkarte, ueber die die Ziel-IP-Adresse erreicht wird
    8. *
    */ private LinkedList manuelleTabelle; /** * Eine Liste, in der angegeben wird, welche Eintraege in der erzeugten Tabelle automatisch erzeugt bzw. manuelle * Eintraege sind */ private LinkedList manuelleEintraege; /** Die Systemsoftware */ private InternetKnotenBetriebssystem firmware = null; /** * Im Standard-Konstruktor wird die Methode reset() aufgerufen. Damit werden alle manuellen Eintraege geloescht */ public Weiterleitungstabelle() { reset(); } /** Methode fuer den Zugriff auf die Systemsoftware */ public void setInternetKnotenBetriebssystem(InternetKnotenBetriebssystem firmware) { this.firmware = firmware; } /** Methode fuer den Zugriff auf die Systemsoftware */ public InternetKnotenBetriebssystem getInternetKnotenBetriebssystem() { return firmware; } /** * Methode fuer den Zugriff auf die manuellen Eintrage. Diese Methode sollte nur fuer das speichern genutzt werden! */ public void setManuelleTabelle(LinkedList tabelle) { this.manuelleTabelle = tabelle; } /** * Methode fuer den Zugriff auf die manuellen Eintrage. Diese Methode sollte nur fuer das speichern genutzt werden! */ public LinkedList getManuelleTabelle() { return manuelleTabelle; } /** * Methode zum hinzufuegen eines neuen Eintrags. Ein Eintrag besteht aus folgenden Elementen: *
      *
    1. Netz-ID der Zieladresse als IP-Adresse
    2. *
    3. Netzmaske zur Berechnung der Netz-ID aus dem ersten Wert
    4. *
    5. Das Standard-Gateway, ueber die die Ziel-IP-Adresse erreicht wird, wenn sie sich nicht im gleichen * Rechnernetz wie der eigene Rechner befindet
    6. *
    7. Die IP-Adresse der Netzwerkkarte, ueber die die Ziel-IP-Adresse erreicht wird
    8. *
    * * @param netzwerkziel * @param netzwerkmaske * @param gateway * @param schnittstelle */ public void addManuellenEintrag(String netzwerkziel, String netzwerkmaske, String gateway, String schnittstelle) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Weiterleitungstabelle), addManuellenEintrag(" + netzwerkziel + "," + netzwerkmaske + "," + gateway + "," + schnittstelle + ")"); manuelleEintraege = null; manuelleTabelle.addLast(new String[] { IP.ipCheck(netzwerkziel), IP.ipCheck(netzwerkmaske), IP.ipCheck(gateway), IP.ipCheck(schnittstelle) }); } public boolean validate(String netzwerkziel, String netzwerkmaske, String gateway, String schnittstelle) { return null != IP.ipCheck(netzwerkziel) && null != IP.ipCheck(netzwerkmaske) && null != IP.ipCheck(gateway) && null != IP.ipCheck(schnittstelle); } /** * Hilfsmethode zum Debugging zur Ausgabe der Tabelleneintraege auf der Standardausgabe * * @param name * der Name, der in der Tabellenueberschrift ausgegeben werden soll * @param tabelle * die auszugebende Tabelle */ public void printTabelle(String name) { LOG.info("Weiterleitungstabelle {} (IP,mask,gw,if):", name); for (String[] eintrag : holeTabelle()) { LOG.debug(String.format(" '%15s' | '%15s' | '%15s' | '%15s'", eintrag[0], eintrag[1], eintrag[2], eintrag[3])); } } /** * Zugriff auf die Liste, in der steht, welche Eintraege automatisch erzeugt bzw. manuell erstellt wurden */ public LinkedList holeManuelleEintraegeFlags() { if (null == manuelleEintraege) { holeTabelle(); } return manuelleEintraege; } /** Zuruecksetzen der Tabelle mit den manuellen Eintraegen */ public void reset() { manuelleTabelle = new LinkedList(); manuelleEintraege = null; } /** * Methode fuer den Zugriff auf die Weiterleitungstabelle bestehend aus automatisch erzeugten und manuellen * Eintraegen */ public LinkedList holeTabelle() { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (Weiterleitungstabelle), holeTabelle()"); String[] tmp = new String[4]; LinkedList tabelle = new LinkedList(manuelleTabelle); manuelleEintraege = new LinkedList(); for (int i = 0; i < tabelle.size(); i++) { manuelleEintraege.add(Boolean.TRUE); } if (firmware != null) { // Eintrag fuer 'localhost' tmp = new String[4]; tmp[0] = "127.0.0.0"; tmp[1] = "255.0.0.0"; tmp[2] = "127.0.0.1"; tmp[3] = "127.0.0.1"; tabelle.addFirst(tmp); manuelleEintraege.addFirst(Boolean.FALSE); InternetKnoten knoten = (InternetKnoten) firmware.getKnoten(); // Eintrag fuer eigenes Rechnernetz for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { tmp = new String[4]; // tmp[0] = nic.getIp(); tmp[0] = berechneNetzkennung(nic.getIp(), nic.getSubnetzMaske()); tmp[1] = nic.getSubnetzMaske(); tmp[2] = nic.getIp(); tmp[3] = nic.getIp(); tabelle.addFirst(tmp); manuelleEintraege.addFirst(Boolean.FALSE); } // Eintrag fuer eigene IP-Adresse for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { tmp = new String[4]; tmp[0] = nic.getIp(); tmp[1] = "255.255.255.255"; tmp[2] = "127.0.0.1"; tmp[3] = "127.0.0.1"; tabelle.addFirst(tmp); manuelleEintraege.addFirst(Boolean.FALSE); } // Eintrag fuer Standardgateway, wenn es konfiguriert wurde String gateway = firmware.getStandardGateway(); if (gateway != null && !gateway.trim().equals("")) { gateway = gateway.trim(); tmp = null; for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) { if (nic != null && VermittlungsProtokoll.gleichesRechnernetz(gateway, nic.getIp(), nic.getSubnetzMaske())) { tmp = new String[4]; tmp[0] = "0.0.0.0"; tmp[1] = "0.0.0.0"; tmp[2] = gateway; tmp[3] = nic.getIp(); } } if (tmp == null) { tmp = new String[4]; tmp[0] = "0.0.0.0"; tmp[1] = "0.0.0.0"; tmp[2] = gateway; tmp[3] = firmware.primaryIPAdresse(); } tabelle.addLast(tmp); manuelleEintraege.addLast(Boolean.FALSE); } } return tabelle; } /** * Methode, um aus einer IP-Adresse und einer Subnetzmaske eine Netzwerkkennung als String zu erzeugen. Bsp.: * 192.168.2.6 und 255.255.255.0 wird zu 192.168.2.0 */ private String berechneNetzkennung(String ipStr, String maskStr) { long ipAddr = IP.inetAton(ipStr); long maskAddr = IP.inetAton(maskStr); long netAddr = ipAddr & maskAddr; return IP.inetNtoa(netAddr); } /** * Tabelle zur Abfrage der Weiterleitungstabelle nach einem passenden Eintrag fuer eine Ziel-IP-Adresse * * @param zielIP * die Ziel-IP-Adresse * @return das Ergebnis als String-Array bestehend aus der IP-Adresse des naechsten Gateways und der fuer den * Versand zu verwendenden Schnittstelle * @throws RouteNotFoundException */ @Deprecated public String[] holeWeiterleitungsZiele(String zielIpAdresse) throws RouteNotFoundException { Route bestRoute = null; if (firmware.isRipEnabled()) { bestRoute = determineRouteFromDynamicRoutingTable(zielIpAdresse); } else { bestRoute = determineRouteFromStaticRoutingTable(zielIpAdresse); } return new String[] { bestRoute.getGateway(), bestRoute.getInterfaceIpAddress() }; } public Route holeWeiterleitungsEintrag(String zielIpAdresse) throws RouteNotFoundException { Route bestRoute = null; if (firmware.isRipEnabled()) { bestRoute = determineRouteFromDynamicRoutingTable(zielIpAdresse); } else { bestRoute = determineRouteFromStaticRoutingTable(zielIpAdresse); } return bestRoute; } public Route determineRouteFromStaticRoutingTable(String targetIPAddress) throws RouteNotFoundException { long netAddr, maskAddr, zielAddr = IP.inetAton(targetIPAddress); long bestMask = -1; Route bestRoute = null; for (String[] route : holeTabelle()) { maskAddr = IP.inetAton(route[1]); if (maskAddr <= bestMask) { continue; } netAddr = IP.inetAton(route[0]); if (netAddr == (maskAddr & zielAddr)) { bestMask = maskAddr; bestRoute = new Route(route); } } if (bestRoute != null) { return bestRoute; } else { throw new RouteNotFoundException(); } } public Route determineRouteFromDynamicRoutingTable(String ip) throws RouteNotFoundException { RIPTable table = firmware.getRIPTable(); Route bestRoute = null; synchronized (table) { int bestHops = RIPTable.INFINITY - 1; long bestMask = -1; for (RIPRoute route : table.routes) { if (route.getNetAddress().equals(berechneNetzkennung(ip, route.getNetMask()))) { if (bestHops < route.hops) { continue; } if (bestHops > route.hops || bestMask < IP.inetAton(route.getNetMask())) { bestRoute = route; bestHops = route.hops; bestMask = IP.inetAton(route.getNetMask()); } } } } if (bestRoute != null) { return bestRoute; } else { throw new RouteNotFoundException(); } } } filius-2.9.4+ds/src/main/java/filius/software/www/000077500000000000000000000000001506574241500220415ustar00rootroot00000000000000filius-2.9.4+ds/src/main/java/filius/software/www/HTTPNachricht.java000066400000000000000000000321341506574241500253120ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.www; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.util.UriUtils; import filius.rahmenprogramm.I18n; public class HTTPNachricht implements I18n { private static Logger LOG = LoggerFactory.getLogger(HTTPNachricht.class); public static final int SERVER = 0, CLIENT = 1; public static final String GET = "GET", POST = "POST"; public static final String TEXT_HTML = "text/html"; public static final String IMAGE_PNG = "image/png"; public static final String IMAGE_BMP = "image/bmp"; public static final String IMAGE_GIF = "image/gif"; public static final String IMAGE_JPG = "image/jpg"; public static final String TEXT_PLAIN = "text/plain"; private String contentType = null; private String host = null; private String protocolVersion = "HTTP/1.1"; private String method = null; private int statusCode = 0; private int mode = CLIENT; private StringBuffer daten = null; private String pfad = null; /** * Zur Erzeugung einer neuen HTTP-Nachricht. Unterschieden werden Server- und Client-Nachrichten. Danach richtet * sich, welche Attribute des Kopfteils (Header) verwendet werden. * * @param mode * ob es sich um eine SERVER- oder CLIENT-Nachricht handelt. */ public HTTPNachricht(int mode) { this.mode = mode; } /** * Methode zur Rekonstruktion einer HTTP-Nachricht aus einem String, der durch Aufruf der Methode toString() erzeugt * wurde. * * @param nachricht */ public HTTPNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (HTTPNachricht), constr: HTTPNachricht(" + nachricht + ")"); StringTokenizer tokenizer; String token; String[] zeilen; zeilen = nachricht.split("\n"); if (zeilen.length > 0) { tokenizer = new StringTokenizer(zeilen[0], " "); if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken().trim(); if (token.equalsIgnoreCase(GET) || token.equalsIgnoreCase(POST)) { method = token; // LOG.debug("Method: " + method); token = tokenizer.nextToken().trim(); pfad = decodePath(token); // LOG.debug("Pfad: " + pfad); token = tokenizer.nextToken().trim(); protocolVersion = token; // LOG.debug("Protokoll-Version: " + // protocolVersion); for (int i = 1; i < zeilen.length; i++) { if (zeilen[i].equals("") && method.equalsIgnoreCase(POST)) { daten = new StringBuffer(); for (int j = i; j < zeilen.length; j++) { daten.append(zeilen[j]); if (j < zeilen.length - 1) daten.append("\n"); } i = zeilen.length; } else { tokenizer = new StringTokenizer(zeilen[i], " "); token = tokenizer.nextToken().trim(); if (token.equalsIgnoreCase("host:")) { host = tokenizer.nextToken().trim(); } } } } else { protocolVersion = token; // LOG.debug("Protokoll-Version: " + // protocolVersion); token = tokenizer.nextToken().trim(); statusCode = Integer.parseInt(token); // LOG.debug("Status-Code: " + statusCode); for (int i = 1; i < zeilen.length; i++) { if (zeilen[i].equals("")) { daten = new StringBuffer(); for (int j = i + 1; j < zeilen.length; j++) { daten.append(zeilen[j]); if (j < zeilen.length - 1) daten.append("\n"); } i = zeilen.length; // LOG.debug("Daten: " + daten.toString()); } else { tokenizer = new StringTokenizer(zeilen[i], " "); token = tokenizer.nextToken().trim(); if (token.equalsIgnoreCase("content-type:")) { contentType = tokenizer.nextToken().trim(); } } } } } } } public String toString() { StringBuffer buffer = new StringBuffer(); if (mode == CLIENT && method != null && pfad != null && protocolVersion != null) { buffer.append(method + " " + encodePath(getPfad()) + " " + protocolVersion + "\n"); if (host != null) { buffer.append("Host: " + host + "\n"); } if (method.equals(POST) && daten != null) { buffer.append("\n" + daten); } } else if (mode == SERVER && protocolVersion != null) { buffer.append(protocolVersion + " " + statusCode + " " + holeStatusNachricht(statusCode) + "\n"); if (contentType != null) { buffer.append("Content-type: " + contentType + "\n"); } if (daten != null) { buffer.append("\n" + daten); } } return buffer.toString(); } public static String holeStatusNachricht(int code) { LOG.trace("INVOKED (static) filius.software.www.HTTPNachricht, holeStatusNachricht(" + code + ")"); if (code == 100) return "Continue"; if (code == 101) return "Switching Protocols"; if (code == 102) return "Processing"; if (code == 200) return "OK"; if (code == 201) return "Created"; if (code == 202) return "Accepted"; if (code == 203) return "Non-Authoritative Information"; if (code == 204) return "No Content"; if (code == 205) return "Reset Content"; if (code == 206) return "Partial Content"; if (code == 207) return "Multi-Status"; if (code == 300) return "Multiple Choice"; if (code == 301) return "Moved Permanently"; if (code == 302) return "Found"; if (code == 303) return "See Other"; if (code == 304) return "Not Modified"; if (code == 305) return "Use Proxy"; if (code == 307) return "Temporary Redirect"; if (code == 400) return "Bad Request"; if (code == 401) return "Unauthorized"; if (code == 402) return "Payment Required"; if (code == 403) return "Forbidden"; if (code == 404) return "Not Found"; if (code == 405) return "Method Not Allowed"; if (code == 406) return "Not Acceptable"; if (code == 407) return "Proxy Authentication Required"; if (code == 408) return "Request Time-out"; if (code == 409) return "Conflict"; if (code == 410) return "Gone"; if (code == 411) return "Length Required"; if (code == 412) return "Precondition Failed"; if (code == 413) return "Request Entity Too Large"; if (code == 414) return "Request-URI Too Long"; if (code == 415) return "Unsupported Media Type"; if (code == 416) return "Requested range not satisfiable"; if (code == 417) return "Expectation Failed"; if (code == 422) return "Unprocessable Entity"; if (code == 423) return "Locked"; if (code == 424) return "Failed Dependency"; if (code == 500) return "Internal Server Error"; if (code == 501) return "Not Implemented"; if (code == 502) return "Bad Gateway"; if (code == 503) return "Service Unavailable"; if (code == 504) return "Gateway Time-out"; if (code == 505) return "HTTP Version not supported"; if (code == 507) return "Insufficient Storage"; if (code == 509) return "Bandwidth Limit Exceeded"; if (code >= 100 && code < 200) return messages.getString("sw_httpnachricht_msg1"); if (code >= 200 && code < 300) return messages.getString("sw_httpnachricht_msg2"); if (code >= 300 && code < 400) return messages.getString("sw_httpnachricht_msg3"); if (code >= 400 && code < 500) return messages.getString("sw_httpnachricht_msg4"); if (code >= 500 && code < 600) return messages.getString("sw_httpnachricht_msg5"); return null; } /** * @return the contentType */ public String getContentType() { return contentType; } /** * @param contentType * the contentType to set */ public void setContentType(String contentType) { this.contentType = contentType; } /** * @return the daten */ public String getDaten() { if (daten == null) return null; else return daten.toString(); } /** * @param daten * the daten to set */ public void setDaten(String data) { if (data == null) daten = null; else daten = new StringBuffer(data); } /** * @return the host */ public String getHost() { return host; } /** * @param host * the host to set */ public void setHost(String host) { this.host = host; } /** * @return the method */ public String getMethod() { return method; } /** * @param method * the method to set */ public void setMethod(String method) { this.method = method; } /** * @return the pfad */ public String getPfad() { if (pfad.trim().equals("")) { return "/"; } else { int queryStartIndex = pfad.indexOf('?'); return pfad.substring(0, queryStartIndex > 0 ? queryStartIndex : pfad.length()); } } /** * @param pfad * the pfad to set */ public void setPfad(String pfad) { this.pfad = pfad; } /** * @return the protocolVersion */ public String getProtocolVersion() { return protocolVersion; } /** * @param protocolVersion * the protocolVersion to set */ public void setProtocolVersion(String protocolVersion) { this.protocolVersion = protocolVersion; } /** * @return the statusCode */ public int getStatusCode() { return statusCode; } /** * @param statusCode * the statusCode to set */ public void setStatusCode(int statusCode) { this.statusCode = statusCode; } protected static String encodePath(String path) { String urlEncodedPath = UriUtils.encodePath(path, "utf8"); return urlEncodedPath; } protected static String decodePath(String urlEncodedPath) { String path = UriUtils.decode(urlEncodedPath, "utf8"); return path; } } filius-2.9.4+ds/src/main/java/filius/software/www/WebBrowser.java000066400000000000000000000275051506574241500247760ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.www; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.util.LinkedList; import org.htmlparser.Node; import org.htmlparser.Parser; import org.htmlparser.tags.ImageTag; import org.htmlparser.visitors.TagFindingVisitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.exception.VerbindungsException; import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.I18n; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.ResourceUtil; import filius.software.clientserver.ClientAnwendung; import filius.software.transportschicht.TCPSocket; /** * Diese Klasse implementiert die Funktionalitaet eines HTTP-Clients fuer einen Webbrowser. Dazu werden folgende * Funktionen zur Verfuegung gestellt: *
      *
    1. Zum Abruf einer Webseite von einem Webserver gibt es die Methoden holeRessource(url: URL) fuer eine * GET-Abfrage und
    2. *
    3. holeRessource(url: URL, post: String) fuer eine POST-Abfrage mit Daten im Datenteil einer * HTTP-Nachricht.
    4. *
    * Diese Methoden haben keinen Rueckgabewert und blockieren auch nicht. Die vom Server gelieferten Daten werden an den * Beobachter als HTTPNachricht weitergegeben. * */ public class WebBrowser extends ClientAnwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(WebBrowser.class); private static final int ABRUF_HTML = 1, ABRUF_IMG = 2; private LinkedList bilddateien = new LinkedList(); private String host; public void holeWebseite(URL url) { holeWebseite(url, ""); } public void holeWebseite(URL url, String post) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebBrowser), holeWebseite(" + url + "," + post + ")"); if (socket != null) { ausfuehren("closeConnection", new Object[0]); } ausfuehren("initConnection", new Object[] { url }); ausfuehren("retrieveWebpage", new Object[] { url, post }); ausfuehren("retrieveImages", new Object[0]); ausfuehren("closeConnection", new Object[0]); } void initConnection(URL url) { if (url.getHost() != null && !url.getHost().equals("")) { host = url.getHost(); } if (socket == null) { try { socket = new TCPSocket(getSystemSoftware(), host, 80); } catch (VerbindungsException e) { HTTPNachricht fehler = new HTTPNachricht(HTTPNachricht.CLIENT); fehler.setDaten(erzeugeHtmlFehlermeldung(0)); benachrichtigeBeobachter(fehler); } } if (socket != null && !socket.istVerbunden()) { try { socket.verbinden(); } catch (Exception e) { HTTPNachricht fehler = new HTTPNachricht(HTTPNachricht.CLIENT); fehler.setDaten(erzeugeHtmlFehlermeldung(0)); benachrichtigeBeobachter(fehler); } } } void retrieveWebpage(URL url, String post) { HTTPNachricht nachricht = createRequest(url, post); if (socket != null && socket.istVerbunden()) { try { socket.senden(nachricht.toString()); String responseData = socket != null ? socket.empfangen() : null; if (responseData == null) { HTTPNachricht response = new HTTPNachricht(HTTPNachricht.CLIENT); response.setDaten(erzeugeHtmlFehlermeldung(0)); benachrichtigeBeobachter(response); } else { HTTPNachricht response = new HTTPNachricht(responseData); if (response.getStatusCode() != 200) { response.setDaten(erzeugeHtmlFehlermeldung(response.getStatusCode())); } else { String contentType = response.getContentType(); if (HTTPNachricht.TEXT_HTML.equalsIgnoreCase(contentType) && response.getDaten() != null) { extractImagesReferences(response.getDaten(), response.getHost()); } } benachrichtigeBeobachter(response); } } catch (Exception e) { LOG.debug("Error while retrieving web page.", e); } } } private HTTPNachricht createRequest(URL url, String post) { HTTPNachricht nachricht = new HTTPNachricht(HTTPNachricht.CLIENT); nachricht.setPfad(url.getFile()); nachricht.setHost(host); if (nachricht.getHost() != null && !nachricht.getHost().equals("")) { if (post != null && !post.equals("")) { nachricht.setMethod(HTTPNachricht.POST); nachricht.setDaten(post); } else { nachricht.setMethod(HTTPNachricht.GET); } } return nachricht; } public String holeHost() { return host; } public void starten() { super.starten(); bilddateien = new LinkedList(); } /** * liest eine reale Textdatei vom Format .txt ein. Diese befinden sich im Ordner /config */ private String einlesenTextdatei(String datei) throws FileNotFoundException, IOException { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebBrowser), einlesenTextdatei(" + datei + ")"); StringBuffer fullFile = new StringBuffer(); try (BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(ResourceUtil.getResourcePath(datei)), Charset.forName("UTF-8")))) { String input; while ((input = reader.readLine()) != null) { fullFile.append(input).append("\n"); } } return fullFile.toString(); } private String erzeugeHtmlFehlermeldung(int statusCode) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebBrowser), erzeugeHtmlFehlermeldung(" + statusCode + ")"); String quelltext; String dateipfad; String meldung; if (statusCode == 0) { quelltext = messages.getString("sw_webbrowser_msg1"); } else { dateipfad = ResourceUtil.getResourcePath( "tmpl/http_fehler_" + Information.getInformation().getLocaleOrDefault().toString() + ".txt"); try { quelltext = einlesenTextdatei(dateipfad); } catch (Exception e) { quelltext = messages.getString("sw_webbrowser_msg2"); LOG.debug("", e); } quelltext = quelltext.replace(":code:", "" + statusCode); meldung = HTTPNachricht.holeStatusNachricht(statusCode); quelltext = quelltext.replace(":meldung:", meldung); } return quelltext; } /** * Methode zur Verarbeitung von IMG-Tags. Mit einem Parser werden IMG-Tags im uebergebenen Quelltext gesucht. Alle * Bilddateien werden in eine Liste geschrieben. * * @param quelltext * @param host */ private void extractImagesReferences(String quelltext, String host) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebBrowser), verarbeiteIMGTags(" + quelltext + "," + host + ")"); Parser parser = Parser.createParser(quelltext, null); TagFindingVisitor nodeVisitor = new TagFindingVisitor(new String[] { "img" }); try { parser.visitAllNodesWith(nodeVisitor); Node[] nodes = nodeVisitor.getTags(0); for (int i = 0; i < nodes.length; i++) { if (nodes[i] instanceof ImageTag) { ImageTag img = (ImageTag) nodes[i]; synchronized (bilddateien) { bilddateien.add(img.getImageURL()); } } } } catch (Exception e) { LOG.debug("", e); } } /** * Liste der gefundenen IMG SRCs wird iteriert, die einzelnen Bilder werden abgerufen */ void retrieveImages() { synchronized (bilddateien) { for (String dateipfad : bilddateien) { try { HTTPNachricht request = createRequest(new URL("http", host, dateipfad), ""); if (socket != null && socket.istVerbunden()) { socket.senden(request.toString()); String responseData = socket.empfangen(); if (responseData != null) { HTTPNachricht response = new HTTPNachricht(responseData); if (response.getStatusCode() != 200) { benachrichtigeBeobachter(); } else { String contentType = response.getContentType(); if (HTTPNachricht.IMAGE_BMP.equalsIgnoreCase(contentType) || HTTPNachricht.IMAGE_GIF.equalsIgnoreCase(contentType) || HTTPNachricht.IMAGE_JPG.equalsIgnoreCase(contentType) || HTTPNachricht.IMAGE_PNG.equalsIgnoreCase(contentType)) { synchronized (bilddateien) { if (bilddateien.size() > 0) { dateipfad = bilddateien.removeFirst(); Base64.decodeToFile(response.getDaten(), Information.getInformation().getTempPfad() + dateipfad); } } benachrichtigeBeobachter(); } } } } } catch (MalformedURLException e) { LOG.debug("Could not retrieve image. Invalid URL.", e); } catch (Exception e) { LOG.debug("Unexpected error while retrieving images via http.", e); } } } } void closeConnection() { if (socket != null) { socket.schliessen(); socket = null; } } } filius-2.9.4+ds/src/main/java/filius/software/www/WebServer.java000066400000000000000000000300321506574241500246060ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.www; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.HashMap; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.Information; import filius.rahmenprogramm.ResourceUtil; import filius.software.clientserver.TCPServerAnwendung; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.transportschicht.Socket; import filius.software.transportschicht.TCPSocket; /** * Anwendungklasse des Webservers * */ public class WebServer extends TCPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(WebServer.class); private DefaultMutableTreeNode verzeichnis; private HashMap plugins = new HashMap(); private String[][] vHostArray = new String[5][2]; private boolean useVHost = false; public WebServer() { super(); port = 80; this.resetVHosts(); } public void resetVHosts() { for (int i = 0; i < vHostArray.length; i++) { vHostArray[i][0] = ""; vHostArray[i][1] = ""; } this.benachrichtigeBeobachter(); } /** * nachdem ein WebServer erzeugt wurde, muss ueber die Vaterklasse Anwendung ein Betriebssystem zugewiesen werden */ public void setSystemSoftware(InternetKnotenBetriebssystem betriebssystem) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), setSystemSoftware(" + betriebssystem + ")"); super.setSystemSoftware(betriebssystem); erzeugeStandardVerzeichnis(); } public void setzePlugIn(WebServerPlugIn pi) { plugins.put(pi.getPfad(), pi); } public void erzeugeIndexDatei(String dateipfad) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), erzeugeIndexDatei(" + dateipfad + ")"); String input; try (BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream(dateipfad), Charset.forName("UTF-8")))) { StringBuffer fullFile = new StringBuffer(); while ((input = reader.readLine()) != null) { fullFile.append(input + "\n"); } erzeugeDatei("index", "html", fullFile.toString()); } catch (Exception e) { LOG.debug("", e); } } public byte[] inputStreamToBytes(java.io.InputStream in) { java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(1024); byte[] buffer = new byte[1024]; int len; try { while ((len = in.read(buffer)) >= 0) out.write(buffer, 0, len); in.close(); out.close(); } catch (java.io.IOException e) { LOG.debug("EXCEPTION (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer): IOException in conversion of InputStream to Byte[]"); LOG.debug("", e); } return out.toByteArray(); } /** * erzeugt alle Dateien, fuer Fehlermeldungen etc. Die Dateien liegen als Textdateien im realen Ordner /konfig */ private void erzeugeStandardVerzeichnis() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), erzeugeStandardVerzeichnis()"); Dateisystem dateisystem; dateisystem = getSystemSoftware().getDateisystem(); dateisystem.erstelleVerzeichnis(dateisystem.getRoot(), "webserver"); verzeichnis = dateisystem .verzeichnisKnoten(dateisystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "webserver"); try { if (!dateisystem.dateiVorhanden(verzeichnis, "index.html")) { erzeugeIndexDatei(ResourceUtil.getResourcePath( "tmpl/webserver_index_" + Information.getInformation().getLocaleOrDefault() + ".txt")); erzeugeDatei("splashscreen-mini", "png", Base64.encodeBytes( inputStreamToBytes(getClass().getResourceAsStream("/gfx/allgemein/splashscreen-mini.png")))); } } catch (Exception e) { LOG.debug("", e); } } /** * erzeugt eine Datei fuer das Dateisystem von Filius. Dieses arbeitet mit TreeNodes */ private void erzeugeDatei(String dateiname, String endung, String quellcode) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), erzeugeDatei(" + dateiname + "," + endung + "," + quellcode + ")"); String kompletteDateiName = dateiname + "." + endung; Datei datei = new Datei(kompletteDateiName, endung, quellcode); getSystemSoftware().getDateisystem().speicherDatei(verzeichnis, datei); } /** * holt eine Datei aus dem Verzeichnisbaum des WebServers. Dieser benutzt TreeNodes */ protected Datei dateiLiefern(String relativerPfad) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), dateiLiefern(" + relativerPfad + ")"); Datei tmpDatei; tmpDatei = getSystemSoftware().getDateisystem().holeDatei(verzeichnis, relativerPfad); return tmpDatei; } /** * sucht nach einem bestimmten PlugIn. Zum Beispiel fuer eine Firewall Bisher installierte PlugIns: /konfig.html & * /log.html * */ public WebServerPlugIn holePlugin(String key) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), holePlugin(" + key + ")"); WebServerPlugIn plugIn = null; if (plugins.containsKey(key)) { plugIn = (WebServerPlugIn) plugins.get(key); } return plugIn; } public HashMap getPlugins() { return plugins; } public void setPlugins(HashMap plugins) { this.plugins = plugins; } public String printVHostTable() { String result = ""; for (int i = 0; i < vHostArray.length; i++) { result += i + ":\t"; if (vHostArray[i][0] == null) result += "\t"; else result += vHostArray[i][0] + "\t"; if (vHostArray[i][1] == null) result += "\n"; else result += vHostArray[i][1] + "\n"; } return result; } public void changeVHostTable(int row, int col, String val) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), changeVHostTable(" + row + "," + col + "," + val + ")"); if (val != null) { vHostArray[row][col] = val; } else { vHostArray[row][col] = ""; } saveVHosts(); } public String vhostPrefix(String vhost) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), vhostPrefix(" + vhost + ")"); for (int i = 0; useVHost && i < vHostArray.length; i++) { if (vHostArray[i][0] != null && vHostArray[i][0].equalsIgnoreCase(vhost)) { if (vHostArray[i][1] != null) return vHostArray[i][1]; } } return ""; } public String[][] getVHostArray() { return vHostArray; } protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), neuerMitarbeiter(" + socket + ")"); WebServerMitarbeiter wsMitarbeiter; if (socket instanceof TCPSocket) { wsMitarbeiter = new WebServerMitarbeiter(this, (TCPSocket) socket); wsMitarbeiter.starten(); mitarbeiter.add(wsMitarbeiter); } } public void starten() { LOG.debug("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), starten()"); super.starten(); if (!getSystemSoftware().getDateisystem().dateiVorhanden("www.conf", "vhosts")) { getSystemSoftware().getDateisystem().erstelleVerzeichnis("root", "www.conf"); } initialisiereVHosts(); } public void beenden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServer), beenden()"); super.beenden(); } private void saveVHosts() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (DNSServer), schreibeRecordListe()"); Datei vhosts; StringBuffer text; Dateisystem dateisystem; String host, directory; dateisystem = getSystemSoftware().getDateisystem(); text = new StringBuffer(); for (int i = 0; i < vHostArray.length; i++) { host = vHostArray[i][0]; directory = vHostArray[i][1]; text.append((host != null ? host : "") + "\n" + (directory != null ? directory : "") + "\n"); } vhosts = new Datei(); vhosts.setDateiInhalt(text.toString()); vhosts.setName("vhosts"); dateisystem.erstelleVerzeichnis(dateisystem.holeRootPfad(), "www.conf"); dateisystem.speicherDatei(dateisystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "www.conf", vhosts); } private void initialisiereVHosts() { vHostArray = new String[5][2]; int row = 0; int col = 0; Dateisystem dateisystem = getSystemSoftware().getDateisystem(); Datei vhosts = dateisystem.holeDatei(dateisystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "www.conf" + Dateisystem.FILE_SEPARATOR + "vhosts"); if (vhosts != null) { useVHost = true; StringTokenizer tokenizer = new StringTokenizer(vhosts.getDateiInhalt(), "\n"); while (tokenizer.hasMoreTokens()) { String line = tokenizer.nextToken().trim(); vHostArray[row][col] = line; if (col == 0) { col++; } else { col = 0; row++; } } } for (; row < 5; row++) { vHostArray[row][0] = ""; vHostArray[row][1] = ""; } } public boolean isUseVHost() { return useVHost; } public void setUseVHost(boolean useVHost) { this.useVHost = useVHost; } } filius-2.9.4+ds/src/main/java/filius/software/www/WebServerMitarbeiter.java000066400000000000000000000130061506574241500270000ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.www; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.clientserver.ServerMitarbeiter; import filius.software.system.Datei; import filius.software.transportschicht.TCPSocket; /* * */ public class WebServerMitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(WebServerMitarbeiter.class); /** * Konstruktor: setzt den webserver, socket und webkonfig. startet anschließend den Thread */ public WebServerMitarbeiter(WebServer server, TCPSocket socket) { super(server, socket); } private HTTPNachricht verarbeiteAnfrage(HTTPNachricht anfrage) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServerMitarbeiter), verarbeiteAnfrage(" + anfrage + ")"); HTTPNachricht antwort; WebServerPlugIn plugin; String dateipfad; Datei datei; String tmp = null; antwort = new HTTPNachricht(HTTPNachricht.SERVER); if (anfrage.getPfad().equals("/")) { dateipfad = "index.html"; } else if (anfrage.getPfad().startsWith("/")) { dateipfad = anfrage.getPfad().substring(1); } else { dateipfad = anfrage.getPfad(); } // add subdirectory in front of 'dateipfad' according to vhost settings String vHostRelPath = ((WebServer) server).vhostPrefix(anfrage.getHost()); if (!vHostRelPath.endsWith("/")) vHostRelPath += "/"; dateipfad = vHostRelPath + dateipfad; if (dateipfad.startsWith("/")) { dateipfad = dateipfad.substring(1); } plugin = ((WebServer) server).holePlugin(dateipfad); if (plugin != null) { if (anfrage.getMethod().equals(HTTPNachricht.POST)) { tmp = plugin.holeHtmlSeite(anfrage.getDaten()); } else { tmp = plugin.holeHtmlSeite(null); } if (tmp != null) { antwort.setDaten(tmp); antwort.setContentType(HTTPNachricht.TEXT_HTML); antwort.setStatusCode(200); } else { antwort.setStatusCode(500); } } else { datei = ((WebServer) server).dateiLiefern(dateipfad); if (datei != null) { antwort.setDaten(datei.getDateiInhalt()); String filenameLowerCase = datei.getName().toLowerCase(); if (filenameLowerCase.endsWith("html") || filenameLowerCase.endsWith("htm")) { antwort.setContentType(HTTPNachricht.TEXT_HTML); antwort.setStatusCode(200); } else if (filenameLowerCase.endsWith("png")) { antwort.setContentType(HTTPNachricht.IMAGE_PNG); antwort.setStatusCode(200); } else if (filenameLowerCase.endsWith("bmp")) { antwort.setContentType(HTTPNachricht.IMAGE_BMP); antwort.setStatusCode(200); } else if (filenameLowerCase.endsWith("gif")) { antwort.setContentType(HTTPNachricht.IMAGE_GIF); antwort.setStatusCode(200); } else if (filenameLowerCase.endsWith("jpg")) { antwort.setContentType(HTTPNachricht.IMAGE_JPG); antwort.setStatusCode(200); } else { antwort.setContentType(HTTPNachricht.TEXT_PLAIN); antwort.setStatusCode(200); } } else { antwort.setStatusCode(404); } } return antwort; } protected void verarbeiteNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (WebServerMitarbeiter), verarbeiteNachricht(" + nachricht + ")"); HTTPNachricht anfrage, antwort; anfrage = new HTTPNachricht(nachricht); if (anfrage.getMethod().equals(HTTPNachricht.GET) || anfrage.getMethod().equals(HTTPNachricht.POST)) { antwort = verarbeiteAnfrage(anfrage); } else { antwort = new HTTPNachricht(HTTPNachricht.SERVER); antwort.setStatusCode(501); } sendeNachricht(antwort.toString()); } } filius-2.9.4+ds/src/main/java/filius/software/www/WebServerPlugIn.java000066400000000000000000000053501506574241500257320ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.www; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.ResourceUtil; /** * * @author Michell wird vom den Klassen FirewallWebKonfig und FirewallWebLog benutzt */ public abstract class WebServerPlugIn { private static Logger LOG = LoggerFactory.getLogger(WebServerPlugIn.class); private String pfad; public String getPfad() { return pfad; } public void setPfad(String pfad) { this.pfad = pfad; } public abstract String holeHtmlSeite(String postDaten); /** * liest eine reale Textdatei vom Format .txt ein. Diese befinden sich im Ordner /config */ protected String textDateiEinlesen(String datei) throws FileNotFoundException, IOException { LOG.trace("INVOKED (" + this.hashCode() + ") " + getClass() + " (FirewallWebKonfig), textDateiEinlesen(" + datei + ")"); StringBuffer fullFile = new StringBuffer(); try (BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(ResourceUtil.getResourcePath(datei)), Charset.forName("UTF-8")))) { String input; while ((input = reader.readLine()) != null) { fullFile.append(input).append("\n"); } } return fullFile.toString(); } } filius-2.9.4+ds/src/main/resources/000077500000000000000000000000001506574241500171615ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/application.properties000066400000000000000000000001011506574241500235720ustar00rootroot00000000000000application.version=${version} application.buildDate=${buildDate}filius-2.9.4+ds/src/main/resources/config/000077500000000000000000000000001506574241500204265ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/config/Desktop_de_DE.txt000066400000000000000000000034471506574241500236300ustar00rootroot00000000000000Befehlszeile;filius.software.lokal.Terminal;filius.gui.anwendungssicht.GUIApplicationTerminalWindow;gfx/desktop/icon_terminal.png Bildbetrachter;filius.software.lokal.ImageViewer;filius.gui.anwendungssicht.GUIApplicationImageViewerWindow;gfx/desktop/icon_imageviewer.png Datei-Explorer;filius.software.lokal.FileExplorer;filius.gui.anwendungssicht.GUIApplicationFileExplorerWindow;gfx/desktop/icon_filebrowser.png DNS-Server;filius.software.dns.DNSServer;filius.gui.anwendungssicht.GUIApplicationDNSServerWindow;gfx/desktop/icon_dns.png E-Mail-Programm;filius.software.email.EmailAnwendung;filius.gui.anwendungssicht.GUIApplicationEmailAnwendungWindow;gfx/desktop/icon_emailprogramm.png E-Mail-Server;filius.software.email.EmailServer;filius.gui.anwendungssicht.GUIApplicationEmailServerWindow;gfx/desktop/icon_emailserver.png Echo-Server;filius.software.clientserver.ServerBaustein;filius.gui.anwendungssicht.GUIApplicationServerBausteinWindow;gfx/desktop/icon_serverbaustein.png Einfacher Client;filius.software.clientserver.ClientBaustein;filius.gui.anwendungssicht.GUIApplicationClientBausteinWindow;gfx/desktop/icon_clientbaustein.png Firewall;filius.software.firewall.Firewall;filius.gui.anwendungssicht.GUIApplicationFirewallWindow;gfx/desktop/icon_firewall.png Gnutella;filius.software.dateiaustausch.PeerToPeerAnwendung;filius.gui.anwendungssicht.GUIApplicationPeerToPeerAnwendungWindow;gfx/desktop/icon_peertopeer.png Text-Editor;filius.software.lokal.TextEditor;filius.gui.anwendungssicht.GUIApplicationTextEditorWindow;gfx/desktop/icon_texteditor.png Webserver;filius.software.www.WebServer;filius.gui.anwendungssicht.GUIApplicationWebServerWindow;gfx/desktop/icon_webserver.png Webbrowser;filius.software.www.WebBrowser;filius.gui.anwendungssicht.GUIApplicationWebBrowserWindow;gfx/desktop/icon_browser.pngfilius-2.9.4+ds/src/main/resources/config/Desktop_en_GB.txt000066400000000000000000000034371506574241500236410ustar00rootroot00000000000000Command Line;filius.software.lokal.Terminal;filius.gui.anwendungssicht.GUIApplicationTerminalWindow;gfx/desktop/icon_terminal.png DNS server;filius.software.dns.DNSServer;filius.gui.anwendungssicht.GUIApplicationDNSServerWindow;gfx/desktop/icon_dns.png Echo server;filius.software.clientserver.ServerBaustein;filius.gui.anwendungssicht.GUIApplicationServerBausteinWindow;gfx/desktop/icon_serverbaustein.png Email server;filius.software.email.EmailServer;filius.gui.anwendungssicht.GUIApplicationEmailServerWindow;gfx/desktop/icon_emailserver.png Email program;filius.software.email.EmailAnwendung;filius.gui.anwendungssicht.GUIApplicationEmailAnwendungWindow;gfx/desktop/icon_emailprogramm.png File explorer;filius.software.lokal.FileExplorer;filius.gui.anwendungssicht.GUIApplicationFileExplorerWindow;gfx/desktop/icon_filebrowser.png Firewall;filius.software.firewall.Firewall;filius.gui.anwendungssicht.GUIApplicationFirewallWindow;gfx/desktop/icon_firewall.png Generic client;filius.software.clientserver.ClientBaustein;filius.gui.anwendungssicht.GUIApplicationClientBausteinWindow;gfx/desktop/icon_clientbaustein.png Gnutella;filius.software.dateiaustausch.PeerToPeerAnwendung;filius.gui.anwendungssicht.GUIApplicationPeerToPeerAnwendungWindow;gfx/desktop/icon_peertopeer.png Image viewer;filius.software.lokal.ImageViewer;filius.gui.anwendungssicht.GUIApplicationImageViewerWindow;gfx/desktop/icon_imageviewer.png Text editor;filius.software.lokal.TextEditor;filius.gui.anwendungssicht.GUIApplicationTextEditorWindow;gfx/desktop/icon_texteditor.png Webbrowser;filius.software.www.WebBrowser;filius.gui.anwendungssicht.GUIApplicationWebBrowserWindow;gfx/desktop/icon_browser.png Webserver;filius.software.www.WebServer;filius.gui.anwendungssicht.GUIApplicationWebServerWindow;gfx/desktop/icon_webserver.pngfilius-2.9.4+ds/src/main/resources/config/Desktop_fr_FR.txt000066400000000000000000000035451506574241500236650ustar00rootroot00000000000000Ligne de commande;filius.software.lokal.Terminal;filius.gui.anwendungssicht.GUIApplicationTerminalWindow;gfx/desktop/icon_terminal.png Explorateur de fichiers;filius.software.lokal.FileExplorer;filius.gui.anwendungssicht.GUIApplicationFileExplorerWindow;gfx/desktop/icon_filebrowser.png Éditeur de textes;filius.software.lokal.TextEditor;filius.gui.anwendungssicht.GUIApplicationTextEditorWindow;gfx/desktop/icon_texteditor.png Afficheur d’images;filius.software.lokal.ImageViewer;filius.gui.anwendungssicht.GUIApplicationImageViewerWindow;gfx/desktop/icon_imageviewer.png Parefeu;filius.software.firewall.Firewall;filius.gui.anwendungssicht.GUIApplicationFirewallWindow;gfx/desktop/icon_firewall.png Serveur DNS;filius.software.dns.DNSServer;filius.gui.anwendungssicht.GUIApplicationDNSServerWindow;gfx/desktop/icon_dns.png Serveur générique;filius.software.clientserver.ServerBaustein;filius.gui.anwendungssicht.GUIApplicationServerBausteinWindow;gfx/desktop/icon_serverbaustein.png Client générique;filius.software.clientserver.ClientBaustein;filius.gui.anwendungssicht.GUIApplicationClientBausteinWindow;gfx/desktop/icon_clientbaustein.png Serveur web;filius.software.www.WebServer;filius.gui.anwendungssicht.GUIApplicationWebServerWindow;gfx/desktop/icon_webserver.png Navigateur web;filius.software.www.WebBrowser;filius.gui.anwendungssicht.GUIApplicationWebBrowserWindow;gfx/desktop/icon_browser.png Serveur de messagerie;filius.software.email.EmailServer;filius.gui.anwendungssicht.GUIApplicationEmailServerWindow;gfx/desktop/icon_emailserver.png Client de messagerie;filius.software.email.EmailAnwendung;filius.gui.anwendungssicht.GUIApplicationEmailAnwendungWindow;gfx/desktop/icon_emailprogramm.png Gnutella (P2P);filius.software.dateiaustausch.PeerToPeerAnwendung;filius.gui.anwendungssicht.GUIApplicationPeerToPeerAnwendungWindow;gfx/desktop/icon_peertopeer.pngfilius-2.9.4+ds/src/main/resources/config/filetypes.txt000066400000000000000000000001301506574241500231650ustar00rootroot00000000000000text;txt,cfg,bat,sh,html,htm,ini,php,xml sound;mp3,mp2,wav image;jpeg,jpg,bmp,tiff,pngfilius-2.9.4+ds/src/main/resources/config/filius.ini000066400000000000000000000047541506574241500224340ustar00rootroot00000000000000# This file is used to set some program configurations # Supported locale, i.e. the application language, settings # are en_GB and de_DE. # Note: This setting is used instead of personal selection. # locale=de_DE # locale=en_GB # locale=fr_FR # Increase accepted maximum round-trip-time for TCP if your hardware # provides low performance. Accepted values are between 1 (default) # and 5. # Note: If this factor is set to a high value, program performance # will decrease. This setting will be ignored if it is already set # as argument at startup to another value than 1. # rtt=2 # Use this setting to change the applications look & feel to the OS # native look & feel. # Note: If enabled, this setting overrules the setting at startup! # native-look-n-feel=1 # Set whether command line tools behaviour such as ping should # behave more similar to linux tools: # - ping will be sent 10 times instead of 4 times # posix-behaviour=1 # Use the old message dialog to observe data exchange. It is recommended to use the new dialog! # Default behavior is to use the old exchange dialog (value 1). old-exchange-dialog=0 # How the desktop windows in action mode are organized on the screen. # - The next window is added to the right of the previously opened window (arranged row by row): mode "row" # - The next window is added below the previously opened window (arranged column by column): mode "column" # - The next window overlaps the previously opened window (default): mode "stack" # row: 0, column: 1, stack: 2 # desktop-mode=1 # The software wizard is used for developing own applications within a Filius development environment. # The necessary prerequisite for programming and compiling your own applications is that Filius is # run on a Java Runtime Environment which is part of the JDK. The default behaviour is, that # the software wizard is hidden if this prerequisite is not fullfilled. # It is possible to force showing of the menu icon for the wizard with the parameter software-wizard: # 0 - do not show the software wizard at all # 1 - show the software wizard in any case (default) # 2 - show the software wizard when Filius is started with JDK VM (not JRE) # software-wizard=1 # The working area to design the computer network is of limited size. # The attributes pane-width and pane-height are used to increase or decrease the available working area. # Default is (width x height): 2000 x 1500 # pane-width=2000 # pane-height=1500 # If the hardware component NAT Gateway / Home Router is available. # gateway=0 filius-2.9.4+ds/src/main/resources/filius/000077500000000000000000000000001506574241500204545ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/filius/messages/000077500000000000000000000000001506574241500222635ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/filius/messages/MessagesBundle_de_DE.properties000066400000000000000000001202011506574241500303160ustar00rootroot00000000000000main_msg1 = M\u00f6chten Sie fortfahren? \u00c4nderungen gehen dann verloren! main_msg2 = \u00c4nderungen verwerfen? main_msg3 = Datei laden main_msg4 = Datei speichern main_dlg_OK = OK main_dlg_YES = Ja main_dlg_NO = Nein main_dlg_CANCEL = Abbrechen languageSelection_msg1 = Sprache whlen languageSelection_msg2 = Damit die nderung wirksam wird, muss FILIUS neu gestartet werden. languageSelection_msg3 = Besttigen languageSelection_msg4 = Besttigen und Programm beenden languageSelection_msg5 = Abbrechen splashscreen_msg1 = Lizenz: GNU General Public License Version 2 oder 3 docusidebar_msg1 = Text docusidebar_msg3 = Gliederungsfl\u00e4che docusidebar_msg5 = Exportieren docusidebar_msg6 = Als Bild exportieren docusidebar_msg7 = Report docusidebar_msg8 = Report als PDF-Dokument erstellen clientbaustein_msg1 = Server-Adresse: clientbaustein_msg2 = Server-Port: clientbaustein_msg3 = Verbinden clientbaustein_msg4 = Nachricht: clientbaustein_msg5 = Senden clientbaustein_msg6 = Trennen serverbaustein_msg1 = Port: serverbaustein_msg2 = Anhalten serverbaustein_msg3 = Starten dnsserver_msg1 = Starten dnsserver_msg2 = Adressen (A) dnsserver_msg3 = Mailaustausch (MX) dnsserver_msg4 = Host-/Domainname: dnsserver_msg5 = IP-Adresse: dnsserver_msg6 = Hinzuf\u00fcgen dnsserver_msg7 = Auswahl entfernen dnsserver_msg8 = Host-/Domainname dnsserver_msg9 = IP-Adresse dnsserver_msg10 = Maildomain: dnsserver_msg11 = Domainname Mailserver: dnsserver_msg12 = Maildomain dnsserver_msg13 = Domainname Mailserver dnsserver_msg14 = Beenden dnsserver_msg15 = Nameserver (NS) dnsserver_msg16 = Domain: dnsserver_msg17 = Nameserver: dnsserver_msg18 = Domain dnsserver_msg19 = Nameserver dnsserver_msg20 = Aktiviere rekursive Domain-Aufl\u00f6sung dnsserver_msg21 = Eingaben sind ung\u00fcltig! Valides Beispiel: \n\ > IP-Adresse: 192.168.0.10\n\ > Domainname: www.filius.de dnsserver_msg22 = Eingaben sind ung\u00fcltig! Valides Beispiel: \n\ > Domainname: filius.de\n\ > Nameserver: dns.filius.de dnsserver_msg23 = Eingaben sind ung\u00fcltig! Valides Beispiel: \n\ > Maildomain: filius.de\n\ > Mailserver: mail.filius.de emailanwendung_msg1 = Absender emailanwendung_msg2 = Betreff emailanwendung_msg3 = Empf\u00E4nger emailanwendung_msg4 = Willkommen in der E-Mail-Anwendung emailanwendung_msg5 = Posteingang emailanwendung_msg6 = Gesendete emailanwendung_msg7 = E-Mails abrufen emailanwendung_msg8 = Neue E-Mail verfassen emailanwendung_msg9 = E-Mail beantworten emailanwendung_msg10 = ausgew\u00e4hlte E-Mail l\u00f6schen emailanwendung_msg11 = E-Mail-Konto verwalten emailanwendung_msg12 = E-Mail verfassen emailanwendung_msg13 = Senden emailanwendung_msg14 = An: emailanwendung_msg15 = CC: emailanwendung_msg16 = BCC: emailanwendung_msg17 = Betreff: emailanwendung_msg18 = RE: emailanwendung_msg19 = schrieb: emailanwendung_msg20 = Mindestens eine eingegebene E-Mail-Adresse ist nicht korrekt! emailanwendung_msg21 = Sie m\u00fcssen mindestens eine Empf\u00e4nger-Adresse eingeben! emailanwendung_msg22 = Versende E-Mail emailanwendung_msg23 = E-Mails abholen emailanwendung_msg24 = E-Mails abholen von POP3 Server f\u00fcr ( emailanwendung_msg25 = E-Mail-Konto verwalten emailanwendung_msg26 = E-Mail-Adresse: emailanwendung_msg27 = POP3-Server: emailanwendung_msg28 = POP3-Port: emailanwendung_msg29 = SMTP-Server: emailanwendung_msg30 = SMTP-Port: emailanwendung_msg31 = Benutzername: emailanwendung_msg32 = Passwort: emailanwendung_msg33 = Speichern emailanwendung_msg34 = Nachname emailanwendung_msg35 = Vorname emailanwendung_msg36 = Eines oder mehrere Felder sind nicht korrekt ausgef\u00fcllt! emailanwendung_msg37 = Abbrechen emailanwendung_msg38 = Neues Konto Erstellen emailanwendung_msg39 = Ja emailanwendung_msg40 = Abbrechen emailanwendung_msg41 = Soll das aktuelle Konto wirklich aus der Anwendung entfernt werden? emailanwendung_msg42 = Sicherheitsabfrage! emailanwendung_msg43 = E-Mail l\u00f6schen emailanwendung_msg44 = Konto einrichten emailanwendung_msg45 = Name: emailanwendung_msg46 = Die Angaben sind entweder fehlerhaft oder unvollst\u00e4ndig! emailanwendung_msg47 = Es wurde noch kein E-Mail-Konto eingerichtet. emailserver_msg1 = Starten emailserver_msg2 = POP3- und SMTP-Server wurden gestartet. emailserver_msg3 = POP3- und SMTP-Server wurden angehalten. emailserver_msg4 = Maildomain: emailserver_msg5 = Domain setzen emailserver_msg6 = Domain setzen. Nur bei angehaltenem Server! emailserver_msg7 = Maildomain wurde neu gesetzt. emailserver_msg8 = Markiertes Konto entfernen emailserver_msg9 = Ja emailserver_msg10 = Abbrechen emailserver_msg11 = Wollen Sie das Konto ' emailserver_msg12 =' wirklich entfernen? emailserver_msg13 = Sicherheitsabfrage! emailserver_msg14 = E-Mail Adresse emailserver_msg15 = Anzahl Mails emailserver_msg16 = Benutzername: emailserver_msg17 = Vorname und Nachname: emailserver_msg18 = Passwort: emailserver_msg19 = Konto erstellen emailserver_msg20 = Nachname emailserver_msg21 = Vorname emailserver_msg22 = Das Benutzerkonto emailserver_msg23 = wurde angelegt. emailserver_msg24 = Der Benutzername darf keine Leerzeichen enthalten! emailserver_msg25 = Geben Sie einen Benutzernamen und ein Passwort ein! emailserver_msg26 = Neues Konto emailserver_msg27 = Konten-Liste emailserver_msg28 = Log Fenster emailserver_msg29 = Anhalten fileexplorer_msg1 = Aktualisieren fileexplorer_msg2 = Importieren fileexplorer_msg3 = Neuer Ordner fileexplorer_msg4 = L\u00f6schen fileexplorer_msg5 = Ausschneiden fileexplorer_msg6 = Kopieren fileexplorer_msg7 = Einf\u00fcgen fileexplorer_msg8 = Umbenennen fileexplorer_msg9 = Neuer Name: fileexplorer_msg10 = Datei fileexplorer_msg11 = Ordner fileexplorer_msg12 = Datei importieren fileexplorer_msg13 = Lokale Datei: fileexplorer_msg14 = Datei w\u00E4hlen fileexplorer_msg15 = Datei importieren fileexplorer_msg16 = Eingabe- und Ausgabedatei m\u00FCssen angegeben werden! fileexplorer_msg17 = Es muss ein Ordner ausgew\u00E4hlt werden! fileexplorer_msg18 = Sicher? fileexplorer_msg19 = Unzulssiger Zielordner! fileimporter_msg1 = Lokale Datei: fileimporter_msg2 = Neuer Name: fileimporter_msg3 = Datei w\u00E4hlen fileimporter_msg4 = Ausw\u00e4hlen fileimporter_msg5 = Datei importieren fileimporter_msg6 = Eingabe- und Ausgabedatei m\u00FCssen angegeben werden! fileimporter_msg7 = Es muss ein Ordner ausgew\u00E4hlt werden! fileimporter_msg8 = File-Importer Version 0.1 firewall_msg1 = Firewall aktivieren firewall_msg2 = Die Firewall blockiert alle eingehenden IP-Pakete, wenn sie aktiviert wurde. Ausnahmen m\u00fcssen explizit angegeben werden. Regeln bestimmen TCP-/UDP-Ports, f\u00fcr die Daten zugelassen werden und ob die Ausnahme nur f\u00fcr alle Absender oder nur f\u00fcr Absender im lokalen Rechnernetz gilt. firewall_msg3 = Zul\u00e4ssiger Port/Protokoll: firewall_msg4 = Alle Absender firewall_msg5 = Absender im gleichen Rechnernetz firewall_msg6 = Hinzuf\u00fcgen firewall_msg7 = L\u00f6schen firewall_msg8 = Port firewall_msg9 = Absender firewall_msg10 = Konfiguration firewall_msg11 = Log firewall_msg12 = Nur lokales Rechnernetz firewall_msg13 = Internet Control Message Protocol (Ping etc.) erlauben firewall_msg14 = Aktiviere UDP-Datenverkehrfilter firewallthread_msg1 = ICMP-Paket verworfen: firewallwebkonfig_msg1 = Fehlerhafte Eingabe(n): firewallwebkonfig_msg2 = Quell-IP ungültig firewallwebkonfig_msg3 = Quell-Subnetzmaske leer oder ungültig firewallwebkonfig_msg4 = Ziel-IP ungültig firewallwebkonfig_msg5 = Ziel-Subnetzmaske leer oder ungültig firewallwebkonfig_msg6 = Port ungültig firewallwebkonfig_msg7 = Subnetzmaske angegeben, aber keine IP firewallwebkonfig_msg8 = Firewall-Regel wurde NICHT angelegt! imageviewer_msg1 = Datei imageviewer_msg2 = \u00d6ffnen peertopeeranwendung_msg1 = Teilnehmer IP-Adresse: peertopeeranwendung_msg2 = Netz beitreten peertopeeranwendung_msg3 = Versucht einem bestehenden Netzwerk beizutreten, nach Eingabe der IP-Adresse eines Teilnehmers. peertopeeranwendung_msg4 = Liste der verbundenen Nachbarn: peertopeeranwendung_msg5 = Dies ist keine g\u00FCltige IP-Adresse peertopeeranwendung_msg6 = Kennung peertopeeranwendung_msg7 = Adresse peertopeeranwendung_msg8 = IP-Adresse peertopeeranwendung_msg9 = Dateiname peertopeeranwendung_msg10 = Gr\u00f6\u00dfe peertopeeranwendung_msg11 = Suchbegriff: peertopeeranwendung_msg12 = Suche nach enthaltener Zeichenkette peertopeeranwendung_msg13 = Suchen peertopeeranwendung_msg14 = Stopp! peertopeeranwendung_msg15 = Stoppt die laufende Suche peertopeeranwendung_msg16 = Liste leeren peertopeeranwendung_msg17 = Entfernt die bisherigen Suchergebnisse peertopeeranwendung_msg18 = Herunterladen peertopeeranwendung_msg19 = Herunterladen der markierten Datei peertopeeranwendung_msg20 = Suche peertopeeranwendung_msg21 = Dateien: peertopeeranwendung_msg22 = Dateiname peertopeeranwendung_msg23 = Dateityp peertopeeranwendung_msg24 = Dateien peertopeeranwendung_msg25 = Maximale Anzahl direkter Nachbarn: peertopeeranwendung_msg26 = Speichern peertopeeranwendung_msg27 = Es sind nur Zahlen erlaubt! peertopeeranwendung_msg28 = Einstellungen peertopeeranwendung_msg29 = Netzwerk peertopeeranwendung_msg30 = Netz verlassen peertopeeranwendung_msg31 = Zur\u00FCcksetzen der Teilnehmerliste terminal_msg1 = root> terminal_msg2 = Unbekannter Befehl! terminal_msg3 = Mit 'help' erhalten Sie eine Liste bekannter Befehle. texteditor_msg2 = Datei texteditor_msg3 = Neu texteditor_msg4 = \u00d6ffnen texteditor_msg5 = Speichern texteditor_msg6 = Speichern unter texteditor_msg7 = Beenden texteditor_msg8 = text texteditor_msg9 = Es liegen ungespeicherte \u00c4nderungen vor, wollen Sie diese speichern? texteditor_msg10 = Sicher? texteditor_msg11 = Alle Tabs schlie\u00dfen texteditor_msg12 = Alle anderen Tabs schlie\u00dfen texteditor_msg13 = Schlie\u00dfen? texteditor_msg14 = Neues Dokument webbrowser_msg2 = Start webserver_msg1 = Starten webserver_msg2 = Anhalten webserver_msg3 = Virtuelle Hosts: webserver_msg4 = Hostname webserver_msg5 = Unterverzeichnis webserver_msg6 = Verwende virtuelle Hosts desktoppanel_msg1 = Software-Installation desktoppanel_application=Startbildschirm installationsdialog_msg1 = Software-Installation installationsdialog_msg2 = \u00C4nderungen annehmen installationsdialog_msg3 = Installiert: installationsdialog_msg4 = Verf\u00fcgbar: network_msg1 = IP-Adresse: network_msg2 = DNS-Server: network_msg3 = Gateway: network_msg4 = Netzmaske: network_msg5 = Speichern network_msg6 = \u00c4nderungen speichern network_msg7 = Eine oder mehrere Eingaben sind fehlerhaft! network_msg8 = Netzwerk Status network_msg9 = MAC-Adresse: buttontabcomponent_msg1 = Reiter schlie\u00dfen lauscherdialog_msg1 = Datenaustausch nachrichtentabelle_msg1 = Anzeige der Schichten nachrichtentabelle_msg2 = Netzzugangsschicht nachrichtentabelle_msg3 = Vermittlungsschicht nachrichtentabelle_msg4 = Transportschicht nachrichtentabelle_msg5 = Anwendungsschicht nachrichtentabelle_msg6 = Schichten ausw\u00e4hlen nachrichtentabelle_msg7 = Tabellen l\u00f6schen nachrichtentabelle_msg8 = Automatisches Scrollen nachrichtentabelle_msg9 = Als Textdatei exportieren nachrichtentabelle_msg10 = Lokaler Weg durch die Schichten nachrichtentabelle_msg11 = Schichtenvisualisierung nachrichtentabelle_msg12 = Anzeige des globalen Wegs fr Broadcast-Nachrichten nicht mglich. jdhcpkonfiguration_msg1 = Adress-Untergrenze jdhcpkonfiguration_msg2 = Adress-Obergrenze jdhcpkonfiguration_msg3 = Netzmaske jdhcpkonfiguration_msg4 = Gateway jdhcpkonfiguration_msg5 = DNS-Server jdhcpkonfiguration_msg6 = DHCP aktivieren jdhcpkonfiguration_msg7 = OK jdhcpkonfiguration_msg8 = Manuelle Einstellungen jdhcpkonfiguration_msg9 = Standardm\u00e4\u00dfig werden die gleichen Einstellungen
    \ f\u00fcr den DHCP Server verwendet, die auf dem Host gelten.
    \ Eine abweichende Konfiguration ist jedoch m\u00f6glich bei
    \ Aktivieren dieser Schaltfl\u00e4che. jdhcpkonfiguration_msg10 = Grundeinstellungen jdhcpkonfiguration_msg11 = Statische Adresszuweisung jdhcpkonfiguration_msg12 = MAC-Adresse jdhcpkonfiguration_msg13 = IP-Adresse jdhcpkonfiguration_msg14 = Hinzuf\u00fcgen jdhcpkonfiguration_msg15 = Entfernen jfirewalldialog_msg1 = Konfiguration der Firewall jfirewalldialog_msg2 = Blockierte Absender: IP-Pakete werden nicht weitergeleitet, wenn die Absender-Adresse in einem der angegebenen Adressbereiche liegt. jfirewalldialog_msg3 = Adressbereich: jfirewalldialog_msg4 = Hinzuf\u00fcgen jfirewalldialog_msg5 = L\u00f6schen jfirewalldialog_msg6 = Untere Grenze jfirewalldialog_msg7 = Obere Grenze jfirewalldialog_msg8 = Blockierte Empf\u00e4nger: IP-Pakete werden nicht weitergeleitet, wenn die Empf\u00e4nger-Adresse in einem der angegebenen Adressbereiche liegt. jfirewalldialog_msg9 = Adressbereich: jfirewalldialog_msg10 = Blockierte Empf\u00e4nger-Ports: IP-Pakete werden nicht weitergeleitet, wenn der TCP-/UDP-Ziel-Port gesperrt ist. jfirewalldialog_msg11 = TCP-/UDP-Port: jfirewalldialog_msg12 = TCP-/UDP-Port jfirewalldialog_msg13 = Firewall aktivieren jfirewalldialog_msg14 = Absender jfirewalldialog_msg15 = Empf\u00e4nger jfirewalldialog_msg16 = TCP-/UDP-Portregeln jfirewalldialog_msg17 = F\u00fcr welche Netzwerk-Schnittstellen soll die Firewall aktiviert sein? jfirewalldialog_msg18 = Netzwerkschnittstellen jfirewalldialog_msg19 = Eingehende TCP-Verbindungen ablehnen jfirewalldialog_msg20 = Alle eingehenden Versuche, eine TCP-Verbindung herzustellen, werden an einer aktivierten\n\ (oben ausgew\u00e4hlten) Schnittstelle abgelehnt. Verbindungsanfragen, die \u00fcber aktivierte \n\ Schnittstellen nach au\u00dfen weitergeleitet werden sollen, werden nicht unterbunden. jfirewalldialog_msg21 = Firewall-Regeln jfirewalldialog_msg22 = rauf jfirewalldialog_msg23 = runter jfirewalldialog_msg24 = Neue Regel jfirewalldialog_msg25 = Regel l\u00f6schen jfirewalldialog_msg26 = ID jfirewalldialog_msg27 = Quell-IP jfirewalldialog_msg28 = Netzmaske jfirewalldialog_msg29 = Ziel-IP jfirewalldialog_msg30 = Netzmaske jfirewalldialog_msg31 = Protokoll jfirewalldialog_msg32 = Aktion? jfirewalldialog_msg33 = akzeptieren jfirewalldialog_msg34 = verwerfen jfirewalldialog_msg35 = Port jfirewalldialog_msg36 = Standardaktion, wenn keine Regel greift: jfirewalldialog_msg37 = Die folgenden Regeln werden von oben nach unten durchlaufen. Sobald eine Regel greift, wird die gew\u00e4hlte Aktion ausgef\u00fchrt.\nAlle Angaben (au\u00dfer Protokoll und Aktion) in einer Regel sind optional. Leere Felder werden zur Filterung nicht ausgewertet.\nWenn eine IP-Adresse angegeben wird, muss auch die Netzwerkmaske angegeben sein! Mit einer Maske ungleich 255.255.255.255 lassen sich ganze Netzbereiche adressieren.\n\u00fcber die Standard-Aktion kann zwischen Blacklisting- und Whitelisting-Ansatz gewechselt werden. jfirewalldialog_msg38 = Firewall aktivieren jfirewalldialog_msg39 = Erst durch Aktivieren der Firewall werden die Firewall-Regeln angewendet. Bei deaktivierter Firewall findet keine Filterung statt. jfirewalldialog_msg40 = ICMP-Pakete filtern jfirewalldialog_msg41 = Alle ICMP Pakete, z.B. Ping-Anfragen, werden von der Firewall verworfen. jfirewalldialog_msg42 = nur SYN-Pakete verwerfen jfirewalldialog_msg43 = Neue Verbindungsanfragen zu gesperrten Ports werden verworfen.\nDer R\u00fcckkanal f\u00fcr erlaubte Verbindungsanfragen wird akzeptiert. jfirewalldialog_msg44 = Allgemein jhostkonfiguration_msg1 = Name jhostkonfiguration_msg2 = Neuer Rechner jhostkonfiguration_msg3 = IP-Adresse jhostkonfiguration_msg4 = Netzmaske jhostkonfiguration_msg5 = Gateway jhostkonfiguration_msg6 = Domain Name Server jhostkonfiguration_msg7 = DHCP zur Konfiguration verwenden jhostkonfiguration_msg8 = DHCP-Server einrichten jhostkonfiguration_msg9 = MAC-Adresse jhostkonfiguration_msg10 = IP-Adresse als Name verwenden jhostkonfiguration_msg11 = IP-Weiterleitung aktivieren jhostkonfiguration_msg12 = MAC-Adresse als Name verwenden jhostkonfiguration_msg13 = Drahtlos (WLAN) jhostkonfiguration_msg14 = Kabelgebunden (LAN) jhostkonfiguration_msg15 = Drahtloses Netzwerk (SSID): jhostkonfiguration_msg16 = Bitte auswhlen jmodemkonfiguration_msg1 = Name jmodemkonfiguration_msg2 = Auf eingehende Verbindungsanfrage warten jmodemkonfiguration_msg3 = IP Adresse jmodemkonfiguration_msg4 = Port jmodemkonfiguration_msg5 = Verbinden jmodemkonfiguration_msg6 = Trennen jmodemkonfiguration_msg7 = Aktivieren jmodemkonfiguration_msg8 = Trennen/Deaktivieren jswitchkonfiguration_msg1 = Name jswitchkonfiguration_msg2 = Neuer Switch jswitchkonfiguration_msg3 = als Wolke darstellen jswitchkonfiguration_msg4 = Name WLAN (SSID) jswitchkonfiguration_msg5 = Gltigkeitsdauer SAT-Eintrge (in Sek.) jvermittlungsrechnerkonfiguration_msg1 = Konfiguration der Firewall jvermittlungsrechnerkonfiguration_msg2 = Name jvermittlungsrechnerkonfiguration_msg3 = Vermittlungsrechner jvermittlungsrechnerkonfiguration_msg4 = Firewall einrichten jvermittlungsrechnerkonfiguration_msg5 = Keine Verbindung jvermittlungsrechnerkonfiguration_msg6 = Verbunden mit jvermittlungsrechnerkonfiguration_msg7 = IP-Adresse jvermittlungsrechnerkonfiguration_msg8 = Netzmaske jvermittlungsrechnerkonfiguration_msg9 = Gateway jvermittlungsrechnerkonfiguration_msg10 = Netzwerkkarte jvermittlungsrechnerkonfiguration_msg11 = Alle Eintr\u00e4ge anzeigen jvermittlungsrechnerkonfiguration_msg12 = Neuer Eintrag jvermittlungsrechnerkonfiguration_msg13 = Eintrag L\u00f6schen jvermittlungsrechnerkonfiguration_msg14 = Als Fenster \u00f6ffnen jvermittlungsrechnerkonfiguration_msg15 = Weiterleitungstabelle jvermittlungsrechnerkonfiguration_msg16 = Keine Verbindung jvermittlungsrechnerkonfiguration_msg17 = Allgemein jvermittlungsrechnerkonfiguration_msg18 = MAC-Adresse jvermittlungsrechnerkonfiguration_msg19 = Hinweis: Zum Umstecken von Verbindungen:\n \ 1. Verbindung ausw\u00e4hlen durch Klick auf Schaltfl\u00e4che links neben der Schnittstellenbezeichnung\n \ 2. ausw\u00e4hlen der neuen Schnittstelle durch Klick auf die entsprechende Schaltfl\u00e4che\n \n \ Sollte die Zielschnittstelle bereits belegt sein, so werden die Verbindungen vertauscht. jvermittlungsrechnerkonfiguration_msg20 = Schlie\u00dfen jvermittlungsrechnerkonfiguration_msg21 = Fremdkomponenten jvermittlungsrechnerkonfiguration_msg22 = Lokale Schnittstellen jvermittlungsrechnerkonfiguration_msg23 = Verbindungen verwalten jvermittlungsrechnerkonfiguration_msg24 = Schnittstelle hinzuf\u00fcgen jvermittlungsrechnerkonfiguration_msg25 = Maximal 8 Schnittstellen erlaubt! jvermittlungsrechnerkonfiguration_msg26 = Automatisches Routing jgatewayconfiguration_msg1 = Konfiguration der Firewall jgatewayconfiguration_msg2 = Name jgatewayconfiguration_msg3 = Gateway jgatewayconfiguration_msg4 = Firewall einrichten jgatewayconfiguration_msg6 = Verbunden mit jgatewayconfiguration_msg7 = IP-Adresse jgatewayconfiguration_msg8 = Netzmaske jgatewayconfiguration_msg9 = Gateway jgatewayconfiguration_msg10 = Netzwerkkarte jgatewayconfiguration_msg16 = Keine Verbindung jgatewayconfiguration_msg17 = Allgemein jgatewayconfiguration_msg18 = MAC-Adresse jgatewayconfiguration_msg19 = Portfreigaben jgatewayconfiguration_msg21 = Neuer Eintrag jgatewayconfiguration_msg22 = Eintrag L\u00f6schen jportforwarding_msg1 = G\u00fcltige Eintr\u00e4ge: TCP, UDP, 6 (f\u00fcr TCP) und 17 (f\u00fcr UDP) jportforwarding_msg2 = G\u00fcltige Eintr\u00e4ge: Port-Nummern zwischen 1 und 65535 jportforwarding_msg3 = G\u00fcltige Eintr\u00e4ge: IP-Adressen aus dem LAN-Bereich jportforwarding_msg4 = Protokoll jportforwarding_msg5 = Portfreigabe jportforwarding_msg6 = LAN-Adresse jportforwarding_msg7 = LAN-Port jweiterleitungstabelle_msg1 = L\u00f6schen jweiterleitungstabelle_msg2 = Neue Zeile jweiterleitungstabelle_msg3 = Ziel jweiterleitungstabelle_msg4 = Netzmaske jweiterleitungstabelle_msg5 = N\u00e4chstes Gateway jweiterleitungstabelle_msg6 = \u00dcber Schnittstelle framesoftwarewizard_msg1 = Software-Assistent framesoftwarewizard_msg2 = Zur\u00fcck framesoftwarewizard_msg3 = Weiter framesoftwarewizard_msg4 = Schritt 1 von 3 framesoftwarewizard_msg5 = Beenden framesoftwarewizard_msg6 = Schritt 2 von 3 framesoftwarewizard_msg7 = Schritt 3 von 3 framesoftwarewizard_msg8 = Fertigstellen panelcompiler_msg1 = Kompiliere Dateien f\u00fcr Anwendung panelcompiler_msg2 =

    Fehler beim erstellen der Anwendung

    Kompilieren konnte nicht ausgeführt werden.
    \ Das kann daran liegen, dass die falsche Java-VM zur Ausführung verwendet wird (VM des JDK erforderlich).

    panelcompiler_msg3 =

    Anwendung erfolgreich erstellt!

    panelcompiler_msg4 =

    Anwendung konnte nicht eingetragen werden

    panelverwaltung_msg1 = Neue Anwendung erstellen panelverwaltung_msg2 = Client-Programm panelverwaltung_msg3 = Server-Programm panelverwaltung_msg4 = Anwendungsname: panelverwaltung_msg5 = Klassenname: panelverwaltung_msg7 = Ausgew\u00e4hlte Anwendung bearbeiten (anschliessend Neustart von Filius erforderlich!) panelverwaltung_msg8 = Eintrag L\u00f6schen panelverwaltung_msg9 = \u00c4nderungen speichern panelverwaltung_msg10 = Anwendungsname panelverwaltung_msg11 = Klasse panelverwaltung_msg12 = GUI-Klasse panelverwaltung_msg13 = Desktop-Icon dmtnfilechooser_msg1 = Ordner aufw\u00e4rts dmtnfilechooser_msg2 = Dateiname: dmtnfilechooser_msg3 = Aktion dmtnfilechooser_msg4 = Abbrechen dmtnfilechooser_msg5 = Datei Speichern dmtnfilechooser_msg6 = Speichern dmtnfilechooser_msg7 = Datei \u00d6ffnen dmtnfilechooser_msg8 = \u00d6ffnen dmtnfilechooser_msg9 = Datei; dmtnfilechooser_msg10 = Ordner; dmtnfilechooser_msg11 = Ordner: guicontainer_msg1 = W\u00e4hlen Sie die Anzahl der Schnittstellen \n des Vermittlungsrechnerns aus! guicontainer_msg2 = Anzahl Schnittstellen guievents_msg1 = Maximale Anzahl angeschlossener Ger\u00e4te \u00fcberschritten guievents_msg2 = Modem guievents_msg3 = Desktop anzeigen guievents_msg4 = Datenaustausch anzeigen guievents_msg5 = Verbindung entfernen guievents_msg6 = Alle Verbindungen entfernen guievents_msg7 = L\u00f6schen guievents_msg8 = SAT Tabelle guievents_msg9 = MAC guievents_msg10 = Port guievents_msg11 = Konfigurieren guievents_msg12 = Komponenten bereits verbunden guievents_msg13 = Farbe guievents_msg14 = Schriftstil guievents_msg15 = Schriftgr\u00f6\u00dfe guievents_msg16 = Normal guievents_msg17 = Gro\u00df guievents_msg18 = Sehr Gro\u00df guievents_msg19 = Normal guievents_msg20 = Kursiv guievents_msg21 = Fett guievents_msg22 = WAN-Anschluss (extern) bereits belegt! guievents_msg23 = LAN-Anschluss (intern) bereits belegt! guievents_msg24 = WAN-Anschluss (extern) guievents_msg25 = LAN-Anschluss (intern) guievents_msg26 = Drahtlose Verbindung nicht mglich guievents_msg27 = Letzte Aktualisierung guievents_msg28 = Tabelle l\u00f6schen guievents_msg29 = NAT-Tabelle guievents_msg30 = Protokoll guievents_msg31 = WAN-Adresse guievents_msg32 = Portfreigabe guievents_msg33 = LAN-Adresse guievents_msg34 = LAN-Port guievents_msg36 = Tabelle zur\u00fccksetzen guihilfe_msg1 = Hilfe guihilfe_msg2 =

    Fehler

    Hilfedateien nicht gefunden.. guimainmemu_msg1 = \u00d6ffnet ein vorhandenes Projekt (Strg+O) guimainmemu_msg2 = Speichert das aktuelle Projekt (Strg+S) guimainmemu_msg3 = Wechselt in den Entwurfsmodus (Strg+D) guimainmemu_msg4 = Wechselt in den Aktionsmodus (Strg+R) guimainmemu_msg5 = Erstellt ein neues Projekt (Strg+N) guimainmemu_msg6 = Startet den Software-Assistenten (Strg+W) guimainmemu_msg7 = Zeigt die Hilfe an (Strg+H) guimainmemu_msg8 = Informationen zu FILIUS (Strg+I) guimainmemu_msg9 = Wollen Sie wirklich ein neues Projekt beginnen? \n Alle bisherigen Einstellungen gehen dann verloren. \n Auch eigene Anwendungen werden entfernt. guimainmemu_msg10 = Fortfahren? guimainmemu_msg11 = Fehler beim Speichern aufgetreten! guimainmemu_msg12 = XML Dateien guimainmemu_msg13 = FILIUS Projekt-Datei guimainmemu_msg14 = Wechselt in den Dokumentationsmodus guimainmemu_msg15 = Aktuelle Simulationsgeschwindigkeit guimainmemu_msg16 = Simulationsgeschwindigkeit anpassen guimainmemu_msg17 = M\u00f6chten Sie die vorhandene Datei ersetzen? guimainmemu_msg18 = Einstellungen settings_msg1 = W\u00E4hlen Sie die Hardware-Piktogramm-Variante settings_msg2 = Standard settings_msg3 = Unternehmen (experimentell) settings_msg4 = Symbole (experimentell) settings_msg5 = Schichtenvisualisierung (experimentell) infodialog_msg1 = Information infodialog_msg2 =

    FILIUS wurde im Rahmen einer studentischen Projektgruppe an der Universität Siegen entwickelt. (2006-2007)

    infodialog_msg3 = Studierende: infodialog_msg4 = Betreuer: infodialog_msg5 = Wartung und Weiterentwicklung: infodialog_msg6 = Version infodialog_msg8 = Melden Sie Fehler bitte an folgender Stelle: infodialog_msg9 = oder infodialog_msg10 = Verbreitung unter der GNU General Public License (GPL) Version 2 oder 3 infodialog_msg11 = Weitere Beitragende: hw_notebook_msg1 = Notebook hw_notebook_msg2 = Notebook hw_rechner_msg1 = Rechner hw_rechner_msg2 = Rechner hw_switch_msg1 = Switch / WLAN hw_vermittlungsrechner_msg1 = Vermittlungsrechner hw_gateway_msg1 = Heimrouter hw_kabel_msg1 = Verbindung hw_verbindung_msg1 = Ein Port von den beiden ist schon belegt. sw_clientbaustein_msg1 = Fehler beim Verbindungsaufbau\n sw_clientbaustein_msg2 = Verbindung hergestellt sw_clientbaustein_msg3 = Verbindung getrennt sw_clientbaustein_msg4 = Fehler beim Verbindungsaufbau sw_clientbaustein_msg5 = Socket zu sw_clientbaustein_msg6 = getrennt sw_serveranwendung_msg1 = Annahme von Verbindungsanfragen beendet sw_serveranwendung_msg2 = Annahme von Verbindungsanfragen gestartet sw_serveranwendung_msg3 = Server-Socket geschlossen sw_serveranwendung_msg4 = Verbindung zu sw_serveranwendung_msg5 = hergestellt sw_serveranwendung_msg6 = Server-Socket konnte nicht geffnet werden. sw_servermitarbeiter_msg1 = Socket zu sw_servermitarbeiter_msg2 = geschlossen sw_ipeintrag_msg1 = MAC-Adresse: sw_ipeintrag_msg2 = IP-Adresse: sw_dnsservermitarbeiter_msg1 = Anfrage ist: sw_dnsservermitarbeiter_msg2 = Versende Antwort: sw_emailserver_msg1 = Dieses Konto konnte nicht gel\u00f6scht werden. Bitte \u00fcberpr\u00fcfen Sie ihren Benutzernamen und Ihr Passwort! sw_pop3client_msg1 = Verbindung getrennt sw_smtpclient_msg1 = Verbindung zu sw_smtpclient_msg2 = hergestellt sw_smtpclient_msg3 = getrennt sw_smtpclient_msg4 = Mail versendet an sw_smtpmitarbeiter_msg1 = 220 Willkommen bei sw_smtpmitarbeiter_msg2 = 221 Server beendet Verbindung. sw_smtpmitarbeiter_msg3 = 250 OK sw_smtpmitarbeiter_msg4 = 250 Sender OK sw_smtpmitarbeiter_msg5 = 501 Syntax-Fehler - Parameter oder Argument falsch. sw_smtpmitarbeiter_msg6 = 250 Recipient OK sw_smtpmitarbeiter_msg7 = 501 Syntax-Fehler - Parameter oder Argument falsch. sw_smtpmitarbeiter_msg8 = 354 End data with . sw_smtpmitarbeiter_msg9 = 503 Falsche Reihenfolge der Kommandos. sw_smtpmitarbeiter_msg10 = 250 Mail queued for delivery sw_smtpmitarbeiter_msg11 = 503 Falsche Reihenfolge der Kommandos. sw_smtpmitarbeiter_msg12 = E-Mail f\u00fcr sw_smtpmitarbeiter_msg13 = im Postfach gespeichert. sw_smtpmitarbeiter_msg14 = Fehler beim Senden an sw_firewall_msg1 = Empf\u00e4nger blockieren - sw_firewall_msg2 = Blockieren wegen Port: sw_firewall_msg3 = Anfrage Verbindungsaufbau unterbunden! (SYN = true) sw_firewall_msg4 = IP-Regel wurde hinzugef\u00fcgt: sw_firewall_msg5 = Portregel wurde hinzugef\u00fcgt: sw_firewall_msg6 = IP-Regel wurde entfernt: sw_firewall_msg7 = Portregel wurde entfernt. sw_firewall_msg8 = Firewall-Regel hat gegriffen: sw_firewall_msg9 = Keine Firewall-Regel hat gegriffen. Verwende Standardaktion: sw_firewallweblog_msg1 = \n\t\n\t\tLog-Eintr\u00e4ge der Firewall</titel> sw_firewallweblog_msg2 = \n\t</head>\n\t<body>\n\t\t<h2>Log-Einträge der Firewall</h2> sw_vermittlungweb_msg1 = Diese Tabelle wird durch das Routing Information Protocol (RIP) aufgebaut und aktualisiert. sw_vermittlungweb_msg3 = unbegrenzt sw_vermittlungweb_msg4 = Diese Tabelle wurde aus der IP-Konfiguration des Vermittlungsrechners und manuellen Eintr\u00e4gen erstellt. sw_vermittlungweb_msg5 = Routing-/Weiterleitungs-Tabelle sw_vermittlungweb_msg6 = Routing-Tabelle sw_fileexplorer_msg1 = Datei konnte nicht importiert werden sw_fileexplorer_msg2 = Datei erfolgreich importiert! sw_fileexplorer_msg3 = Datei existiert nicht oder ist zu gro\u00df (> 150KB) sw_fileimporter_msg1 = Datei konnte nicht importiert werden sw_fileimporter_msg2 = Datei erfolgreich importiert! sw_fileimporter_msg3 = Schlieen sw_terminal_msg1 = Anwendung sw_terminal_msg2 = ist bereits installiert.\n sw_terminal_msg3 = erfolgreich installiert\n sw_terminal_msg4 = IP Adresse . . . : sw_terminal_msg5 = Netzmaske. . . . : sw_terminal_msg6666= Physische Adresse: sw_terminal_msg6 = Standardgateway. : sw_terminal_msg27= DNS-Server . . . : sw_terminal_msg7 = | Ziel | Netzmaske | Gateway | Schnittstelle |\n \ -----------------------------------------------------------------------\n sw_terminal_msg8 = Verzeichnis ist leer\n sw_terminal_msg9 = Verzeichnis sw_terminal_msg10 = \nAnzahl Verzeichnisse: sw_terminal_msg11 = \nAnzahl Dateien: sw_terminal_msg12 = Fehler bei Touch\n sw_terminal_msg13 = Datei erstellt\n sw_terminal_msg14 = Datei bereits vorhanden\n sw_terminal_msg15 = Bitte einen Dateinamen angeben!\n sw_terminal_msg16 = Fehler beim Ordner erstellen\n sw_terminal_msg17 = Ordner erstellt\n sw_terminal_msg18 = Ordner bereits vorhanden\n sw_terminal_msg19 = Bitte einen Ordnernamen angeben!\n sw_terminal_msg20 = Verzeichnis nicht vorhanden\n sw_terminal_msg21 = Ordner gewechselt! (..)\n sw_terminal_msg22 = Ordner gewechselt!\n sw_terminal_msg23 = Test\n sw_terminal_msg24 = Erfolgreich erstellt! sw_terminal_msg25 = Liste verf\u00fcgbarer Befehle:\n \ arp zeige Adresstabelle des "Address Resolution Protocol" (ARP)\n \ arpsend versende eine ARP-Antwort\n \ cat / type zeige Dateiinhalt an \n \ cd Verzeichnis wechseln\n \ cls Anzeige zurcksetzen\n \ copy / cp Datei kopieren\n \ del / rm l\u00f6sche Datei/Verzeichnis\n \ dir / ls zeige Liste der Dateien im aktuellen Verzeichnis\n \ exit beende Terminal-Anwendung\n \ help zeige diese Befehlsliste\n \ host l\u00f6se Hostnamen zu IP-Adresse auf\n \ ipconfig Netzwerkkonfiguration anzeigen\n \ mkdir erstelle Verzeichnis\n \ move / mv Datei verschieben/umbenennen\n \ netstat zeige Liste aller Verbindungen\n \ nslookup l\u00f6se Hostnamen zu IP-Adresse auf\n \ ping teste Verbindung zu anderem Rechner\n \ pwd gib Pfad des aktuellen Arbeitsverzeichnisses aus\n \ route Routing-/Weiterleitungstabelle anzeigen\n \ tcpdump Datenaustausch anzeigen\n \ touch erstelle Datei\n \ traceroute analysiere Stationen des \u00DCbertragungsweges \n sw_terminal_arp = arp - show or manipulate Address Resolution Protocol (ARP) table sw_terminal_arpsend = arpsend - send an ARP reply sw_terminal_cat = cat / type - show file contents sw_terminal_cd = cd - change directory sw_terminal_copy = copy / cp - copy file sw_terminal_del = del / rm - delete file/directory sw_terminal_dir = dir / ls - show list of files in current directory sw_terminal_exit = exit - exit terminal application sw_terminal_help = help - show the list of commands or info on one command sw_terminal_host = host - resolve hostname to IP address sw_terminal_ipconfig = ipconfig - show network configuration sw_terminal_mkdir = mkdir - create directory sw_terminal_move = move / mv - move/rename file sw_terminal_netstat = netstat - show list of connections sw_terminal_nslookup = nslookup - resolve hostname to IP address sw_terminal_ping = ping - test connection to other computer sw_terminal_pwd = pwd - print working directory sw_terminal_route = route - show routing table sw_terminal_tcpdump = tcpdump - show data exchange sw_terminal_touch = touch - create file sw_terminal_traceroute = traceroute - analyse hops of connecting path sw_terminal_usage_arp = Usage: arp [-d [<IP address>]] [-a <IP address>] \n \ -d \t delete ARP entry for IP address or reset ARP table \n \ -a \t show ARP entry for specified IP address sw_terminal_usage_arpsend = Usage: arpsend <sender IP address> <target IP address>\n \ <sender IP address> address to be published for own MAC address\n \ <target IP address> target the reply must be sent to sw_terminal_usage_cat = Usage: cat <file name> sw_terminal_usage_cd = Usage: cd <directory path> sw_terminal_usage_copy = Usage: copy <original file> <new file name> sw_terminal_usage_del = Usage: del <file | folder> \n \ <file | folder> relative path to current directory sw_terminal_usage_dir = Usage: dir sw_terminal_usage_exit = Usage: exit sw_terminal_usage_help = Usage: help [<command>] sw_terminal_usage_host = Usage: host <domain> \n \ <domain> the domain for which to look up A and MX records sw_terminal_usage_ipconfig = Usage: ipconfig sw_terminal_usage_mkdir = Usage: mkdir <directory name> \n \ <directory name> name of new directory either as absolute path \n \ or relative path sw_terminal_usage_move = Usage: move <original file> <new file name> sw_terminal_usage_netstat = Usage: netstat sw_terminal_usage_nslookup = Usage: nslookup <domain> \n \ <domain> the domain for which to look up A (and related NS) records sw_terminal_usage_ping = Usage: ping <remote host> \n \ <remote host> the remote address as IP address or host name sw_terminal_usage_pwd = Usage: pwd sw_terminal_usage_route = Usage: route sw_terminal_usage_tcpdump = Usage: tcpdump sw_terminal_usage_touch = Usage: touch <file name> sw_terminal_usage_traceroute = Usage: traceroute <remote host> \n \ <remote host> the remote address as IP address or host name sw_terminal_msg26 = Verwende den Befehl 'help' um Liste der verfgbaren Befehle anzuzeigen\n \ oder nutze 'help <Befehl>' fr weitere Infos zum Befehl. sw_terminal_msg28 = hat die IP-Adresse sw_terminal_msg29 = Ung\u00fcltige Host-Angabe!\nNur korrekte URLs ohne Pfadangabe erlaubt.\n sw_terminal_msg30 = Hostname konnte nicht aufgel\u00f6st werden!\nEventuell ist kein DNS-Server verf\u00fcgbar.\n sw_terminal_msg31 = Zeit\u00fcberschreitung! Keine Antwort empfangen. sw_terminal_msg33 = Datei erfolgreich kopiert\n sw_terminal_msg34 = Datei konnte nicht kopiert werden\n sw_terminal_msg35 = Datei erfolgreich verschoben/umbenannt\n sw_terminal_msg36 = Datei konnte nicht verschoben/umbenannt werden\n sw_terminal_msg37 = Datei erfolgreich gel\u00f6scht\n sw_terminal_msg38 = Datei konnte nicht gel\u00f6scht werden\n sw_terminal_msg39 = <CE: frei>\n sw_terminal_msg45 = Paketstatistik sw_terminal_msg46 = Paket(e) gesendet sw_terminal_msg47 = Paket(e) empfangen sw_terminal_msg48 = Paketverlust sw_terminal_msg49 = | Proto | Lokale Adresse | Entfernte Adresse | Zustand |\n sw_terminal_msg50 = | Internetadresse | Physische Adresse |\n sw_terminal_msg52 = Zieladresse nicht erreichbar sw_terminal_msg53 = Ping zu Broadcast-Adressen wird nicht unterst\u00fctzt. sw_terminal_msg54 = Datei nicht vorhanden sw_terminal_msg55 = Beenden mit Strg+C sw_terminal_msg57 = \n\n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ sSSs .S S. .S .S S. sSSs \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ d%%SP .SS SS. .SS .SS SS. d%%SP \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ d%S' S%S S%S S%S S%S S%S d%S' \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S%S S%S S%S S%S S%S S%S S%| \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S S&S S&S S&S S&S S&S S&S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S_Ss S&S S&S S&S S&S S&S Y&Ss \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S~SP S&S S&S S&S S&S S&S `S&&S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S S&S S&S S&S S&S S&S `S*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*b S*S S*b S*S S*b d*S l*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S S*S. S*S S*S. .S*S .S*P \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S SSSbs S*S SSSbs_sdSSS sSS*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S YSSP S*S YSSP~YSSY YSS' \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ SP SP SP \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ Y Y Y \n\n sw_terminal_msg58 = Mails werden behandelt durch sw_terminal_trace_msg2 = Fehler: Verbindung zum DNS-Server nicht m\u00f6glich. sw_terminal_trace_msg3 = Fehler: Domainname konnte nicht aufgel\u00f6st werden. sw_terminal_trace_msg4 = Verbindung nach %s wird bestimmt (max. %s Stationen). sw_terminal_trace_msg5 = Verbindung nach %s (%s) wird bestimmt (max. %s Stationen). sw_terminal_trace_msg6 = %s konnte \u00fcber %s Stationen (hops) nicht erreicht werden. sw_terminal_trace_msg7 = Analyse abgebrochen! sw_terminal_trace_msg8 = Fehler: %s antwortet "ICMP Network Unreachable" sw_terminal_trace_msg9 = Fehler: %s antwortet "ICMP Host Unreachable" sw_terminal_trace_msg10 = Fehler: %s antwortet "ICMP Destination Unreachable" sw_terminal_trace_msg11 = %s wurde nach einem Sprung (hop) erreicht. sw_terminal_trace_msg12 = %s wurde nach %s Spr\u00fcngen (hops) erreicht. sw_terminal_trace_msg13 = Fehler: n\u00e4chste Station konnte mehrfach nicht erreicht werden. sw_switchfirmware_msg1 = Port sw_serversocket_msg1 = Socket auf Port sw_serversocket_msg2 = schon vorhanden sw_socket_msg1 = Ziel konnte nicht bestimmt werden! sw_tcpsocket_msg1 = Fehler beim Aufbau einer Server-Verbindung sw_tcpsocket_msg2 = Zeit\u00fcberschreitung beim Verbindungsaufbau sw_tcpsocket_msg3 = Aktives \u00d6ffnen des Sockets fehlgeschlagen sw_tcpsocket_msg5 = Fehler beim Aufbau einer Server-Verbindung. sw_tcpsocket_msg6 = Verbindung nicht aufgebaut! sw_tcpsocket_msg7 = Verbindung wurde unterbrochen sw_tcpsocket_msg8 = Zeit\u00fcberschreitung w\u00e4hrend des Versands einer Nachricht sw_tcpsocket_msg9 = Keine Verbindung hergestellt! sw_tcpsocket_msg10 = Zeit\u00fcberschreitung w\u00e4hrend des Empfangs einer Nachricht sw_transportprotokoll_msg1 = Port sw_transportprotokoll_msg2 = nicht vorhanden sw_transportprotokoll_msg3 = Server-Dienst nicht verf\u00fcgbar / nicht gestartet sw_ip_msg1 = ARP-Protokoll : Kann gesuchte IP-Adresse sw_ip_msg2 = nicht finden - Ziel MAC-Adresse: sw_ip_msg3 = - Gateway: sw_ip_msg4 = Fehler bei sw_ip_msg5 = Kein Eintrag in der Weiterleitungstabelle\nf\u00fcr das Ziel sw_ip_msg6 = vorhanden. sw_weiterleitungstabelle_msg1 = Manuelle Eintr\u00e4ge sw_httpnachricht_msg1 = Informationen sw_httpnachricht_msg2 = Erfolgreiche Operation sw_httpnachricht_msg3 = Umleitung sw_httpnachricht_msg4 = Client-Fehler sw_httpnachricht_msg5 = Server-Fehler sw_webbrowser_msg1 = <html><head><title>Fehler

    Server konnte nicht erreicht werden!

    sw_webbrowser_msg2 = FehlerFehler rp_lauscher_msg1 = Nr. rp_lauscher_msg2 = Zeit rp_lauscher_msg3 = Quelle rp_lauscher_msg4 = Ziel rp_lauscher_msg5 = Protokoll rp_lauscher_msg6 = Schicht rp_lauscher_msg7 = Bemerkungen / Details rp_lauscher_msg8 = Netzzugang rp_lauscher_msg9 = Vermittlung rp_lauscher_msg10 = Transport rp_lauscher_msg11 = Anwendung rp_lauscher_msg12 = Protokoll rp_lauscher_msg13 = Suche nach MAC f\u00fcr rp_lauscher_msg14 = MAC ist rp_filiusclassloader_msg1 = Klasse rp_filiusclassloader_msg2 = konnte nicht geladen werden. rp_szenarioverwaltung_msg5 = Datei nicht gefunden oder fehlerhaft. hilfedatei_entwurf = design_mode_de_DE.html hilfedatei_simulation = simulation_mode_de_DE.html hilfedatei_documentation = documentation_mode_de_DE.html modemfirmware_msg1 = Zielrechner konnte nicht erreicht werden modemfirmware_msg2 = Fehler beim Verbindungsaufbau jsidebar_tooltip_ipAddress = IP-Adresse(n)/Netzmaske (MAC) jsidebar_tooltip_gateway = Gateway jsidebar_tooltip_dnsServer = DNS-Server report_overview = bersicht report_component_config = Komponenten report_base_config = Basiskonfiguration report_nics = Netzwerkschnittstellen report_apps = Anwendungen report_routing_table = Weiterleitungstabelle report_network_traffic = Datenaustausch report_title = Filius-Dokumentation report_dns_rr = Konfiguration DNS-Server report_domain = Domainname report_type = Typ report_data = Daten simulation_control_drop_packets = Pakete verwerfen schichten_lokaler_weg_msg2 = Schicht schichten_lokaler_weg_msg3 = Protokoll schichten_lokaler_weg_msg4 = Daten schichten_lokaler_weg_msg5 = Ethernet schichten_lokaler_weg_msg6 = IP-Adresse: schichten_lokaler_weg_msg7 = Netzmaske: schichten_lokaler_weg_msg8 = MAC-Adresse: schichten_lokaler_weg_msg9 = Gateway: schichten_lokaler_weg_msg10 = DNS-Server: schichten_allgemein_msg1 = vorwrts schichten_allgemein_msg2 = zurck schichten_globaler_weg_msg1 = Schichtenvisualisierungfilius-2.9.4+ds/src/main/resources/filius/messages/MessagesBundle_en_GB.properties000066400000000000000000001110071506574241500303340ustar00rootroot00000000000000main_msg1 = Do you want to continue? Changes will be discarded! main_msg2 = Discard changes? main_msg3 = Open file main_msg4 = Save file main_dlg_OK = OK main_dlg_YES = Yes main_dlg_NO = No main_dlg_CANCEL = Cancel languageSelection_msg1 = Select Language languageSelection_msg2 = FILIUS must be restarted to apply change. languageSelection_msg3 = Confirm languageSelection_msg4 = Confirm and Close Program languageSelection_msg5 = Cancel splashscreen_msg1 = License: GNU General Public License version 2 or 3 docusidebar_msg1 = Text docusidebar_msg3 = Structure Field docusidebar_msg5 = Export docusidebar_msg6 = Export as Image docusidebar_msg7 = Report docusidebar_msg8 = Create report as PDF document clientbaustein_msg1 = Server address: clientbaustein_msg2 = Server port: clientbaustein_msg3 = Connect clientbaustein_msg4 = Message: clientbaustein_msg5 = Send clientbaustein_msg6 = Disconnect serverbaustein_msg1 = Port: serverbaustein_msg2 = Stop serverbaustein_msg3 = Start dnsserver_msg1 = Start dnsserver_msg2 = Address (A) dnsserver_msg3 = Mail exchange (MX) dnsserver_msg4 = Host/domain name: dnsserver_msg5 = IP address: dnsserver_msg6 = Add dnsserver_msg7 = Remove entry dnsserver_msg8 = Host/domain name dnsserver_msg9 = IP address dnsserver_msg10 = Mail domain: dnsserver_msg11 = Mail server domain name: dnsserver_msg12 = Mail domain dnsserver_msg13 = Mail server domain name dnsserver_msg14 = Stop dnsserver_msg15 = Name server (NS) dnsserver_msg16 = Domain: dnsserver_msg17 = Name server: dnsserver_msg18 = Domain dnsserver_msg19 = Name server dnsserver_msg20 = Enable recursive domain resolution dnsserver_msg21 = Input invalid! Valid example: \n\ > IP address: 192.168.0.10\n\ > Domain name: www.filius.de dnsserver_msg22 = Input invalid! Valid example: \n\ > Domain name: filius.de\n\ > Name server: dns.filius.de dnsserver_msg23 = Input invalid! Valid example: \n\ > Mail domain: filius.de\n\ > Mail server: mail.filius.de! emailanwendung_msg1 = Sender emailanwendung_msg2 = Subject emailanwendung_msg3 = Receiver emailanwendung_msg4 = Welcome to the Email Application emailanwendung_msg5 = Inbox emailanwendung_msg6 = Sent emailanwendung_msg7 = Retrieve emails emailanwendung_msg8 = New email emailanwendung_msg9 = Answer emailanwendung_msg10 = delete selected email emailanwendung_msg11 = Manage email account emailanwendung_msg12 = New email emailanwendung_msg13 = Send emailanwendung_msg14 = To: emailanwendung_msg15 = CC: emailanwendung_msg16 = BCC: emailanwendung_msg17 = Subject: emailanwendung_msg18 = RE: emailanwendung_msg19 = wrote: emailanwendung_msg20 = At least one email address is not correct! emailanwendung_msg21 = You have to enter at least one receiver address! emailanwendung_msg22 = Send email emailanwendung_msg23 = Retrieve emails emailanwendung_msg24 = Retrieve emails from POP3 server for ( emailanwendung_msg25 = Manage email account emailanwendung_msg26 = Email address: emailanwendung_msg27 = POP3 server: emailanwendung_msg28 = POP3 port: emailanwendung_msg29 = SMTP server: emailanwendung_msg30 = SMTP port: emailanwendung_msg31 = User: emailanwendung_msg32 = Password: emailanwendung_msg33 = Save emailanwendung_msg34 = Last name emailanwednung_msg35 = First name emailanwendung_msg36 = One or more fields are not duly completed! emailanwendung_msg37 = Cancel emailanwendung_msg38 = Create new account emailanwendung_msg39 = Yes emailanwendung_msg40 = Cancel emailanwendung_msg41 = Should the current account really be deleted? emailanwendung_msg42 = Security request! emailanwendung_msg43 = Delete email emailanwendung_msg44 = Account emailanwendung_msg45 = Name: emailanwendung_msg46 = Wrong or incomplete data! emailanwendung_msg47 = There is no email account yet. emailserver_msg1 = Start emailserver_msg2 = POP3 and SMTP servers are activated. emailserver_msg3 = POP3 and SMTP servers are stopped. emailserver_msg4 = Mail domain: emailserver_msg5 = Set domain emailserver_msg6 = Set domain when server is not started! emailserver_msg7 = Mail domain was restarted. emailserver_msg8 = Delete selected account emailserver_msg9 = Yes emailserver_msg10 = Cancel emailserver_msg11 = Do you really want to delete the account ' emailserver_msg12 ='? emailserver_msg13 = Security request! emailserver_msg14 = Email address emailserver_msg15 = Number of mails emailserver_msg16 = User: emailserver_msg17 = First and last name: emailserver_msg18 = Password: emailserver_msg19 = New account emailserver_msg20 = Last name emailserver_msg21 = First name emailserver_msg22 = The account emailserver_msg23 = was built. emailserver_msg24 = User names may not contain whitespace! emailserver_msg25 = Enter user and password! emailserver_msg26 = New account emailserver_msg27 = Account list emailserver_msg28 = Log panel emailserver_msg29 = Stop fileexplorer_msg1 = Update fileexplorer_msg2 = Import fileexplorer_msg3 = New folder fileexplorer_msg4 = Delete fileexplorer_msg5 = Cut fileexplorer_msg6 = Copy fileexplorer_msg7 = Paste fileexplorer_msg8 = Rename fileexplorer_msg9 = New name: fileexplorer_msg10 = File fileexplorer_msg11 = Folder fileexplorer_msg12 = Import file fileexplorer_msg13 = Local file: fileexplorer_msg14 = Select file fileexplorer_msg15 = Import file fileexplorer_msg16 = Select input and output file! fileexplorer_msg17 = Select folder! fileexplorer_msg18 = Sure? fileexplorer_msg19 = Invalid target folder! fileimporter_msg1 = Local file: fileimporter_msg2 = New name: fileimporter_msg3 = Select file fileimporter_msg4 = Select fileimporter_msg5 = Import file fileimporter_msg6 = Select input and output file! fileimporter_msg7 = Select folder! fileimporter_msg8 = File Importer Version 0.1 firewall_msg1 = Activate firewall firewall_msg2 = The firewall blocks incoming IP packets, if it is activated. Exceptions must be declared. Rules define opened ports and whether they are opened for global or local sender. firewall_msg3 = Opened port/protocol: firewall_msg4 = All sender firewall_msg5 = Local sender firewall_msg6 = Add firewall_msg7 = Delete firewall_msg8 = Port firewall_msg9 = Sender firewall_msg10 = Setup firewall_msg11 = Log firewall_msg12 = Local network firewall_msg13 = Allow Internet Control Message Protocol (Ping etc.) firewall_msg14 = Activate UDP traffic filter firewallthread_msg1 = Dropped ICMP packet: firewallwebkonfig_msg1 = Erroneous input(s): firewallwebkonfig_msg2 = Source IP invalid firewallwebkonfig_msg3 = Source subnet mask empty or invalid firewallwebkonfig_msg4 = Dest. IP invalid firewallwebkonfig_msg5 = Dest. subnet mask empty or invalid firewallwebkonfig_msg6 = Port invalid firewallwebkonfig_msg7 = Subnet mask available, but no IP given firewallwebkonfig_msg8 = Firewall rule was NOT created! imageviewer_msg1 = File imageviewer_msg2 = Open peertopeeranwendung_msg1 = Member IP address: peertopeeranwendung_msg2 = Enter network peertopeeranwendung_msg3 = Enter an existing network. peertopeeranwendung_msg4 = List of connected neighbours. peertopeeranwendung_msg5 = Invalid IP address. peertopeeranwendung_msg6 = Identification peertopeeranwendung_msg7 = address peertopeeranwendung_msg8 = IP address peertopeeranwendung_msg9 = File peertopeeranwendung_msg10 = Size peertopeeranwendung_msg11 = Search item: peertopeeranwendung_msg12 = Search for substring peertopeeranwendung_msg13 = Search peertopeeranwendung_msg14 = Stop! peertopeeranwendung_msg15 = Stop current search peertopeeranwendung_msg16 = Clear list peertopeeranwendung_msg17 = Clear results peertopeeranwendung_msg18 = Download peertopeeranwendung_msg19 = Downloads selected file peertopeeranwendung_msg20 = Search peertopeeranwendung_msg21 = Files: peertopeeranwendung_msg22 = File peertopeeranwendung_msg23 = File type peertopeeranwendung_msg24 = Files peertopeeranwendung_msg25 = Maximal number of direct neighbours: peertopeeranwendung_msg26 = Save peertopeeranwendung_msg27 = Just numbers are allowed! peertopeeranwendung_msg28 = Settings peertopeeranwendung_msg29 = Network peertopeeranwendung_msg30 = Leave network peertopeeranwendung_msg31 = Clear list of connected neighbours. terminal_msg1 = root> terminal_msg2 = Unknown command! terminal_msg3 = Type 'help' to get a list of available commands. texteditor_msg2 = File texteditor_msg3 = New texteditor_msg4 = Open texteditor_msg5 = Save texteditor_msg6 = Save as texteditor_msg7 = Exit texteditor_msg8 = text texteditor_msg9 = File changed. Do you want to save? texteditor_msg10 = Sure? texteditor_msg11 = Close all tabs texteditor_msg12 = Close all other tabs texteditor_msg13 = Do you want to close it? texteditor_msg14 = New document webbrowser_msg2 = Start webserver_msg1 = Start webserver_msg2 = Stop webserver_msg3 = Virtual hosts: webserver_msg4 = Hostname webserver_msg5 = Subdirectory webserver_msg6 = Enable virtual hosts desktoppanel_msg1 = Software Installation desktoppanel_application=Home Screen installationsdialog_msg1 = Software Installation installationsdialog_msg2 = Apply changes installationsdialog_msg3 = Installed: installationsdialog_msg4 = Available: network_msg1 = IP address: network_msg2 = DNS server: network_msg3 = Gateway: network_msg4 = Netmask: network_msg5 = Save network_msg6 = Save changes network_msg7 = Invalid inputs! network_msg8 = Network status network_msg9 = MAC address: buttontabcomponent_msg1 = Close tab lauscherdialog_msg1 = Data exchange nachrichtentabelle_msg1 = Display layer nachrichtentabelle_msg2 = Network layer nachrichtentabelle_msg3 = Internet layer nachrichtentabelle_msg4 = Transport layer nachrichtentabelle_msg5 = Application layer nachrichtentabelle_msg6 = Select layer nachrichtentabelle_msg7 = Clear tables nachrichtentabelle_msg8 = Autoscroll nachrichtentabelle_msg9 = Export as text file nachrichtentabelle_msg10 = Local path through layers nachrichtentabelle_msg11 = Layer visualization nachrichtentabelle_msg12 = Visualization of broadcast messages not possible. jdhcpkonfiguration_msg1 = Lower bound of address jdhcpkonfiguration_msg2 = Upper bound of address jdhcpkonfiguration_msg3 = Netmask jdhcpkonfiguration_msg4 = Gateway jdhcpkonfiguration_msg5 = DNS server jdhcpkonfiguration_msg6 = Activate DHCP jdhcpkonfiguration_msg7 = OK jdhcpkonfiguration_msg8 = Manual configuration jdhcpkonfiguration_msg9 = Usually the same settings as configured for the host system
    \ will be used for the DHCP server as well.
    \ However, different settings are possible if this checkbox is selected. jdhcpkonfiguration_msg10 = Base Settings jdhcpkonfiguration_msg11 = Static Address Assignment jdhcpkonfiguration_msg12 = MAC Address jdhcpkonfiguration_msg13 = IP Address jdhcpkonfiguration_msg14 = Add jdhcpkonfiguration_msg15 = Remove jfirewalldialog_msg1 = Firewall settings jfirewalldialog_msg2 = Blocked sender: IP packets are not forwarded if sender address is not in one of the given address ranges. jfirewalldialog_msg3 = Address range: jfirewalldialog_msg4 = Add jfirewalldialog_msg5 = Delete jfirewalldialog_msg6 = Lower bound jfirewalldialog_msg7 = Upper bound jfirewalldialog_msg8 = Blocked recipients: IP packets are not forwarded if the destination address is not in one of the given address ranges. jfirewalldialog_msg9 = Address range: jfirewalldialog_msg10 = Blocked recipient ports: IP packets are not forwarded if the TCP/UDP port is blocked. jfirewalldialog_msg11 = TCP/UDP port: jfirewalldialog_msg12 = TCP/UDP port jfirewalldialog_msg13 = Activate firewall jfirewalldialog_msg14 = Sender jfirewalldialog_msg15 = Receiver jfirewalldialog_msg16 = TCP/UDP Port Rules jfirewalldialog_msg17 = Select network interfaces for which the firewall is activated! jfirewalldialog_msg18 = Network Interfaces jfirewalldialog_msg19 = Reject incoming TCP connections jfirewalldialog_msg20 = Incoming connections will be rejected for those interfaces \n\ with activated (selection above) firewall. Outgoing connections \n\ are not affected. jfirewalldialog_msg21 = Firewall Rules jfirewalldialog_msg22 = move up jfirewalldialog_msg23 = move down jfirewalldialog_msg24 = add new rule jfirewalldialog_msg25 = delete rule jfirewalldialog_msg26 = ID jfirewalldialog_msg27 = Source IP jfirewalldialog_msg28 = Netmask jfirewalldialog_msg29 = Dest. IP jfirewalldialog_msg30 = Netmask jfirewalldialog_msg31 = Protocol jfirewalldialog_msg32 = Action? jfirewalldialog_msg33 = accept jfirewalldialog_msg34 = drop jfirewalldialog_msg35 = Port jfirewalldialog_msg36 = Default action, if no rule matches: jfirewalldialog_msg37 = The following rules are evaluated from top to bottom. For the first matching rule the given action will be used.\nAll fields (except protocol and action) are optional. Empty fields will not be considered for packet filtering.\nIf an IP address is used, then the subnet mask has to be filled in as well! A subnet mask different to 255.255.255.255 can be used to address whole subnet ranges.\nUsing the default policy switch the approach can be toggled from blacklisting to whitelisting and vice versa. jfirewalldialog_msg38 = Activate firewall jfirewalldialog_msg39 = Firewall rules will only be applied if the firewall is activated. In the case of a not activated firewall no packet filtering will take place. jfirewalldialog_msg40 = Filter ICMP packets jfirewalldialog_msg41 = All ICMP packets, e.g., ping requests, will be dropped. jfirewalldialog_msg42 = only drop SYN packets jfirewalldialog_msg43 = New connection requests to blocked ports will be dropped.\nBack channels for allowed connection requests will be accepted. jfirewalldialog_msg44 = General jhostkonfiguration_msg1 = Name jhostkonfiguration_msg2 = New Server jhostkonfiguration_msg3 = IP Address jhostkonfiguration_msg4 = Netmask jhostkonfiguration_msg5 = Gateway jhostkonfiguration_msg6 = Domain Name Server jhostkonfiguration_msg7 = Use DHCP for configuration jhostkonfiguration_msg8 = DHCP server setup jhostkonfiguration_msg9 = MAC Address jhostkonfiguration_msg10 = Use IP address as name jhostkonfiguration_msg11 = Activate IP forwarding jhostkonfiguration_msg12 = Use MAC address as name jhostkonfiguration_msg13 = Wireless (Wi-Fi) jhostkonfiguration_msg14 = Cable (LAN) jhostkonfiguration_msg15 = Wireless Network (SSID): jhostkonfiguration_msg16 = Please select jmodemkonfiguration_msg1 = Name jmodemkonfiguration_msg2 = Wait for incoming connection request jmodemkonfiguration_msg3 = IP address jmodemkonfiguration_msg4 = Port jmodemkonfiguration_msg5 = Connect jmodemkonfiguration_msg6 = Disconnect jmodemkonfiguration_msg7 = Activate jmodemkonfiguration_msg8 = Disconnect/De-activate jswitchkonfiguration_msg1 = Name jswitchkonfiguration_msg2 = New Switch jswitchkonfiguration_msg3 = Display as Cloud jswitchkonfiguration_msg4 = Wi-Fi Name (SSID) jswitchkonfiguration_msg5 = Retention Time SAT Entries (Seconds) jvermittlungsrechnerkonfiguration_msg1 = Firewall setup jvermittlungsrechnerkonfiguration_msg2 = Name jvermittlungsrechnerkonfiguration_msg3 = Router jvermittlungsrechnerkonfiguration_msg4 = Firewall settings jvermittlungsrechnerkonfiguration_msg5 = No connection jvermittlungsrechnerkonfiguration_msg6 = Connected to jvermittlungsrechnerkonfiguration_msg7 = IP Address jvermittlungsrechnerkonfiguration_msg8 = Netmask jvermittlungsrechnerkonfiguration_msg9 = Gateway jvermittlungsrechnerkonfiguration_msg10 = NIC jvermittlungsrechnerkonfiguration_msg11 = Show all entries jvermittlungsrechnerkonfiguration_msg12 = New entry jvermittlungsrechnerkonfiguration_msg13 = Delete entry jvermittlungsrechnerkonfiguration_msg14 = Open as window jvermittlungsrechnerkonfiguration_msg15 = Forwarding table jvermittlungsrechnerkonfiguration_msg16 = No Connection jvermittlungsrechnerkonfiguration_msg17 = General jvermittlungsrechnerkonfiguration_msg18 = MAC Address jvermittlungsrechnerkonfiguration_msg19 = Note: For reorganising cable connections:\n \ 1. choose cable connection by clicking the button next to the interface name\n \ 2. choose new interface by clicking according button\n \n \ If the target interface is already occupied by another connection, then those cables will be swapped. jvermittlungsrechnerkonfiguration_msg20 = Close jvermittlungsrechnerkonfiguration_msg21 = Foreign components jvermittlungsrechnerkonfiguration_msg22 = Local interfaces jvermittlungsrechnerkonfiguration_msg23 = Manage connections jvermittlungsrechnerkonfiguration_msg24 = Add interface jvermittlungsrechnerkonfiguration_msg25 = At most 8 interfaces supported! jvermittlungsrechnerkonfiguration_msg26 = Automatic Routing jgatewayconfiguration_msg1 = Firewall setup jgatewayconfiguration_msg2 = Name jgatewayconfiguration_msg3 = Gateway jgatewayconfiguration_msg4 = Firewall settings jgatewayconfiguration_msg6 = Connected to jgatewayconfiguration_msg7 = IP Address jgatewayconfiguration_msg8 = Netmask jgatewayconfiguration_msg9 = Gateway jgatewayconfiguration_msg10 = NIC jgatewayconfiguration_msg16 = No Connection jgatewayconfiguration_msg17 = General jgatewayconfiguration_msg18 = MAC Address jgatewayconfiguration_msg19 = Port Mapping jgatewayconfiguration_msg21 = New entry jgatewayconfiguration_msg22 = Delete entry jportforwarding_msg1 = Valid entries: TCP, UDP, 6 (for TCP) and 17 (for UDP) jportforwarding_msg2 = Valid entries: Port numbers between 1 and 65535 jportforwarding_msg3 = Valid entries: IP-Addresses from the LAN scope jportforwarding_msg4 = Protocol jportforwarding_msg5 = Mapped Port jportforwarding_msg6 = LAN Address jportforwarding_msg7 = LAN Port jweiterleitungstabelle_msg1 = Clear jweiterleitungstabelle_msg2 = New line jweiterleitungstabelle_msg3 = Destination jweiterleitungstabelle_msg4 = Netmask jweiterleitungstabelle_msg5 = Next gateway jweiterleitungstabelle_msg6 = NIC framesoftwarewizard_msg1 = Software assistant framesoftwarewizard_msg2 = Back framesoftwarewizard_msg3 = Next framesoftwarewizard_msg4 = Step 1 of 3 framesoftwarewizard_msg5 = Exit framesoftwarewizard_msg6 = Step 2 of 3 framesoftwarewizard_msg7 = Step 3 of 3 framesoftwarewizard_msg8 = Finish panelcompiler_msg1 = Compile files for program panelcompiler_msg2 =

    Errors while building the program

    Compilation could not be completed.
    \ The reason might be that the wrong Java VM is used for execution (VM of the JDK is required).

    panelcompiler_msg3 =

    Program built successfully!

    panelcompiler_msg4 =

    Program could not be inserted

    panelverwaltung_msg1 = Build new software panelverwaltung_msg2 = Client program panelverwaltung_msg3 = Server program panelverwaltung_msg4 = Software name: panelverwaltung_msg5 = Class name: panelverwaltung_msg7 = Edit selected program (after changes restart of Filius necessary) panelverwaltung_msg8 = Delete entry panelverwaltung_msg9 = Save changes panelverwaltung_msg10 = Software name panelverwaltung_msg11 = Class panelverwaltung_msg12 = GUI class panelverwaltung_msg13 = Desktop icon dmtnfilechooser_msg1 = Folder up dmtnfilechooser_msg2 = File: dmtnfilechooser_msg3 = Action dmtnfilechooser_msg4 = Cancel dmtnfilechooser_msg5 = Save file dmtnfilechooser_msg6 = Save dmtnfilechooser_msg7 = Open file dmtnfilechooser_msg8 = Open dmtnfilechooser_msg9 = File; dmtnfilechooser_msg10 = Folder; dmtnfilechooser_msg11 = Folder: guicontainer_msg1 = Choose number of NIC of the router! guicontainer_msg2 = Number of NIC guievents_msg1 = No connector available guievents_msg2 = Modem guievents_msg3 = Show desktop guievents_msg4 = Show data exchange guievents_msg5 = Remove cable guievents_msg6 = Remove all cables guievents_msg7 = Delete guievents_msg8 = SAT table guievents_msg9 = MAC guievents_msg10 = Port guievents_msg11 = Configure guievents_msg12 = Components already connected guievents_msg13 = Colour guievents_msg14 = Font Style guievents_msg15 = Font Size guievents_msg16 = Normal guievents_msg17 = Large guievents_msg18 = Very Large guievents_msg19 = Normal guievents_msg20 = Italic guievents_msg21 = Bold guievents_msg22 = WAN port (external) already occupied! guievents_msg23 = LAN port (internal) already occupied! guievents_msg24 = WAN port (external) guievents_msg25 = LAN port (internal) guievents_msg26 = Wireless connection not possible guievents_msg27 = Last Update guievents_msg28 = Clear Table guievents_msg29 = NAT Table guievents_msg30 = Protocol guievents_msg31 = WAN Address guievents_msg32 = Port Mapping guievents_msg33 = LAN Address guievents_msg34 = LAN Port guievents_msg36 = Reset table guihilfe_msg1 = Help guihilfe_msg2 =

    Error

    Helpfiles not found. guimainmemu_msg1 = Open Existing Project (Ctrl+O) guimainmemu_msg2 = Save Current Project (Ctrl+S) guimainmemu_msg3 = Change to Design Mode (Ctrl+D) guimainmemu_msg4 = Change to Simulation Mode (Ctrl+R) guimainmemu_msg5 = Create New Project (Ctrl+N) guimainmemu_msg6 = Start Software Assistant (Ctrl+W) guimainmemu_msg7 = Show Help (Ctrl+H) guimainmemu_msg8 = Information About FILIUS (Ctrl+I) guimainmemu_msg9 = Do you really want to start a new project? \n All previous settings will be discarded. \n Also new applications will be removed. guimainmemu_msg10 = Continue? guimainmemu_msg11 = Error occured! guimainmemu_msg12 = XML file guimainmemu_msg13 = FILIUS project-file guimainmemu_msg14 = Change to Documentation Mode guimainmemu_msg15 = Current Simulation Velocity guimainmemu_msg16 = Adjust Simulation Velocity guimainmemu_msg17 = Do you want to overwrite the existing file? guimainmemu_msg18 = Settings settings_msg1 = Select Hardware Icon Theme settings_msg2 = Default settings_msg3 = Enterprise (experimental) settings_msg4 = Symbols (experimental) settings_msg5 = Layer visualization (experimental) infodialog_msg1 = Information infodialog_msg2 =

    FILIUS was developed as a student project at the Universität Siegen. (2006-2007)

    infodialog_msg3 = Students: infodialog_msg4 = Tutor: infodialog_msg5 = Maintenance and Further Development: infodialog_msg6 = Version infodialog_msg8 = Please report bugs to infodialog_msg9 = or infodialog_msg10 = Distribution under the terms and conditions of GNU General Public License (GPL) version 2 or 3 infodialog_msg11 = Contributors: hw_notebook_msg1 = Notebook hw_notebook_msg2 = Notebook hw_rechner_msg1 = Computer hw_rechner_msg2 = Computer hw_switch_msg1 = Switch / Wi-Fi hw_vermittlungsrechner_msg1 = Router hw_gateway_msg1 = Home Router hw_kabel_msg1 = Connection hw_verbindung_msg1 = One of the connectors is already reserved. sw_clientbaustein_msg1 = Error while connecting \n sw_clientbaustein_msg2 = Connection established sw_clientbaustein_msg3 = Disconnected sw_clientbaustein_msg4 = Error while connecting sw_clientbaustein_msg5 = Socket to sw_clientbaustein_msg6 = disconnected sw_serveranwendung_msg1 = Stop accepting connections. sw_serveranwendung_msg2 = Start accepting connections. sw_serveranwendung_msg3 = Server socket closed sw_serveranwendung_msg4 = Connection to sw_serveranwendung_msg5 = established sw_serveranwendung_msg6 = Server socket could not be initialized. sw_servermitarbeiter_msg1 = Socket to sw_servermitarbeiter_msg2 = closed sw_ipeintrag_msg1 = MAC address: sw_ipeintrag_msg2 = IP address: sw_dnsservermitarbeiter_msg1 = Request sw_dnsservermitarbeiter_msg2 = Send answer: sw_emailserver_msg1 = This account could not be deleted. Please check user and password! sw_pop3client_msg1 = Connection closed sw_smtpclient_msg1 = Connection to sw_smtpclient_msg2 = established sw_smtpclient_msg3 = disconnected sw_smtpclient_msg4 = Mail sent to sw_smtpmitarbeiter_msg1 = 220 Welcome to sw_smtpmitarbeiter_msg2 = 221 Server closed connection. sw_smtpmitarbeiter_msg3 = 250 OK sw_smtpmitarbeiter_msg4 = 250 Sender OK sw_smtpmitarbeiter_msg5 = 501 Syntax error - Parameter or argument wrong. sw_smtpmitarbeiter_msg6 = 250 Recipient OK sw_smtpmitarbeiter_msg7 = 501 Syntax error - Parameter or argument wrong. sw_smtpmitarbeiter_msg8 = 354 End data with . sw_smtpmitarbeiter_msg9 = 503 Wrong order of commands. sw_smtpmitarbeiter_msg10 = 250 Mail queued for delivery sw_smtpmitarbeiter_msg11 = 503 Wrong order of commands. sw_smtpmitarbeiter_msg12 = E-mail for sw_smtpmitarbeiter_msg13 = saved in mailbox. sw_smtpmitarbeiter_msg14 = Error while sending sw_firewall_msg1 = Receiver blocking - sw_firewall_msg2 = Blocked because of port: sw_firewall_msg3 = Connection request interrupted! (SYN = true) sw_firewall_msg4 = IP rule is added: sw_firewall_msg5 = Port rule is added: sw_firewall_msg6 = Delete IP rule: sw_firewall_msg7 = Delete port rule. sw_firewall_msg8 = Firewall rule matched: sw_firewall_msg9 = No firewall rule matched. Using default policy: sw_firewallweblog_msg1 = \n\t\n\t\tLog-data set entry of the firewall sw_firewallweblog_msg2 = \n\t\n\t\n\t\t

    Log-data set entry of the firewall

    sw_vermittlungweb_msg1 = This table is generated and updated by the Routing Information Protocol (RIP). sw_vermittlungweb_msg3 = unlimited sw_vermittlungweb_msg4 = This table was created from the IP configuration of the router and manually added entries of the forwarding table. sw_vermittlungweb_msg5 = Routing/Forwarding Table sw_vermittlungweb_msg6 = Routing Table sw_fileexplorer_msg1 = File could not be imported. sw_fileexplorer_msg2 = File successfully imported! sw_fileexplorer_msg3 = File does not exist or is too large (> 150KB) sw_fileimporter_msg1 = File could not be imported. sw_fileimporter_msg2 = File successfully imported! sw_fileimporter_msg3 = Close sw_terminal_msg1 = Application sw_terminal_msg2 = is already installed.\n sw_terminal_msg3 = successfully installed\n sw_terminal_msg4 = IP address . . .: sw_terminal_msg5 = Netmask. . . . .: sw_terminal_msg6666= Physical address: sw_terminal_msg6 = Standard gateway: sw_terminal_msg27= DNS server . . .: sw_terminal_msg7 = | Destination | Netmask | Gateway | Interface |\n \ -------------------------------------------------------------------------\n sw_terminal_msg8 = Directory is empty\n sw_terminal_msg9 = Directory sw_terminal_msg10 = \nNumber of directories: sw_terminal_msg11 = \nNumber of files: sw_terminal_msg12 = Error in Touch\n sw_terminal_msg13 = File created\n sw_terminal_msg14 = File does already exist\n sw_terminal_msg15 = Please enter a file name!\n sw_terminal_msg16 = Error while creating new folder\n sw_terminal_msg17 = Folder is created\n sw_terminal_msg18 = Folder does already exist\n sw_terminal_msg19 = Please enter folder name!\n sw_terminal_msg20 = Directory does not exist\n sw_terminal_msg21 = Folder changed! (..)\n sw_terminal_msg22 = Folder changed!\n sw_terminal_msg23 = Test\n sw_terminal_msg24 = Successfully built! sw_terminal_msg25 = List of available commands:\n \ arp show or manipulate Address Resolution Protocol (ARP) table\n \ arpsend send an ARP reply\n \ cat / type show file contents \n \ cd change directory\n \ cls clear screen \n \ copy / cp copy file\n \ del / rm delete file/directory\n \ dir / ls show list of files in current directory\n \ exit exit terminal application\n \ help show this list of commands\n \ host resolve hostname to IP address\n \ ipconfig show network configuration\n \ mkdir create directory\n \ move / mv move/rename file\n \ netstat show list of connections\n \ nslookup resolve hostname to IP address\n \ ping test connection to other computer\n \ pwd print working directory\n \ route show routing table\n \ tcpdump show data exchange\n \ touch create file\n \ traceroute analyse hops of connecting path \n sw_terminal_arp = arp - show or manipulate Address Resolution Protocol (ARP) table sw_terminal_arpsend = arpsend - send an ARP reply sw_terminal_cat = cat / type - show file contents sw_terminal_cd = cd - change directory sw_terminal_copy = copy / cp - copy file sw_terminal_del = del / rm - delete file/directory sw_terminal_dir = dir / ls - show list of files in current directory sw_terminal_exit = exit - exit terminal application sw_terminal_help = help - show the list of commands or info on one command sw_terminal_host = host - resolve hostname to IP address sw_terminal_ipconfig = ipconfig - show network configuration sw_terminal_mkdir = mkdir - create directory sw_terminal_move = move / mv - move/rename file sw_terminal_netstat = netstat - show list of connections sw_terminal_nslookup = nslookup - resolve hostname to IP address sw_terminal_ping = ping - test connection to other computer sw_terminal_pwd = pwd - print working directory sw_terminal_route = route - show routing table sw_terminal_tcpdump = tcpdump - show data exchange sw_terminal_touch = touch - create file sw_terminal_traceroute = traceroute - analyse hops of connecting path sw_terminal_usage_arp = Usage: arp [-d []] [-a ] \n \ -d \t delete ARP entry for IP address or reset ARP table \n \ -a \t show ARP entry for specified IP address sw_terminal_usage_arpsend = Usage: arpsend \n \ address to be published for own MAC address\n \ target the reply must be sent to sw_terminal_usage_cat = Usage: cat sw_terminal_usage_cd = Usage: cd sw_terminal_usage_copy = Usage: copy sw_terminal_usage_del = Usage: del \n \ relative path to current directory sw_terminal_usage_dir = Usage: dir sw_terminal_usage_exit = Usage: exit sw_terminal_usage_help = Usage: help [] sw_terminal_usage_host = Usage: host \n \ the domain for which to look up A and MX records sw_terminal_usage_ipconfig = Usage: ipconfig sw_terminal_usage_mkdir = Usage: mkdir \n \ name of new directory either as absolute path \n \ or relative path sw_terminal_usage_move = Usage: move sw_terminal_usage_netstat = Usage: netstat sw_terminal_usage_nslookup = Usage: nslookup \n \ the domain for which to look up A (and related NS) records sw_terminal_usage_ping = Usage: ping \n \ the remote address as IP address or host name sw_terminal_usage_pwd = Usage: pwd sw_terminal_usage_route = Usage: route sw_terminal_usage_tcpdump = Usage: tcpdump sw_terminal_usage_touch = Usage: touch sw_terminal_usage_traceroute = Usage: traceroute \n \ the remote address as IP address or host name sw_terminal_msg26 = Use command 'help' to show list of available commands \n \ or 'help ' for further information on the command. sw_terminal_msg28 = has the IP address sw_terminal_msg29 = Invalid host string!\nOnly correct URLs without paths are allowed.\n sw_terminal_msg30 = Hostname could not be resolved!\nPossibly no DNS server is available.\n sw_terminal_msg31 = Timeout! No response received during waiting period. sw_terminal_msg33 = File successfully copied\n sw_terminal_msg34 = File could not be copied\n sw_terminal_msg35 = File successfully moved/renamed\n sw_terminal_msg36 = File could not be moved/renamed\n sw_terminal_msg37 = File successfully deleted\n sw_terminal_msg38 = File could not be deleted\n sw_terminal_msg39 = \n sw_terminal_msg45 = packet statistics sw_terminal_msg46 = packet(s) transmitted sw_terminal_msg47 = packet(s) received sw_terminal_msg48 = packet loss sw_terminal_msg49 = | Proto | Local Address | Remote Address | State |\n sw_terminal_msg50 = | Internet Address | Physical Address |\n sw_terminal_msg52 = Destination not reachable sw_terminal_msg53 = Ping to Broadcast is not supported. sw_terminal_msg54 = No such file sw_terminal_msg55 = Cancel with Ctrl+C sw_terminal_msg57 = \n\n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ sSSs .S S. .S .S S. sSSs \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ d%%SP .SS SS. .SS .SS SS. d%%SP \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ d%S' S%S S%S S%S S%S S%S d%S' \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S%S S%S S%S S%S S%S S%S S%| \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S S&S S&S S&S S&S S&S S&S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S_Ss S&S S&S S&S S&S S&S Y&Ss \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S~SP S&S S&S S&S S&S S&S `S&&S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S S&S S&S S&S S&S S&S `S*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*b S*S S*b S*S S*b d*S l*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S S*S. S*S S*S. .S*S .S*P \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S SSSbs S*S SSSbs_sdSSS sSS*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S YSSP S*S YSSP~YSSY YSS' \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ SP SP SP \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ Y Y Y \n\n sw_terminal_msg58 = mail is handled by sw_terminal_trace_msg2 = Error: could not connect to DNS server. sw_terminal_trace_msg3 = Error: domain could not be resolved. sw_terminal_trace_msg4 = trace route to %s started (max. %s hops). sw_terminal_trace_msg5 = trace route to %s (%s) started (max. %s hops). sw_terminal_trace_msg6 = %s could not be reached via %s hops. sw_terminal_trace_msg7 = Analysis interrupted! sw_terminal_trace_msg8 = Error: response from %s was "ICMP Network Unreachable" sw_terminal_trace_msg9 = Error: response from %s was "ICMP Host Unreachable" sw_terminal_trace_msg10 = Error: response from %s was "ICMP Destination Unreachable" sw_terminal_trace_msg11 = %s was reached with one hop. sw_terminal_trace_msg12 = %s was reached with %s hops. sw_terminal_trace_msg13 = Error: next hop could not be processed. sw_switchfirmware_msg1 = Port sw_serversocket_msg1 = Socket at port sw_serversocket_msg2 = already exists sw_socket_msg1 = Target does not exist! sw_tcpsocket_msg1 = Error while establishing server connection. sw_tcpsocket_msg2 = Timeout while connecting sw_tcpsocket_msg3 = Active opening of socket failed. sw_tcpsocket_msg5 = Error while setup server connection. sw_tcpsocket_msg6 = Connection could not be established! sw_tcpsocket_msg7 = Connection was interrupted sw_tcpsocket_msg8 = Timeout while sending a message sw_tcpsocket_msg9 = No connection established! sw_tcpsocket_msg10 = Timeout while receiving a message sw_transportprotokoll_msg1 = Port sw_transportprotokoll_msg2 = does not exist. sw_transportprotokoll_msg3 = Service not available / not yet started sw_ip_msg1 = ARP protocol : Searched IP sw_ip_msg2 = could not be found - target MAC address: sw_ip_msg3 = - Gateway: sw_ip_msg4 = Error at sw_ip_msg5 = There is no entry on the forwarding table which is\napplicable for the target address sw_ip_msg6 = ! sw_weiterleitungstabelle_msg1 = Manual entries sw_httpnachricht_msg1 = Information sw_httpnachricht_msg2 = Successful operation sw_httpnachricht_msg3 = Relocation sw_httpnachricht_msg4 = Client error sw_httpnachricht_msg5 = Server error sw_webbrowser_msg1 = Error

    Server does not exist!

    sw_webbrowser_msg2 = ErrorError rp_lauscher_msg1 = No. rp_lauscher_msg2 = Time rp_lauscher_msg3 = Source rp_lauscher_msg4 = Destination rp_lauscher_msg5 = Protocol rp_lauscher_msg6 = Layer rp_lauscher_msg7 = Comment / Details rp_lauscher_msg8 = Network rp_lauscher_msg9 = Internet rp_lauscher_msg10 = Transport rp_lauscher_msg11 = Application rp_lauscher_msg12 = Protocol rp_lauscher_msg13 = Search for MAC rp_lauscher_msg14 = MAC is rp_filiusclassloader_msg1 = Class rp_filiusclassloader_msg2 = could not be loaded. rp_szenarioverwaltung_msg5 = File not found or corrupted. hilfedatei_entwurf = design_mode_en_GB.html hilfedatei_simulation = simulation_mode_en_GB.html hilfedatei_documentation = documentation_mode_en_GB.html modemfirmware_msg1 = Target could not be found. modemfirmware_msg2 = Error occurred during connecting. jsidebar_tooltip_ipAddress = IP address/netmask (MAC) jsidebar_tooltip_gateway = Gateway jsidebar_tooltip_dnsServer = DNS server report_overview = Overview report_component_config = Components report_base_config = Base Configuration report_nics = Network Interfaces report_apps = Applications report_routing_table = Routing/Forwarding Table report_network_traffic = Data Exchange report_title = Filius Documentation report_dns_rr = Configuration DNS Server report_domain = Domain Name report_type = Type report_data = Data simulation_control_drop_packets = Drop Packets schichten_lokaler_weg_msg2 = Layer schichten_lokaler_weg_msg3 = Protocol schichten_lokaler_weg_msg4 = Data schichten_lokaler_weg_msg5 = Ethernet schichten_lokaler_weg_msg6 = IP address: schichten_lokaler_weg_msg7 = Netmask: schichten_lokaler_weg_msg8 = MAC address: schichten_lokaler_weg_msg9 = Gateway: schichten_lokaler_weg_msg10 = DNS Server: schichten_allgemein_msg1 = next schichten_allgemein_msg2 = back schichten_globaler_weg_msg1 = Layer visualizationfilius-2.9.4+ds/src/main/resources/filius/messages/MessagesBundle_fr_FR.properties000066400000000000000000001257501506574241500303720ustar00rootroot00000000000000main_msg1 = Les derni\u00e8res modifications n\u2019ont pas \u00e9t\u00e9 sauvegard\u00e9es.\n\ Voulez-vous vraiment fermer l\u2019application\u00a0? main_msg2 = Fermeture de FILIUS main_msg3 = Ouvrir fichier main_msg4 = Enregistrer le fichier main_dlg_OK = OK main_dlg_YES = Oui main_dlg_NO = Non main_dlg_CANCEL = Annuler languageSelection_msg1 = Slectionner la langue languageSelection_msg2 = FILIUS doit tre redmarr pour appliquer les modifications. languageSelection_msg3 = Confirmer languageSelection_msg4 = Confirmer et fermer le programme languageSelection_msg5 = Annuler splashscreen_msg1 = Licence\u00a0: GNU General Public License Version 2 ou 3 docusidebar_msg1 = Texte docusidebar_msg3 = Bloc structurel docusidebar_msg5 = Exporter docusidebar_msg6 = Exporter comme image docusidebar_msg7 = Rapport docusidebar_msg8 = Exporter un rapport comme PDF clientbaustein_msg1 = Adresse du serveur\u00a0: clientbaustein_msg2 = Port du serveur\u00a0: clientbaustein_msg3 = Connecter clientbaustein_msg4 = Message\u00a0: clientbaustein_msg5 = Envoyer clientbaustein_msg6 = D\u00e9connecter serverbaustein_msg1 = Port\u00a0: serverbaustein_msg2 = Arr\u00eater serverbaustein_msg3 = D\u00e9marrer dnsserver_msg1 = D\u00e9marrer dnsserver_msg2 = Adresse (A) dnsserver_msg3 = \u00c9change de messages (MX) dnsserver_msg4 = Nom d\u2019h\u00f4te / de domaine\u00a0: dnsserver_msg5 = Adresse IP\u00a0: dnsserver_msg6 = Ajouter dnsserver_msg7 = Supprimer la s\u00e9lection dnsserver_msg8 = Nom d\u2019h\u00f4te / de domaine dnsserver_msg9 = Adresse IP dnsserver_msg10 = Domaine de messagerie\u00a0: dnsserver_msg11 = Nom DNS du serveur\u00a0: dnsserver_msg12 = Domaine de messagerie dnsserver_msg13 = Nom DNS du serveur dnsserver_msg14 = Arr\u00eater dnsserver_msg15 = Serveur de noms (NS) dnsserver_msg16 = Domaine\u00a0: dnsserver_msg17 = Serveur de noms\u00a0: dnsserver_msg18 = Domaine dnsserver_msg19 = Serveur de noms dnsserver_msg20 = Activer la r\u00e9solution r\u00e9cursive de domaine dnsserver_msg21 = Saisie incorrecte\u00a0! Exemple valide\u00a0:\n\ > Domainname\u00a0: www.filius.de > Adresse IP\u00a0: 192.168.0.10\n\ dnsserver_msg22 = Saisie incorrecte\u00a0! Exemple valide\u00a0:\n\ > Domaine\u00a0: filius.de\n\ > Serveur de noms\u00a0: dns.filius.de dnsserver_msg23 = Saisie incorrecte\u00a0! Exemple valide\u00a0:\n\ > Domaine de messagerie\u00a0: filius.de\n\ > Nom DNS du serveur\u00a0: mail.filius.de emailanwendung_msg1 = Exp\u00e9diteur emailanwendung_msg2 = Sujet emailanwendung_msg3 = Destinataire emailanwendung_msg4 = Bienvenue dans le client de messagerie emailanwendung_msg5 = Re\u00e7us emailanwendung_msg6 = Envoy\u00e9s emailanwendung_msg7 = Relever emailanwendung_msg8 = \u00c9crire emailanwendung_msg9 = R\u00e9pondre emailanwendung_msg10 = Supprimer les messages s\u00e9lectionn\u00e9s emailanwendung_msg11 = G\u00e9rer le compte emailanwendung_msg12 = R\u00e9diger emailanwendung_msg13 = Envoyer emailanwendung_msg14 = Pour\u00a0: emailanwendung_msg15 = Copie \u00e0\u00a0: emailanwendung_msg16 = Copie cach\u00e9e \u00e0\u00a0: emailanwendung_msg17 = Sujet\u00a0: emailanwendung_msg18 = RE\u00a0: emailanwendung_msg19 = a \u00e9crit\u00a0: emailanwendung_msg20 = L\u2019une des adresses est incorrecte\u00a0! emailanwendung_msg21 = Il faut saisir l\u2019adresse d\u2019au moins un destinataire\u00a0! emailanwendung_msg22 = Envoyer le message emailanwendung_msg23 = Relever les messages emailanwendung_msg24 = Relever les messages du serveur POP3 pour ( emailanwendung_msg25 = G\u00e9rer le compte emailanwendung_msg26 = Adresse \u00e9lectronique\u00a0: emailanwendung_msg27 = Serveur POP3\u00a0: emailanwendung_msg28 = Port POP3\u00a0: emailanwendung_msg29 = Serveur SMTP\u00a0: emailanwendung_msg30 = Port SMTP\u00a0: emailanwendung_msg31 = Identifiant\u00a0: emailanwendung_msg32 = mot de passe\u00a0: emailanwendung_msg33 = Enregistrer emailanwendung_msg34 = Nom emailanwendung_msg35 = Pr\u00e9nom emailanwendung_msg36 = L\u2019un des champs est mal renseign\u00e9\u00a0! emailanwendung_msg37 = Annuler emailanwendung_msg38 = Cr\u00e9er un compte emailanwendung_msg39 = Oui emailanwendung_msg40 = Annuler emailanwendung_msg41 = Voulez-vous vraiment supprimer le compte actuel\u00a0? emailanwendung_msg42 = Demande de confirmation emailanwendung_msg43 = Supprimer le message emailanwendung_msg44 = Param\u00e9trer le compte emailanwendung_msg45 = Nom\u00a0: emailanwendung_msg46 = Saisie erron\u00e9e ou incompl\u00e8te\u00a0! emailanwendung_msg47 = Aucun compte n\u2019est encore disponible. emailserver_msg1 = D\u00e9marrer emailserver_msg2 = Les seveurs POP3 et SMTP ont \u00e9t\u00e9 d\u00e9marr\u00e9s. emailserver_msg3 = Les seveurs POP3 et SMTP ont \u00e9t\u00e9 arr\u00eat\u00e9s. emailserver_msg4 = Domaine de messagerie\u00a0: emailserver_msg5 = D\u00e9finir le domaine emailserver_msg6 = D\u00e9finir le domaine seulement lorsque le serveur est arr\u00eat\u00e9\u00a0! emailserver_msg7 = Le domaine de messagerie a \u00e9t\u00e9 r\u00e9initialis\u00e9. emailserver_msg8 = Supprimer le compte s\u00e9lectionn\u00e9 emailserver_msg9 = Oui emailserver_msg10 = Annuler emailserver_msg11 = Voulez-vous vraiment supprimer le compte ' emailserver_msg12 ='\u00a0? emailserver_msg13 = Demande de confirmation emailserver_msg14 = Adresse \u00e9lectronique emailserver_msg15 = Nombre de messages emailserver_msg16 = Identifiant\u00a0: emailserver_msg17 = Pr\u00e9nom et nom\u00a0: emailserver_msg18 = Mot de passe\u00a0: emailserver_msg19 = Nouveau compte emailserver_msg20 = Nom emailserver_msg21 = Pr\u00e9nom emailserver_msg22 = Le compte emailserver_msg23 = a \u00e9t\u00e9 cr\u00e9\u00e9. emailserver_msg24 = L\u2019identifiant ne peut contenir d\u2019espace\u00a0! emailserver_msg25 = Saisissez un identifiant et un mot de passe\u00a0! emailserver_msg26 = Nouveau compte emailserver_msg27 = Liste des comptes emailserver_msg28 = Journal emailserver_msg29 = Arr\u00eater fileexplorer_msg1 = Actualiser fileexplorer_msg2 = Importer fileexplorer_msg3 = Nouveau dossier fileexplorer_msg4 = Supprimer fileexplorer_msg5 = Couper fileexplorer_msg6 = Copier fileexplorer_msg7 = Coller fileexplorer_msg8 = Renommer fileexplorer_msg9 = Nouveau nom\u00a0: fileexplorer_msg10 = Fichier fileexplorer_msg11 = Dossier fileexplorer_msg12 = Importer un fichier fileexplorer_msg13 = Fichier local\u00a0: fileexplorer_msg14 = Parcourir fileexplorer_msg15 = Importer le fichier fileexplorer_msg16 = S\u00e9lectionnez un fichier source et sp\u00e9cifiez un nom\u00a0! fileexplorer_msg17 = S\u00e9lectionnez un dossier\u00a0! fileexplorer_msg18 = En \u00eates-vous s\u00fbr\u00a0? fileexplorer_msg19 = Dossier de destination invalide! fileimporter_msg1 = Fichier local\u00a0: fileimporter_msg2 = Nouveau nom\u00a0: fileimporter_msg3 = Parcourir fileimporter_msg4 = S\u00e9lectionner fileimporter_msg5 = Importer le fichier fileimporter_msg6 = S\u00e9lectionnez un fichier source et sp\u00e9cifiez un nom\u00a0! fileimporter_msg7 = S\u00e9lectionnez un dossier\u00a0! fileimporter_msg8 = File-Importer version 0.1 firewall_msg1 = Activer le parefeu firewall_msg2 = Le parefeu bloque tous les paquets IP entrants, lorsqu\u2019il est activ\u00e9. \ Les exceptions doivent \u00eatre donn\u00e9es explicitement. Les r\u00e8gles sp\u00e9cifient \ les ports TCP/UDP qui doivent \u00eatre ouverts et pour quel type de source \ (poste du r\u00e9seau local ou tous les postes). firewall_msg3 = Port/protocol ouvert\u00a0: firewall_msg4 = Tout le r\u00e9seau firewall_msg5 = R\u00e9seau local uniquement firewall_msg6 = Ajouter firewall_msg7 = Supprimer firewall_msg8 = Port firewall_msg9 = Source firewall_msg10 = Configuration firewall_msg11 = Journal firewall_msg12 = R\u00e9seau local uniquement firewall_msg13 = Autoriser le protocole ICMP (Ping, etc.) firewall_msg14 = Activer le filtre pour UDP firewallthread_msg1 = Paquet ICMP rejet\u00e9\u00a0: firewallwebkonfig_msg1 = Saisie(s) incorrecte(s)\u00a0: firewallwebkonfig_msg2 = Adresse IP de la source incorrecte firewallwebkonfig_msg3 = Masque de la source manquant ou incorrect firewallwebkonfig_msg4 = Adresse IP de la destination incorrecte firewallwebkonfig_msg5 = Masque de la destination manquant ou incorrect firewallwebkonfig_msg6 = Port incorrect firewallwebkonfig_msg7 = Masque sans adresse IP associ\u00e9e firewallwebkonfig_msg8 = La r\u00e8gle n\u2019a pas \u00e9t\u00e9 cr\u00e9\u00e9e\u00a0! imageviewer_msg1 = Fichier imageviewer_msg2 = Ouvrir peertopeeranwendung_msg1 = Adresse IP d\u2019un membre du r\u00e9seau P2P\u00a0: peertopeeranwendung_msg2 = Rejoindre le r\u00e9seau peertopeeranwendung_msg3 = Essaie de se connecter \u00e0 un r\u00e9seau P2P \u00e0 partir de l\u2019adresse IP d\u2019un membre. peertopeeranwendung_msg4 = Liste des membres connect\u00e9s\u00a0: peertopeeranwendung_msg5 = Adresse IP incorrecte peertopeeranwendung_msg6 = Identification peertopeeranwendung_msg7 = Adresse peertopeeranwendung_msg8 = Adresse IP peertopeeranwendung_msg9 = Nom du fichier peertopeeranwendung_msg10 = Taille peertopeeranwendung_msg11 = Mot-cl\u00e9\u00a0: peertopeeranwendung_msg12 = Rechercher une chaine de caract\u00e8res peertopeeranwendung_msg13 = Rechercher peertopeeranwendung_msg14 = Arr\u00eater peertopeeranwendung_msg15 = Arr\u00eater la recherche en cours peertopeeranwendung_msg16 = Vider la liste peertopeeranwendung_msg17 = Effacer les r\u00e9sultats peertopeeranwendung_msg18 = T\u00e9l\u00e9charger peertopeeranwendung_msg19 = T\u00e9l\u00e9charger les fichiers s\u00e9lectionn\u00e9s peertopeeranwendung_msg20 = Rechercher peertopeeranwendung_msg21 = Fichiers du dossier /peer2peer\u00a0: peertopeeranwendung_msg22 = Nom peertopeeranwendung_msg23 = Type peertopeeranwendung_msg24 = Fichiers partag\u00e9s peertopeeranwendung_msg25 = Nombre maximal de voisins directs\u00a0: peertopeeranwendung_msg26 = Enregistrer peertopeeranwendung_msg27 = Seuls des nombres sont autoris\u00e9s\u00a0! peertopeeranwendung_msg28 = Param\u00e8tres peertopeeranwendung_msg29 = R\u00e9seau peertopeeranwendung_msg30 = Quitter le r\u00e9seau peertopeeranwendung_msg31 = Supprimer la liste des membres terminal_msg1 = root> terminal_msg2 = Commande inconnue\u00a0! terminal_msg3 = Saisir 'help' pour obtenir la liste des commandes reconnues. texteditor_msg2 = Fichier texteditor_msg3 = Nouveau texteditor_msg4 = Ouvrir texteditor_msg5 = Enregistrer texteditor_msg6 = Enregistrer sous texteditor_msg7 = Quitter texteditor_msg8 = text texteditor_msg9 = Voulez-vous enregistrer les derni\u00e8res modifications\u00a0? texteditor_msg10 = En \u00eates-vous s\u00fbr\u00a0? texteditor_msg11 = Fermer tous les onglets texteditor_msg12 = Fermer tous les autres onglets texteditor_msg13 = Voulez-vous fermer\u00a0? texteditor_msg14 = Nouveau document webbrowser_msg2 = Afficher webserver_msg1 = D\u00e9marrer webserver_msg2 = Arr\u00eater webserver_msg3 = H\u00f4tes virtuels\u00a0: webserver_msg4 = Nom d\u2019h\u00f4te webserver_msg5 = Sous-dossier webserver_msg6 = Activer les h\u00f4tes virtuels desktoppanel_msg1 = Installation des logiciels desktoppanel_application=cran d'accueil installationsdialog_msg1 = Installation des logiciels installationsdialog_msg2 = Appliquer les modifications installationsdialog_msg3 = Install\u00e9s\u00a0: installationsdialog_msg4 = Disponibles\u00a0: network_msg1 = Adresse IP\u00a0: network_msg2 = Serveur DNS\u00a0: network_msg3 = Passerelle\u00a0: network_msg4 = Masque\u00a0: network_msg5 = Enregistrer network_msg6 = Enregistrer les modifications network_msg7 = Une ou plusieurs saisies sont incorrectes\u00a0! network_msg8 = \u00c9tat du r\u00e9seau network_msg9 = Adresse MAC\u00a0: buttontabcomponent_msg1 = Fermer l\u2019onglet lauscherdialog_msg1 = \u00c9changes de donn\u00e9es nachrichtentabelle_msg1 = Affichage des couches nachrichtentabelle_msg2 = Couche r\u00e9seau nachrichtentabelle_msg3 = Couche internet nachrichtentabelle_msg4 = Couche transport nachrichtentabelle_msg5 = Couche application nachrichtentabelle_msg6 = S\u00e9lectionner les couches nachrichtentabelle_msg7 = Vider les tables nachrichtentabelle_msg8 = D\u00e9filement automatique nachrichtentabelle_msg9 = Exporter comme texte nachrichtentabelle_msg10 = Chemin local travers les couches nachrichtentabelle_msg11 = Visualisation des couches nachrichtentabelle_msg12 = Impossible d'afficher le chemin global des messages de diffusion. jdhcpkonfiguration_msg1 = D\u00e9but de plage jdhcpkonfiguration_msg2 = Fin de plage jdhcpkonfiguration_msg3 = Masque jdhcpkonfiguration_msg4 = Passerelle jdhcpkonfiguration_msg5 = Serveur DNS jdhcpkonfiguration_msg6 = Activer le service DHCP jdhcpkonfiguration_msg7 = OK jdhcpkonfiguration_msg8 = Configuration manuelle jdhcpkonfiguration_msg9 = En g\u00e9n\u00e9ral, le serveur DHCP utilise les m\u00eames param\u00e8tres que ceux de l\u2019h\u00f4te.
    \ Pour utiliser des param\u00e8tres distincts, cocher cette case. jdhcpkonfiguration_msg10 = Param\u00e8tres de base jdhcpkonfiguration_msg11 = Adressage statique jdhcpkonfiguration_msg12 = Adresse MAC jdhcpkonfiguration_msg13 = Adresse IP jdhcpkonfiguration_msg14 = Ajouter jdhcpkonfiguration_msg15 = Supprimer jfirewalldialog_msg1 = Configuration du parefeu jfirewalldialog_msg2 = Exp\u00e9diteur bloqu\u00e9\u00a0: les paquets IP ne seront pas transmis lorsque les exp\u00e9diteurs appartiennent \u00e0 l\u2019une des plages d\u2019adresses sp\u00e9cifi\u00e9es. jfirewalldialog_msg3 = Plage d\u2019adresses\u00a0: jfirewalldialog_msg4 = Ajouter jfirewalldialog_msg5 = Supprimer jfirewalldialog_msg6 = D\u00e9but de plage jfirewalldialog_msg7 = Fin de plage jfirewalldialog_msg8 = Destinataire bloqu\u00e9\u00a0: les paquets IP ne seront pas transmis lorsque les destinataires appartiennent \u00e0 l\u2019une des plages d\u2019adresses sp\u00e9cifi\u00e9es. jfirewalldialog_msg9 = Plage d\u2019adresses\u00a0: jfirewalldialog_msg10 = Ports destinataires bloqu\u00e9s\u00a0: les paquets IP ne seront pas transmis lorsque le port TCP/UDP de destination est bloqu\u00e9. jfirewalldialog_msg11 = Port TCP/UDP\u00a0: jfirewalldialog_msg12 = Port TCP/UDP jfirewalldialog_msg13 = Activer le parefeu jfirewalldialog_msg14 = Exp\u00e9diteur jfirewalldialog_msg15 = Destinataire jfirewalldialog_msg16 = R\u00e8gles des ports TCP/UDP jfirewalldialog_msg17 = S\u00e9lectionner les interfaces r\u00e9seau pour lesquelles le parefeu doit \u00eatre actif. jfirewalldialog_msg18 = Interfaces r\u00e9seau jfirewalldialog_msg19 = Rejeter les connexions TCP entrantes jfirewalldialog_msg20 = Sur les interfaces pour lesquelles le parefeu est actif (liste ci-dessus)\u00a0:\n\ - toutes les requ\u00eates de connexions entrantes seront rejet\u00e9es.\n\ - toutes les requ\u00eates de connexions sortantes seront accept\u00e9es. jfirewalldialog_msg21 = R\u00e8gles du parefeu jfirewalldialog_msg22 = Remonter jfirewalldialog_msg23 = Descendre jfirewalldialog_msg24 = Nouvelle r\u00e8gle jfirewalldialog_msg25 = Supprimer la r\u00e8gle s\u00e9lectionn\u00e9e jfirewalldialog_msg26 = ID jfirewalldialog_msg27 = IP source jfirewalldialog_msg28 = Masque jfirewalldialog_msg29 = IP destination jfirewalldialog_msg30 = Masque jfirewalldialog_msg31 = Protocole jfirewalldialog_msg32 = Action jfirewalldialog_msg33 = accepter jfirewalldialog_msg34 = rejeter jfirewalldialog_msg35 = Port jfirewalldialog_msg36 = R\u00e8gle par d\u00e9faut (appliqu\u00e9e si aucune r\u00e8gle de la liste n\u2019est satisfaite)\u00a0: jfirewalldialog_msg37 = Les r\u00e8gles sont parcourues du haut vers le bas de la liste. Seule la premi\u00e8re r\u00e8gle satisfaite rencontr\u00e9e est appliqu\u00e9e.\n\ Pour chaque r\u00e8gle, tous les champs (except\u00e9 Protocole et Action) sont optionnels.\nLes champs non renseign\u00e9s ne sont pas pris en compte par le filtrage.\n\ Si une adresse IP est renseign\u00e9e, le masque correspondant doit l\u2019\u00eatre \u00e9galement.\nTout masque diff\u00e9rent de 255.255.255.255 permet de repr\u00e9senter un sous-r\u00e9seau. jfirewalldialog_msg38 = Activer le parefeu jfirewalldialog_msg39 = \u00a0 Les r\u00e8gles ne sont appliqu\u00e9es que lorsque le parefeu est actif. jfirewalldialog_msg40 = Filtrer les paquets ICMP jfirewalldialog_msg41 = \u00a0 Tous les paquets ICMP seront rejet\u00e9s (ceux d\u2019un ping, par exemple). jfirewalldialog_msg42 = Ne filtrer que les paquets SYN jfirewalldialog_msg43 = \u00a0 Les requ\u00eates vers les ports bloqu\u00e9s seront rejet\u00e9es.\n\ \u00a0 Le canal de retour pour les requ\u00eates de connexion autoris\u00e9es sera ouvert. jfirewalldialog_msg44 = G\u00e9n\u00e9ral jhostkonfiguration_msg1 = Nom jhostkonfiguration_msg2 = Nouvel ordinateur jhostkonfiguration_msg3 = Adresse IP jhostkonfiguration_msg4 = Masque jhostkonfiguration_msg5 = Passerelle jhostkonfiguration_msg6 = Serveur DNS jhostkonfiguration_msg7 = Adressage automatique par serveur DHCP jhostkonfiguration_msg8 = Configuration du service DHCP jhostkonfiguration_msg9 = Adresse MAC jhostkonfiguration_msg10 = Utiliser l\u2019adresse IP comme nom jhostkonfiguration_msg11 = Activer routage des paquets jhostkonfiguration_msg12 = Utiliser l\u2019adresse MAC comme nom jhostkonfiguration_msg13 = Sans fil (Wi-Fi) jhostkonfiguration_msg14 = Filaire (LAN) jhostkonfiguration_msg15 = Rseau sans fil (SSID): jhostkonfiguration_msg16 = Slectionner jmodemkonfiguration_msg1 = Nom jmodemkonfiguration_msg2 = Attendre les connexions entrantes jmodemkonfiguration_msg3 = Adresse IP jmodemkonfiguration_msg4 = Port jmodemkonfiguration_msg5 = Connecter jmodemkonfiguration_msg6 = D\u00e9connecter jmodemkonfiguration_msg7 = Activer jmodemkonfiguration_msg8 = D\u00e9connecter/D\u00e9sactiver jswitchkonfiguration_msg1 = Nom jswitchkonfiguration_msg2 = Nouveau switch jswitchkonfiguration_msg3 = Afficher comme un nuage jswitchkonfiguration_msg4 = Nom Wi-Fi (SSID) jswitchkonfiguration_msg5 = Temps de rtention SAT (en seconds) jvermittlungsrechnerkonfiguration_msg1 = Configuration du parefeu jvermittlungsrechnerkonfiguration_msg2 = Nom jvermittlungsrechnerkonfiguration_msg3 = Routeur jvermittlungsrechnerkonfiguration_msg4 = Configurer le parefeu jvermittlungsrechnerkonfiguration_msg5 = Aucune connexion jvermittlungsrechnerkonfiguration_msg6 = Connect\u00e9 \u00e0 jvermittlungsrechnerkonfiguration_msg7 = Adresse IP jvermittlungsrechnerkonfiguration_msg8 = Masque jvermittlungsrechnerkonfiguration_msg9 = Passerelle jvermittlungsrechnerkonfiguration_msg10 = Carte r\u00e9seau jvermittlungsrechnerkonfiguration_msg11 = Afficher toutes les lignes jvermittlungsrechnerkonfiguration_msg12 = Nouvelle ligne jvermittlungsrechnerkonfiguration_msg13 = Supprimer la ligne s\u00e9lectionn\u00e9e jvermittlungsrechnerkonfiguration_msg14 = Ouvrir dans une nouvelle fen\u00eatre jvermittlungsrechnerkonfiguration_msg15 = Table de routage jvermittlungsrechnerkonfiguration_msg16 = Aucune connexion jvermittlungsrechnerkonfiguration_msg17 = G\u00e9n\u00e9ral jvermittlungsrechnerkonfiguration_msg18 = Adresse Mac jvermittlungsrechnerkonfiguration_msg19 = R\u00e9organisation des cables\u00a0:\n\ Pour permuter les cables de deux connecteurs (rouges ou verts),\n\ 1. s\u00e9lectionnez un premier connecteur en cliquant dessus (il devient jaune)\n\ 2. cliquez ensuite sur un second connecteur. jvermittlungsrechnerkonfiguration_msg20 = Fermer jvermittlungsrechnerkonfiguration_msg21 = Composants distants jvermittlungsrechnerkonfiguration_msg22 = Interfaces locales jvermittlungsrechnerkonfiguration_msg23 = G\u00e9rer les connexions jvermittlungsrechnerkonfiguration_msg24 = Ajouter une interface jvermittlungsrechnerkonfiguration_msg25 = Le nombre d\u2019interfaces est limit\u00e9 \u00e0 8\u00a0! jvermittlungsrechnerkonfiguration_msg26 = Routage automatique jgatewayconfiguration_msg1 = Configuration du parefeu jgatewayconfiguration_msg2 = Nom jgatewayconfiguration_msg3 = Gateway jgatewayconfiguration_msg4 = Configurer le parefeu jgatewayconfiguration_msg6 = Connect\u00e9 \u00e0 jgatewayconfiguration_msg7 = Adresse IP jgatewayconfiguration_msg8 = Masque jgatewayconfiguration_msg9 = Passerelle jgatewayconfiguration_msg10 = Carte r\u00e9seau jgatewayconfiguration_msg16 = Aucune connexion jgatewayconfiguration_msg17 = G\u00e9n\u00e9ral jgatewayconfiguration_msg18 = Adresse Mac jgatewayconfiguration_msg19 = Redirection de port jgatewayconfiguration_msg21 = nouvelle entr\u00e9e jgatewayconfiguration_msg22 = Supprimer l\u2019entr\u00e9e jportforwarding_msg1 = Entr\u00e9es valides: TCP, UDP, 6 (pour TCP) et 17 (pour UDP) jportforwarding_msg2 = Entr\u00e9es valides: num\u00e9ros de port entre 1 et 65535 jportforwarding_msg3 = Entr\u00e9es valides: Adresses IP de la port\u00e9e LAN jportforwarding_msg4 = Protocole jportforwarding_msg5 = Port redirect jportforwarding_msg6 = Adresse LAN jportforwarding_msg7 = Port LAN jweiterleitungstabelle_msg1 = Supprimer la ligne s\u00e9lectionn\u00e9e jweiterleitungstabelle_msg2 = Nouvelle ligne jweiterleitungstabelle_msg3 = IP de destination jweiterleitungstabelle_msg4 = Masque jweiterleitungstabelle_msg5 = Passerelle suivante jweiterleitungstabelle_msg6 = Via l\u2019interface framesoftwarewizard_msg1 = Assistant logiciel framesoftwarewizard_msg2 = Retour framesoftwarewizard_msg3 = Suivant framesoftwarewizard_msg4 = \u00c9tape 1 sur 3 framesoftwarewizard_msg5 = Quitter framesoftwarewizard_msg6 = \u00c9tape 2 sur 3 framesoftwarewizard_msg7 = \u00c9tape 3 sur 3 framesoftwarewizard_msg8 = Terminer panelcompiler_msg1 = Compile les fichiers de l\u2019application panelcompiler_msg2 =

    Erreur lors de la génération de l’application

    \

    La compilation a échoué parce que la mauvaise version de Java-VM \ utilisée pour l’exécution (la machine virtuelle du JDK est nécessaire)

    panelcompiler_msg3 =

    Application générée avec succès.

    panelcompiler_msg4 =

    L’application n’a pas pu être insérée

    panelverwaltung_msg1 = Nouvelle application panelverwaltung_msg2 = Application de type client panelverwaltung_msg3 = Application de type serveur panelverwaltung_msg4 = Nom de l\u2019application\u00a0: panelverwaltung_msg5 = Nom de la classe\u00a0: panelverwaltung_msg7 = Modifier l\u2019application (red\u00e9marrer FILIUS pour la prise en compte des modifications) panelverwaltung_msg8 = Supprimer l\u2019entr\u00e9e panelverwaltung_msg9 = Enregistrer les modifications panelverwaltung_msg10 = Nom de l\u2019application panelverwaltung_msg11 = Classe panelverwaltung_msg12 = GUI de la classe panelverwaltung_msg13 = Ic\u00f4ne pour le bureau dmtnfilechooser_msg1 = Dossier parent dmtnfilechooser_msg2 = Nom de fichier\u00a0: dmtnfilechooser_msg3 = Action dmtnfilechooser_msg4 = Annuler dmtnfilechooser_msg5 = Enregistrer le fichier dmtnfilechooser_msg6 = Enregistrer dmtnfilechooser_msg7 = Ouvrir un fichier dmtnfilechooser_msg8 = Ouvrir dmtnfilechooser_msg9 = Fichier; dmtnfilechooser_msg10 = Dossier; dmtnfilechooser_msg11 = Dossier\u00a0: guicontainer_msg1 = S\u00e9lectionnez le nombre d\u2019interfaces du routeur. guicontainer_msg2 = Nombre d\u2019interfaces guievents_msg1 = Il n\u2019y a plus de port disponible. guievents_msg2 = Modem guievents_msg3 = Afficher le bureau guievents_msg4 = Afficher les \u00e9changes de donn\u00e9es guievents_msg5 = Supprimer le cable guievents_msg6 = Supprimer tous les cables guievents_msg7 = Supprimer guievents_msg8 = Table SAT guievents_msg9 = MAC guievents_msg10 = Port guievents_msg11 = Configurer guievents_msg12 = Une connexion avec ce composant existe dj. guievents_msg13 = Couleur guievents_msg14 = Style de police guievents_msg15 = Taille de police guievents_msg16 = Normal guievents_msg17 = Grande guievents_msg18 = Plus grande guievents_msg19 = Normal guievents_msg20 = Italique guievents_msg21 = Gras guievents_msg22 = Port WAN (interne) occup! guievents_msg23 = Port LAN (externe) occup! guievents_msg24 = Port WAN (interne) guievents_msg25 = Port LAN (externe) guievents_msg26 = Connexion sans fil impossible guievents_msg27 = Dernire mise jour guievents_msg28 = Vider la table guievents_msg29 = Table NAT guievents_msg30 = Protocole guievents_msg31 = Adress WAN guievents_msg32 = Port redirig guievents_msg33 = Adresse LAN guievents_msg34 = Port LAN guievents_msg36 = R\u00e9initialiser la table guihilfe_msg1 = Aide guihilfe_msg2 =

    Erreur

    Fichiers d’aide introuvables. guimainmemu_msg1 = Ouvrir (Ctrl+O) guimainmemu_msg2 = Enregistrer (Ctrl+S) guimainmemu_msg3 = Passer en mode conception (Ctrl+D) guimainmemu_msg4 = Passer en mode simulation (Ctrl+R) guimainmemu_msg5 = Nouveau projet (Ctrl+N) guimainmemu_msg6 = D\u00e9marrer l\u2019assistant logiciel (Ctrl+W) guimainmemu_msg7 = Afficher l\u2019aide (Ctrl+H) guimainmemu_msg8 = \u00c0 propos de FILIUS (Ctrl+I) guimainmemu_msg9 = Les derni\u00e8res modifications n\u2019ont pas \u00e9t\u00e9 sauvegard\u00e9es.\n\ Voulez-vous cr\u00e9er un nouveau projet malgr\u00e9 tout\u00a0? guimainmemu_msg10 = Nouveau projet guimainmemu_msg11 = Une erreur s\u2019est produite pendant l\u2019enregistrement\u00a0! guimainmemu_msg12 = Fichier XML guimainmemu_msg13 = Fichier projet FILIUS guimainmemu_msg14 = Passer en mode documentation guimainmemu_msg15 = Vitesse de la simulation guimainmemu_msg16 = Ajuster la vitesse de simulation guimainmemu_msg17 = Voulez-vous replacer le fichier existant? guimainmemu_msg18 = Paramtres settings_msg1 = Slectionner le th\u00e8me de l'ic\u00f4ne settings_msg2 = Dfaut settings_msg3 = Entreprise (exprimental) settings_msg4 = Symboles (exprimental) settings_msg5 = Visualisation des couches (exprimental) infodialog_msg1 = Information infodialog_msg2 =

    FILIUS a été développé dans le cadre du projet d’un groupe d’étudiants de l’université de Siegen. (2006-2007)

    infodialog_msg3 = \u00c9tudiants\u00a0: infodialog_msg4 = Tuteurs\u00a0: infodialog_msg5 = Maintenance et d\u00e9veloppements ult\u00e9rieurs\u00a0: infodialog_msg6 = Version infodialog_msg8 = Merci de signaler les erreurs \u00e0 l\u2019adresse suivante\u00a0: infodialog_msg9 = ou infodialog_msg10 = Distribution sous les termes et conditions de la GNU General Public License (GPL) Version 2 ou 3 infodialog_msg11 = Contributeurs\u00a0: hw_notebook_msg1 = Portable hw_notebook_msg2 = Portable hw_rechner_msg1 = Ordinateur hw_rechner_msg2 = Ordinateur hw_switch_msg1 = Switch / Wi-Fi hw_vermittlungsrechner_msg1 = Routeur hw_gateway_msg1 = Routeur Domestique hw_kabel_msg1 = Connexion hw_verbindung_msg1 = L\u2019un des deux ports est d\u00e9j\u00e0 occup\u00e9. sw_clientbaustein_msg1 = Erreur lors de l\u2019\u00e9tablissement de la connexion\n sw_clientbaustein_msg2 = Connexion \u00e9tablie sw_clientbaustein_msg3 = Connexion interrompue sw_clientbaustein_msg4 = Erreur lors de l\u2019\u00e9tablissement de la connexion sw_clientbaustein_msg5 = D\u00e9connexion du socket sw_clientbaustein_msg6 = sw_serveranwendung_msg1 = R\u00e9ception des requ\u00eates termin\u00e9e sw_serveranwendung_msg2 = R\u00e9ception des requ\u00eates d\u00e9marr\u00e9e sw_serveranwendung_msg3 = Socket du serveur ferm\u00e9e sw_serveranwendung_msg4 = Connexion au socket sw_serveranwendung_msg5 = sw_serveranwendung_msg6 = Le socket du serveur n'a pas pu tre initialis. sw_servermitarbeiter_msg1 = D\u00e9connexion du socket sw_servermitarbeiter_msg2 = sw_ipeintrag_msg1 = Adresse MAC\u00a0: sw_ipeintrag_msg2 = Adresse IP\u00a0: sw_dnsservermitarbeiter_msg1 = La requ\u00eate est\u00a0: sw_dnsservermitarbeiter_msg2 = Envoyer la r\u00e9ponse\u00a0: sw_emailserver_msg1 = Ce compte n\u2019a pu \u00eatre supprim\u00e9. V\u00e9rifier l\u2019identifiant et le mot de passe. sw_pop3client_msg1 = Connexion interrompue sw_smtpclient_msg1 = Connexion \u00e0 sw_smtpclient_msg2 = \u00e9tablie sw_smtpclient_msg3 = interrompue sw_smtpclient_msg4 = Message envoy\u00e9 \u00e0 sw_smtpmitarbeiter_msg1 = 220 Bienvenue sur sw_smtpmitarbeiter_msg2 = 221 Connexion ferm\u00e9e par le serveur. sw_smtpmitarbeiter_msg3 = 250 OK sw_smtpmitarbeiter_msg4 = 250 Exp\u00e9diteur OK sw_smtpmitarbeiter_msg5 = 501 Erreur de syntaxe - Param\u00e8tre ou argument incorrect. sw_smtpmitarbeiter_msg6 = 250 Destinataire OK sw_smtpmitarbeiter_msg7 = 501 Erreur de syntaxe - Param\u00e8tre ou argument incorrect. sw_smtpmitarbeiter_msg8 = 354 Terminer les donn\u00e9es par . sw_smtpmitarbeiter_msg9 = 503 Ordre des commandes incorrect. sw_smtpmitarbeiter_msg10 = 250 Message en attente d\u2019acheminement sw_smtpmitarbeiter_msg11 = 503 Ordre des commandes incorrect. sw_smtpmitarbeiter_msg12 = Message pour sw_smtpmitarbeiter_msg13 = enregistr\u00e9 dans la boite aux lettres. sw_smtpmitarbeiter_msg14 = Erreur lors de l\u2019envoi \u00e0 sw_firewall_msg1 = Bloquer le destinataire - sw_firewall_msg2 = \u00c0 bloquer \u00e0 cause du port\u00a0: sw_firewall_msg3 = Requ\u00eate d\u2019\u00e9tablissement de connexion interrompue\u00a0! (SYN = true) sw_firewall_msg4 = R\u00e8gle ajout\u00e9e sur IP\u00a0: sw_firewall_msg5 = R\u00e8gle ajout\u00e9e sur port\u00a0: sw_firewall_msg6 = R\u00e8gle supprim\u00e9e sur IP\u00a0: sw_firewall_msg7 = R\u00e8gle supprim\u00e9e sur port\u00a0. sw_firewall_msg8 = Une r\u00e8gle s\u2019applique\u00a0: sw_firewall_msg9 = Aucune r\u00e8gle n\u2019est satisfaite. Action par d\u00e9faut appliqu\u00e9e\u00a0: sw_firewallweblog_msg1 = \n\t\n\t\tJournal du parefeu</titel> sw_firewallweblog_msg2 = \n\t</head>\n\t<body>\n\t\t<h2>Journal du parefeu</h2> sw_vermittlungweb_msg1 = Table cr\u00e9\u00e9e et actualis\u00e9e par le protocole d\u2019information de routage (RIP). sw_vermittlungweb_msg3 = illimit\u00e9 sw_vermittlungweb_msg4 = Table cr\u00e9\u00e9e \u00e0 partir des param\u00e8tres IP du routeur puis compl\u00e9t\u00e9e manuellement. sw_vermittlungweb_msg5 = Table de routage sw_vermittlungweb_msg6 = Table de routage sw_fileexplorer_msg1 = Le fichier n\u2019a pas pu \u00eatre import\u00e9 sw_fileexplorer_msg2 = Fichier import\u00e9 avec succ\u00e8s sw_fileexplorer_msg3 = Fichier inexistant ou trop volumineux (> 150Ko) sw_fileimporter_msg1 = Le fichier n\u2019a pas pu \u00eatre import\u00e9 sw_fileimporter_msg2 = Fichier import\u00e9 avec succ\u00e8s sw_fileimporter_msg3 = Fermer sw_terminal_msg1 = Application sw_terminal_msg2 = est d\u00e9j\u00e0 install\u00e9e.\n sw_terminal_msg3 = a \u00e9t\u00e9 install\u00e9e avec succ\u00e8s.\n sw_terminal_msg4 = Adresse IP . . .: sw_terminal_msg5 = Masque . . . . .: sw_terminal_msg6666= Adresse MAC. . .: sw_terminal_msg6 = Passerelle . . .: sw_terminal_msg27= Serveur DNS. . .: sw_terminal_msg7 = | Destination | Masque | Passerelle | Interface |\n \ -------------------------------------------------------------------------\n sw_terminal_msg8 = Dossier vide\n sw_terminal_msg9 = Dossier sw_terminal_msg10 = \nNombre de dossiers: sw_terminal_msg11 = \nNombre de fichiers: sw_terminal_msg12 = Erreur de Touch\n sw_terminal_msg13 = Fichier cr\u00e9\u00e9\n sw_terminal_msg14 = Fichier d\u00e9j\u00e0 existant\n sw_terminal_msg15 = Saisir un nom de fichier\n sw_terminal_msg16 = Erreur de cr\u00e9ation du dossier\n sw_terminal_msg17 = Dossier cr\u00e9\u00e9\n sw_terminal_msg18 = Dossier d\u00e9j\u00e0 existant\n sw_terminal_msg19 = Saisir un nom de dossier\n sw_terminal_msg20 = Dossier inexistant\n sw_terminal_msg21 = Dossier modifi\u00e9! (..)\n sw_terminal_msg22 = Dossier modifi\u00e9!\n sw_terminal_msg23 = Test\n sw_terminal_msg24 = Cr\u00e9\u00e9 avec succ\u00e8s! sw_terminal_msg25 = Liste des commandes disponibles:\n \ arp affiche la table (ARP) de r\u00e9solution d\u2019adresses\n \ arpsend envoyer une r\u00e9ponse de l'ARP\n \ cd change le dossier courant\n \ cls rinitialiser l'affichage\n \ copy / cp copie un fichier\n \ cat / type affiche le contenu d\u2019un fichier \n \ del / rm supprime une fichier ou un dossier\n \ dir / ls liste le contenu du dossier courant\n \ exit quitte la ligne de commande\n \ help affiche la liste des commandes disponibles\n \ host r\u00e9sout un nom d\u2019h\u00f4te en adresse IP\n \ ipconfig affiche les param\u00e8tres du r\u00e9seau\n \ mkdir cr\u00e9e un dossier\n \ move / mv d\u00e9place/renomme un fichier\n \ netstat affiche la liste des connexions en cours\n \ nslookup r\u00e9sout un nom d\u2019h\u00f4te en adresse IP\n \ ping teste la connexion avec un autre ordinateur\n \ pwd affiche le chemin du dossier courant\n \ route affiche la table de routage\n \ tcpdump affiche \u00c9changes de donn\u00e9es\n \ touch cr\u00e9e un fichier vide\n \ traceroute analyse les sauts n\u00e9cessaires pour atteindre une destination\n sw_terminal_arp = arp - show or manipulate Address Resolution Protocol (ARP) table sw_terminal_arpsend = arpsend - send an ARP reply sw_terminal_cat = cat / type - show file contents sw_terminal_cd = cd - change directory sw_terminal_copy = copy / cp - copy file sw_terminal_del = del / rm - delete file/directory sw_terminal_dir = dir / ls - show list of files in current directory sw_terminal_exit = exit - exit terminal application sw_terminal_help = help - show the list of commands or info on one command sw_terminal_host = host - resolve hostname to IP address sw_terminal_ipconfig = ipconfig - show network configuration sw_terminal_mkdir = mkdir - create directory sw_terminal_move = move / mv - move/rename file sw_terminal_netstat = netstat - show list of connections sw_terminal_nslookup = nslookup - resolve hostname to IP address sw_terminal_ping = ping - test connection to other computer sw_terminal_pwd = pwd - print working directory sw_terminal_route = route - show routing table sw_terminal_tcpdump = tcpdump - show data exchange sw_terminal_touch = touch - create file sw_terminal_traceroute = traceroute - analyse hops of connecting path sw_terminal_usage_arp = Usage: arp [-d [<IP address>]] [-a <IP address>] \n \ -d \t delete ARP entry for IP address or reset ARP table \n \ -a \t show ARP entry for specified IP address sw_terminal_usage_arpsend = Usage: arpsend <sender IP address> <target IP address>\n \ <sender IP address> address to be published for own MAC address\n \ <target IP address> target the reply must be sent to sw_terminal_usage_cat = Usage: cat <file name> sw_terminal_usage_cd = Usage: cd <directory path> sw_terminal_usage_copy = Usage: copy <original file> <new file name> sw_terminal_usage_del = Usage: del <file | folder> \n \ <file | folder> relative path to current directory sw_terminal_usage_dir = Usage: dir sw_terminal_usage_exit = Usage: exit sw_terminal_usage_help = Usage: help [<command>] sw_terminal_usage_host = Usage: host <domain> \n \ <domain> the domain for which to look up A and MX records sw_terminal_usage_ipconfig = Usage: ipconfig sw_terminal_usage_mkdir = Usage: mkdir <directory name> \n \ <directory name> name of new directory either as absolute path \n \ or relative path sw_terminal_usage_move = Usage: move <original file> <new file name> sw_terminal_usage_netstat = Usage: netstat sw_terminal_usage_nslookup = Usage: nslookup <domain> \n \ <domain> the domain for which to look up A (and related NS) records sw_terminal_usage_ping = Usage: ping <remote host> \n \ <remote host> the remote address as IP address or host name sw_terminal_usage_pwd = Usage: pwd sw_terminal_usage_route = Usage: route sw_terminal_usage_tcpdump = Usage: tcpdump sw_terminal_usage_touch = Usage: touch <file name> sw_terminal_usage_traceroute = Usage: traceroute <remote host> \n \ <remote host> the remote address as IP address or host name sw_terminal_msg26 = Utilise la commande 'help' pour afficher la liste des commandes disponibles \n \ ou utilise 'help <commande>' pour plus d'information a la commande. sw_terminal_msg28 = a pour adresse IP sw_terminal_msg29 = Nom d\u2019h\u00f4te incorrect!\nSeuls les URLs sans chemin sont accept\u00e9s.\n sw_terminal_msg30 = Nom d\u2019h\u00f4te non r\u00e9solu.\nIl se peut qu\u2019aucun serveur DNS ne soit disponible.\n sw_terminal_msg31 = D\u00e9lai \u00e9coul\u00e9! Aucune r\u00e9ponse re\u00e7ue dans le temps imparti. sw_terminal_msg33 = Fichier copi\u00e9 avec succ\u00e8s.\n sw_terminal_msg34 = Le fichier n\u2019a pas pu \u00eatre copi\u00e9.\n sw_terminal_msg35 = Fichier d\u00e9plac\u00e9/renomm\u00e9 avec succ\u00e8s.\n sw_terminal_msg36 = Le fichier n\u2019a pas pu \u00eatre d\u00e9plac\u00e9/renomm\u00e9.\n sw_terminal_msg37 = Fichier supprim\u00e9 avec succ\u00e8s.\n sw_terminal_msg38 = Fichier inexistant ou n\u2019ayant pas pu \u00eatre supprim\u00e9.\n sw_terminal_msg39 = <CE: frei>\n sw_terminal_msg45 = Statistiques des paquets sw_terminal_msg46 = paquets transmis sw_terminal_msg47 = paquets re\u00e7us sw_terminal_msg48 = paquets perdus sw_terminal_msg49 = | Proto | Adresse locale | Adresse distante | \u00c9tat |\n sw_terminal_msg50 = | Adresse IP | Adresse MAC |\n sw_terminal_msg52 = Destination inaccessible sw_terminal_msg53 = Ping non support\u00e9 sur les adresses de diffusion. sw_terminal_msg54 = Fichier n'existe pas sw_terminal_msg55 = Avorter avec Ctrl+C sw_terminal_msg57 = \n\n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ sSSs .S S. .S .S S. sSSs \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ d%%SP .SS SS. .SS .SS SS. d%%SP \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ d%S' S%S S%S S%S S%S S%S d%S' \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S%S S%S S%S S%S S%S S%S S%| \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S S&S S&S S&S S&S S&S S&S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S_Ss S&S S&S S&S S&S S&S Y&Ss \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S~SP S&S S&S S&S S&S S&S `S&&S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S&S S&S S&S S&S S&S S&S `S*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*b S*S S*b S*S S*b d*S l*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S S*S. S*S S*S. .S*S .S*P \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S SSSbs S*S SSSbs_sdSSS sSS*S \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ S*S S*S YSSP S*S YSSP~YSSY YSS' \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ SP SP SP \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ Y Y Y \n\n sw_terminal_msg58 = le courrier est trait par sw_terminal_trace_msg2 = Erreur: impossible de se connecter au serveur DNS. sw_terminal_trace_msg3 = Erreur: le nom de domaine n\u2019a pas pu \u00eatre r\u00e9solu. sw_terminal_trace_msg4 = \u00c9tablissement de la connexion avec %s (en %s sauts max.). sw_terminal_trace_msg5 = \u00c9tablissement de la connexion avec %s (%s) (en %s sauts max.). sw_terminal_trace_msg6 = %s n\u2019a pas pu \u00eatre atteint en %s sauts. sw_terminal_trace_msg7 = Analyse interrompue! sw_terminal_trace_msg8 = Erreur: r\u00e9ponse de %s: "R\u00e9seau ICMP inaccessible" sw_terminal_trace_msg9 = Erreur: r\u00e9ponse de %s: "H\u00f4te ICMP inaccessible" sw_terminal_trace_msg10 = Erreur: r\u00e9ponse de %s: "Destination ICMP inaccessible" sw_terminal_trace_msg11 = %s a \u00e9t\u00e9 atteint en un saut. sw_terminal_trace_msg12 = %s a \u00e9t\u00e9 atteint en %s sauts. sw_terminal_trace_msg13 = Erreur: le saut suivant n\u2019a pas pu \u00eatre effectu\u00e9 malgr\u00e9 plusieurs tentatives. sw_switchfirmware_msg1 = Port sw_serversocket_msg1 = Socket sur le port sw_serversocket_msg2 = existe d\u00e9j\u00e0 sw_socket_msg1 = La destination n\u2019a pu \u00eatre d\u00e9termin\u00e9e sw_tcpsocket_msg1 = Erreur lors de l\u2019\u00e9tablissement de la connexion au serveur sw_tcpsocket_msg2 = D\u00e9lai imparti \u00e0 l\u2019\u00e9tablissement de la connexion \u00e9coul\u00e9 sw_tcpsocket_msg3 = L\u2019ouverture active du socket a \u00e9chou\u00e9 sw_tcpsocket_msg5 = Erreur lors de l\u2019\u00e9tablissement de la connexion au serveur. sw_tcpsocket_msg6 = Connexion non \u00e9tablie\u00a0! sw_tcpsocket_msg7 = Connexion interrompue sw_tcpsocket_msg8 = D\u00e9lai d\u2019envoi d\u2019un message \u00e9coul\u00e9 sw_tcpsocket_msg9 = Aucune connexion \u00e9tablie\u00a0! sw_tcpsocket_msg10 = D\u00e9lai de r\u00e9ception d\u2019un message \u00e9coul\u00e9 sw_transportprotokoll_msg1 = Port sw_transportprotokoll_msg2 = n\u2019existe pas. sw_transportprotokoll_msg3 = Service non disponible / non encore d\u00e9marr\u00e9 sw_ip_msg1 = Protocole ARP\u00a0: l\u2019adresse IP sw_ip_msg2 = est introuvable - Adresse MAC cible\u00a0: sw_ip_msg3 = - Passerelle\u00a0: sw_ip_msg4 = Erreur sur sw_ip_msg5 = Aucune entr\u00e9e disponible dans la table de transfert pour la cible sw_ip_msg6 = . sw_weiterleitungstabelle_msg1 = Entr\u00e9es manuelles sw_httpnachricht_msg1 = Information sw_httpnachricht_msg2 = Op\u00e9ration r\u00e9ussie sw_httpnachricht_msg3 = Relocalisation sw_httpnachricht_msg4 = Erreur du client sw_httpnachricht_msg5 = Erreur du serveur sw_webbrowser_msg1 = <html><head><title>Erreur

    Impossible de joindre le serveur !

    sw_webbrowser_msg2 = ErreurErreur rp_lauscher_msg1 = No. rp_lauscher_msg2 = Date rp_lauscher_msg3 = Source rp_lauscher_msg4 = Destination rp_lauscher_msg5 = Protocole rp_lauscher_msg6 = Couche rp_lauscher_msg7 = Commentaire / Dtail rp_lauscher_msg8 = R\u00e9seau rp_lauscher_msg9 = Internet rp_lauscher_msg10 = Transport rp_lauscher_msg11 = Application rp_lauscher_msg12 = Protocole\u00a0 rp_lauscher_msg13 = Recherche de l\u2019adresse MAC associ\u00e9e \u00e0 rp_lauscher_msg14 = L\u2019adresse MAC est rp_filiusclassloader_msg1 = La classe rp_filiusclassloader_msg2 = n\u2019a pas pu \u00eatre charg\u00e9e. rp_szenarioverwaltung_msg5 = Fichier introuvable ou corrompu. hilfedatei_entwurf = design_mode_fr_FR.html hilfedatei_simulation = simulation_mode_fr_FR.html hilfedatei_documentation = documentation_mode_fr_FR.html modemfirmware_msg1 = L\u2019ordinateur cible n\u2019a pas pu \u00eatre contact\u00e9 modemfirmware_msg2 = Une erreur s\u2019est produite lors de l\u2019\u00e9tablissement de la connexion. jsidebar_tooltip_ipAddress = Adresse IP/masque (Adresse MAC) jsidebar_tooltip_gateway = Passerelle jsidebar_tooltip_dnsServer = Serveur DNS report_overview = Expos sommaire report_component_config = Composants report_base_config = Configuration de base report_nics = Interfaces r\u00e9seau report_apps = Applications report_routing_table = Table de routage report_network_traffic = \u00c9changes de donn\u00e9es report_title = Documentation Filius report_dns_rr = Configuration Serveur DNS report_domain = Domaine report_type = Type report_data = Date simulation_control_drop_packets = Jeter des paquets schichten_lokaler_weg_msg2 = Couche schichten_lokaler_weg_msg3 = Protocole schichten_lokaler_weg_msg4 = Donns schichten_lokaler_weg_msg5 = Ethernet schichten_lokaler_weg_msg6 = Adresse IP: schichten_lokaler_weg_msg7 = Masque: schichten_lokaler_weg_msg8 = Adresse MAC: schichten_lokaler_weg_msg9 = Passerelle: schichten_lokaler_weg_msg10 = Serveur DNS: schichten_allgemein_msg1 = avant schichten_allgemein_msg2 = dos schichten_globaler_weg_msg1 = Visualisation des couchesfilius-2.9.4+ds/src/main/resources/gfx/000077500000000000000000000000001506574241500177455ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/allgemein/000077500000000000000000000000001506574241500217025ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/allgemein/Text-x-generic_with_pencil.svg000066400000000000000000000203401506574241500276120ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/allgemein/aktionsmodus.png000066400000000000000000000021531506574241500251310ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#%tEXtSoftwareMacromedia Fireworks MX 2004vtEXtCreation Time04/23/06kIDATx]UofN~wbmuuقJbQ)ԢJ/;AQ.UA7Puӭl7̌1amwM&sҤ $s}|=gΑ !";P7Hs "!h~*6G6)Tt*P=gUS;'tEs6^ᔷ[?"gr*]bS1ʴ3Oxȁ U&K1 L59;ϡ,D׷"jUlCtI~oJ"s.VU/z[Θ(^قm̓؊TGyz/g(%fD g!"7C6br%M/8۬QެPVUx\j|kjg-BJ4Lq06GYjɋgx=l4NHGR3g؅-βEU̞mu˱`b|/}*zh|ȇgae%kE=Z纜0Ϯ>a*cPSDE9Ǥs?f bsGzg7^qOf=^=| Ù=g=s7h'MxҼw ԭgBt Bɑѽl6ҁ㵄Ʋ;C|Mxz}'ٯj΄lfx;5&=:fOS X5`QvV;EGH]v><,#Gk]9vsXM I:Nm|1^N=$Z(7_Nj3CYEFvUqy":v)G2IDAThMHaȮf&fe*udAA R!H}\*PyѠ.BR` E_DCQaaZn:3!胒vuFwdvx=~5%8'>usohn V wv0n'6_ɒgΟcc1NÞKYe62e&t.? bAvb1.ޥ%6s3dXs,oRʈ+%o/I,v2(\+vR@)<ŷ}fewbGƏħUD_٤ݖx7C! 7.DcU3YXXJk6)Ǵh\& G7R3xܚe _-]h3 Yb/+\'nӿؖQҌ2q~ Z6>28J&gRa"jHZ?#IѻëIMD1L YPdMؗHۋVQ>lڽ%žb99%m4됆*g1O d`cX\Zy;WCCì;#IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/arrow_left.png000066400000000000000000124422271506574241500245720ustar00rootroot00000000000000PNG  IHDR@JtRNS7X} pHYs+ IDATx x$" IDATD IDAT'e IDAT,s IDAT x9 IDATR_ IDATtI" IDATl IDAT x?U IDAT%Ec IDAT˜ IDATZ IDAT xb0 IDAT IDATbӓ IDAT IDAT xow IDAT#( IDATz( IDAT|( IDAT IDAT x IDAT,Q IDATBIT IDAT# IDAT xM0- IDAT?S IDATwS IDATs8I IDAT xF7 IDAT.!: IDATݐ8 IDATk IDAT x1 IDATqxqxȭ ZD IDATqxȭqxȭ IDATqxȭqxȭq IDATqxȭqxȭ x;@ IDATqxȭqxȭi IDATqxȭqxȭ^ IDATqxȭqxȭqxȭ1 IDATqxȭqxȭ+R IDAT xqxȭqxȭB$ IDATqxȭqxȭv IDATqxȭqxȭ7 IDATqxȭqxȭ IDATqxȭ xqxȭpkM IDATqxȭqxȭqxȭ9 ! IDATqxȭqxȭ0Lt IDATqxȭqxȭ S.] IDATqxȭ xqxȭ; IDATqxȭqxȭ1 IDATqxȭqxȭk:A IDATqxȭkqxȭji7d IDATqxȭjqxȭj xV!D? IDATqxȭjqxȭjqxȭjr IDATqxȭjqxȭjbOJ IDATqxȭjqxȭj` IDATqxȭjqxȭjV` IDAT xqxȭjqxȭifO IDATqxȭiqxȭi-4 IDATqxȭiqxȭi>Ҵ IDATqxȭiqxȭiqxȭi IDAT xqxȭiqxȭi{ ( IDATqxȭiqxȭi+ IDATqxȭiqxȭiS+ IDATqxȭhqxȭh8 IDATqxȭh xqxȭhZ\Q IDATqxȭhqxȭhC8n IDATqxȭhqxȭhqxȭc IDAThqxȭhqxȭhGq IDATqxȭh xqxȭhZⷧ IDATqxȭhqxȭh IDATqxȭgqxȭg!J IDATqxȭgqxȭgm%* IDATqxȭgqxȭg^~xEm IDATqxȭgqxȭgPJ IDATqxȭgqxȭgqxȭgR IDATqxȭgqxȭg IDATqxȭgqxȭg! IDATKsqxȭfqxȭf'P>X IDATqxȭfqxȭfg IDATqxȭfqxȭfj^ IDATqxȭfqxȭf S IDATqxȭf xqxȭfqxȭfO IDATqxȭfqxȭeGa IDATqxȭeqxȭeHc IDATqxȭeqxȭeR IDATqxȭe xqxȭek* IDATqxȭeqxȭeask IDATqxȭeqxȭe IDATqxȭeqxȭe:C IDATqxȭeqxȭe xqxȭe] IDATqxȭfqxȭf IDATqxȭfqxȭfwT IDATqxȭfqxȭfТv IDATqxȭfqxȭfX/U IDAT xqxȭfqxȭf< IDATqxȭfqxȭf롥 IDATqxȭfqxȭeqxȭeR IDATqxȭeqxȭe:lg IDAT xqxȭeqxȭeH# IDATqxȭel%qxȭey IDATqxȭeqxȭe~3 IDATqxȭe~qxȭe}̭k IDATqxȭe} xqxȭe} IDATqxȭe}qxȭd}b IDATqxȭd~qxȭd~qxȭd~k? IDATqxȭd}qxȭd}_y% IDATqxȭd} xqxȭd}2 IDATUzZqxȭd|qxȭd|LY IDATqxȭd|qxȭd{ ++ IDATqxȭd{qxȭd{# IDATqxȭd{qxȭ~cz xZ^ IDATqxȭ~czqxȭ~czqxȭ~cz8x IDATqxȭ}cyqxȭ}cyy IDATqxȭ|cxqxȭ|cxz IDATqxȭ|cxqxȭ|cx;([ IDAT xqxȭ{cwqxȭ{cwKd IDATqxȭzbwqxȭzbwe& n IDATqxȭzbwqxȭzbx#) IDATqxȭybxqxȭybxB IDATqxȭybx xqxȭybwqxȭzbwa IDATqxȭzbvqxȭzbvX~L IDATqxȭybvqxȭybvo<9 IDATqxȭxbuqxȭxbu/fj IDATqxȭxau xqxȭxau$! IDATqxȭwatqxȭwat] IDATqxȭwasqxȭwas9X IDATqxȭvasqxȭvasqxȭuar_XV IDATqxȭuar xqxȭuar IDATqxȭuarqxȭtaq} IDATqxȭtaqqxȭtaq>, IDATqxȭs`qqxȭs`q IDATqxȭs`qqxȭs`rd IDAT xqxȭs`qqxȭt`q , IDATqxȭs`qqxȭs`q~ IDATqxȭs`pqxȭs`pqxȭr`pբ IDATqxȭr`pqxȭq`o-Y IDAT xqxȭq`oqxȭq_o IDATqxȭq_oqxȭp_n IDATqxȭp_nqxȭo_mIA IDATqxȭo_mqxȭo_mD IDATqxȭo_m xqxȭo_mJ IDATqxȭn_lqxȭn_lqxȭm_lXq IDATqxȭm_lqxȭm_k+1 IDATqxȭm`kqxȭm`k2-= IDATqxȭn`l xqxȭn`l.K IDATqxȭn`lqxȭm`kd0Mi IDATqxȭm`kqxȭl`kS IDATqxȭl`kqxȭl`j  IDATqxȭl`jqxȭk`jC}qxȭ,F IDATk`jqxȭj_iqxȭj_iV{ IDATqxȭj_iqxȭj_h IDATqxȭj_hqxȭi_h ^ IDATqxȭi_hqxȭh_goq IDATVtqxȭh_gqxȭh_g<5 IDATqxȭh_gqxȭg_fv#_ IDATqxȭg_fqxȭg_e` IDATqxȭh_eqxȭh^eqxȭh^e.X IDAT xqxȭg^eqxȭg^f^ IDATqxȭf^fqxȭf^f[(h IDATqxȭf^eqxȭf^e IDATqxȭe^eqxȭe^ek IDATqxȭe^e xqxȭd^dK IDATqxȭd^dqxȭd^cDb IDATqxȭd^cqxȭc]cqxȭc]cf;J IDATqxȭc]bqxȭc]b1 IDATqxȭb]b x K IDATS\%$%ZUY}~ o+)ۦxw IDAT o+)ۦ o+)ۦQn IDAT oS\ۦYUX o+)ۧ $ IDATS\$#$YUX o+)ܧCd IDAT x o+)ܧ o+)ܧ IDAT o+)ܧ oS\ܧXUW o+)ܨ~ IDAT oS\ܨWUW o+)ܩx IDAT oS\ܩWUV o+)ܩ IDAT x oS\ܩVUV o+)ݪ+8Δ IDAT oS\ݩVUV o+)ݪ4A IDAT oS\ݪVTU o+)ݪ; IDAT oS\ݪUTU o+)ݫga IDAT oS\ݫTTT x o+)ݬX IDAT o+)ݬ o+)ݬ oS\ݬSTSHp IDAT o+)ݭ oS\ݭSTS2 IDAT o+)ޭ o+)ޭͭ\i IDAT oS\ެSTS x o+)ޭ6 IDATS\"#"RSR o+)ޮe IDAT o+)ޮ o+)ޮ IDAT oS\ޮQSQ o+)ޯac IDATS\!#!RSQ o+)߮ x o+)߮v@ IDAT o+)߮S\!#!QSP IDAT o+)߯S\!#!PSPd3 IDAT o+)߰S\!"!PSOsf IDAT o+)߰S\!"!OSO IDAT x o+)߱S\ "!OSNQF IDAT o+)߱ o+)߱jȻ IDATS\ " OSN o+)౭M IDATS\ " NSN o+)ಭS\ " MSM IDAT x o+)೭S\ " MRM IDAT o+)೮S\" MRL$)y IDAT o+)೮S\" LRL+& IDAT o+)മS\" LRK IDAT o+)മ xS\"KRK F IDAT o+)ᵮ o+)ᵮ'A IDAT o+)ᵮS\"JRJB IDAT o+)ᶮ o+)ᶮS\"JQJ" IDAT o+)ᶯ xS\"JQI6>l IDAT o+)ᶯS\"IQJ= IDAT o+)ⷯS\"HQJ"w IDAT o+)ⸯS\"HQI$n IDAT o+)ⸯS\"HQIS:A IDAT x o+)ⸯS\"GQHzT^ IDAT o+)⹯S\"GPH o+)⹰=- IDATS\"FPG o+)⺰en IDATS\"EPG o+)㻰.Z IDAT x o+)㻰 o+)㻰F IDAT o+)㻰S\"EPF۬ IDAT o+)㻰S\"DPF5 IDAT o+)㼰S\"DPE~ IDAT o+)㼰 xS\!CPED IDAT o+)佰S\!CPE o+)佰k IDATS\!CPD o+)佰 IDATS\!BPD o+)侰M^ ~ IDATS\!BPC x o+)侰.ɩ IDATS\!APC o+)俰Wj IDATS\!@PB o+)! IDATS\!@OB o+)\ IDAT o+)S\!@OB{S\- IDAT!@OB o+) o+)m ݅ IDAT o+) o+)ȟ" IDAT o+) o+)wÁ IDAT o+) o+); IDATv o+) o+)Ir IDAT o+) o+)~ IDAT o+) o+)!/9P IDAT o+)S\!?OA o+) IDAT x o+) o+)n IDAT o+) o+)A~ IDAT o+) o+)p]N IDAT o+) o+)x* IDAT o+) x o+)ue IDAT o+) o+)A" IDAT o+) o+)a& IDAT o+) o+) o+)&q IDAT o+) x o+)Qx\ IDAT o+) o+),q IDAT o+) o+)3} IDAT o+) o+)vi9 IDAT o+) o+);&A IDAT x o+) o+)[71 IDAT o+) o+) o+)olO IDAT o+) o+)  IDAT o+) o+)ǘ IDAT x o+) o+)= IDAT o+)俰 o+)俰 IDAT o+)侰 o+)侰2 IDAT o+)佰 o+)侰 IDAT o+)佰 x o+)佱]$ IDAT o+)㼰 o+)㼰 o+)㼰_ IDAT o+)㻰 o+)㼰Yr IDAT o+)㻰 o+)㻰^ IDAT o+)⺰ x o+)⺰s IDAT o+)⹯ o+)⹯Ux IDAT o+)ⸯ o+)⹯c\y IDAT o+)ⸯ o+)ⸯh̘ IDAT o+)ᷯ o+)ᷯ x IDAT o+)ᶮ o+)ᷮ o+)ᶮ IDAT o+)ᶮ o+)൮br IDAT o+)൮ o+)മW IDAT o+)೮ o+)മY IDAT x o+)೭ o+)೭*< IDAT o+)ಭ o+)ಭ IDAT o+)߱ o+)߲sN% IDAT o+)߱ o+)߱ o+)߰F/ IDAT x o+)߰ o+)߯a.R IDAT o+)߯ o+)ޮA3 IDAT o+)ޯ o+)ޯ IDAT o+)ޯ o+)ޮ3Œ IDAT o+)ޮ x o+)ݭ IDAT o+)ݭ o+)ݮbe IDAT o+)ݭ o+)ݭzH< IDAT o+)ݬ o+)ݬ o+)ݫ]{ IDAT o+)ݫ x o+)ݪ IDAT o+)ݫ o+)ܪ) IDAT o+)ܪ o+)ܩ IDAT o+)ܩ o+)R IDAT o+) o+) xmu IDAT o+) o+)H IDAT o+) o+) IDAT o+) o+) o+) IDAT o+) o+)-m IDAT x o+) o+)DU IDAT o+) o+)} IDAT o+) o+)/EW IDAT o+) o+) IDAT o+) x o+) z~ IDAT o+) o+) o+)m]2 IDAT o+) o+).a( IDAT o+) o+) IDAT o+) x o+)^_M IDAT o+) o+)kL IDAT o+) o+)#B$e IDAT o+) o+)A IDAT o+) xz70r IDATpٝ IDATuS IDATmAB IDATj IDATewӓh IDAT IDATG-E IDATm5 IDAT xOW IDAT[ IDAT( IDAT/< IDAT xT IDAT͌ IDATK\ IDAT3[I IDAT x IDATYv IDAToX3 IDAT4 IDAT xc IDATE5 IDATs IDATd IDAT IDAT x] IDATE' IDAT0ci IDAT IDAT xtҨ IDATz IDAT<6 IDATkykIDAT0x xIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/arrow_right.png000066400000000000000000124422271506574241500247550ustar00rootroot00000000000000PNG  IHDR@JtRNS7X} pHYs+ IDATx x$" IDATD IDAT'e IDAT,s IDAT x9 IDATR_ IDATtI" IDATl IDAT x?U IDAT%Ec IDAT˜ IDATZ IDAT xb0 IDAT IDATbӓ IDAT IDAT xow IDAT#( IDATz( IDAT|( IDAT IDAT x IDAT,Q IDATBIT IDAT# IDAT xM0- IDAT?S IDATwS IDATs8I IDAT xF7 IDAT.!: IDATݐ8 IDATk IDAT x1 IDATbju IDATbjbjbj IDATbjbj-p IDATbjbj xA IDATbjbjxp IDATbjbjE IDATbjbjzg^ IDATbjbjTV7 IDATbj xbjbj5 IDATbjbj:O#) IDATbjbj IDATbjbjt) IDATbj xbjCm IDATbjbj IDATbjbjp5 IDATbjbjCV IDATbjbj xbj IDATbjbj L IDAT'%bjk'%bj# IDAT k'%bj  j'%bj2 IDAT j'%bj j'%bj x#$ IDAT j'%bj j'%bj0 IDAT j'%bj j&%bjŶ IDAT j&%bj j&%bjP IDAT j&%bj j&%bj j&%bjͧ IDAT x j&%bj  i&$bj IDAT i&$bj i&$bj⺳ IDAT i&$bj i&$bjw IDAT i&$bj i&$bj+ IDAT i&$bj x i&$bjz$ IDAT i&$bj i&$bjl IDAT i&$bj i%$bj i%$bjo7 IDAT  i%$bj  j%$bji IDAT  j%$bj x  j%$bjM40 IDAT  j%$bj  j%$bj3 IDAT  j%#bj  j%#bj $ IDAT  j%#bj  j%#bj`/ IDAT  j%#bj  j%#bj x1C= IDAT  j%#bj  j%#bj-c# IDAT  j%#bj  i$#bj  i$#bj IDAT  i$#bj  i$#bj9G IDAT  i$#bj  i$#bj^~~X IDAT  i$#bj  i$#bj(5 IDAT  i$#bj  i$#bjŒ IDAT  i$"bj  i$"bj2| IDAT  i$"bj  i$"bj$M IDATKs  i$"bj  h#"bjʁ`3 IDAT  h#"bj  h#"bj  h#"bjdݩ IDAT  h#"bj  h#"bj){ IDAT  h#"bj  h#"bj-d IDAT  h#"bj x  h#"bjH.^ IDAT  h#"bj  h#"bj_kD IDAT  g#!bj  g#!bj. IDAT  g#!bj  g#!bjY: IDAT  g#!bj  g#!bj x  g"!bjYuM IDAT  g"!bj  g"!bj Ѝ IDAT  g"!bj  g"!bj[ IDAT  g"!bj  g"!bjQu= IDAT  g"!bj  g"!bj x\^ IDAT  f"!bj  f"!bj#k IDAT  f"!bj  f"!bj`~ɒ IDAT  f"!bj  f"!bj| IDAT  f"!bj  f"!bj  f! bj" IDAT x  f! bj  f! bjs IDAT  f! bj  f! bjȧ-w IDAT  f! bj  e! bj܈{ IDAT  e! bj  e! bj. IDAT  e! bj x  e! bj.6 IDAT  e! bjl%|A*>e IDAT  e! bj  e! bj  e~5 IDAT! bj  e~! bj  e} bj(e IDAT  e~ bj x  e~ bjD IDAT  e~ bj  d} bjQb' IDAT  e} bj  e} bjI IDAT  e} bj  e| bj귉 IDAT  e| bj  e| bj xl%o IDATA*>el%A*>eo IDAT  e{ bj  e{ bjl%A*>esH" IDAT  ez bjl%A*>ejE IDAT  ez bjl%A*>e x4U IDAT  d{bj  d{bjbN IDAT  d{bjl%A*>e, IDAT  ~dzbjl%A*>e5 IDAT  }dybj  }dybj+ IDAT  }dybj xl%A*>e'` IDAT  |dxbjl%A*>e  {cxbj(<, IDATl%A*>e  {cwbjφ IDATl%A*>e  {cwbj(: IDAT  {cwbj xl%A*>eU IDAT  zcvbjl%A*>eƬ IDAT  zcvbjl%A*>eUE IDAT  ycvbjl%A*>e| IDAT  xcubjl%A*>e x  xbu( IDATbjl%A*>e  wbtbj} IDATl%A*>e  wbsbj@ IDATl%A*>e  xbsbjl%0 IDATA*>e  wbrbj xKP IDAT  wbrbj  wbrbjl%6~: IDATA*>e  vbrbj IDAT  vbrbjl%A*>e2 IDAT  uarbjl%A*>e  uaqbj0 IDAT xl%A*>e  tapbj IDATl%A*>e  sapbjl%r IDATA*>e  saobjl% IDATA*>e  saobjl% IDATA*>e x  ranbjl%^W2r IDATA*>e  ranbjl% IDATA*>e  qaobjl%1t IDATA*>e  panbj  panbjG9t IDAT  panbj x  panbjSjj IDATl%A*>e  oambjl%s IDATA*>e  oambjl%' IDATA*>e  o`lbjl%| IDATA*>e  n`kbj x!֎ IDAT  n`kbj  n`kbjl%W IDATA*>e  m`jbjl%|sy IDATA*>e  l`jbjl%A*>ez IDAT  l`jbjl%A*>eC}v? IDAT  k`jbjl%A*>e ; IDAT l_ibj l_ibjl%Γ IDATA*>e l_hbjl%!tl IDATA*>e k_hbjl%) IDATVtA*>e j_gbjb'0 IDAT j_gbj j_gbjl%A*>e IDAT i_fbjl%A*>e) IDAT h_fbjl%~A*>em IDAT h^fbj xl%~A*>eԅD IDAT h^ebjl%~A*>e IDAT g^ebjl%~A*>efP IDAT g^dbjl%~A*>e IDAT f^dbj f^dbj xl%~ IDATA*>e e^cbjl%~A*>e1v IDAT e^bbjl%}A*>eJ" IDAT d]bbjl%}A*>e IDATd]cbjl%}A*>e x/! IDATnҬo IDATnnp IDATnn` IDATnnnn& IDAT xnn'0h IDATnn? IDATnn9 IDATnnG IDATn xn9 IDATnn+3 IDATnnO IDATnnn8 IDATn xnԯ IDATnnD7S IDATnn < IDATnn3- IDATnn xc IDATnns IDATnnn IDATnn< IDATnn x"W IDATnnF+ IDATnn IDATnnޛa IDATnnb> IDATn xn7c IDATnnnj IDATnnSe IDATnn TH IDATn xnjdz IDATnnu IDATnnd IDATnn'cA IDATnn xnAv IDATnn:! IDATnn7L IDATnnH=?N IDATnn xs IDATnnR IDATnneυ IDATnnZ IDATnnn6 IDAT xnn$w IDATnn?~ IDATnn# IDATnnl IDATn xn* IDATnn||& IDAT·n·n÷nLE IDAT÷n÷n6 IDAT÷n x÷n| IDAT÷nķn IDATķnķnC1 IDATķnŷnLXұ IDATŷnŷn xi IDATŷnŷnŷH s IDATnƷnƷnN IDATƸnƸnk1 IDATƸnƸn{l%l2 IDAT]t`ƸnǸna{ IDATǸnǸnUye IDATǸnǸn@MG IDATǸnǸnp IDATǸnvǸnǸnG. IDATǸnǸn?i"1 IDATǸnǸnו IDATǸnl%Ǹn`&B IDATǸn xǸn0{ IDATǸnǸn IDATǸnǸnϚ IDATǸnǸnǸn&d IDATǸn xǸnQL IDATǸnǸnI IDATǸnǸnг'j IDATǸnǸn* IDATǸnǸn x IDATǸnǸn IDATǸnǸn ]4 IDATǸnǸnǸn"V IDATǸnǸn`E IDAT xǸnǸnDn IDATǸnǸn! IDATǸnǸna IDATƸnƸnR IDATƸn xƸnl IDATŷnŷnŷn2K* IDATŷnķn IDATķn÷nd&a IDAT÷n x÷nxs IDAT÷n÷n_t IDAT·n·n&v IDATnnOh IDATnn xi  IDATnnn( IDATnnLf IDATnn IDATnn xAcS9 IDATnn8y IDATnn6y IDATnn]. IDATnnna IDAT xnnMQ] IDATnn) IDATnnn IDATnn  IDATn xnj IDATnn IDATnnAP IDATnnn91r IDATn xn[ IDATnnї IDATnnj IDATnn IDATnn xk IDATnn IDATnnnS: IDATnn΃ IDATnn x IDATnn IDATnnfj_[ IDATnne܂ IDATnn]T IDATn xn _[ IDATnnn|{ IDATnnu9 IDATnn7e IDATn xn7, IDATnn_A IDATnnd\ IDATnn$QF IDATnn xn+o IDATnn~+ IDATnnH IDATnnm IDATnxzE8 IDATpٝ IDATuS IDATmAB IDATj IDATewӓh IDAT IDATG-E IDATm5 IDAT xOW IDAT[ IDAT( IDAT/< IDAT xT IDAT͌ IDATK\ IDAT3[I IDAT x IDATYv IDAToX3 IDAT4 IDAT xc IDATE5 IDATs IDATd IDAT IDAT x] IDATE' IDAT0ci IDAT IDAT xtҨ IDATz IDAT<6 IDATkykIDAT(F3IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/auswahl.png000066400000000000000000000007751506574241500240650ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B4tEXtCreation Time08/16/06Zz^tEXtSoftwareMacromedia Fireworks 8hxRIDATx1 1Qj & zt}G: g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 qz:ГIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/blanko.png000066400000000000000000000004101506574241500236510ustar00rootroot00000000000000PNG  IHDR22?sRGBbKGD pHYs筯tIME jzIDATh bE6NG/Fl#U 7%EfXkŐ7$7_"t bgE!r!r!KF΅ȅGh-@X ;YQ;a%_Gk k.7IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/blanko_aktiv.png000066400000000000000000000003611506574241500250540ustar00rootroot00000000000000PNG  IHDR22?sRGBbKGD pHYs&&:#tIME $7&7qIDAThA DѝKG =Uժp{"x-Ily AlHbZ eZ 1-W2-hĴ@.vM Dbc ~AIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/button_wizard.png000066400000000000000000000106451506574241500253110ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs筯tEXtCreation Time04/23/06ktEXtSoftwareAdobe FireworksONIDAThݚٓ}?Jh4@BB"[edbKU\_TRʿ vʄc زb`[BH1hEb$4sgνsvU:u>۷o7? K855y Fbۊ~J>/IGɁ!.9QRX8o`iFXv=icoɲq ߈ݻf{>. !r p68|rL^@;f}o8JWȑi}NgU9k V? ŃsI#vhK*+׻罝spSƘyf=S,J8 :!fj-w=iGMˮd6>JXq(m9`rrhm֮?SXaHt88n/nØfZ{o'F 8U.<焄^)停k#O;?:ŊKǾ64FlM退61<$Ꮶ72NG i,_9Op1Te`[ \}w} 6ԙcl| ;ߍ: 4tVŞ?eh͵HR#6H4C7 N8ʕ);0 5JȺ5#\58Foaɝ@8w1h0 _ d?㡇-UT Ω05^ʗ$p'FnF%pz1ڠ@"VҸd+r{ C_F ^O/ ~}'-k%q2~Vgp55.L2X^RghDd2 JCS-sLzkVl⎛>9n"ǏᮻJ?ڵs6 C1 ѺX%.%5SZNr/~ݭX,ːRʫ;=$I̖ā0uG "d"] Jg'|ì:<1FE Y090;;Ŗ{'_,@ !1-0ruwrw3]kP(?Yh5Hӌ(JfכwGNrIs/#_˽"RѦá$|J"==%V, "ItR*8|xÇ1tv_bYے(K␀}8\c8v z1l9R uCJH__j@㺊V^M*@(X,0/lmr(%PR %q6H^c JؖC ZkzΛ&q b@Zժm&$1Y븮KRaϞ45;k-w >?BY8UoEr;!I4ijHS@byL ǵд) ۶i4uH)yWضIJ-%-Trj-[͹I\fD1ۆvjeD݆-hl\,y墄"mX֚84 2?8qrCbeM~[ސ@dDm#Me[fBKӎ Q$IR4"uC|f\JGRqlylE."ZNHt:s,&i.DDQ>ؐY(]_T*Q*Hqͷ`Yv & C}'(%,憯T~qwQ"FOO˲v+&KAy^XKo_޾"bH8v@NJ!<%RgϞ}ypUͲ/~/3cb[6sSfOa#r>WEST*!B ,PZ7FZ-: 8ÓO~$86di>ɽŀ\HjP,W%cy۲`9ϕah6Ab v:b8Vx5LiKf1n;]I4s|5 &Ga=C:8s]q' A! K^s===] | ^,$"XRhf~N{{ϗz=>aX$CzZ΢ov!kmß[b=}Z!ڜM =ojh5W_MOwvz8(hɲ}#/G2??,f&MSn8LOO1I000@j5(bjjBK/Ľ}UثQ\\.wϳ,#c癞frr4Mj$S=!}c =[n] K"J( ˜kk{۶u?lQ*F9j4q(KT$6R.MbFaIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/conn_fail.png000066400000000000000000000013121506574241500243350ustar00rootroot00000000000000PNG  IHDR ֛^?sBIT|d pHYs  ~tEXtCreation Time06/05/07ԷtEXtSoftwareMacromedia Fireworks 8hxIDATHŗAByAdAQCJ,vXH5by).&9.TD"N0a7o o뺡)eܢs1͗|7~00 )%k=.]̳+<xp_B*l.ɋ5]oq,;"w!B`Yzaf pJd|ylnApi}7gBv\l,~ǡ}O 3>qᄤT nh654G MUJVP"[GZqΓ}3HOedaұ/mL `ܤȃ#P9KdYj\[^ H;Kth~T\a!@'=hRFP.Y0 > _@4(c#- [絰no_jt @)d.1YJ.\-[ЂJx {J_J+G,偣p#8 ^I>/8V 5k2]NF>.hj0v`5+/UUeK$Ж.% 9J J)FkEFq꽰UX1ҹ O0kk粒3gR]]mܹЮ]Wk"WGE2p0X| ^pqR.t,rϝs˗/;N2t33fX/{͖Cc.Żj=;-~|oMlZiH9h{,Zhw444,фqbKl +w&@ONV9Y +WdD"e[nm\|y <o@b4|vXUOC[kos˖-aYeJHDmlkk`{{{fϞ=pGj,#Z,n;Ma(ތzbݻwZ__IJ,4t:dz'N0-ٳb&Lc)m]]mcSJ92TVH-nذ/H$ׯ_ÇogY;bV9oLD)/e;WJy'_`o56661\__ݝvZgKKT*z~`ZޞkGBHG򆆆xd_+pGן꾳"؂2䘬dm3śB& ?M^zIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/dokumodus.png000066400000000000000000000264011506574241500244250ustar00rootroot00000000000000PNG  IHDR22?zTXtRaw profile type exifxڥu;# ބ?F7=DlVUWl5K&Rs[lW? y}hyFHX:{=oӲ~ c?~?}q>߱xN~,?Cx _0Ӈ /y3]^#:jYy}?n>g+%So '|?޵Lj>E_Y933IL#jZ[Tv=]str&C~_x$]:XCO2BԷ;Vdt޴_c>;nx_}9jl}ܸ+A? j~2eH#qߏte=o@xtb0.Br}q@V2RAB&7Ѽ{OÆHW!7-tc~JPO!ŔRN%R!ǜr% { %J.Jƚjjo@3JyfΝww.}FɌ<ʨ>)gyYg}*vRqwuv9O9_LȚ{fL5r '8I &:2^ +g2y 2)g)cd0nq/3QeGy3%~ț/35oemأ T}29Ddv:l .I^Qab+rϞ3蔝1#Z7ZQ?b~2gOFZ 1?  \kylXc f{{RXm<+dMn5Ù\L' ZݚG)9JN_G+8 3w|43%LϴlY!,"KqÂSL~_7sE)՜cтYZElgve@bR{>ivc6O7WjTʡ|^gkk2ZsB`>~pd ^GSm(@h~j0py }Y>ﱯML/Wt#"uAr YHY1ǦEVPhu#ڽ*> &!-{@,=#c<`ZX'r8&5pE c4\96I:0HmSi״vߓr u80i36)>1~á wS:?0k{Zv W3 Ό˱R>1N'"4(eB<+IEOA^,WFDY7j)d !#&8g'.B#2=?(kêpnk +t.{ʨ(;í:=Dh<=Dc'lAaH%Fc/Q%a_&Rwy+E1bȌZtJF"dCІiH\(}5&i@ g^:NEWnaAD(z;T* 9\H?hKgn&0<4&m%l ϵ!2} .ۂy423fчYۙ*g?@sٕC7O&9mc3n?+]onyеtuob,; sPyPs30 &C{ణJzDH A3#r8z50Һ9]{Dm[g~/[n\H^*@QEEE "È+ QXd E#?EKC#}# M#z2Dg+I2"?$Lq4L̢1i",/#v$cY]g!B*FHcY9 H2ǺcߚЉ'' t\ڃuL:4Nx/{TQ4Z8#31% IQ:M<ʦbBq %)jy" +gFw"A6z" :ңbh|æ:(^~4WO 3 "Ĺ=D& n0I!bG:G%Ƅ;> D<7%Aah%=APLƄ10e iJۯtL  0ѱWU(jBtYIa“{l$F 8^,刨Jo@FOw1)AyN!5{jꞶi>HEAMwHȑY:) C\D(u}9V{tPy'Wμ=-ub߼S{J j(@7Pp[Qp>PCqbI7:(朄w\26{P7piCIWR!avaʕIb$#]R)G0gIhqݛ I7ѯJC7RwѐM }CRB.XR@%ب,;{B)첮+LUS;h˱r7toM/Q+<}m3'|]M?6i",jIHhCo;K)bQ)%p5eRNywR@P}ydyhgZ#.v f ɐN`!XP'3eY.?$i= Z)\ޥ-2vOVGb!>. @Gk*#"lKSGZ ^C!QCKkk(pin=Azx[ikI*LU. <qBԤitB%U"[hJ?E*`u&an.FK)l؎\UA:< eL]T. PXi2ьڂ 䊭ɀF MhEDb|p '䣬/6khLYAbI=9+~FﵟVhS`-yH Z A!F[Y@7kۡsj /FWLʟ&42[KVJSU-Jx'e=P<2MgoNԢa:EFP߷.!} KAϐsFw,SJA5+ w#zij]xp3Xԁ$&a>9IEq1iLѐW`hlxR$1ܖ:Mƿit2aw'c̱aF`,Kn=Qb费Syk/Ք^UA m1 e QFB4Vq}Rɿ;h1&xZy6&n]c5ܐv|RRF% Šd31O1eNDY '´H8n=Pi(,յJ #w#Y<ūP4Am1E7^YEfD|"VLB6GWָVE4*IM/zs{7,gx|/b Q(ڝb`- a^]N͒t6D/Ƙl@6X#4({d5L`jfqƶ芤%ŏLύ HbX #*V 45|-PO/N`Jjb7%4zφQhLHQJ4MĺД=P398ATk^RC3p>F  ͿMiSL9U!`Du|4߬f׫T>o fIx4dEI"jMF*!Ie'b]Mh,[=Wfp<,N!:Gk &A>EmjJZsAJv6kw` k I^«1(-Y[ѩI]ojN0#1 !S#4}WDVfKeHGAE4L0H)1"H(౹!XzaRG7laO6x5vtt}V5>|v4#C"[6DC潖+RՑE UYtY`+XaW4*Z~N`d1"KIbAW `NBkZTcA h^#,[6\r% p0#\ U]?kxjo: C@)ByIpl2[My1!Bm2&$-_G򼽝wcYW4?|[AQ8lN|iҰ^+O<:֊VBWJ4>%/bժˁ(I3GKg]]9+PzĔJ 7],E&C4V8H#4,"DK q{*MaFy7mu3JuصZ +nA9i)@!CDE, 7IRԫcus@ܵ ŻIj0QB'OalGT8WZM>\~GfNBP  ,+}tZm^f[{SMW Uv2vP%T8Ĥɻ-)z.&+Evږ=HS7 '8"񤼄ޡ9bUdOɛZ︫2-W-+0 +s3Lp2jǗ}׮D&u߶ [ pyw;8R6.T FXJ4Wp" bZRx_@|Rgb({SVPHAs#H* Sګ9Idʕ7٤=ce;`YRܨ)|GoRQ>ѡakR?QAˋ~#>lB_81&:cLFE1Ծѥf12 R :]ЦDY xJ+N;` [GbWNMJ˦pЛkysCm=! Y6[0Ȏ#,Hވl~48OGVz/nw͜aܼpӴ-YL[gahyX["_dsĜ$|kwC=dB,:kAq 8Meo߁uP%rAZ63[ʲ&: ̦ d̡Uڊrt@>D ނow }hG& &qjsz.7ꣶ]БUഹ4U[GS"}%l zKSLi&p+65ziKbj+ ]W̼H\j31W-p bA{YɈ~,VmvZJpqpd jw#gׅyu-/bǿ]OywKoTaKo}鬏[ `Ӵ+ڗCIQx]`ˡ./$U%G1m#vFV]\ TrklǤx>u %{g^FP~_O֒>|גޝ4֏'ͿIc.ZwX_aP`,oXt0a[QZ:uX3Z7Z7ZQd5?#A~9c~kOr|1¢IRZ9~bDk8;}*RNf wBLN|iCCPICC profilex}=H@_S"A;8d:hATQP Vh MGbYWWAquqRtZxp܏wwVb6e$bQ1^=L}N_.³92Y>xEA bKGD3&M pHYs筯tIME"IDAThioTsfm6xLb6C JDRm>HկhjKj($6xˌg3؞@F9y>G:s]$5D$iGC$dCLܑ"I1 Il$..nqG$PpTV,/I90Fab:Srl6,q>y|+^#?>:W9y=2} @o>#E\dyhszB 72>g^;it_'RXΖG0Nq?Qu5_ 4 n^ĝ/ذ<| nh^/|N0ѣS!{V|DBQb\xmOZr|GŽ0>+:,J_8}f:̵3}ji srPkkQ,ov!{Zˮd T3j&~[ idfI"C0m5Ȳ<;ɏIVis5K &XZ9}mLXbME-qH"5?Ln͛Ipăh_ġb%(*ΠXi5~.ūJgޚs4§P3M}u9v aYh3~" BH̄I 0s/G:AjzhSWaljMv",KDȠeyeEel9DVN sko\ٿlV` p h4ld=g(1Z\{KyKeЈzfG@DGkDMoHbboY_:`qm";3I0(n z]*=c S_h >oO*Ϝ '>!1zְ4- !@  b^@&4k5VXVv]ߤhUc˯k.6@X%!6كALjʠGŷ>~,˲Ϛ:֮oGX&JhdŒbYt&Mi>:Nw'?5;./na:JMla \j/}2vij?`ce*W"d,-Ftr54]iPײG?~Vehʶl r3e Sώj!7ʑw(%..~ Gi.D*l`,_'F̋"Q$-k_t}G'>4MO)ZMqmBxxB"1iw* M…vAX K2VE]6J_윅NܸzKIN00҉*bkNa_ Ķ,H'E+6a*6CDn04wƞ7ƗHHH\xn|8ֵT&,EǴL4E#ϴ7!5 q->_\9PЌ4PS,[-0Gk˅8oXJ߁9ΛoO 1LwqzӘT'ds5[?O __ܸ}Vn? DNL>zЕ!bd\ d~p}q@Vԙ2RIB&7khR='dž32B*$+DXB)JC9K(J4%\J^C5\K| fjV[k3fʝowN}FɌ<ʨ>)gyYg}*vRqwuv9O9߳L/?5̚҉=k|Z%$)g$ qd(Wlu1zeN9 dRΖS`Χrg#ʛ)%of(u_]֖hhތ=PA0c@F",$}4,ճVa (cs}^̍s9y]q N~Ir5?X5"^G!= 3~D-|A>"gs$n9Lo@Pj~szs6,Y}ug͐.eUkgH*͍ @|[w35Jů,vAZ1:B9GsT\a* =?j-mJ\y6xavS6c& 6l&|z5}SjTC"  (5迨lK׌(bI rҖYmeEz} T9XbbVN0|G܍C1!kx[?~ã[|G/|)R ET&ĴD02B%O"X1w)=r<+.Dfό9\#UF  %T*Y,]tF=}XVcD29]j*r9P9S3!wQ%sn0Mw0DAv{OLTiMAu>gE829W%5i֩3TQa?l>r{DzبE$v̲J3pAm |{Ρ >A^IDv=]XN/O gq9@.09aYa!vˮcG=.íS5Ml;d3zL4@Uo><X ֧P\W)~eMya/bZPyNgѫ$q0,^|rbM :`-av$Wm\-/cY;q4E}'ٴp\x*KNI&*]O L0l9k(FՉ?ao}gwnW:ƭ]BV\ToaRbd9G0e1lXJ gEYhA;, Z0a_I| Ig7(=OXr c9Loejt0E:Ĝ @Ƀx1uy'/ yO Oó 䃂4GK]2:o-ێ2R>ZPiWqkBO ZIElp~-q^fy `r.˂Bߞc{Zh]Gpa3B.4l%ʃ,MB9՞ #"DlډD@;n\YL#`NH6s)&/vmڮCl> O8ٌ݉ `Qh^*\ZeRXA_m$Ov(61VCK5ԸP}NmDI K;cJ읯 fK8)k!&zl6vqH\݅,{I)WW"G̟ #A*>)8^Ԋ,_.gKx_vmT}q KmLfH^!X)~)@p TX7C~$$ig@{ ( ٘;4oϩ)ZiCZ 5gA 6ճ Y6FX`4 NwIUQd qr7@+pVV_1!m'l H:$v~g<8 i81+RZ'8cԠK|GC Uh4ni&&*-P*A=&MKte3G1l` aHZ8AGF0WE9cpTnm;?v+d }/(g6JiHuhRjV- F KTTPa<]\s f$TESN+D icc`G]'s QߚС@5p jGH7*I0ڏax4;i?s[Ovz9q]QΌS;ܦvjJN̐# i LPQSuMmGNtW@.l/%wO+Er"w*Q㔶ΔUx) ]WRMoJ|~NL6Bh8sBn8C=0DnahݷW6-KF1Oo({AR k_CCej;|:7PP %ld&šHZY3L"Fq B5 =E݄E"&HGG8|ŻÃN=ħi"B{00.D˹`Z[j4:#,x&0mϱ^S{G+ |"H4"s!Ұ%( |@]UYB =4`Y9f.I@H[f #08Ċ5:vL!-~HMo 3&߶g7PKDً]?Y[y#pt{sǐU#RrTwJ$0u~hanfLqͭ2ͧ`vrG2(GKB t݊R(kT !4!@LmHƋ/;`CRR Zn:ăPnZ_w5A'J`V)N7dQMCU[/2=* @) W 553Fgy-!&^-@b,1T*L TE&!OD-@Ft#)?-"nQwl#O@=K Dh#A9S(ࣥC ӏ-եnH9f.e56"iS+#s*a0?l@?8Ș(zc1P\qr N>MւiwH.Δ]S0RR=h2Ю gAxD[I-WUԒ6Z7G^YVD4.<3EJPzԕEݴ8 ͱNgFCfCoH-dy92 "ѝtlyb VicpئFd=Wz=ujZ?lU[]5߉Pd6N$Ч[P5|:td`[ͤ-0 H@,m\UF; Z6<`Qڊ~^BGV,Y#50pi \p9`  I=X7]0ɨ*SifNNq7 (t-8f\m0ݠ`őzr;M%_P~ܮʀU؛;Eh}j>5jy6꽥~jOiۧ reM[5mm%P GSۭ=N* @8ҲbAP`![5 &xȠ=DId A:/f ":˟qtVHY=5sPC.;oOUWXN\1hIѷ|׳(e|+%L5cc/|F { (ĊkUt.{ F-O}`F s#ƽ1 eo):(؅G<9[> Cǯ>Ƿa.Bsm۝qPr,mx23e Rs[bGhZ"/E5^, "|fmB!FȨ&ژ3R=0B8u(&L BG!1^SP@mOR\Y'v!ڟߐ6oo\E_{ a*Bim@G3b<݂2/{Pkϸ;@LX&AqBjKg&Qz1 d1v5<ߺFaI2E 86O%%d: V>[&u=u Ɉ Lx"in8#8>E6aRWp nMs%zJRBdD$ hCS>NJzDO&duӇ)Ps!P.FQtj-ԡCF#7e67 8zRUg2S(mj'F|9hn̝1?UC=`<.xpn3<_u_[g?~g %阻q=Wz#.. 2ޠ(E` =9 bA0qoחnn6xu ? c M C}w@+ZGwRj~< }xEA l-!bKGD3&M pHYs&&:#tIME2 IDAThYlTw+61^6MLBShiH4ڼ$UUUyhTUՇJxI(TT) mH !:eV{f<{sOk 1|{~~}=JSS X6EQx5ɓ-XxBS +(ʚeߌKg~CB6Dfp6o_ߢﱱl˗jg-! |rWtqlECSPewWbbm_! o*l،}.Ӊvq$LESkO#B^=hZ(*P4=$k:QC`$C|G _:gCzMM޽T F>EnIaU' s?\׋磙)4aAhCC&2w} ]^bs?a4G;wԼ4Ak B#l6L\c*hy-eg]Ug<\= E'/7 2p{V:cDJϩmnO(;5bs(HO)I#}ztfv Onۿ!* BLyrwnT_fE&XxHDJXtXfɷ(.bb9|Ksd3[0P3$g MSB bN0>OA>;cC٢bu۷>f)5PՂ-:dPDP]Gix|ҫkٻ%j-UbZ1&9;RA+I(]zdf.A폏&،aC$txao?B~{ 9h7;? _΁&<b8=a-nc֦8- M~~爍SQbzr[KQ _z'$lWGqm,SX' ba\~ ݥW o^{#7L^;}uY"AՕIDyx]zvy"'cȖa[#w\x[A#Cx98_~s^+=f> /r  9&/r  9/rL^9@x#^9@x#^9&/r  Gȱ@  G/r  G/r W @x#^961y#^9@x#^9@7)c G/r  䘼/r  9&/r  G/r  G/r\a9@x9&/r  G/r  G/r\a9@x#^X @x#^961y#^9@x#^9@x 䘼/r  G|N5lIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/entwurfshg.png000066400000000000000000000004461506574241500246100ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#tEXtCreation Time05/05/06XtEXtSoftwareMacromedia Fireworks 8hx{IDATh P Qwb^dw>{?Ch2CCZ0Bd> !BcK63&30ӛKc!4/MŒNo/Y6pRIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/entwurfsmodus.png000066400000000000000000000027731506574241500253460ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#%tEXtSoftwareMacromedia Fireworks MX 2004vtEXtCreation Time04/23/06kJIDATxݙOOW͘18DHI,eaȢYxee|vE]d}VJ(Mp" %TMQP 4DQT$`\tAl<&ϘGzh,{9FDQT!G]bDK]B $`Z| ###,..9}4\.㨖X[nF0==͝;w$,eGVWWk׮ֆemmׯ377ɓ' K3Xnܸ,AGG===:SSS߂ Ν@bb||".]BURϻߩ½^/pP(T3Y# ( RJ(Ry&5S^糜ș3g󬬬*(J8p&aiiyN'@`Xx111ax%###,//j? MOOϟ)ӧܾ}T*U"S.`hh-J# t?1Dm$RwoHg/Bi: rq7⻹yi 4z7I{{ _aɗ1. .T[P 8Ozy?| aHϡJ 8X)La^6XB &}IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/entwurfsmodus_aktiv.png000066400000000000000000000024721506574241500265400ustar00rootroot00000000000000PNG  IHDR22?sRGBbKGD pHYs&&:#tIME 0<IDAThݚKL\e~w^2S(ci3@ /T1bIbV]İsac +nLiSLV[yښuG:Rˣpl;.' 3'sg1Huuuav@($˶hn~ ;D{[:*+fay3EUUhiyϱZz<Ƶkp8\xݎngnn6vc$I/:::eGBQQ8NzqnxPEQ!B~FGG1  dYF8.];}ZdY]Q{4TU˗?7GΟ@*!WUWH$4!TUU%|&QTmh f!Lss;'z! w7whjbZ.5a2:B~k7l7&'' Ճ$i[`N>9WY߰X,:Af;{b.F <pظp1#? gϞeq1Ý=i\ s} axдaS566{7q5 U\fpĉjd Dy} 5(jB>Cuw/е~@Z_~71ekgwA0!A afI.+0'@Y;05 @ɎUG V!Lsĵ׋8sLN+`4C=(L;DګVОNHjr@?۫_j=#m s_# EDi󈘸!D`vFt(iO2ۏHgW`=OͼۖAmoīTذ'!K[`٪ }k3vikДCؒ{ix|ߍ?x<4)+/+GaB3%ӱ)1-HrxQ%8 q }6ؐyϐ$ܰaV 6l_FfԃoU)/![s0 d36B(_l{'ю]?6l.l?Z90H`,—r:2=*eygFr!44 nZYI'O?I=BkpIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/error_hg.png000066400000000000000000000005021506574241500242140ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time07/03/06 5IDATxA0ðʟ`Q ߶5@h Zt5@h Zt5@h Zt5@h Zt5@h Zt5@h Zt5@h Zt5@h Zt] IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/hilfe.png000066400000000000000000000041731506574241500235040ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs筯tEXtSoftwareAdobe FireworksONtEXtCreation Time04/23/06kIDAThݚKl{p"EJ(Q^Vv.4@]骛ݧ@RxS ]uAcu[Íq"c[~JZ-QwkP(P,x"RJѣG9y$Xx$ Cazz4bH)y8s?}gK;d{ ׍FR"˾Q;oii>"U`Y 4M\%H!q/')Rn:tg<,1==źޫ`.i[t۳& zmu\%Q(HRѵlȭFH{rN@VxG?0 S*MӺM6;2Լév=ɫ?x.ޤP(0== |˲F vxpR,frrt:]y6N]Ǹ}Ldll|>$044eY#'!^NJ```\.E2 m陼R2kɎDd28sssd2m/ȉ${m\ellrmq7D^DʘI?CCCylj{BHmH XE<( Zctd{G#s}*m>y7nG{,|zZ!Yxq3ިUUh+޶Nϫ7Q˲37x: JU|w ("H$X~ȪljBD|dQhݗ9^D"RgRʠR쾍3~7ZUUVB,}OXy#jK\ZattvUv˲*dYzkwoj$si"b$O;n\CG\ZH$rEraDuEhF:n:b1lu]Lz xa& fFZoZ*IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/info.png000066400000000000000000000052721506574241500233510ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#tEXtCreation Time04/23/06ktEXtXML:com.adobe.xmp Adobe Fireworks CS3 2007-07-30T12:02:18Z 2007-07-30T12:06:30Z image/png $CtEXtSoftwareAdobe FireworksONIDAThZMh[=hQiILEZ1v@qNH&+ R;}&nխd*Hj& \Z'jB21-JL3I&M>~n&T0/ EQ~d2C?KO( wځ~ᯉ" D(:Z }?-UUD"Y6 O,?W{ڎX ~eY,ˈD"B_uG ;D"(˸tR_"QǛ7o`t:{e~|~A Y箉D""MDku6)EQ3)>SlXpILNNiJ6\P(\.kgΜ XmcA\__ƫ*666( Ο?Si_|'OtE(baa-EQ I&Fq=9qDKE`)GI@XH`0]}P̌uLLL`ccl6t:m8g#F*AӴiqӻQ(vW`pp7o4M{jnXW- aaMLMMjbhh+[N2$bk aESm6$Iׯ_p8p8BAtz v);###ZM#LMM!Ud2 ar077a|><\i@Fc~~p\+FZ8w|P@<eY\~,"#vp8<zR ȑ#8tPKgϞmIme7$߿{.AМx"$|>Aǣj( 8v,KMڶիI Ia=y.m޻w Ν;&ÇZ@>$I(FFXT*jr:Nc}}r6Mo.g\.@ X,B2LT*B}y,[gp:ZfX[R޾} χ NL&/^tTTJrFܺu >PH7P( atsf#[Ju|M{f,$TkLή/Ϟ=HPIPTLiDPynnMu9Ə?:WU_urM//"l'ek,˿b/˵:fFZPw P*z&n hS=Ӊ@ Ы kAv)eqmBԓ}G>} B]qg82kEEDL |pa\rNLe˲,!xؘl77 J2"M!T/]ƳhXU!T]W?A L̬$dGZBADD;%  o#/M_[rN@UU.}![k?O!T]{%(~Bd1~/ BEB S8 L~* 5 # <%&/$ N!TT0D8U*+ƞMY?zZB>˗ [; Bx2%6; Bx}!]v*f, ]=f^B^龃j7?u+pm-Ef* *) JqFU Y8XBP!Tmi/<Ō m2#A=G;[`( j'}Wt^55՞k \*wDcڏea0L|nBPN>^ &)|Q B x !pGɣ.M@d&?BPCS/;׏,;wSB!5$"J6SUwFP!T!0VPw^*=4DŽNa 7[8bӪZBP/@1tㅬ1ƹ( BZf@$T{o\mKHbNv'n7O-6{i0# /v.Pf!Hakii#/%Ǭ)ljDt=54ۅM!T]<'F<8 b B/a/v^/uM|]v3 $W݋BuJXXm47o@&P:J^?+9+`Y# 'Q(5Vj U(8lf!1 oH%ǜ[^Towɏ9`6%h<7.X"l5ӊՂ9j‡ 8u/m7a;JK.}>SK4LݽQ;&1pZB kf2j4ԚrkBi j3R4M/R$&/"4Z>_H#w &0E<᱿.PW3flE1$Q.B1jzM!TWQYK.,$mIaߘB[!M430I[6p;!AvA>,!APU`0qs7n!4gGwWQb]E’ZOfG%2@!T`s=G+6UtbK)5SK+.t6m /;2#Wd]EI)ѻxg{sGBթizC7H@*W`  h(Mbqy,vWQHBYnx.N(p0Zj BxJ-Uإ?c@P+=c ?29U͌]'tv4YxR*B,~ICq]O|P79FWoIf"_* NSwT!-QP!<QΗG3rEr8WrGG`'ߴQv>q)IBI'ܨ7I"Bb1b뼛畧X xC6O n*0}OW=s8X0Z(s3O2f$A|bW?1 %[ZL0Ik; nqV(NO ]-&vBx 52='ۉ.-*aA$~Pvq$Xt7Lӟ DS-`@]^bdRY #d ; V̤KFq~MݗG=uHFc d\evM}闫}jW8(ؿhZwz3=ڼ8Y֗+v7H~Y='G;Zi vS7|Q<7f"޹'U^:cBx i7dGe>zU,w,9w8&r7\F|L"7LM4R71C:Of @NuchEP3-w+_&a"%|3,K`ueO:ųXW´& ˜c A@ F?ϑ4=;pRA}`('MZ[]i"Fhs(xo&Xs ǎG~@T;"ErbdaB\(ϒ*JF_Q/ЛAF Hb +Bރh0Y4HqUW,{u{eQ)Ȗ<ɤI$Ӯ)M<[^eK:2o<S'Ap{ܘ&Za%9^q׷:Owp2L(`k4zVBad@RCxxn!/~'0^ $; Pc{C cH 9Rgۍćfp߬9o6'6߅r# m B\F.-[ ffifr-pdk|MttJW~hw>ewGlI.n%?J]Rs^ֲ)~6 Nvc2:l{Ϟ|IGc2.% l--wTN}W2ʲ)&ϥ0~ !.m79/ 7$8|j\{;xdxx/# W"@&vV48R` 8p& pbeAS&q醔DAY|~mdU8ps "@.fЗ<ɟ0#_)1䎸v.g0++T^M0e33!f8o{& _G"| 0"N/G1sn$jvi!HJ-3:mhG,)$ sUӳ`ag ,;O%}3s;-Db†0Y^2u!(=>ԾL!̦i]綴LKRO6 *bq `~l Bj,_#׈ooݥ !Т(JFkgJjY=2-\oq$ޝ>]>^F r8OXt\O ) ߊތ]n\SLvB-QREbHI oV*wD@(d|(0L{|LCvK&k2Y7-eߵOvw38a1nInO<$].8@g7Wۦƍzh(/O!zpgpr ߘ_:́.}-2B<g#C_]!&^PW.M*S_l ?K<;6sA"+x+O7b&pӖX*yvH>)G`G m !ƥ"6r8'DEBT<N%֖< ŖBX?5-'qp$W~Jy ^| vK> aI![ J2*\aDʾW{z L1YۨleUqYDH9eK ]OX"kX-,?%z:^wEha XA n\h0a; Pkb"--p9q|oB%O@l.wEQ恓G؆LZAr=6q0@.8=XB㎦AHӠʼn?911qYዕg{9e)΢Br863:&JqQんɀizd>ï}Lg.Y8Rv:z!@] kGrYrQ, 0Bأ 7^x/%׻ z|8[|K2*H6<]*9p36ifkw|IG;"/E|O"?^Q2'O$CqE@ .Tj7S^9 镮kĽ n6pƋ{_h᎗+N3O~""^ro2!Y726a*Ԕ)$mRQ4/ d] NGfG 60 TAZ{uʛ,3$bč<4 JƇ2iTI6ޅ r7,U?ZeQ~ 'T aFSc0}ہ_b0Mm,p}" rZ_֣d>,$$/10RgoS*` yyw`*% SaD%{X x,[3JI~c93Vw"}Dnۼ#u0\ q3=U)_Q ;kۺk0lwm M\M?(rk8w] yO.maҰUm ɣן싄`2%xՕ@G%Um˹uۦ Z MLH2f҈ۤo+-G"pX'V@u]#M*:zNںm;ڔlh4H= &ȔJF8"}jgaQfL҅Aȥc"*vܽ{sr%#Jk%њ}e}pL!%}\Y5S]/3IKOn]0( x&B&"^$k`8ci[Lg!͋\(]"ێ73k Mo ^E-p \tCxƆVgXgJ /R8lKzn e͠@'N eſ.!9vƄҽ0khb )5i3@&ZuSK]:RV,12%S(z&,̑~jO2Y.n$)2w%?Ln ]~`^~ H$mEw5= aAQߊziFs$#>ek]r|[5.Rl(^NK!a+iNP!oPwh??ɨTۺpQB]S.B(Un',:_M.3NY a4OnL Dk xn;-/'rwe͈{[Vo :3eB8]Q(Giαۍ`!!}uS9R=vI|$Si#/. Z}eB(T(<4ܠPb| dhē!à 5Zr/@¿6f owc:&*ƏȾG~T' l.8ZC8Qo_*^^9`siyc8xM ҚnH-sRf /|գEBi`4{T ]cf N+d憠l;m`36p>%s$/>sn:7wtRK"8`z_J5*N6Z#ܧPy AxO YB A݌qOa@֟1a7!\_ t^<[[R. BuZ~/^ ?A)u;4IT|SսC(S陸@EA(خz揉P`̻  3Px- 1',^`$͡xx@ "-WV{% e}եBN Ac!rJc`'-aYBPH!\ LT!\Q%f0XTgsh)k >':w\139CG#J#3JD"V a=TTyCH% G|UC8=cTyBgϟSH U+nX}bTԹB8J݋:%:s%Lhu&e^];U/ %m֢~Vꃑ..B!ػ\)S2KйAx8PuA:3'J輨btfM۫/pLW_TuA:'dzbé6yE,qsPzcꋪ)\Zˌ8՛E@ثT_to@u xR݀^eg QP<irWdQ_TV4_p8;8gaw0{oOjCD4ՁP<ѽ_m qx& ~?pm W< ķv_ɶG?i*N'1ߣxϦۥrCՆx%1u)Kז?;i{6%&cK'gc:!lF^eœ K_3r? BZ!ejjf'?gY&O!L)O] ~*& T],~3n7;%|T{9BxZiE^Bm[o L"W!~G&/ ^΋\A\A4{_@o\LW (znor3QN+}O?/,48Lf_Waw0PkCo%{tK+Fv?w?7W!A@MeDA`x$KECVÚ\x2#X¤" 'OG? ?-5~(k FK-.qT}mjp~Bh rD+s-)ʯsS7۾]Wz?ա p_#ۨNK!\ChpGZ1?]S3^s;RHٜg&SKh[Za jcv5ad[H4k'߯FYҖP!DpU.h+rܟ=d*( [F)'~ᗠU]21rP, ETmiE`{?{qn%Xk}C`fPd7UlïH!BrGuPDhV+tmL" a֧BB`lH!ܣ69U,ưpevˢ >5=Lue Fv'@1T @!?z@ Ui&hN݋b4y$PAD\`g:;rNN?d ^`@ RtW{ oxrKw:)=덒nzzXGu@Ix  "]Ro8߅0JeEo~BDwML|Tp$B!|t>MRi"\.p|p'yGċbS] #-s熳;Ima="lwcrn[ KpO.sm6'F[?0ں}C0k-dVn  bMCK ᵉgGz9~ ɼh>'ނ<|+rw[rFxj.yB|:j)U&-Bc-3bݚ ~ ! Oiw3DO BHTS_#h>~?Vo􁏷V7wץt/gI80RL,&:[ E|K4%3NXEA.^?+Q /9 ߈S91n[Ƀ&fl8Z fd |')QX*T;6KNy 1;PAӴ&֗Xn$ħcFibԚB`=6Uש/%oA)Y xT"04 D|hH3SǃPV JRʹ* !❽,Q\4^zuLpPWjsDG/S OGyS$S?p7~ /B P|Σ}'3) Mc` Bh5Yl GK̂~'ɣIXhI̷,?~"|e\TZ,G+\L#\!7Q'O%l{0)Ϫ(Rb^R 5=yMDꘘJJf.onGړvWuJp)Mz7z*wfG6< G·LzLJ0+x;\NަziG]~JygeGɓ6<ζ%ָ;'Y-ej1;/X*&xo++<:qC&Toy?yoQI_M=FQH-0J2}xJ\85o׬SLCy\iPtsV/L%Yӗȗrʹl*npDŽ=6 +gO(zKMZJcFrE ~xx"r=)71l1$ST4 ? ?5ͧ>r` ] xjAnE&SYߵ]>{)%RG+hCbMSCOZopZ5C*ppw8ac5uA"t5y"G܁.[﹓$(qr-q81(~%lyeUz5wz2Rs- Ae=vÄoZpeu݀E"}Acn ɣƒEG*|d el?ʜ*-# =ȧ> gMlK^*! t+C,@ᗺrV~i.?^,6eiK`"\sEEXtB?&k4mmm w5fG}VqC T!]aKȄ.h6bo!ʃًY,¯KcLcۉaWw̸"VL/Bd qwͬ # Ĵd}K5AMbB)b?7߾!wi%6ci)=3CKۈC;WPdwNXh/ c{3YaaÑ6~v c1e3iXxDPG Sxķ 6J<N)=)u#I٩=)( K=aIl>Аk8%}TaJ1&'S@O!w2zqAܒE"zh\@M%z OYƒ$KQX虰0P̎~rNe٧+{; ҷ<HsW1#-%MEv vR|h3f҉2[񥘐!\O!I}io jަNauVd@"΍]兕L*Vop[Lfr62;*0K{ gA8ڃn~O+'HON= dtl!J8m"/R&zʎ Z@ Vw N` %ՌCcL''3xz~f$ !}-Ÿ*p )L\] &R}B@яd!% iBm5_G2 |ʶ j Ih%t}cdTJ=h¬$SR B|fiAuLHgv>3oU޴)xGɅ 9{7CZOp͡,$0s*Jd=SY(ӎ!qLmrMpAiȪnKJu Ug(*з!DEb ɭ4=MT?Hf Do=0LH9 օKijbl"ե[Rs4M4\1V<=fjd~+Z\D dWlTE- g ;yM&^PK:#/`PlsXE}%&^'fbmJӸ&źR>ޢE % 0)/#=1orT`^h!i&RChn6m)Aۻ-"P J[{I夤A= 2e+׷!;B .$+iכkLW 4Ti wJWfy,Bp\і{X67TnDXR6jww_?/ BrGСA/ ;\2(nhT|kb2!p.'x}gQ<`'ogNx};&xPnNV\QoLs%XFvPX}hSW I,(gDi( M~qǼRۭۿj 7/JϾAShy 7DF&3߀0^Pz t!);!T/gnY2*Y ~SoͩӛȚ@HDq siVf0*YE-?a˷d ?bw:0x.FݾN F NQ,v{̸ >6-F2w(VJӂ *@cE&u_:T\w{.L#> 5ɮeW.[jN&P l"RXj&A#@ E'^ llJeirp&'^P1Li3.*1ΟU} _Lid(p+Du~At@Gdfh! xӋXgAOMb 1 y5x:<)˂M lpK|?F41Uj&w.(s ܙ8FxJ[i!Qx}4vQ,ًg6>=~׮FzI6Rr20&QS]L*-^sc8&Y6,M~_`(Cba2.+^ E@HyV0x*;z%ҥb,*+#;iEfº YKZZJבg%68@:skUQ.:=~s.<`wBfx߁p[ ;F1ldC @A,`4ҹ!LmyI &d͝K;̧NIyzAh U368hyNQDp&7 Sxqx\3R"Z(홙O=lC. HʜL\apJ(Ǐ0 }89K+8Jc@&[l~'i_d'(z߰T61ZrQR_ ps0ܺĞxϹWSZ)_c﫝|[sשkGNƃws{b+wM9D=QoSZdsoy@P }H2!0=E[T+O(+-BsnTU4e\7UK!PVx TԞ'l 409`#m8aRNν*%|B*_:NSŪ &mRS*wo};rYn$)T{Y/^%J!uiZt߽ʍ~BVlBZoqA}8y4!EsJسku'bZ 睡0m 3aƳgڶ$dՍNh^gx㇃JuPBpjvx.YwTf"'j t:Tuy:("8Rp$z]/$`&d BQՆѺ޴Q/^Jk*uL0j [>Es.e뤢&T.I'RKlub~fr)щAVoXzXgJUs߄(WfUKgh+LJu:KQOOFB^opF.] !Tt}ZZw9;0pu~Ju"cۜC,`!b9MWR_5/ti^TS:k%ZTtrKLzXzJ(of^T(a?Alܵ=VeO<ƅ");N:GBd/4SR >ygF%]%a 奨79P0[)\6y-a5c0A-!1 %4L l %>,iOxU )_j vPC *B*=fkf\w8A;p5KҖMS Tvb=03UD:_ǽ`?SC@vƒ:vǓxBveٳ6yMmz:S:7-@MmBO 2 Qz>>xo&>>gq}:nSs͘٩A1ttTuV{?Lcv-o a-wW:A]%SJCG?+4gI+ ڊFu@g+Z)UN[a)5ENX !)uH7U.[g0-{iJu`Wz~:ɨTr!8p4yP@[BNV a"S:=:M]>5)UCuJU'K_8ynuJU'K0QwH^NtRI}킡:-]h_W tEΒ. wNtt]BNt_ittuNoNTpƽj?ւC !:)RNCQu.M7,{?˝JZT uW391 MrI ȱ0yO;Q݋iƟ =~oz1̗ۍ>92NL2~,ݽGͿlHJGEZ\4c`^oywZ#L N7䬆FXu'.--prүBp*; \{2liH'[E8Y ª7͹\{s,2)r|S oka0–§mw'=~(*.tDDQ^ssE0mNZlqMG$|ֺ[)=(1I ECm}|[is9(v N)" jPFA_BOGDzŲ(~xp%D%pmKٖ'&OOM͟1 B/o!&_B FF10Ft5gTwy/M!ܛ&`H̏@A_3lfQL!(t3/U(yy<*zF i hw-r=㈒GJJ9Lj\V>Sͭ;&op(NA|缋}PV.*(fsݢۖTjILq +GZuw\<=LܚɷmKhe(RXrª3*z`<xDM7_@̤9LBZb.K56_vhmi-gN|]EЍrJK/y"+gF5E4IhQ|H7fEbfn`qnwrWh33'g~̻ ~L5d㿎:;%9֎o<Ʒ4ӡdi7mslEM{v1.27M7wS"R="z!rJa3&PPZHoK"8ɕ-@ ſS5ZpB.n|crb8JO$64 6h4V1Yo$H4f1)9Qq=S~Y!b}ɣqjO19 3n_rG\YFԗYV2<x!n9ZB ИD9Iw<֞& U$j+Iq_]w0;B Fů$-?v8E~!֧,6?כHMAL%T!e<'֜ؽ-uoDY &qAMO *̝nuBF 'JQ?uGr;,q wLq~TjjeEfnӅ@XQ gEJ|3GI4F3ϑvq_oGw훂g$1@(V6PQ4ӧ&J̧M9r}h(]`UN  ;gQ>dPSJ(|ȖORSu?pۚ3t Fdp bXHs}i=ue ҸFqxkcŝ&IÇ]HsV`/AI$0S4 LgjB HYz[Ə/h8@IAc8}+."CnS}+%-8XokZFw^ဪa[" f rK!u;3kw{\j:Lu'eW2p7 1J|gkp(Z^WA<]v`A%8?$…6b{K mLrÄ(5U3MO5m4{gUh䚥~ a7/WpŠZX Uvᗚwɸ[cH;Y%s sGq ;pIycE=xd0ZRk}L7eދzc`pcp>AY溛w9K?!đ6)(lP[G0hߎr,7k]&!i4;YZLO 4aι/[&8w4+3wi8>Ca*BH -8RgBU]2Ģl $H.].-zÙXci= F\XM<|W}2E5D/qɦN-ڍ -_cxŀp(Fbhb(oJ`,!力ٰ)㷓ȰOu^?0S?"Rnm6`dCV$s%yuSg&x8rgvo|{[ Ne}d$SӗIKHyQ ʤTn0  - 89#^^4O X9X n>~r^M[S6m+,l%ӤkT"C/ͱ1YlsI,Z_C #r.!Kpg!(YT(Sjfl3(L]5/1{wp Căp6 {˫zg (c{1U^1;9Z|ig -5h;2&H@ťZH(V[K]?ΡIy'3%|q;` ƊE@Z+`԰,a2Ydrm3Q)`4}z/Oy%Js {.>ҀR|W1Q<}f?gWjrMS(Oƥzts?JRC&m]iQ@ -C raRoQpz 'j_N.(rk' ZߚFF1e!q("Ȗ IN|Ə0ʿAk s( tfV,WoaWAW %.ssBq*2^v+=f ,BظM %=) brT8^a zNںu\em o[iZj 'SB *i+v/nS59Ía:۔| 'Od&`˒4ʗZ 1ޯ6@I2CJ((t g{cg0/'lfnӥӭb|!-!41sc¢@2X>Bxѳ|õC/49De Qsh\S|[Zٶ%KE^kGه +:-R Ċ|,} *l=<grCτ$"HE;J[|waKLA_\=]tohR;뽖\LyPcY63(uqFE8 .ےNUxć3Ax ߶gn-)30hb"JQ:aoGSE_l^;y) JC|*rJG,;[:O)JdqpzEAݮ޺5J&c6 3+ӢhS\4܂g\d lIa|^+EЭ7f}Ec tQ?.E`KjyQ vƒVFw"ncӕ+xB",,l;cOUWI_IY$LAbc搧OLc,rMk!lD۹k|֋o~KP P%;P݀J>?^wp$^Ӥp4(FއwYGt yBA qupAUFn&1O^>i|\:|hy%e5izkq WJb i5d{H1~r{s0ny&SxB3I΅󇐧Od!;9L\5m2~xZƵ k~Po݋iVo~rJ¬LDŽ22t& @Ӛ.~+ZWQ@Zͻ|Rac(37&WݑӁ_ͣ==I(L&S8kFI(bGу]S((f6b{mGeCUӟ:` F<6g$#j߽|4+>pE^gv<1)3f0ϻ-wite&'; 7/ !bqEqo DpV8lrd8CUTY{8R"vREJ8s9IW΢D1cb79LOa; C;o\eJ!{{ p2mV<"ikݔ YiRK#1-,{~|U3vA)ΛƱ lA&36oS@p(Ąbsܷ `ԟbp+;ts~9?!pHj3,gVuBL/Jϩq8\lpHa44VKuG֘;$*eU7;+޻S7@.B=bLT_7}Klg&\yTfSvPW$=8dE>.cdɞ!$\mΰm4I֛ɪIf}l҅dٙd#:Si 1&.%X!AbϗZqC8f!Y9P\R$0,5ī)~BAxwJ12ʞ]HpQLZteRΗلJ 8 vm_ѻ*K8rEqAUw%$]IlyT%U-U&KZJV5@QeJj1CQvťs8!nv;F;"RڷFô@HY+\![Iee2-x2#7)kd?·1&4K6YV&Fҭ=89tBs YʥW㐦@%k,th8u|jyjm8Yvq;FYAk1('dFkk&V$U6䑦`{xqT+DEvN2:Nt;YN,Y`LH(W:,-R/H8BMwJIpv%bD9MS9DHՁf{5I+wycjy`{>rg'&xwEIX-3iԏcB^d(S,V:)۶IC%7S3%ۂz+%B!jf!ѯV#!@ 'lxoi*%yfS9A/r_9N JrG{sd7݁i iY7HC]oM8NWڨ5}) :l/e{Po*t|ٷôk-& 6`'S'T})bEˏ;ݥ =-/)\OTCSBrY,?m!ZlKȞ'k%5 )~"RĤq3(,٭E\dSSX/M&aWdJi L_+5k~qX'5lpp1ƥ&) ⑾?QEYUKOm@mC[$!|d aȤ];#J'P@PN+}i(p8%/.-ʔ)a~ O}$o3=xC8??pԓSLD!/bTQ~j%9_t1[D2$T3ҁSf"Ҿ錫qIH(h q=:</_'Mje?"n{Zj+⻒hHRss_@ȗpi W+ @M+#f #OLȑ 6ˠE58,WhwpJy.'4Rq45~9+GZ?I_WE !YQifIIi l9\I˶)+Jiwi.pj&͂;_A]~iX?HpO9 <^w8@IT[IIuJQmF 5ɤ7K i1q/KtϒV\uzGrmZ~. ;Mfefܷn'ҕ?)Os(GctHw" ¥2[#O 9`Ycoi!/o&kBRY Jud)*ՑTGBRY Jud)*ՑTGBRY Jud)*ՑTGBRY Jud)*ՑTGBRY Jud|"NTIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/konfigPanel_hg.png000066400000000000000000000026601506574241500253270ustar00rootroot00000000000000PNG  IHDR/sBIT|d pHYs&&:#tEXtCreation Time04/23/06ktEXtSoftwareAdobe FireworksON IDATxRP@Qd|bz'M$`65Vds!|{\KyW1y#^9@ rL^9@xca9@x#^X @x#^961y#^9yc G/r  䘼/r  9&/r  Gȱ@  G/r,lc G/r  䘼/r  9&/r  9/rL^9@xca9@x#^X @x#^961y#^9@ rL^9@xca9@x9&/r  Gȱ@  G/r,lc G/r  䘼/r  9&/r  Gȱ@  G/r,lc G/r"ex]?'[~y>폏&،aC$txao?B~{ 9h7;? _΁&<b8=a-nc֦8- M~~爍SQbzr[KQ _z'$lWGqm,SX' ba\~ ݥW o^{#7L^;}uY"AՕIDyx]zvy"'cȖa[#w\x[A#Cx98_~s^+=f> /r  9&/r  9/rL^9@x#^9@x#^9&/r  Gȱ@  G/r  G/r W @x#^961y#^9@x#^9@7)c G/r  䘼/r  9&/r  G/r  G/r\a9@x9&/r  G/r  G/r\a9@x#^X @x#^961y#^9@x#^9@x 䘼/r  G|N5lIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/leisten_hg.png000066400000000000000000000120771506574241500245400ustar00rootroot00000000000000PNG  IHDRV4٭zTXtRaw profile type exifxڭk( FY%!Y;' tj\!Bu0M.Jr^c 9 >s ]*I_u}; g!#vW0Cu"c?:"b 1!=A@i]!B?wp;{l?'>z]G`OunvR0HxdSأ7I8 2"sF+T;-GEފG#LeųOg_kDV}iE'Gk :9[a"vuVW*ZL \W|x}O:k djljeeJTcX^_nO@wgSA[BnmF؂z\ɭU]! mUu?{wn?MQ{~N?#{w}u7&Cv}3t n<JCп\?g/zn~ӷ# iTXtXML:com.adobe.xmp q@>iCCPICC profile(}=H@_?E* ␡:Yq*BZu0ChҐ8 ?.κ: "ƃ~{1@,#J zEQ#n(9]gys*>xENR@@ @@ @@ @@ @@ @@@ @@@ @@ @@ @Z$@?@@ @ @@ `һW/AIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/markierung.png000066400000000000000000000526541506574241500245700ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B4tEXtCreation Time08/16/06Zz^tEXtSoftwareMacromedia Fireworks 8hxprVWxֱ @w 1VNabk(ƒU'"ߟ@Cq_rxCyFʵ~WsOu@sϟ?ϟ?ϟ?ϟ?ϟ?ϟ?ϟ?F%ik#wix>]v~>Zq8rگT~h9D7i 1{HmkBF)3 mkTSx][w8L.v'={y9{v)=Kzn'KD6[&:/3a EB EҺ8 n"UP/7f6k_Gc]MNZ5oNް~kzye1upuѿIպWӽ+J_qwJ[*gp4RN' 9g)/ w8tG}xfuꃉ'>= MN.rQRmKJj&}tf5;N{4ؓjevݻ{KwX+uR?F2$:ަ~dP҄:$:K L&W2ʶg 8:\B`iL[f8fa"6&f*u#ب ظegFG3<=OSf<~ OO#@ ~ru0j҅ 7&4u+ ^ 4MKBSдb{'Ahv8, CCZ8R\S`zJ`nbyb췽6=8Aւܳe v 6ry:-gr=aG[Yg}˧:t#AI(wLRZLa6U%ʼnSvId1,uiOQ|_b0L=LẠF#pܿg,m8qZӘ肻fN˦2hTʠQ4*F@Cqֽ $CP:" c ʟ PLv!t2B&Cd>kdć͠ Ȃ?nz d R' E(3 sWҁR}n"ZTOp|'hxՌκ2$I/iz95F m]0.Cn BMr+ddZ /SFnRGE[D!lnHԬn5o@n:Yibѳ` 4࣯Q:C>XaS_jd7|/Mw _rc=10SGNɎdjlgo9џH dmHI#sKD%A'3,͘; uΌ{&X(Bp0F2fyd9)LI "h!ۢh2Ia9AQOapX‘gsͳ809LOyW.M,8VjI uGd'liWO!'Z/@I~4T.(l5k&$#w=3SU?">&cGn B:NvStwf]ftdBF0d>u|dCtsM42Df/A04ԐaǞlEFۏUF܏ xyJi`QGfIx7J%nKLC}HIA㔭! R eI[)d|(pf=7D}2{cy=SZʗ5**и"PX'ʗ&p$FR6g=ʴ_lJ/0̷a|``3R mTؼ'aՆ+@.nF[1u,'ٴI8{KIeg L׳㴖9|[!NQm eƐ֪ehΏm~/R! %*#Z@'߉lsB>MtL͸ށ, ȍB[,&.ViɉtC{=>ǜ'VVi5 A/Uf*"'\Z /WⷰsGv+ɲdY)dE,4zLz׉gerr 9eF0þ^#ȧ9|Of&A!hz)mǩZY-"OUͶE8BK^%/eO_"th[S4Y4O#Dq/!C[CG9jQܷF_=K <G F9_< WPi-U|=9 뽄e3y@U|eu_76sZV oڳ*q&VK>p9ɷRc`e=d]?A;2Dh/d" ~"ffh5>d1yNya\Mmf\5KѦm0BaFJWp﹭*(R^³yVJrmE 8v ]Ε GEW9myƴJW?_5rf״JkIjeJ%i]6URᏩD Qˠ)Ev|^|=furqICjZ*azChr9Rxe^#҄RlMDDK=P DE{?QL (> XQP#^#ᔇFdJ:m@ \ z~ܼjC$Wr^3P%]QqHb5㹿_ybɍد+o8g}<v })]?%Lݡ\ .zLmL]0uz#`k ^6[[l]uNv9|G|[/E-غ`낭lͯikXm/ .;#or~K-<= ^6__|]uH .zlml]uRhwO+0u`ow\ S L]+`L+.u`낭`j j .;+ |]+ёg5_w|[l-g:BH8[O]2w ad'7#_^!9eSqWJh,_=ĺ3ñAW|kJۙNlavqWάq_mq #K}ȵ`Oŭ7>z=TPU_ }PYe\(Ao6?^g?\DOW${4"mjqhW`9ڀ#=O7ew~ٷX-,a{_cqtp3~jO'*t z_u[|+iVql.4W[mʇܚP?DP?3:9_Jw/zCBM|ˆjGƘFC6G]]'LǷ@kMhtŬ5U=T֪^dC"ELv݋e4WP$!_qbyeY#&l8l4OƟ3 tn,a RlʀW!#j$!IB8-ırPC"߈FQGwts*FYdӔ|FFj*xmG dL:fm;62ޡbH y֜uޡ_vz)r(՗$ s;^Fe>pؚaW)$t}l@> R~JjT?)O[&|y+os~[=4O{Ac9bKH_a?ǠqxDDFӶXf ڝ03 l9~ޘr\s;=vz13V[x sڝ YɈG8;Yy)E"; ӌ8+|4gknшaƝӈ ^%d?=wn{3>i'߰d2=[;{o/򞏧,>9mw]vԸbKZxΠ`sJI9{AG6}{O.waGpt6Էhέ1๷b$ Z`R3L-BQ^UcBx$ w%Hٜrl$rBTR-7[oc孂aݨ}1؁S/=})Gy>CğЍVe aɪϥ%j؟vXP2YG$gޥIlsh !)ljd7 fA/f׌Y԰v+*n!lyLمR+QEؗp\ Ɲ?$ްD+@Ѵ0^a9~Z ?Z@$ Ɏ^~?ҌopG>V/_vAςoǴQ wXB?m&Ige\Y,Q vt/'_+X=9U.z(ͮw.iF@p-/=Zn+XQyWm%e1r[4+fX_O.i_'j4w[ i#լ/ }fkZ>YU:NIZU|}U.Nſɞ)3g&YyJ*[, ڷDəS!=U8>쎢 >8U6N)y,lGqC6Z22 !Fat]N8P ָThkblzc4_~kxd􄽩B#I\Rt j_BF ]K{4`؄g#+Ql߮ z}yH+p]f;q]p .3K,ɊI,XpLѦ5=7_$XUOj*"YdA&[͊5<f6|cA}Vww#~>6 +zxҖVg˪et A$3H;B֤~L5*Bcny=<̽&_>S|gpͪ0UcUç:%4vRgW줮c2ԠꝺW]oM~(MG(Cɫ>y> 8}']r-Z2wANы "Mv;. RdHP> 23[_2V+|БCT|?pKmkBSx]N0S#WCJo`lŋI/w[@nfd(6`<VLC@CG6ya -mJobD2Hrv"?@$'y#QɄ %Y\H\_  oyX92>Qbf9}81S _;&} mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 %mkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)PM-S%#O -סVgrqagvx<ڍQnd N~;0l[8x3g4OQq8ݗ6n$x("}S ; Z#[98-`$3,9pA`}o8xosz 32O{`mã{wr/pU 0: z?O޲_u^LR/+U x@#w!U!۽ǘ hП]k:ʵ'@x>pBt kc@_G oec(P5vnY' q ?H ܃I]dd,GnDr1|8 |rB'"%Mۧ.ĥ%՜6tkW.( Qo(! d?i~`{u`ۆ;ȘN9p}s?zzS_K-JUz.f!/*Im(̉>434fI1&i~0 T~ўneC~@,‘@Im3cXOf'cY SfQ+c`Ip1_Kf^|Fv9ϛOʎO/-LcJZps,ӲH<rT!bNgVw=niaXH ).Vu~Ɨ>d2j?=EGEqH.TM*:0忁#D/{?? t'LRz5L=PTB|!C'gA^pbJ3`׊c}U˿~p }юq#/27/8䲝#LXp#e2o?> Qe DaͰ\Vٛݟ5rAi%0,vy {\PC]hV?_qyQ 8@UdvGM!D{[C63" Y!Ǣ߱Yr_t|Kkٔ cKGV/Y,E^n]_JEԒh- (At?T'<ʤ R/%UT/)P dIlRR/:kLcz]CG.Վ}r}T妧9i~ u?Xh(}5 ;-],JմEG寳lOկWYܽ$XjAZDFB-^Ko6p~+6@6zupA_$5t}S??~A)gҍc aʯ w"חݢY%#_4wP>2.8V2=`?6x+(_^E].]ï׿^ 0ulp\g-pWFS>5"uO*}=A8E_Wf0r}k=\V{=ob/ށ]7P[+x?jKJ{ag[a{MZ8iO!`:8C NBuJv>7BCg 6}~F?Bk #ܸ.t'ƻ<Ux?czST^Ө BO8‹gP;_s {\) J4eG?G&ճP+1w ?hrX6ͅ-dϐۣz\e5]_'S@:@K9Yha |״YedQT (Y6QSڭG1O&B^sSfݙvzxRSKDb'P?eѪӉ'\^ /=N j^Yp羘w2lOL!zTaa'BC8p u#^eosؐr1Dt*U'ϣS?o~#cJyeVe#ڧǜh-B84T:IR(7{M/=qO*PaGƝ+SW`k.ɡ 5g?ݬȧ[0}F[O`:_PiTG])wE >dm)b56ɗg5 b3v23`}:N,_>QCR/Ƶ{<ǚI>[lɚv'/p8@b+%< dx3~C~:"]/g2ݗ@Ԗ}[X:ސKu2ySȕ{NXwOa' _Qp؄!t6h)sOޟ0R=SF}U0SetuXFUvyI[*-9w.Ll)\|i1#$}q垇y(E5"|;RN+C m5:WOIDATx! 0@ߐb %A_3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8 q3@ g8=-ET#IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/max_to_left.png000066400000000000000000000144141506574241500247150ustar00rootroot00000000000000PNG  IHDR ;zTXtRaw profile type exifxڥmr8s$8MfNdYh@a_'K$ji\rM:'z}Wz~ƯCKZs9Oa|mC{nC|na]U_#a?!$O޾gI";3GI&9XXS\R昒==pzw5 +~Yxg+sIz>~ '˹>u+=Y)_h/ָjEt9Q $]5,Μsv5*TpR1;]?-X›'o߱ ͛G:WNEN`&,r(3 ӡUԼO/k\L7Rf'- 6mőZR #f-#pVb1RCRg;eYyo+ItQ}'$,7dªZ{ŞUK} ωi/BRHNY+W=Oښ1RlIxUih }ag=Ar&i4R-.F͖~R*!3{XiF hueLfUBfP!V3Bcc{9cC{2㠝MumAByv-XUp/cHϓ%E}|cuVsԠ\; hTct8(&_;P;1hDK%b,-?ʣ>9ᔲ.cX#G0KWVt-4'křY~\ $ݎ蝻{4 rMj"$s8;S$2e#GCA _ގ TC&5dsXH0b{67`3HR 4G9RNtzcN;pOK)֖W[$LG5G' քVdK,NSc1`͚&HmM=y2; uѓr$s0Z%(z^M׏z_b~VS|7W#nD=GJ&Y;'l pguEfbcAֳ ExUǻ߈5@ioh\R(ۏ3Dztm.ek aٮp'i;KLKJ!` OΙFji Ws-Q$ T|M.I +!dACI264XXe+/КΣm.a.<;&8Ϧ=Ĥܓۤ^Ed}"BnmpePM#b gnn)ou׈'3ݱ1I7F}m- iTXtXML:com.adobe.xmp sxiCCPICC profile(}=H@_["8dNDE EjVL.4$).kŪ "%/)=ZiV鶙JLvE ",cV_.Ƴ9՜ŀH< &^'ڴ +*9I$~A)@,ZXiaV45I⨪xr⬕*qOHN_^:!$EH `#FN}_"B 0ṛ Zq/)_c|;N=Wz_ӟWZ.\Olʮ3,w tz5q pp({ݝ{#!rxbKGD3&M pHYs&&:#tIME)F"IDATH햽SAuonw߷1˲[XXY 6>A ɝOpƛ6vJ d9\u||\rΤ9ߺJ)hb(Tp)g1(֐V9e٨"bҞR 뜓++B)U!ks90ƐsyMsRn@BHa!bU iR1* *K~ =lkx0o/:=:]d=xxLx>Ns~tXpqo_'/O݄۳Wן{5;=<߁_ضz;SC`ñH֚3d XD0֔Rދ$X=Su9T)Ehc5VF, X[8˶m麮,▫ZZ4t]d2}O)e-rm[11FRnVZH){Obmz],sV9gBxjUmSg4IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/maximieren.png000066400000000000000000000013441506574241500245500ustar00rootroot00000000000000PNG  IHDR; sBIT|d pHYs&&:#tEXtCreation Time04/23/06k%tEXtSoftwareMacromedia Fireworks MX 2004v3IDATxՖAk1dB)=,o"xꩇңWQТB]RPv&IfxX[t>&dDdk-9g!Blz㽧{99. )%mbZKuy1NJ1ga Ƙ f:RUB1%0 ٥!TB܃M'V1cp9!PiSz&''M$s.w圣:BXks)s;v&^o_h Ji$]em,֚i:oJʎP;)K./Xhu{`SuZk֚ [Zx,go^< s&.1URV)E5u]sss Ƃ[^;F}b$ahRT۶x뚺icLw G];8<"r`TIݲˡ+R kmM#lnsZeVcL5[W &Dc1)Vzdв1"*~H#wz(IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/menue_hg.png000066400000000000000000000007621506574241500242040ustar00rootroot00000000000000PNG  IHDR>a pHYs  ~IDATx;OP&u0CK4TEn=}<3h>zX+(c@ A J3a A> "@D0˗nqjT`L]4jm76 Ll1՟s NNy\ɹ u %g+~W}AJ2dG7 W͂$se/fPΌA"IBVr-7{|z! R☞ƿϡsuc|~b5Qr䗇 %ynėuV ໘y;-e$KϘ_ sb00ƹ$e;TOão}b%zley I_@.o߳rC̹/{<Y1E+w(S׀k}klº5flQW}ge1NDbM&MfroJ.''ʇ/]3ܳUf^1=/~>p/~8ظa0 T~o_?kAu}4@heZq&&4x:K2` IɉUdRŧPQr:b*\Y9+cC]fU-jZi/䢥+.ݒ`j̪55\jV{MmYޙctg@CFyheب>I2mf_B?VYjImv?I䣧;?XeukŚL@`foD3D71gq朳 B'9[o<:s[7w-/CfYX}`Jakꃼͳv+ulM*cBhg5,k/KöK݋ p@^fUn Wm857K9_=mH'Ie_y84009m,;=?q ;u; lj6n@0]^)4rWҾf]$>>adMS)׈SeY [KG Dγܫ6,4]`c7<>;MpYu.8vVMԐR/YPe@EgۅHc:^pMvm|i[HsOjPh{vIk*d2#JCwmzhيKKVfmD;fhTLr|`)Yzt+ctr*7eQ:ZWof)̓ =g'8Ɂ7=r;ETvzfLɱK;gQX'1DhY+,`c%Eհޭ7%ě/4|&v>?#E+}s(ٳ}y Džp : t,>x-BcF!c"JPʍ&5 %ιKqpqMc8pCxw8y6+W2Z4DRW*G\^uPjܲWb&x9>Z'r[>w%#{NwHkD,خ'rߜ3OxhcRB,"p³@\QJE't3 XD\Vo`v=/5-iFA#D^uz@WFU1C {^ &=o ^SlKZ8LӔx)#H\,ɇ~!L )YPfsEh ZH +֚ (Ki@r,˖OEAD7 %BiP^>=4*Y1$r6]j/SUiQ%E=4X*3Ѯ~502tCMJީ.u^n{WEK85$/WR`܁NyZHf<+0x?I ,Q7B]Uy`cF+ dF&.tn3歅Jbhii -^[x|*x.7ڲr_v2N iTXtXML:com.adobe.xmp r iCCPICC profile(}=H@_["8dNDE EjVL.4$).kŪ "%/)=ZiV鶙JLvE ",cV_.Ƴ9՜ŀH< &^'ڴ +*9I$~A)@,ZXiaV45I⨪xr⬕*qOHN_^:!$EH `#FN}_"B 0ṛ Zq/)_c|;N=Wz_ӟWZ.\Olʮ3,w tz5q pp({ݝ{#!rxbKGD3&M pHYs&&:#tIME0;KIDATH햻AtuO ¾&b "fFk .^0P|1Eel2XNMO쬘9չNL&4MC]TUE]9GUUhsxx9ZsmL&LSڶisf3#Ţ@9g1TUsZrhZcH)D5 䜙b~֢:Ď!` (iqΡB)Eq#RJH)1Ơ&@uKHR !{OUU1[BB`XGJ L`ٌ1&tYKW)K 4/lm =_ ZynBkOׇ^əx~M._aw5tzFpyw/Wo<{tr#}{wn35[|˽Imoop4 [[[m[(il CI0V5v"w1Eɡv<83W(KHr:Dڡ6$ƸvιHT;E">Rxe`c V41D+K)U36mzIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/minimieren.png000066400000000000000000000013731506574241500245500ustar00rootroot00000000000000PNG  IHDR; sBIT|d pHYs&&:#tEXtCreation Time04/23/06k%tEXtSoftwareMacromedia Fireworks MX 2004vJIDATxՖ1kAov8i!b3XE tmBFTAl"ZǰHď nvvvfgY7L x>v] J)8Gu!RBJ ^)%cDuQcmm J)4Mk-sh61"f1 nکcL?|'^=@ECP d1FhF]װpAƷp=^xSe/+K36e \ݿnVAoDpxY"sjl]"Zi`y#RjaO)Z fSL:ʠ03Pݏ!lйIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/minmieren.png000066400000000000000000000013731506574241500243770ustar00rootroot00000000000000PNG  IHDR; sBIT|d pHYs&&:#tEXtCreation Time04/23/06k%tEXtSoftwareMacromedia Fireworks MX 2004vJIDATxՖ1kAov8i!b3XE tmBFTAl"ZǰHď nvvvfgY7L x>v] J)8Gu!RBJ ^)%cDuQcmm J)4Mk-sh61"f1 nکcL?|'^=@ECP d1FhF]װpAƷp=^xSe/+K36e \ݿnVAoDpxY"sjl]"Zi`y#RjaO)Z fSL:ʠ03Pݏ!lйIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/nachrichtenfenster.png000066400000000000000000000066551506574241500263010ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs筯tEXtCreation Time04/23/06ktEXtSoftwareAdobe FireworksON IDATh[o[Wvrnzp} a #i/㘏niFd{sgm/vg c>;8ձth6ֲyahyZjA)V XdKu?;5q8./'ĩX˵np۝97֖_}9XfOc{ w.tj㜻!G㌿C9sk-|l~|Eh !Ӝp9kT1)%YRE')Q%uւT )<_d-c y~o'4uPRaEcJRJdAd<=Ogug6$˲{&C?yZN@k XY %=<6Vu)Ҙ~RYR |J)R,e1ќj!!H KaLc4&zxG(>T=ϧ|'\ԡ, Ph%Ja4*FC$,+)ʋ% bl\:]c! I2R5ixJe9QAGJ'*b:R@s Xki_!˛iJ z>Z),e;SR%Bj〰ܺ>Bl:ò ,p=%xU>I1ŧ.C9>a[%4)˒,TkuBF ju-Hi68PJ6Ek116B,8/ t9Z|}>?C+A!RH +v--iK%({Vq"O Z!$؂ao Fjj%+UmeHq pX$ J Ln1jq΅ =ZC Ko j|6(f()׫dNج))V8]xfs<zEdɂ"g^\evdz0uBHS$Te2*5\vX5H!&S+5YiO:'![Ս ȉӜ–Y ^ߵVb2wqߝGjjIP{:.>)Y&91{DA@<U`J@KtPuEd/ %Z}X{ {GNΐd>f׫P}$cc Vxt0`g8& L&C1{OviuTZ)xN?zm!Yru)Y,@b(;lsSE֚4dp]5l,Igf ZkT\ή-2G*{>z޺ERϘ44Z li80hpњ,181qN%^m D`O&j >E|AsqlBzmvvyX4g&rBO! +!XGb.ҭ1X#D1k'1?0FF2Zvc?/+Un޼EeLSϣ2ק^SP 5Jl5 Jce0yMOf<h 4Y0J¼RJi oe$(KKVpݐ/l^4%Eib8i †BZh5 GRx5|paf A#Zo! ΕȧIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/nachrichtenfenster_icon.png000066400000000000000000000052141506574241500272770ustar00rootroot00000000000000PNG  IHDR, $ - pHYs * >IDATxڅWnޗ"¢$ vdQbGN @YA$A lG 7Q$[{wN %#@ kO{ιwϿy>"/fX\Vfyr#Ტ֓Ye\[V{tB;A\YR^ ~~[ >,=#v'OlM =rn"O\ܲyRMl%GmsAwgѽ_Y?e*" xNُg__ىKYŻՁ?mg/R]u͐S*}r8zMF$~zʗ{$ţw+x<("o˻,pvhF\>mXO_ ~quޭHHXr 1?1=@|>|kL9&Na Or*jPK94A)s㱗0 0>NW֖YNO BjVUA8E$:<:*L#2J_RJ0lA:\QZKXV%k6LS?ɳg2"^]o4`Wldel;=e)O00*Q2<&EqHy? N*er+DdIB`ͺ:0|YG(/C(LP 9ľ(H]Uq? EAD4MxQjˣ8MdhQ8E0sI0uMiEr-r c<$NǀhV, bѠ%1KnQןf<)Y)zMב!{{{c)ޏ)p1{^? |1&+JA_C`++z{Jsβ^yvE *MIW<>3S_Uk"yA:,Ng$"\pQ3@+YA(rEKP2RH  |O1JP'%Ep# yD' <-MEV|2V-9+j T*>4%:C^+vGYŁ[~//w[EGӂs KחZDPM+dM}I/AIe{t6*눒V{&p;d߱oqjCϷl[Q,IQB} <{8(QQ1xލ'spBؗ t2/Q6xF^xѷ[7`gF·, {yXu.NFD $aU3(rgPa1p:'3˂_q] @dYv`Si(-7W QIzBeqƓIYtdžVK/Y:FC$:2@:On^I/&VY j5OxL(1Z5F DAD$O 7͒jߗ=)ƕ+ X /iH2jhɝըeQ e-+Ip]O_۬uTUUdd=_]_7@ 8ESiX5tSqv$,̭,_P9,(J"Ifbu;^ZSeKx r) ,^ a;eߨ`۠8V*l^-]^_})VbK7j 0w=vM粔"8zVw8@j~_t=uЍLfTN&2A}&LwGL^{ձN@l* UG2Nf fQVeyn B ]0,`4wh4 bNt2$I\5pd4 R^ y$:%jFP|?P Z񪳢`7a*)iۣAOyk:nڔgjl+'TGY)iJŒ .S>Ί8Ǐ֘mkO։Ď、)(,c#`iQ71x.2۲,@(YּZ_B\cvݯ[kgYEܚI`yH@*& _[ٯDh:AыZs2#IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/neu.png000066400000000000000000000012071506574241500231770ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/23/06kIDATh=0߄ @@1!@CR * eWلOr'h^+40Cx<"":!"DETE <wb" fz1fv;tUm#X,Zdـ1``"zZe F>. F1N'c3i$Ip>97T] 8& (BeRjʼnC$Ie~cb6CDat:vj"hgOFF+'ĺ!J)GU%GG=PJ}1z+W\('U,#Bpέ=~-!n$ U׿W# xL`=!6eBtL0#>L5ȈjeF3fI.*1jә1}\ƙ|n?~^GeYh-V>u IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/oeffnen.png000066400000000000000000000037531506574241500240400ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#%tEXtSoftwareMacromedia Fireworks MX 2004vtEXtCreation Time04/23/06k:IDATxnS==+gHin$VKAR#Ar#}rȝ}Y6neez:nrJH@2(LOu:rm ?'BɈȇDϝdD8-Dc'"D jmow{P ⃀*8 ,,}5 +k۴s R;f /֦0AW{!BU7Τ1#3{?жu:?YҨTkh7EYCvN'byP8&~:OCYWI<^0`ZK+/x|oܚhg˳(?"* 5쎀j \?A0 8 oy0 ;E~CZØA kY<vHhGy >ڷ/*UpeRhOů-/f=:wSr5p-H`$ iFDcp1hS^6y||?~j0"zciM[u{D>'yׂ ů˵-}}5&8:؍JTe^+Q[8+*=S$#bJ`wM>_cjҾ[${a k/-ssc'!c8\"<^d}(B~4MIҀv{$@Tkɱ|Jτ]Dq*$Σ\.F i^J])wmՈs4zذOi4MG@籞\an}HAAHRGl+Xg؂ٙ,=]> SV1?QFD:QXJ,JE'< ;MX:'[f"b)S5X]8ӮLK1J*S#D^PŦe6gV\ i@ HY]% Ap8"j9V(kN o$/2?\C̎~( 줐lt3?/qpQfrhudfwNH]H/)13;Ω2-+@ [y6FGJgO,$ZGij3ǎptL\&Q|v:S@ 'R=xy}|L):/[L}69zD|m0=]7yd^[P HYYoPo_',^:[&z,x z )jtTyuWTpFo>o%բ(skg-~x1N3*ai'_nH~5U (p` Z`ȜC "1??Ro=`9lɆ!Re"TS35U`Al(.#@!J/5( ֘7W<+_{ #44A$C (ʀ(/wܟI@0Zj{~DO~S"?˥#TD,A4"פnYXq  ~6R'ך5̱vZ>o糘r՚ [/o6VMp3h7 WABh`8PAPW.{'N:?>$hTqCQW"\޴yLmv}0i pw{SZIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/optionen.png000066400000000000000000000061031506574241500242430ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#tEXtCreation Time04/23/06ktEXtSoftwareMacromedia Fireworks 8hx IDAThݚ{l?3;}cRE FZc OCmBc k&Fhؚi*(DmUc.7dg=]IGG'N}'ⲉ9644( h8ضM&Ͷm4em6;Ƒ#GG;g+TVV:tOii)Lfξ I;v`rrk&/$61_q )++:3 x/yᇑer֬Y,E˲, Y EQu]W_G( w/׼,X_8#& ,h4iX8HQUUE8&`͚5gH,g$ILT4 0 Tt:-{^|>@BdY&*^$O`hhY BœTU0 8ӂKDe^/~˲mb>.]ʢEBpq&*++t:aN,Cu JdPUU ")µ^/̘ءCp{'/!Yy)((J>N8frr)hΎR)dϠ(27xc 3+|в,L$J B~~?t:O*4MJKKX<䓢"MشiׯEtT*%"Tvm&&&&`Ν$ɫ#Ix7! 8LqpM(Lsc={Q,YrH/G}4$IȲ,(s0,/**~Ýwŋs*(x^t:9( x\ey!!HDKK $J$@ iq1H$B<' .u.y$I"8֭[sǯ~;?/, M4`0H0EQŋHDqq1AU C=88p~N8-[.{7r2{{{;팎rќ@@$;+B! 戬kR^^Ώ?ڵk`Y 1::, m6Ν׿>|hkkRSS#,Ximc6eL&Yx1ǏKYY/jD 6PUUEgg'h9IsiN(,,DUULĶmAčfi211# q馛8}4 .ض-|)۷ooc&LD"8AE$ `rrRO=Ü8q /_.V<|$  9;"2Wr-9qt]e pp8̖-[܌9 qwGp8g'bLMMc|,#_5elp \p˲`ʕ~GUU#HI&''D"DQ .s{=r5y.nV)++D"̘cpp4dʕd2^{58XL! ܼUQQ!L\4|YƼZmmmh4O?͒%KoH$tttJ /ZZ-[&4rxX,SSSvݕx'F"hX,4m(((bQ9 cYmmm#W[n?d0nI&N28aȲ"JMM ^xǏ㏋{nu4H);m41MSw\ٳ1b|&A<4M?!~^ W\_ q?T`Y֜B-H--Բyf4?ptR~"/x^TUŶmuu'ڥ>p17sb_ ʩSr'Jƹs$p|> c9ï3<ٳgg/D:n6!ipbW"$ Ο?̘BN4~x3U8̔:䅈'Ƕm`&i➫jhT8$Il۶mihSSSݻ7,Hxn+))T%%% DqL&ES''7MUUE澔#HN)3g022"mOi&֭[Dze˘w4m#2`PNXxOogɊqfCOOuuu9˲,qݲ*SYillqw%΅a àoq\pa4msξ?gn˗/CQ[[a|WB:C( Fy饗hiiA$ |aaV$Ix'ԩS455zjlۦEQhhh`9λχ7 hnn`ݺu|wX+VdmiժUyVI\!ZgEV@@IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/pfeil_links.png000066400000000000000000000011501506574241500247040ustar00rootroot00000000000000PNG  IHDR} sRGBbKGD pHYs B4tIME ,{IDAT8˝TKTQy2sƄhD-lbx2 Wd`lDZMƭf PD(1 aikxo~r6{}谆w~3N 6ob2TTh-q8}z@y'p oh'`~)T߈|v#%B0ae\XTXHX ,M4r{(3(ᒻԬW&Э^../=@#~B -XRO ޑwss8ubr^Pƾ \\@:~&,a!q" λR HSnJKzSSt摰a E H4Iyh=OWW nDiRfOVWw-ܘ,[/ ~k3}[X aY)ǻƃ|%v":8BֱHiÓVOvdiQƙۀ)3J;iDQ$CJtVT+u[G̕ީ*Y&x4J)@K7A)XISX~"ȳő@G= {)~RP_{b◃ ]f i\}{Y5]HWFkoKc4p낤(9@m[X g_QNҢDq&!oÍnV OE/O `6"أ|IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/rtt_norm.png000066400000000000000000000066131506574241500242620ustar00rootroot00000000000000PNG  IHDR22?sRGBbKGD pHYs筯tIME+1p IDATh{tUv?$7/n ^#<TQG CKEKJSG̪::tmcJFYEJa!d! 'y$^sZVkZ;g={={H !=zDuu5k ! "Əe*g֬Y\BgΜyfڵ8q" MAļ*++,^yV^ڵk"4nܸAt%c^uV\#< #ߚ1vX^~e>(PW B̛7۷o΂ BQ^qq13fR6pсH Q\nfƍ,\1c0m4JKK),,d֭+9Flۦn!YYY[+VPZZ3Haa!'O*4RSSCMM ڵ ϖ-[F"OKK R]]}M2]י4ioEEE֭[شijYYYvmر㺁8w>,ׯgĉlذ+WrܹSŋiA{1/_δiB0m4 Xl`0:vԨQ5 ;foB㵾IW \, a&i*DDK -ӲcNerda5Rq{܈Ԯ =>q x\lg:ɞX tw0-4MCkr-0M˲%y0BΪTx{Ql8HIW7IIQ0Xcf1'ՌiW!JHe^$Һ߿MScN .w33JDP{o>E &•5Օ@dK˾ˎK[b|  tK-A0l \{dmӴzdfQy|eH˼3Dz *H["- "˔`O~˸,IwA4{qOsg`XB!!%%%q5b6iqx5oD 7 s iKR^زC&h_'CHd#] ̐R8]Hԩ1 X UDM[O@<3uo;IoT m2 B 0 t]YF&݃mdgl*LdĈL2Kii).]¶mv˙>}zt1iDQ7ܠ%a s-MǀxiH ~Ak˿R#m*#-lFQLwR0M|ϟ?OJJ Æ #š5k⩧$V\\͛Q$޹OMx=!&iБ=bR)):vW12FzǷ-+ zϞ=;,\Er8~8P([z5yyy={x|<Ϡ]GGϟnʶmDp's1Qs7+їaIILDz[߶mSNeҥ8fΜv!++,p&L9\QwXjfI 7| IN`:4͇n;Bd.D^lכ/Q/^HNNN\ux饗Xt)n;˲t =]g>.bv9^2>M#lB qs_b4ݍ2f!]QUp8|l@uN'dgg8>>,~Dt+I xLӌma̸o|z$w3uioo jZŜ9s5k':E "kr@ɌL, \^3Qv7eNfD(i7.7<԰j*>̩Sxx7Ann./uuu۷;w@F-=̏[e߇/_txnJ1=~ɓ'S__Ͼ}8tٳgAmm-3g$###ƻ\.nf9ݻ9}4wu>(. )||UxEX?`9LI̘A0z'OϟMMt*?!k4M)-˒ɶ혿RJ*ٳgeSSS]KK B1cohhUUU܅ +?sF5k>]JO ްA@@R~(@^=[… ɓRKDqSy&?5H?݈h*99A旚$#~?~mBS$&"zd t|3t:QJJPӉQNloO&p8--dFH4 &ۂ ޘ WZC,uvi& O}۷3sLBBlBaa!. ʕ+)//ۣt'݈LӤ'p'75kJJJJضmv=>χ;wBAMM L0IDAVVo&K,5k3,{{&xGYd O=%%%oXxqj>^/[nΆdYG7RVVQU5]8*#F`ƍlڴ@ pOc޽{ٶm1[l;vPZZʦMl5gee1n8vyϘx"/k׮e[Kŕؼy3N\%sI^ ̟?EQPPYp!`!EEE?~| ~& 4ysGFWFX(BuEAQdY6M0L#bG"1Xk>wW|סiaQS;kȭgX+.jh 8l ^&G[[;AQTU| N$b@ 8XϗxSqHԵ}yLU-dPW&9 DQ3izskL2ܵ;MGp:,$; {}[ןc%,bfY=Xd 4uGӢjliֹ MQJ #2,]vI0"pЏ_p[mT!luo(i3y2D454_SljT&Mk_)_W':HG8|p0 $D/:C0HIز \DIv%mv;߇1rAd)vM]n}3m&qIyhJK0$LKKK u؃n!}ۗHC]#bF[4͎C<?=-}0G`WUuN r }y I$IJxΦHB&"N-3jolšY$NLiԀ ΞP1q"+; o֑4MBİ!K $M:w`~)80aftpز_Sglk-6l~BHӰtJi 7}oQRAs=Kvaqo8<ӷo_|AN'466b.C2[Z\rÇ#  Vs?xuU EXױt7 uWFp{$}z?a I{W(ԩS1c/"iiidddt\PW8c1wE2M\~8GmA»ᨽ~^KYPU4-۰?9Hݻwyff͚ٳl|嗄B!y饗(,,… ϠT>w~K.ee͞h|+Gǣz.|hkkر#"NaGRaĕW^%'''YW#WUM6ꫯ`\cƀ't QFJLq;_^'EQkڭ!phm?odG~쎟vw"2pl@UUl6Cn~Ls 7 [O˖=(6 E_bA0i)cؾG·4dY&//SN|K5M6"~hd@<0)N;X,84M~xӅJ"nchD\'U1`O<4Kxs ,e˖q!N<o rLIcLOgذaX+WPVVƮ](,,Ym_cw{ ^J׳ˋ-Ziq:Xl_`h$;瓔亥>{^󩫫bX2e \|Ǔ٧x8r>}yaYR`ƹnܧic4ĉfZZ6JCjr]Ea%kQ"]555Ԅi$%%vB؈+) 5Dv68>"~:@UUq8[N[kb9-ئL!i56G$I"%% VG<\/Ayjj*_zzzǗءCjHMzzD pH3@QDjTTs`vH&LV$;ˏFXin܈HeS-HSIL5#>#^ST%%jRt@\t3v6XdfQ:.NJ v.wӧi4-[Maaa\رcqSL*M;ɓ')++cܹ۪ei3MFdWS6+U&:BձL+# YfRJ8 99q|>UUU^HNfΝA"8N~?TUUfaF3H$Ở֞[)iBQa#F}}}YUUeJ]ӊۮI^k**ݙr/GŊ. s=A !`H/C1|<|DiORB##tcpL%vmnFBP\..E,ME";ϟx7[XIII477#Ν; [D$NIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/rtt_slow5.png000066400000000000000000000070271506574241500243600ustar00rootroot00000000000000PNG  IHDR22?sRGBbKGD pHYs筯tIME(iq IDAThypUU?!B al-Q-DD&c+R5VTС]Ai%! A?=H5էV{6m$ b?i8R?yi(p3iz Ѱa@ @^^K,w[ڱc~x_KWJSR,ˢ^ze˖ꫯbs Mo[O(ߑػ :_|*iO}4HiӦvk׮{!ޙig"Bw7'ˊ+9s&C aSXXȚ5kzq &P__Orr2cǎe;gضMyy9x?DRdff|r.\Hqq1/ix뭷8t[neƌ,\-[t-$--M6h'pB> 0=wu@zoRTTTPQQAii)7n$G=uuuhFyyyssswGN'GwaժU477>`֭Y>>m۶}`0HFFEѓ333Y~ĉ<5~%Kpĉ8͜9sohmm%_h"y䑎'5={vZpz 0~xR?͛G8F)<˹k3f l۶M~AcS@bH4u]P(4M#bc[6einwr0 ϋjBk9¼/pJְi kĵX ԌaZ躎| #4,DFDGqmvÆP+ƾ-M6N'P$%ŁǀqMMCK\2` b iZ}.}6s_R{T! zԁ%˅[Nå9F6J3}ae;WR}%[C fb~ԅ%E4tMaU"", YDwo5#4n?#iAe0[xqYD*x*:RDQǁs9Sσ4?_$x oG- (Pvѳ۶m MC#DT Ó9͓@e{rᲣ -6xV},~' 55=mۘ"#}uAjD{QiSH@lA, b_Ss{\@6W<_>Gڪ7;mepIR-KضaZNDjWGktcq.nK-h۱ߣGsM5L)jԴ.t+!5p)?pS-i}siM Xʡ:)]n+k7^<#is@ZC \~Gss3)))<󔕕h"/hu@E۷s0Mc Hˤk'*]Z0qmw4Mcذa:t.)ZŝwI^^>/>7)))~1ލ\KT@ DoFWS3F6p: tmV5DƍۦbXb*D@NKsN +W t: bgl޼ 60eʔ xfoj^ |[n9sxLHN۰MR&M"j 'OJMMH.< P+W=aJ#Q"?, "kk 9~Ԉm""RWW'H$-"w۶Jĉa+zP}iS*N JF[A  %b 7b/]*r?_CE|Pdݺ8l۾1KD+ҥ95UV Jt2pZ6yDP0իa:hnRR` 6 Qcس{7H#R~ȬᠮB!i t9U3GhMS,Kge"mm""*yI"}̘!> NI;!A~5{\N,rصKBARU[$,<*"Q͏iW,r_yEdH/dK fy Yn|f܇S_BZfkF =z<~5{eo9Bӹ&/1s!y;{ɓPӧsDfR m`m @ ӟ>|x_eZm{"Mcĉض}!:snԾ_Z$4 ;ͥs&(݀H=|Yx12ng'LD)IMMYDf_O$2ML%%%R]]ݭnT˲nbW}O>^Aץ)+V$/O[DE^==VORRR"z4Lcc/2ܹC0s֬؟ԪUHq1j4uB+PGd@A\<u8zͰBa渻5hhht>֝z+Q隰mR+t ض} ']Us^"J)*++Q+㝡IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/settings.png000066400000000000000000000120151506574241500242470ustar00rootroot00000000000000PNG  IHDR22?zTXtRaw profile type exifxmPA }Z|mm?/"mjI!#="\Īj ]ùDs\%{Y`Ut3ͅu q1bKGD3&M pHYs筯tIME %!IDAThYN@]vBĄ@p"'3S`IҙYݗ3S!:hn!A_bѯȑCB999( Q#8$plEdYx1vW8y̓=C`0@񗗗- x~^/1.ZYu@0M=Yq8"2&I `8^CUl̞昈-K,X Hdc_@mk01MjB ^,ː$Ih_BeF#S ZPe Be 9(W&Bt'=$ iT*śr]OOO8t]UklF˸i0 ~~+< _nfT[&Ϧ*FޚzQ!v:.5MM 41?N1N6|xW:apK!kUUsUݹxk\ ҝWnn>sb \j}l6+ʦM&T*Quضi&J @LT(EQp}}b(!fykbmnR ۶A)E\jl6Z:* 4Mzxx@m;1Ѩ:noo!IRI:==EV 4M DxssXY4nn]snoC)9},DcZ^1f6_IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/simulationshg.png000066400000000000000000000007271506574241500253040ustar00rootroot00000000000000PNG  IHDRL\sBITO pHYs&&:#tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/23/06k-IDATxA 0MF4 z;3'kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` 5kX` `fHfIENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/speichern.png000066400000000000000000000032101506574241500243640ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#%tEXtSoftwareMacromedia Fireworks MX 2004vtEXtCreation Time04/23/06kIDATxIoEzzL`Q9=!ANpDH 8!.9"8G >9D v\c Apy}++믳2=.^֭[;vW_}ۻe7ˮfmsT /?oΛo͗_|>= -5F01"^|,_>t\D|gv͵zgwHdZsh(oQh $s"kGƹZa//#+%<5 "9xDdԚ918G\ ɥ"M4J Bw+#GK ΋(Vx;A$ øDw#fOxGtQX_2΁9ٖjd?$ ̳O>"9ŒRhJ Ri*R ÐV=5N" ;Z-s|yڷkloCR{+[GJA'7kֲq V+s4;D ܬ㜢T*·HV VuŽ+4xflp.n *{r ADXZqp3O)rϲ_7 g::<( *(h}?]( 4b7casi8\D(Ǭlmml6Zi qz(AС<^DIƆ!_dyy k! ;XbMG~c >}=~vrbJ|n޼;g&8y$ϟ}u9taHh-APհr9ovUCLŴ6Xgݼ .]bkk BѠZh"ŢO٢?{Q*~?2j$ZT_"J,]gfx8jpt+$= f5JwG;Wޑ^eY ;3(MDڵmvww9W^Oh&eG믿@Z\v URt:RsIQȕ쯝9W.~95J$DjgϞnml6gbyiiyI6j/Ǐ4ͨPV9z(GaaawZ2 {Ƚc^m?fRxg#7IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/speichern_klein.png000066400000000000000000000022221506574241500255500ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs&&:#tEXtCreation Time08/13/07tEXtSoftwareAdobe FireworksONIDATX͗oUs̝7(ɽ%⑊ *DCC : )B@"H*Gqdwvbv׎wDH#=ޑ#Giz[^'^zܹӲ1 HA`_~k@~jL/p!"L"c$Mj!q?TjP:Y:Ƙg 3??wx!b @֦ܾ}+W~>D2}QQ9OduuF9s%r+eYb\*r1B: IvBH!ݻx!vloo\DYC N09t?Uө64V Ak `LHFGp{^9-1/`0!Q;7vYru |&"!b\Yr).^>ôt'ŧfꩌo_8@cׯ_o_}# Yf Ezի]<œQAz|wK>+fXgͶqF/]ҋ/v^m@!ozJK/ɗ` y~G._EG~{|˪U.]~_(~ >r~b `&aFH#J`a0O!8& y ^~e2`"8xX*vZ~ʕ6w4^|EwӦMiDyp .tgC=q0')xLuxfC Cq D(y:%FPg3Y|e)L|A *FRBzG| uR6]1m ɩ ]h*4ӋAoġxGCi3ˆab6z"ecHBP_H$@?L"GހP@J`OO} mH') (APNAI$B@# |B| Lq# 'VnCR7ʡѽ#\e/Vt1az-N#GO  6whf<[DHBP #tzޣ3GLj8G$Su #1;&I=)OjlʧAzm11/d-:M(I?$e\@"!%y=dKeDUo %֨(8%nr*G`2HLxjK!aӏ| / ԳوxIÆut0GL3]UY^NH]@0Ze fE#KՊ= ŗz?; z:Bs c\:01|#PA1G"?J}ftmE/`zydlРA6n8馛ҥ{Ξ=c0QFv[vsqϣ>ځ( 9Dzz5+lN#94kժe;'0@zֱcG;ud;w380axtM$@L)--uZG룻%0aÆv';& Ѽy`0*"l)#`<[z5NFL&NNr6>y2<X4eӀ^ѣG񄡿s==G]C-[ZVZK.5r)"qg@CW;uE 8Ћ4>1"5Ç7;𨄱cǺDE5z-n[;p#,:۟yٳ |Iwoꘌ߾}{Ɵvi`D]^}y p k av;\|ֻwo7L uYnnϰ#xQH *( 6Com2&bx /rz< o۶c>p!{ -<>>sn< 䁕?}thxGuT^BF9"VcUEWK^z6?`7m]@!znKqD>q>a>(_NC:g7fcġ'Nt0,{8Rʁx0~֭/YcuOG]`#.H8iGYtJ Sg}KJk`z8WLjMǬK.VRR$:1~>}فNt>c,otg̮lIQP"luMJ-8> akVnbnq`{ꩧaB"zu͚51^{m`%`=OT\T,qYb5{{1jvh ZРֺilo}{I@Pd'Ԫ]B'2dX|cPR)^XyC@ыɇ7Y݉fuǭ=̴&XbzooiSXn}pfY+[!Z1?Z;>xcuׂץBk=@@22ia#}nk#<6> dP:dNe!}͛liSj5[ZƧCVe;l-:}TrՎAWJ ٳf!>͚-Og8ɞ} Nv[7mmZ:8O>jd]ueOҹVw*sBJ=Lm[͙>>EϾZgԂHOg2A+/0HƮ(-qgNc]b2j?J>o{ Yf{L2۽llm䫺s?8@K{tyB}ƶ *A7flJhh.Zl[Cΰ]_j5یݻ$Ao9#]X^9:~:Yl1r|x0ʧ̙3Lu4) ?˂l&=lfۛVR +-ck.ykdQo$b|ۡŨ7jvf/} x݂ڔ^lPoQoTS4'z0u݁g LWdx,‚ LrGy?)iO,6+;:]醍tt۰qSZ_רx@_xtzeUEH]w) z֦M7ƪ 33jҤիWz^6~񎍟Swuiw)-,z6)2%n  4 ; tB+hhР[c \? S/k֤}?pq|Csft]WYfѿR.e #D7{MC?zW0I0ˢ,;D3" =E(v&`3,:Y" 4n\: f%v5xq r`.*@#?s },| H!e!Į`w Z9$oct'{*>qc1&ud`rI6?n$-CHF/X8&wk u1yS|Dnc=/-&:I|v!8Up<1 Y(N@FmaGPNac8Lv*Op* jY<8ya>zL!7ŋSg\Yf?0\`b\:?HT e#Og 0of21<,04I|84F&: 0tM+&z'6Xul11^u 0@> | +F:'(} h]xI5L)JPd,"BͥfͲy湛Y>g'Zh{*YfUGv?؋R^1Y6fg1-9΂t7mQRXhM oV^U b ,374+ͬ)%=-׹:4zĬ㤷gYx_eFPvג#/NÌYS~ʰ D~SԋX(7=P&㔌ҽd|3`M'V8-~̨ג <ǩAe8p}8cdbO y1mQ8@{HH.W&Y-NAO@0NCO$-JL%/'վg`Y@ _3; =cozcS_L~B 3QֈdO(NسjĴmg+.U{?zͨ`YcZ;` (WӃfvV0Z蕞43?-!`iLĉm㘷 ɐi?HHS7G,v_+dA"{t)iϧP3K㗇aib`4o_>i"q"@qjkl:FGzg_ЗEqs ̘>Q/ya_k,3{9L'c 7C xm '*낎|bzR F 뙖!4f:du>)Bc@)~QrZ2)V>^ubUXqL3,Kg$H @LN0 8H A #̬D$Hг.`@ \H A .S$ A)H A$H eD$H2 J2\"; \FH xw%/Ҳ,hZHT!Ab(0 UPώ68Q| #/F 3bz~+,//#υ OlnnͥREe Ń(I @!=.Eϟ%ACƭhP 1mZ×Mfffd$2 1~?|jj Ɖ)N³gΜq5I @!=.%NpYP'da׀:"FB)bQBƾ曑4AF $D'|sP!j>FWo=y$ܹs` b`GP@τ D @|7}> DHeY{}GcH 4 G믿 h@#$ *O?/?cx>m;'xgyo0 *CG(J'\/ 0'|;~8d=JE SO=mp1.#v$^ A0{}Q|b1ML;>0K/n#(j0# d(g^=z8q;Z0+*np~.&Q"Vzfn.=}Y>CvvvnF.1&=   +6@TT*IC{߃?ST{0KUpM7݄g~P;#R)_0# d(w Xoz!h00qcA+Bǿ{fXn믿(.қf}z+4b =O EA$H0"k7܍qP(SpރI @!?k#<ꫯ*.JԷr 9A3܍ː _)h[||ss QXh- _ȑ#ླྀ=Ck? 7Ac,?~'NU'@4wqǃ> {뭷GRn{G_uW2%|W^!G_I~‰lD F֠qϿgϞ]\\X1čǯö( w}P,xGkGqw< Ɓq W_::*1p~g?gS&0W_>kY!N#`vۏ;0{9DW`܀QlDT"  p(j;Ux@o333b 7 DQ=u/y(y* O  qxڎ |6Ν;M"w}ihz׈ggg7<_H2GRFJy8 F+2C@!o~gACPi'X.ޫX{5wCx6HJHc5= a$#@>Z[KsE055uTF \H]L+dD."W8xhм^J|;h?`C@#A@(_ D&P;?7>(E ADIJED( DC!!H `H q#fB4ۏ>-º4A!Dz- !KCa$H/Hw^_Ԁ/ٳ~)&t'HpH q#8r=`&B-$HWl\z+^P՗QÇy@Ooӧ12#$#1r Dz- \| l6KkDhY@" "A^$zBߛd L?sL^+;-O 0A@J`bD&P6^ #-QNd Ab>[;w ߛd =Nڳ( _ʊop8р Ƅ .>-KLE09O8Ho"m'7Fb Ș x'{ǏF&0i~}n΋/hU:bIwP# 8 NbyLPG?uK/=o6~yfqğ\;(Ahƒ~)0+~;}by|mghap?Ac dT՞C`@B~Ch#{̙3Qf7# ~H+@@6v~I$> Qꃮx9H0H X/> >wSZon{;F5N44`IH;UZV;7B| 2р Fq W_M@[nV%~߃}ї^z>.D.Fr CWWWsq4΃$`~[ p" `"xG{RIRmmpM`@@CH뭷O>$ZJT$ C|'xP_/| ?>Hߛ"A0#@ᅬJnYG$G$l1F|vв:E7EAB bH@d(Cxx}5Iк2OF F#ɔ .O 0"E_{7G}!S0{ AȀX6@N<uʍ$S$<E)$@+~m "f?_~?UR-V rG} $H0 ( G7߈ȿOJ\wu|W^"CG¿bTh(J%bx< A"}]T8of@|ڰsgge(AT C"c|mٳЀ ҵ93_#2`N Q'u[ x;}N>G z@ ,,7A&( QM &_}շz>e#P?F,~~_ywȗv:w\:4;>p(EM7D?͌ Ub4L AX M4@P LO>m1O!`C3\' .|y#;vvC= /8hrQ2rpqEnDaxbZ8 u @^4awi+= zJ v@H):a g)[n`^H8_|Ȉ]2ا'y o|@C|!FдII &e3@/PZ4*@8 a"d]x#ދ# C!.c}rN~;rz7ʅ/@?4I_ѡm|lD @km̙3𑡸hy@ pѲ,5~` Y O/9}݈ Cz UW@P#H}  Fv;0蒁)J `(@mG@ u;[7H>ނwx/ގ8Pb!g_~#h@Nᄃ~Z^:K ,Zߏo +_ p{D^z)4@H }-cO<ĭ]W9nx#W_}?$3=(-܀ {"T@q9"8z9} m\ \^V;qn0/hyS“ D"|^q@LyȾsh(,@v X m D1miRC]@p曐@</";AiyA`_4n#<f[ n h\炬A 8_a/BeCv(B~G9D Ѷ-?4A1Tt2-ڻG}tip߈G(=-xHF/ 䝡N$OxG@4fh7&)Np!l.<CW@$ɀs᳃  т)#%A@vJT!S"u`=zN|݊A\JCpWA[Ν;ˁT`P0Ap&]oB߫󡡶y;\ OӖhC'O6h%BAW'9p:w=558A~v^C (LP8hȄF;.~!L(J V$+h3r70 䇾-w"jZyK+塡j9m>,b"{h봾|dH =R"ڵݤo'۪###/88lj %0= ؖ ?SP_{oW94.9ʕS%9, 6^9{Ģf<O'1ho}E тg0$H^ FD`UjvA£й@P#M'iO`RgQ 'Az ֠vK0F$ | }@zQb&Hp8 . \@,O] 'H$ bqq) u.(>a F1y9 K@)4$H0I m˨M35a L |eY'd 6a(<(W84:L;$H`@ŕwH A$H eD$H0.u7fc>5G$KjĬQ0 ^G 54qh㽴/ Ɗlo=ʞ&xZrTQ:ʕj 9vкaZVMakIﱀ1_zwk A+hVZPV^;M>JlٌwĶ̹eizU.3(\ lBn/rFLJmzv5`zI ԣ |yrpQ8콣aͭ0&lP3rFq+8NsP^V7eM_Jgs嵲hy8c`Fp(𬣩),ɽg 72Q?ů&q0U ~}rJ)_Xq /b=24n[a[x/ODU,}oz4Ns9~aBX 3<}  R]BJ% g=u~S  ťM+xC˕zs>0LJ59#U'5^{ R:33J=x6%%ۼ6$vDvDQئUvG3,iXBnpӑq56uӭr)hNT&N 5ϭ-[/.Uvq5o2\ժ}t\pB}kyc:[\,3uem+W(WVz.Q* n9NG'|"ASYP;̿ttP\5p~Q'P!6}F 8]c=^I2@vH-Fֳ<0ͽZks {l!wjJ͟;76"F+bC:p3gD`4JI9 k37Z\ZN{@_(AVs"$gYk*T"AVaY52VPbC uպhwn8po;tTNA4AlHE\V@ ޏ\w )ld E-h*J#+3۫KZdep@ D/ S0jPNnbhw8^L58zYL$޴#;F+7Ӻh+{ 8k1 @_CF`#hȚpk3] BOcQA ٭-mC|Wj>---C76(fu֝P $MxvLzuumu :)T !:\JuQ T}UoVUպ p0B7åV?Z7*PQ#s༡FTԠlq:dz=*_㻞*D9XS2L~!_]` &ەz6WʤWQ0;ekAum=~3V׃ ZZ^ަI\lCdu֥T1l:co '^`]Q·ݨ; Y5ޟ|פ2V]hum@ҍ5 -q>ۼSt=M2fz]~ $iaa<_3FƆi2X_o&MsWU 3q+N8 2T֦k\6 (U$T=X2@kYͺcsª[^kfa.laĖϑÆ_Q.: 9(oe:B 6-vijXTsςr.t5:v,4ZmPl\+-sRՌVjmTsZ-_ zݣ(M+9Y6HkDU3a8X EhLMOg[9E,.CBO@7*o[Novn7PQ }a&AR84IjEH;m7u\.6fi*,E3S_C3uR(+FuS5y_fFe6ތ1KB/ dB,Pib񂇙rE$ 1U`4a9%éf.TY62lyN͂Q-M%G$rv$o C(7/FWUlqՅj9Yv-S,ʇM]е^Kt*?Vp by0t^4(oJjb:==7 o@MYHE@EmZm#67vVZՒ} `K9׺mptB*-U3F6enBa=ҳ44VHb30X!i6& OVKe&\0۶ӄb9\fAV6lhU6Jiڦ*:Ԧ'[4[|ͅ艮/U6Uc9 Ο?/9?_^|qsO9JR2 @67Vөjk%i؁ Mv S1jc.H79y̏dfhCA{_kx;|9BОjhFmi,5BY6͊ekMWk6kf٬b)5|6tXbΔ5~ƬN۵ݓ|O t⎵5&[[qFP2'bOPŷYS Ŷ޶wu`h -?'2wHyKtV+Қ'Kfn3ƌݴ+ 63}J+^4P94x5Vb ;ġ;q閫:[{N3pJ~an0 ٍln;Δ˥z.Mg'V`|Exd%6ii ߭7ۣAD&K_5M57r5&LP#WZE77  4>$g@,jL֓oÿF ~9<08Rm>0LYjtD0v2SnC[hny~lԶ|Jٶ96ƙ~Җ+UE;~1-.fW k5Ē[[[ M0l + /u?cs0~]x.`HebCyNE ̛\g5\wxSy`LCsuYC4v=;P{f8MIQVGuf6Gkf&˖0" n1AwzYcsm\n7*zak+JzeuJT(\[`ηn^`ZNܛBgRJe]i}.Z`CG#1-.unzxB:mdҋK 4CKTV76x`XO߲nekF/,P$.X$ؒ$:r|@"\SPV#C] y[Ґ`fP2nۭjQi f6jsm @K?hFh#GUrq:Xm:춷Ec*b>,,.,&#p:ZMgfղϝ/Bo (<$ IoNRBZ#{N,Sibɗ\QQvwakݣRcUջN-ݣ[7pLyp1v66d2r Ϧ76sl&M¦յj5}>,'& &[ߛly5`.V'4+>1nWBC7m+yh)[hcSjөTjeE0V(,C'qJ6I\Ý&~HZȂN\z. $~ GҌh{$UCGW 6xw%&Ռ_ 9W?clS *{v{0fR 8y/Z&?+se~JQ>L`m fv݄iWrU6I]|..vX `,,Nj~j8 Lr6m ٢HYlftk"\]pm0-ɻax9 yq5uo3TSfV{|E YeM|TrdSSShw!`W$` Z 'GB%zh e1ʻx'3w@p 6d @]pظ^MڪeUzMAڭtZ ~Yֵm6`["̓+[[KKˋKgΜ_uԙHq_R;`䐷r\óRM"TezMOS[׵mi蝶i:*?*qKq wَڱa.t3-U_DZ{*zrDX}.[^YAyZYY2l/@`a{K`{;/gffE ]>Dhr89K.pw􍎹q` r=g*r,tve.uJ [N[Uզy긝ۊ d`Hz ŠШw 7IGZoܠiw-mvV+bút54 t N,Z#[[mTgϢǧӡ  A.iBU3]SKB!H fr, G ==hwt+ @OE6TYv &5ihH @ Ӏh:N ~8lB@HZ&)- `~&BG뒦D-.ԄO+rܹs3ssZW(#y)阹ޱ%1G43N@(yTg.E+nBۅ5*~~B:rhȵ@ uޒ3?h9t@f=쨼,R01Jb[BF5Du6 ơ / oR0-,O;!̾s' ``;{5bnvF@nsCOG+2ZeQA5o0g opR]G !v=k9bǟ{kHURgFw]nθ TX]8T ?`;zcjmo-UF*Lb,i^i1o \1% Z [)H@yWQ*Ti4 ;*v(rky n9N`m߯m!S+M{iK<`UqO\>}0!ke-u$:m$ QiHc-ocm-y6`v̳Ne(`7D w~mm._Nly2B* nav$NGcof9Wbn[BH'\_-&+Nӊrt纬@9ȶ9ZW{#lS Y2bByF8;vAMeĿF\`Җ԰3:v[d"y4E.@ *' @G>aQ(l_V*^ddMQF<ʕh9Nv6˖Y4oU[M186 mMo!]`LD~u]x ^[c{8pV@!x# CB(4CGsVK:u;-|F9hfE54T/ D'"'e 3XtR,wb59tÈ8AyZ2>c[**NKkdD)t[tķ}@[Aphx-Ќ4}bt[-˯wo=HZ .vkHDeZR-u!4p3k3٭-ӓ$(! Y㢥eF|PH?&:Ŝ():;KtHE Mm2_t(? uπ1^>GY*ξ64tVyV^&Ӕ[F~.'۹=b@ZrcOkO. 8=?Wt7JШ% vpa $dAF!G$w mw821%DoyMZwwj,8nKw.i_L\;GS_qLBe 8|FkuS䚇ZhpC=oT/{L(q0dq9a~L+{`1AIӶMP_З {{NX^K 8G-Z<!jY _nu͡sY<_a.߮R͡9V`q9T.{P3iF2][{=ʟ68YzIz= VwKVJSM0وjuqn5}//{DDCIoJAID [ۼn` QGq9Q:)nAğP+ڲHt1TФe. Օ}>iX下OtqXΪX[zju]Uޫ(mՉ_(YtJ7R<ږznc>_Xkx)i4$ z].11цQɪ}aP֐n[W[ ߽Bi+C4 ?y0-qd2F? Z~J䋟, @ӽLsJV]=b[ĭAzmJC^Y5I>O6x A\Tx/䏌b1ECG,e{VdՑȬA+!@ --Ā"!Վk|zNBT#=\&PUՄ`L|_A+@m4 ̾~b5^X́iyWWyWW4}VwYu_ԴC \xUyX$hKHqG=8^p'k \u^Mp } jgLZ[; ^ mCE @p4TٳNGcкGQ-x慿Zۥ~O CkNK$huRkR/[,ZZ@\H<w~k]FѮ(g6H=/U{u:MM)5~ΐNzc o':<7XPE3KlOߕ3ߕot}A/}7S7;{-߱M.ꍐσi"i@url/ȷ}~G ܉MSz&QfTwu~Ƀ}k ̈́Fz_ R7vǶ)#Kh]7& Uhuq8 - lOR2l6w~MpVГm;ngǍVg"i7Wbo@ XV|O]M{Yˢ^5{j/7tq뚭n#8 ɐP-@Ttju+E#(Qq(1CN0 fNK|ya @/_ rN]@6, 9 r."%,='DAo đ:\?Ob"иl(R:"r_ 7gfB5j6;SSUibyyZ^]-/ \+( SSig~^j5E1i*ÿiSՐLְuK:l\,UJ~s {}71~Q3Se}5/:DL/x0>  59LyxХj^]%n;RխL&QZ^6ggۜݩF*/66r[[\P(KrRjX.J:mR8!@9%V( Vf5D}Ad_ u&;4Ym, cL*Me۠@8(MZQ ~bѡF88WF> pU +LMrLXkkw! e&r̦\v-ҧt-K3ͲA ej賳74M3xS@.XxZE58nj 'lOD s#OqN'qEM>& E؞q&=85#|9zC=ec2wxa??wn#\43HL0Z ^j'mp0É=sApsv*Eه'WۅLllJcAe[[RZZV*unƸlNK<;Vsw#)l9|ZԹ\wcޝhWeR>QܰC Ƈ aof*C ~h4G(;A(b(J08N0M'..fsZ9uVW|ʺXvD@A˹\ZlGK"LO (uk?iUͮq#j"S??J &moQ(\Z?19Z5DBV2HO+O}r @HlX`L&UoC بR1ظqpQ(k[[[yMc]:JkqB^)Q`mp, [wgg+\(P_P-5F@Wph @;@| LC̻AA /(+WQ=cAZGB@tW5d믿FKpH1>Nei_/: $p!ns"d Rta}=W,-=(%6(1̌;=]fk^5X7JZ5^d&}`zr#nٝh މ񹴲Ǥ-z(98ٗJ0>_UW]W_]w}G_R 2〢 |"dPC6qfgs۬W.w|\"%;r9^[-h񆕕rbaЈsO=28w~Њ^~9bHDN 6 (y'q4. ß'J W$mЎq1 @(5luePG~p"vgfJJjFJJr9&J/zMk/,0Y\,(&J4\K˵^v )9Ꟑ"U./7J0|9&;ZM;1xygR9#+x I|H?? okFf z~>ND*jiew~9ssl6UM77;Nq#uf?pDTU+~cS=M& & iT]1їIFx|%_W(\oQD,|3-zC-v ?Ñ#GPK Fs_|JnL~tFouڿ׳._[Oetq?kkrYefcg34 ]"]>[M(5鮬 d쿻t`myf @ݨ(j)vxy?&t]>J>(Ź.""_zcZf64Tg 6b/t*xW{,=< 6ļg|ɨ[ (6 ZZ.g/,o77'6hK|vȆkZ6R~dtrTjwֲBTBm"ؿP4L [z4<fUBEw%B! hJ' ӌ`=c7׃ d,'? Ar׿u*LRy{2Ayk U&qTFEӪ[[ؿP5Ir‚jml[[z 04,??O|gYhj>5e5ƥlp~PםJq2XLjÄ PmW/MPz @Ly/ jV;jL(vqCNW(WmEhniio(8T+TWvALj1aC%X~j yb4N+NffvvR)bki]ZjV_˟':Ÿg &c\\Y:\: +ȕjh}QP|E.Eg=j0NW̻MD&qvklM>s[ o?!'vXس:Ljp T7n܍Z*eR͙;H'wqgkzZ<4 2OOo-/gsjha n! nWlOj Ud!~7+Cd≮3JґfTbAϦIzGOA1λx#ߣ,dGn[nwyn @s]:=˃"b =LZS$rumI'J\!r l\.2CXomU "4mVEau-3ԝ(@7e+@k& @@Ϯ X/N/..͎3Ĥ "(Q1Y87yw ٌ2u2aT[nQ=`4Fq(;O=(zr*O~KB)[A i5k=_{|'Lj]3ի}}lwM3Jٳ;33r0S{WPuFWWT͟mRpQ:@p`|^r]@ݜ9ۮu^M}vX__@mPajg̵`i澢p"3$DөT .,,oP܄-ዹ×\2Q\sO# 7o~s(|Oy'N˯[oG!P({+sEF] v樛1%{B`d20=d"-#'QxGDCP5R}f\V|{N=-k$ C t #? ˱Ł$МzAN (*emHHC8Z_ G=ۋq' '%...Ns /(fc'iTؤq G ]xAEN>Heଞ7x`1?uԳ> +_W]u5\?~pT]@~Г k+u{ c^EnmEpm}d(>LTdٷ R*l h1h@aB(vRZJFx<փ ؊vS0a2Ԕ]ف8]P $~~>YF9$a4$?*;#@Q:waxGF(`jeC@B7R9q'd(tf'br׎I>ѣ??wDq;?O? "@A=[B;9D(80*BЗSX%_>D(({ fMmVo_7vJvyU6"H&Q9cH5CDP}CCDӁ$A x CW |uj*lNJ38?kKKKhH!6O Ou]?/BālCZ@ _9C-2P@9갳qWIшS4H FS@#B?Q - ½ E!VQ!D>XiIH!@BUQQq3Y: },{D(^ y Lތ՘]@xN`vCr+  AP8x @KeE7 :|B>@Pǩ7|{~aD:zP曗^ziT_uUr'O ʄR4ݍpd}Rl(,2)f46^L'L(()N4|e'Js(]voCRCDp=ZLCAzHt#Z1j U~N/x4"{ #i;Ds 4 &4| \PS۸O`v['ȧ~za!P?\y啸Ehw?ww{g7^@!B#}v@_`;(0jw(i(MGbti*E?n >_rǜt 4 Ht&|R7LIԥ_GHgc!EvFS?SOG?=ĉhNwL}+ZD,j0ə3g&xW_}\?*メr 8rPh ]@Z;;^s}(TٮԹpNEp^B1AD)@d{{کY@ =62:EHIf9\L곤L,I_89gn͉͉yR ǙC%h#}$wWY" w;2x"no^kPSYM0g04sJ fP*W4wg?54'w ʞMntԺа0yz} @Z'C !΢fH*r^1ȝ|(;#(H9pbZ^fP 6Q9xWE.P\, 1MfwZl |jW7#GdO{=u)(Xiq=;>9{ B<]s%0\Xƿ`j9n %VF1/ꖩm׳[&ӆnAISkĤ T3JZ;b8tmޕCAeYDC`%jh ܇&S"qhzV[8778Dtz%Z'N$/JRAj!:jӆ.Zg1U_p%zDPtA4 ;wNя~o#dj2 'K="h ^yTiꪫK̫FO]DZn Zեj9\U˅:*pP,d6TV2YTTb %nЅ A#`n +%-΂] r)zNQ([qVr3).V$zxi?eA4*iL'P._b 9zPëskARZJSP`pƟ@\>~̌XAB?rI\A sߌ(^{>֎~ h){7z}@-v)672(E^+humf4KU2mVesjZʙtf;W^ @S70Z#!Z9OYno-v={I˾zĬХL10&pˀ)`'J _OS}~#%N+7m$ OMMQ 9%ZqaE/D7S:㯽 CKZN&DlH:m;ـ/Yoedv.˕:^G]-Vu2k 6_] )fRMCZ(ܠoZ#F 6+vf"$mjwO./v%]>ꟛ?EgזCh*}GExGꭷޢ)R4TN3 hRc;A C> cb:Y1=O/-,,Bt[=pMAzR>ٗa 0,Q?| Vz#O_c@QZSh L|Q>}G{N&qF;aUM >a^1念oK9-yǦ{y!Ym8 ?~}`ւY.$jg=w Qǫ\\ TKrt*|`(/sOJIS> !x~0/fS333陙E_>?76 lZF|XB+G}D+u?GȠF E POͥR󩙵Tji!f'!&s\͇Op5A`wȬ -n'$XB^hXK}NO$D G  o e랝4E!yEߣ-454s|SxssH-U bO׶a$z~w~$Pmq3lE[ ,-γb*>1M|#ɭ:@>g?:!q|ef_ԯ#/TH'khŕIA4+b)`xn+߄!I ʿŒ4;[ܕ/Ci訊Qh:$+OA|?O,F[Ҷb3 }vy)OüY<·6[n<ܾ֐>V/x?\[[yH WWWJ=Z"(" m&7|뭷F++++W_}5+ (䱺cз (9o"bw;s77Rv_P~ 7 U]vwGրv<w81c<_9K%:οz{kpm4? B5uѣ# *(1!]@:!WUgaT'NCZ/@=q-ZZZ~^P&Pq_vH pfm 1;|._$S蜪khQI &=i 6< ,//,wv.C= H|"(J_|7~0jb/tnFg 藲)T'4B|&pZ‹G =aڡ׷.?O_,&FOp|]rTV|~2[PXdrL櫃I>pIP<S$F%N.uT0:7bS."Φox?S]s5! QƏ2lk#; jqw]hJqg0 5 ஶČbwς YǮ7s_ue-6 BX HnbhЌ=``oSnkz2&2O$)}??D -*Q8}a˲*p(PS_ r/=#K8.N+mC4lbvw&*B <!( _W 0r@sꇠ+`>;,Yt_ܢ"`0`ᮍ]wv7bshQk(ɝZ F m풥0]h٥tEWt-tpLuh,v>I^$8lc`&B*Y dcBE;3ь4K^wW/ی۾un׽U] tWsνܜ봣&c7e\"g:ډvc\#W98+|RdM|QqAZʲ έ)"a;gŊ!:ŀh\θJ0Gi]dldIO(IfD"nz6AP7nܘd:.2^{-Oz['|/J"8^ol8QS@?:-&'~ ]AeFqs?E9-OM>h!Yʡ:ոq] 9uOޅN,ݫ1M tZQzQ Xګb&#BVܥUѶ;b9duUEjŴx4CyCTfөt&vG¡^ԳH8$cTxvfz<LO͆X_#7w g=E`3 p7oذ?<@|v.fs)}'7o4Ն GST$ 0[Aci?MGbр? ٵ+ ^mƢ&P,$J1LOh\>٤7 :5>Ebx$'%oqݺuO=T#Z)'p@$O:餾}x 7C~CL| YuYrǚ%o[`4}sqC#+ )دxU_S` NxD2`*0zHi!/Z<,cr۵Q713D>?Nyt" N{Y=<Eg}DJ I뙜D"!1gPFυ>IEuRȥ~_c^&#Z,IG8z QE/'(D7뮹v4 /Yh4<ߞdJ 7X⃉h<)KáP ?gj&NWpTqrs_@Ohc/C,@Bl GLhDH2P&jU:增3OhM!~S3YTXdif'^v撵ں~ $೻H@.˩^UrIϤ~L0#]c3~ϞP4 SSpDKeR!t ss_"΄ l$4W]oD+vCO#cx*Iz&'ZmV6=SI=3( j[$g>8EؔR*BOÑlԲ1"Zd0`<(FZ%@v+r֭n?Lx%@vR,p8RDp  bi)Kٖb0hIE\+), פJ W-@-B"~ -0_i!e +@ɴ5 * s+@^&[p`ل`3fz{ES^P;bء\|d͍b4}Џ+hc@_ 2-2+CcG3ЬEDLK{ʚ$PhqG5.i4(i$J"XߨL/46X.7:-mR,2PX^{U.W\q嗯\rÆ * l>є~A?ihbf1?3ԇCEp2aD^+*дhx$b[:ʉ.X@,%tPI.s~";,t?IA}Oᧂ! =7D*X!J970"BDn/H<EZ:,ŢY /fC3QV/9Zל|G҉0~c:]8c{ x_Kaљt@>:"]S˻|bNr 5~q 6bWWr蚏IFy@X.x{5s5ps+_̶q(d.p饗 /Wz_ܶmۯgy NlJ_ WxgАþ0H fg;fG4K/Ćz8xƠ(tI框]کK^{1|1s/'w?=h4b!m$r[PAU u9JtSl =Dx`3<OggfAT0Rϊ1 CusJD F1E=(!-dRD 3do kE S!A?tEjl eԞ|MV]HJqt[׼CED’s\`T~Ӕ !{p@7x#Ŋ@ʎh &0MK}>Q C㞡=3C'<Ն}0HM[PY ۂvZxNnřܚ2[.xN4 X,dYcqS6>"[kiT@@~yH9oOoa-_ ,Y_Hbdx[~yd]fy023t*iTHrT*BMa ,9s"YeVrP.d-C քO{Psg;{e*VkXW[ K;ʣ\f|QU[ڨҴ-X *c ;;fL yd¥@@8@\ʻOC·%'WX4<]vZ?033CA{>ݖs=g\DB񟝝TQ[*W@@$L0R  O\i9k|vj0sJϗ*R*` T*9,E ~*l4i·t2ӌiN*V\̘EFtfMjQ*\x<C],s³-VX/X'Z~=~^{-8|r=φ!29x=O`?f,D`` R`h֏ˮ4є+V [~Yb[@1Ee Enpv[x<(hLCAf72+@& /ɖJX.+* o+VXޞIWv]GNƨ[u~:@f FŊ5T1Gb~x,dР侳^:|֯5+\b- MA, ղC9&|@Ǔ=mL Ccl"p >#CWxUR ֯ӿ(.u}d8Y#[I&?dWO5(@愁BCWa?v4+Z]+eht7UAtBIP,^/䰣8&ڋ_+M*y~.Q Xڍl@@׭[w 7Zꢋ.@sŜ<3gunذt2LR`wʇLcOԐup81F)-%rb,%WK=t* ǟ tAM!&C)@Vy>2~@MHv \_TomԾ۹SRPAp%j+!vl[@DCP^/܁֒䳏R6CZ"Fjm8 k_gʚ5k4 ) ㏧-=tիWtI$T!&(uT"qzȯ* E#ZeD[׌1^CcCã0c 4&;P'`6$0*iɕϽ@ @*-8;j#v@0ªʡ'ނ aC&^UyFs.LnJ4= RB,+6u Lr]vW_M@/_b xG}OΞ4l iE=kT: g-cƾ,adh|hh{ ǺOQӿl]?t^g^,+A9 &V6U}oZ#]鴇Q<:#4[ht)u}WZqv6 4'ZQDz ixUf2|c䢉1^XCCs'moJ_G}ε :xUȲZZ :@9Q%|/1P/@ XL 2NbGx'Gѝ-?{mxGxLACsRIC8˥oK $#X-J*ޠнDX,=2ΧoNbxdZa0`w˂T@4<*+: ]edO;!{wP{:bi>#(¯\R* vvl+NrUի r&={dx'C<D?%f%h=ѽdžz@ږ]@.{OU}ڐԃA6ޣP.Y>Uɗ2s6jZt-"];7#}oNS߸q ._zPP6 F'\o:-^7JsWD& `b"{+7\h({(2HPi!d+ttlZKB9Xv ,Z[AO)9ڒw `j["*'gbn [j@Sh6AG@@pMUY#'pMqK#{TP\4 Q,jtvAY49LGOx8mL8ͣ#@Ab3^H-& i$J '!`ZK$^+Zi#WȾϭ<1.hͲZ|ZnAZgr^ ; Of(>XpbsjIɟCĆCng 1wy 22VZ5"B/{R o#</eeh |-p_(I}oZ.}D)L/5Φ˳Y:~-)Da*Qv[8jF$ YlZC]2CI-hdt4>˭ڏwa9U΋ɪM:'`9tVTK mk9Z snc!cG}UyWYW&6 |*DБx"71aMgqO4 ŊZ"VN%p8O$RI3153㋰'hrRyTt\4{A7ޕꧭcN?|Aٶfff%J,-(2C2mǎ{w ?S." \i.Y_UׅVʱ L;OOihh<`'SxTK#H$$Z$awge?}~vRJ6J%c>mlxbApG*0V\CBC Wm TՊD"@:Jv# e`]T 7(!)l(m>u߯R[0h OF Ӟ?;x=,; p oXKhx?ill o8MbS㓑Dh:'A*@0*@;wP昜@W1UO͕;眜?}1e;Lt -{nO{W<81Wh:Coln/kS;'_~d>? 嚁]hV7=v▶>vɶs3Fe>d@;=Hġŧ=h,ZNāޘ6$pyYt׎@ @MXr]tr0P?>_y׿/U.K.d?:L&.abz}{YH%,X(\)uDT;m5ulW bl2:51[lz&WhosZO)7>_{YU??s5jfJZ/%ҹjZ_"զoY& 'Xr՗Zr,KO|:s3i4-ޟ}Y;wtR]n2Lr 38(pM=1kȦRjrDj8%׼Q6 z$x:蝡?IdS驩PDR3Ҧ3҈vUdN)Q00`XܻKv+VVLiTT,jl!ȴ Z3 -ob*ȝǾ[kvJv o\ @Δ.н2l|s7b cHD@/S=̶oj3kͤB?K'Y4"q-f|~MUұgjFF33"#Q6yseЏN9@A5  2E8WeiR)e{F@nA 2iɕuyvI?5#MᷡE~3pUg%i>W ~((4>>NGv<hz)kLK]ddj@|,Q`IS&}zGDp1 &df|ܱ-I5@U C9J*凼]b/je.dd 7H^1O%d >;yejP/l˸JWW}bbϞ=A&Fs\G٫ژvBD9fٳSG@[R/~s,S~'?Loi#M5RU39n5U (U#:.x-MK"XSiwi8C?wy&Ixr}rHmfx5ϕI.xwp{5Z*MN\{ږ!ttݻ)>n A@g [j!NY3mR'rfI]n ~ md_˥T܋\fG@'8)Bqω x?1`Q8jڜ 5XBv(s@=Xl3 2֚Z3/'p3ޔilasN0HbU`]`?8`̴ /r9gѹVT8z4aCӷ@) @?;A 讄}m3c2U..xT,"\] \ rPT.]Fž,*?P;N\ bDTge[ܺ^&^bP"QmeTeiy #0 =-_4k]:@5蝕&;Aצoo߾}ΝM +NJzFcp[ۡ\HtO*Bݏ"[JG?c |W٠*܍w+ ik*E(S+ӲM9RDk~fV:"ty w$t.a_d2xrGdRI@ԀUWib#Yv/Бp.!`sub*\L (%YuY nS⋿nʎ;p©]f%+ފZuF1t<Bv  A9ɺ_q1m]zP!u^qcEu"2Cϝi;Z!ܧiWvv!M$)Ahڀoi@IE;(4 -\;tVF<*v6iJ+g9w5e)}9Ao믿,TAssg%+Jƹ 0CAw6Fq@˄#kZ.i2u(ӵܙTo4rΜ̌N'*+k{dUȓK=Jdw:B<#4 eot&>!pr[>UXmIT֧ ѓ \Rb m59Ϥ5Ӕ& !m۶'|iygo&Ƹ ,j㹣G<-!|G): JGn- ~B@*w2e$*^aS)sbWN]% '):O~Ba( @hЏT`^$ԑdjU=o* ^3$h"B #8ԕLR=(9@9mMu{oLP.?́NGCہ̅rMyF^|k__HC[3_I401u! yAsq-_qe*ʲwM'[fqDIL̛ܺE*?899LBh).A?1Y]@% F4YHJB9_dK?S9#-&2E-U$ؖnuO˵@O[E, o!za؞P#4QCI0T.*}kcJGy)"% pSFZ\VvJ뢖 ^ l:݀whYn' P W ƸV!֗d=z~(II[=p{b11LER@~k| "@z<]觲%0A:W\+S\ m-[h&0lHom S ![$F5QAӋ1xf=A;vjÂ)+@6tړOO ABJgRXCh EJbPq}y˨/nguop'WpxGEJJQ~3| #-i~ oF)tV/BůTh^L6K5x؞fy *]Iր~ yv=m2J-!YCN!ʚJ'fo믿OB وL۷o pBXbV{7@ J-'o>@8o|o#zl 4ơp|Q6bKXt~Ntߌe2z%|>)1S',Gqq[QݔY_ч P5EXbɎ2&v6Q?\]kv^ FA1Q34F-̣?\fNg\JS~̯Q'PcMyw)w rtG<h-AD-OQ@'vw EG/t~£{!ܭJC ><<<>:>a.!|VNFgƽiZ;M ZoOKYvEp-WwU%=ߚ|ƾvA5:DA atz<.ə@z.߆Mw(~ st`@AMI)@iiN<CQXͦLA+Pc+{NvK ߢ*^4e{Syh%Fd ]Pţ> ⃂00(h aVfsˢ5U6c#CON?0p3@ CC@{H} T/ v-4`k(pJ݁Q2T%+bt&Z sN55W^M!ȣeZ-DD)D3>BZX`]$p^Ut"ۤrGZU |2+J!7'=m66K4uD/_ޔ F6ؼyO<kvba _kbdhdx|x322=2{dt(' {<:An!F#DWl_4<)̞IO(f2%S3/b^Aޑ]R -&3̷X2b?@ ݅"4_o3h[|֗ "Fr2@yTlNg@}4G(W(}ӆ"U3}e TKp9q)>Ӕgvmr˭&*4O˯ Rn[m @޹stΩ#Ӗ.% ePɼ^(O- ': cJovi;*D'=qnnl YhYv68x߇m%py7{ m1q^D| 苟p_~t[(IsvȨ\:ŎE;Y_iG;(mٗ{mߊy嬈o.}ꩧ~a(4 ٢-UlٲRS~*JbtLMOf/FG)($mdC?ܴ \lǐGK6 {";Ohx?721; -ffn}sEito@> (ILν@2lvL$8?>J 7[Vw5$͹4y`ܓiP"]gqu!hZ嗿%p믿Au6)]PRm7:1`*82{g\6SaîZtLW @Z11fJPX4k-vOgse|M[>2h+L94nuMvIP-=뮻kVltD"[Nd/ ~7ݵ^H ÊO6:zfwk `,ӯ.&dRjH0ʍA2( #ذW02O4,U9s5sw',t EtA@ƙ16:{ttlĤo' 8 T 5Wm|iOX`*mߜOeAj7H ` "XD(;#P{3QBX(}Qyk.0*Sֶk*D*-'|A?Tru[`lҮI䁎jہvR.QZƹ-hjI8FˣڨV F#Ha43V%ɝ #3DqPA D50Kp6 E\@wCAzᑝmXg; +? d.[x;==k)۬-P*vJb>'Ax8~اۙع!tiG.VY#ȕMJT!E/T|'lLyyZ^-2THR I)UV 4V-"]ZyWjjx Z#h F|qfL`Ly}D}I뎖X. LZ3@ d|.ߞXlr<25<{5bqi3>U@GůYjQ؃,ϩWK2.WKg;!4( >XmrtY.X DF| (>a3wiD/fh]GD")_0I9Rm'K.rxe\´@^b3p41) 8m|KwEt.OPU@ VP/ςnM3<LEF7Lp'Ւ#ܱ[-[,QܛoyES.K*lqM7( ) (K/;F,ݡrQm~\*u=jt>W1 3l?db2ʤB{ie[v.txt&^>flE(Ae@ b] 8M%*_(O MՆYf@XeDZY@l*\.EBt$Y-Ԓ=Ih`(GC2JՒSdHt-{"{|jfd·sjv生;=QO f0 `W 0_ \гs;'Ur6Di]TKq>7(2wGVNQp@F/@dlx4XHBn 3;p?pݽл0'XBh|;c#;[M9_ TUرbշv] Q 믿{~!pڶmݻ; H@uth;DOE=SPT7*y`ybq`H$Sl:չR>y=!(SRVDQa DʎOnxv12x%GMk`,=RaE1$( O[Lm|>D"CP򎈬^ =tDW6т:Œ..6QPad˂˘#^w;W̃ձ> ? %I߶lp+伦,p 7lڴ.0)oL'΃xO<9 Ȯ,uTB,=1LH( DTL #1-6AP&R,l6_b4炟wh]t]#;G ,`/B47MQ6+P d͕۵5yIS&;V۳ n⡏jŀJJWjo0.@(IۡGH?A|[19'ߑ%9tzQ]0ؑ+@<_WP׬Y_]qs}\ڵ ݎ\g rtM]£{챗_~W_%x衇r6o x.t'T2JerCљ ^*Y篲y:niaZ)[ʻM+%ˁ h51WrZ0lF&GFw1{;r@ql̜YXm4R"(\EbO i F_3=8vrsb.ޙ@$ANehDӢ0tH&;osZB5r{ @G\':+qY* ـo|LKGGGmZA>,\kb=nVc6^3<)g4L/oTHno߾ [.P30&Z(0tXX< -7aUtO37c_vi:􇺋9!^\Q3 :d(SPXOa3sb171# e:Q+nq溂Z kǪSEC-O ~]qf_aX<i'ykIV B&0ƙv_ҳp(If l6UGD*%G #Z$נ/3>42IgX& Pb& \ QeYDHxpaz ľ~1'm.ɷf0ސmwdLJwy'~R8ˢ- D$_;OLLn .Ň+W*5tQaڵr"}ݏ?8,4^z) | ꪫpV-^mĮWRʉMia1 0ȒMyDP$g?2a MNj["-SYu*#>W^y-^^O~YgWkCF9.Eg?=OV%śULw ŤݗRRĂcA`@{'QSSSH|qՌ x";}ڮ٘6- b- Uƣl҇Re1- cyתXo[F4l.o4# HGeS Mfg<^h1OA!ɤb>|h`?փ3>%Y/~sl"xJ30/s6efX1"ʛg;ZjU A/׏ֱgu=<pd۶md'w?9UK!W5mhh@m׶b(X= 1կRkoڴGhZkZ駟~TvWE[~=~ᇉ`s1p~4 'J<_|1"we\ʇ>l;uܮ{~Gˤ9b@|9zGPb ^sA.V*b)y"]H8-Wcxu49?QPk[@; w޲ˆ,3~w),z|t.I%>o4ζk}3 M<nGԬg %3.=0nY%<3L@ \7Z$  dӓ` 3P;s>- MYb7Fͫl@ tz!9ٜJ s`Y`?ҿ/si> xp A3&ff;@MV;11ꕋ>Qr)  џg.] N &曝c6P؆իW'M'#зlr[gy9M\F0sUׅ6='٣ '*%ơCh1aG4vj`d@D#(MT*|3D20WL6V I2Cb> ТQ*_44G+Lv"V%'sD.Ws뭷?G|Ϲ-@pBŁ))]/޼e{9`=o)nSޣ>ڢ>g~/[nAW . 'p瞋xpdOys+W^x|,{lsSC J԰keKs OVoz)E㖥&a Tb!XLFpqZˠxHYL';#o͝P'lye*3/p sdJ7\LxyX_ݫ7WbOkC$@Y&Ɓ\Dȫ\% #~z~|~t~_G房6HRo.PݱcwUU·<׎z+cNOOr)/Xਣz}Q5\|_*>%q` m͙z)*ꫯ0@$҂nKtA$rmt3Ϋl<8\Ӄ2i{ͳy@Iqo Ldy%%v{9UwB,`5/ rI`ճ)hh,m(F5Ϛq ս!̚+;QhC@_"*΅~h9w M@y |oko|>{"|u虑b ++$^x1G"B8rxUpk.,gm ~`}FtkC;rbp;\.. [7g/ m0@$aHdtjށFKf2twήl.' þL64bH"-l.i)-=wVAU8dߵY@^@;xR:ap D,|߽rwVb߿߿`O?{}w}^C_*q? \̌%UE;<8k׮(_eA-4+_ 0 5K.qsip _o=l߾n"4{ν1.Y+VжDge+,+(Qkͷ a Q"%S4 $ 38U<1TR=Vd@8stH< Z/P{X`8r߰xq<GY֥ yq6,c.g^ضmSO=ʙe.wGgzg6K9߻"` A~Wo?"S`(}7n>Fm_y /! +Xv-._ v@%'{LӤK̩K":D h֗$zy p:(Wm?UvZ %kܤ7l2V녢^,3Mdw}6_ҋFd6tx6l"J澀j0ޞ3<3 gঋ0N?O\9 ,Yt/}K@|q@9/*?>C]ؔ5kրD_^jۡeA؂b:\J1>:5Sx]vU- Q kR3>oY|1m,w:QddP4Ca"S,a'%&TPi;+~d@ǹO,:E='³~4NyO6[<dp߻jwW/`f>|ӦM;9&qGiS>(3xuDr1ѼUV?/EmFy7w}衇ɢs?7 W_}5!;(@|  7Xr%x;O:$F a/|;J]N؈Pаfu;HL  T.ZPIE74uR/ #TMMT\o{Ŝb>m8,hKIE[ZnF!0O/ޱ򷗬|}%.7.zq/Z>x$_ "28~A2`\;؊?ᕧdtvhhHs4?HĴH4`w;S݀< f۶m?9s衇Nlٲ/| {ի+V?U vGD!޹sg_k!Cfp=reb-+_ȚEfh>mL&)d@<^KOc H$Bs6x`r7f;% ]/VrJZ7RY#3Y3Rz9(R*i颙s|o do[jWA (W\Q!١wE ZzȒ,%[+_[|ˋ.޾/7.Ou]_W?sC?O%SO=E1$`q '?Vќc7z MmvqDOOhFQD< ~"Mٚ:AtwSF6_+ ߳M|7sJT-(d@f`?ִR8= PyF%\ثV POC~>7sɥ7,zxɕ-%d_\ts{jُ.<ᅧn]xN0D ~x[+_OX}ƹ hzjM.wQJJRБ~Gy衇G$'|/~//o?!~2c@iųnc902A$!ת9/ڀAVRƯjU cVo,e*#*  u#-‡bE7VZڮ'<A/ 'B;x@b-z*g2 }>m}82̡\M ~S5&tA/& Yڲ ީ/#Cn->X >X?݌ e7_bf<Ϙ,L^{W-Ϊ=lАeC\pZH7]<w}6l׾&:O/\>z$p8\w15w}AԳ|A:M ,/FpŸE-JrE/V3@o iW^+UM|w]F-P(;>Qzo:|&ė_dȎ-5׀*g2}G.wJuaTM(]hwp! K|Xvgn!b0Ɓw麉׍-fdU3`՛Gb"0-<>sZgNc< hXt189vG~֭7o޴iwqK_@ٟoo;#?x80_v \4-qrU3LX2*|+&3DY-TsZX kݭ״+UgU*/Lw'd+W؊OZ`ܡWä~׌ ,T<d,!qS,fdWW]mg3a30ڡp]ţީhYoK3V>ѷvM7 _OO7MG?:裏=SO=#HRw!Sॗ^sʣܗt]BNPkd_ԊJE/EkίSI{6K^'|/C${eewLpo)TWx+^Zt }z!$x\86p:>R8ڣ3o<ޡ.\A)@;K._}_~xs1?6%xގP,F~:S (}3`ׂ"jx8p|P %h7!h g.WͼmUREF+V#GJs~cם  6I{:'Z-@A`Y!8xi@C~]8K_2qe_8o-eWSM9M.];ka<AЉw4p?~W-U~¥g>W^y%c;ݙr0hjN ?׾5@G6l؀նm۶L>!ehzlnUbX^eeth-N*|P!M [WB*|M7)W,3Ӣ~s$hhى 7j| J6 &؃{k7'#hl%WxF/4k?"@1|w  ˀߴ 6!?Or[gkxΚ\K_]tыv9/Xrl[zl̲̂Ms€19UC7Wmb?x\s1X[<ܤ3L0s(+|{嗏Yí% J|' ߁vկ;bQ},Hz枤*ywC.V%{]߹&tTzsAѭ M">+ ^&Y ~P^!bxt+ u<p-W]H (;#|gW\zY-:qglE6% C2X1͌ /3˦  8Fex駟~q9ppexZK.9t“囧n]xC %0zyO->Ed%b}2{-Cn-ۘ> F$Rg~;2,[a4p%^I4GNrO:'?l 8YANۂ++( +nP( s?3N:c=B`Ps^RBa= zr%;װ]7sO뚱hLe2Sx{Pe;zOZWϩ2175+7PW(ߊKUzͱ#k ς^x:?ꨱn4_n6-7OܮU;W!A!H<5ߑֿ jA_u}, w1J8~GqŌ ^ZrǒYrGL~77& 6& h֏2zwm#t 8qXhx\t[0{|&B=T#/k.q;wigdu%|ʟ[ FFF(tĚ5kzcnv^YV^$/|ٌLPwZ_kuU]e|o@+ ~^vxl#qc!M//xbսg(ݿn= G* )" P0Gfݾw ݰ۲nܸO<otR5HkL+ł`_L:Ύ]dq J,"!%E { 4'+F+iC| [KҔQ oZU c9W|O@P>;xӮLp@OA|Kh _ꫧkG Z2(byAkiٯ_78s&yw~{w˖uc~7tMxi 33 (qKlFBKZa߻63sxMymxM\T-@}wV-wWwuW*Ȭ ? oܹ]4qE9 ΢ҐjYF cxyL?~iMi#.h 9!] 2)v E{^tOc' Licw b=Ga p6-x)j ]0Ī SĄ?Ar@ me օ/tvG"]B_Qz3 xz,;~!Ls '0ruV`oJ^SqQ4&yZVRuu sv1X;cE5]{*kgM \/&X:R FyS@/x8([xAE2/=H).z``GQ>d{&QC[\N:af$ŗ!-[|9 [|9qC8SHSLG .ba( 7`M1pϽ`poN~~avf! ЛT&C&|>軅yd1(@#7ʗt\s^s;ﰝ}>Juz{&7;pѐrXJ=ϸ{O`;acm _as<'(^~w,pkQ!+eIB^NXqե—pleMc| xUqBA]*XO=^c\p\/_6ZC}3W7ZFlذ?/00*$~LztQ`bظB0|`p@BIb{ljg@qJ[ |J>!Aӷ~h`op"awwDdxgzy~nGr4S8oޞ#y鲽.H\ /=a7#~/c×L=iq0r9vۋ_'^TJ.x+p?s.ǂaEʂw5*芋cAw*Z#j@keG<Z1hى}-^R⅏2;<z^{<;CSa[zV7*nGғA`T bX'{97 /K?@yLf)_0tt6Î۷/G |;~? Cz>;~?w"yA?;CF& AT3||/.Y«E/R'ῡEf.x$.xi̹E|\ԃW5,=Caw5)﬋sQ֮jGy4KҰ#^I68ipxQվ@`Is]v-_}Aȗ.YntիWY;7 +o(*ظ]Hgq( 1N\V/!^!;lii)VaNU1D+iT_W^/;ó_9sΑ^w.ߏv;`a;(B8q:2bȱ 9a8#u'MFknCkՈz \jUxʉ"Э^"rH4^ T/λhʫN,祱+r*|^.Mg% *4t?zA s­Vf. 7<~Bx8W)LupƐj 5֍2K;C-gPJwIZ >f&-2[@@*~vvq37޸΅ǜ8u^ON?N >/O?N'8a:ꄏr/8%F. =kÔ:2/e3}f#lowMLBMY;{iI]:{묿+ns[OuhG$cGb;5[X{zQZ9^VTWdLk^1YṃI™;<; !5->sdK.]Bu &GdnOb˨I&6 aK/ Q2ƮS!4zws6PGʼnڋ@/OxTq5\3{l`ێ)o/Zܼ.v={<>\(zn_G9!Ǎ#_Яn㜹\j6=Q!>3n~iݘeO%{ш'= Y#B[u k;O&"I{E JAZG 50C@*Fg{Ow(\Gg] g ПV~a3A <;嬌x'ɫqD2@$QN*=LxˣxqYxSRwc9Bh% @T3`&K`>?qw'viӧO:S{キt'y~]l#.re&N=n̙{3{rGɠWs7 ~ZXwờjDn|gcTX+^;VmĆxw!fK`T@[b['Jƍ0) ++xAW\qvXblc|-ҿg|:h@g]7_ aPy577+N P,QJRP2ŹA׏?n5j_|g??19O?I&ܦl+~wsqMpݐ'~C~93n,s8o7W)pÔ :=?=T@.j߹P촋 kY>oZyi/xizY e5 I߼E@|s#{9o(E:b㜟PB`cGЫғr"*<+6n悻< @'P @j@"~9yǏ?s/Rcx>묳>裎: 4pT/c 8>x BՆ:0Nu~;::Y;a6z +$.!`DjW ?3©>;cڴiPOr)\tE\rg &8C`MO:a3 ?>ʯFzcn㼻hK(OOxaI{XLxڰZL׭ߟK*^V@H3@4b(N4(pC= z v'w@Wh@ nH/ o$6H@Tu&"Vaਯ)F$FƝJOŃ @q)p̙sUWM2eƌ7xe]T\28^ K}XCjXua#p~gЖ: -pR\⮇'4$| ;wxVΓpLP~in 缻2~gy:>y⋯s/_W]u3f,ZFرc/B%\/կ~#%!r1[nvO>>wO3}m$}upB/S<:Axuڠxo KzѦ6]jTmۆ_R'"K@ėwQ@! DuN3t%Nl~8}n0 S|uwh)SLYJ </'= @|1ƻ ^/tQ 2M v lgNJk{UpuLA&0sWb<{15\jgyiwySN5dց=G  .f(S;ğ_\2`;N8?C>`ɫLμ $ $ \_5a*7A<1=V>jY7z+ ~.@2g0`@"4ocmX_R!ʎrLl!_.۵, >V(:y7^o?^Ȇw =z4c8ӡʡ3c3 }`k݀'@pADn$l8#scar+VW_ +4x'*ʗp}nT<.y&+x}iv۬Y@m9%Kn馩SOk4}t#FW&Oְ)IwYe 9&+QIV!9 sRNZephp7@j+o ^Cqbp9jZ`f bh!xG?7ྻ /0f^}kH8zėˋw.fwu_uRM- Xm жR+w bu-Ip|A`U% _0~ϑ^nœC{ At=XY"xHn*ei[ "#,p )@@*>q ~!vhĉWX1o޼1c0zPW]usW\2`կs ~g|MZ:j-/Fk!>xWxsC|\aHֽ@uVYP[=8:Ԝ<lֲy ^4La'| =rUiINOZʽnCL[) ` ̚5t;Ô)S-Zz l Xp!LnmԨQPx; O;4 s'zt [ncS`?OK.R ZSϻF>m/zL]QMuKUŻ50C)L6 Nc vWc];|$lK2)MuT*'К?cCnS 8Tuȑ#W^y#.`4ӧϙ3L{CDpƏMv]w+`@?N! 8OM v {챠SN9>C3N<@jֵ7((/O >{v[O:$1Ѷ2½d"|liKP9`(,䈱"G VZ.r4]! _ jQV#?J z;@L7 \- /|C7o@+<`"gΜ zGLW >W sLaK.Ο?4=q8/wyqƽipZ"o (`4M{vK8S1̠l(lW2&zW'A *nSN叻Q>wsFbp4s/_%bUwA0~!{M7Y 3qn 54w̴qƁ }n_q'O}h~x}l B l.B`=\s5g}6+@*z*s> XAPSuyz#<Pi%nn9#6/%Eo-kNшa׿u[gvg ` DnҭQUnchL)%K_R)z2.,g`UV\~z^)O@(|̀Z)6LcbʁVob*^~74rH(GƧwyY=ZNm^z%γWyYkׂ>#^` KY]!Cw7ĻPug09EWLV #s=z ):WI< 0$G/ r2]ƲׁheJ4Y@իW!)p \r ~駜r _~m wW_}50b:5p뭷-ҥ=вe@ 3g5׏YT$,ʄUV9h> 8G}GybqpC A rgΜ9{l_~9^gFo %K}]NÄ%*c"ssOPQ% G51n T) * awQLpܹ~i|Xߘr)4k%G ^E gx)עUCB 'A$&GuG}]#Gwoc `q` B>5o޼z^,r}֭[4=ck!Wpyj},i*L<7SOac `Ћzg|@Q9ƴkjjZfʕ+?׳g-Ztu] N>:äz1 0/XL"`N3f{rŪˀ'IQj1s(kgs5̚eg=C1zwu];100Xri4;]աtcN?wǙĬ"U2bj3`i&|4},c_?̂%K%c)shg h`ĉx`b~p վTQ[vRe1ݰ1PkXw;mJ=dwtek hb,{0`!_^ n@!_A_N!dlժU=܊+>'Zd1i`ȑ#F^Yx1–H?]9@R~(oGyK]Sw I θA{vQONcc#:ԅ!!>V ? 0 ߸q#ʕ+W^ͱ%cD L\~ӏim]vh]v+<@ ynFy&)"U ٓwkB%!.]0r{q{10G0kx㍗_~g}'y_|^ÄڼysXbF"% g_+_`c2 @٩+(ގOG}+L*Pܛ('(o@r\΂Lg%Y=?|&śo3qkvҤIup ބ>ϧ~g@&G+cn˓y")5 #?d^sdp@Ĺx{{{||;ˡzh(c\)2† Ы"@# ^n1B0 \®A$%M^I ~ /⋯*6d9amvt9jY=8G!kҮ$õ`=ֺ%Np!?<0,LЍ f,p4WI1 .*}#֍ReSW< @_:,7SO=3@/nT2!:tZc;y Wp|Jy؎ֺPV.&nmV WXԂt]6o޼K@ŹpkpbLxYzJ] L,#>7YRL8-V_̤]S.eD !8ɗkhh V\&7}??e~aZQ׳,,]#阪aE7;HRHt3tfƕw䎾&KFr"S~@- qHA%ԩSk;lKv{8b)Xu n@!ÔA W  O ϓzg~ znn |^_$R GQ2 iY욤&pvڗ^ziɒ% YbFPqH J'jTCƻj$ ݒq]UDQlPޭ'Kv@Ed>$W` ۪* >j #nxUAQ]f4$E5Ѱx|``C x‘ t=6KVCE" LcJXQU9' \-Fi7=KᶐC[oȁ hW1vg@, b94D%TrsRJ\SOa0v & &ı4au=蓚J$ذuk{|fOS3K^/ W>=.'0dBWB2Tr*P4z=0<O%JIcvZTEL)kK! i6FdXA]}&8M{fP_?u]&ӻZ'> sI~L'SRTRW(f]U?&iky59U:+Y\;%$4%JS gT}&Ҕ&%.(!&,M5Jq\vYhtCvCC Ǵ9W,x'YmnouxQ^"w}kB0Te3dmh$;v͛~"7K|ӦׄӾN\x&9ۃbs\8_DteY={vo>eʔz@H_@A5]zY=P1,Q>^ko :}T@J(a*7X%cfSocE\޵+A7$/,Zj@@J *{{",j V9M%ҙ> k]Xl~ıjwPƈSs5LӱhG Qdtɔe!ynn~F֑ҳG; YNJr@8^paƍaUQ.R'JaP.GOSSUunG2kԨZ5h(4 ZR8HH x[|[x}8Kk`Euo':O%L2s5;*֌$ۙd\f2ꆙOd6ATqV. [oc';mb.C }dpUM bټys*߯o_זaN"DA@&,8p 3I@|1v4)ԈxZZj { }+P(J>š6mihjlABƨ H0**| B8 I[%l7$->&ᨪ'{S},ɚ'K~I6yȚG\.."4jJ3P 2aĕk,,/I*n@@R69{6\8hT0$ACj 2 0X9V'޵To;mJluл/З!N6ShѢM6U-\lr 78_'hdOA4Kdnli6e5lބ" SD$kԦ&x=>fA8 _ KЌ": Tp FAӨ1u-,X)hqbZ7$c*t!EJM&͐Dz/(Zf"D➶6r)T|@QFB4a`j&>}t>nՍ| h^+ò@@K.X 8«D$Y[nvIjHlЅKM ij0HWl|[eZ6ЂAѰ+q0l DӍ(B<ťV.joz1X)")PO%F3 Y4jdZ8N׆Z(xE!5*\+!F1#D"q33~RohUK'ggTzQ 6^;.6 [ov^.  SH1444̚5 >jԨ^"~'&K% Cn 556S׷_/ DT5.e%_czVT5ni[^‰MlTԨYY% KeH$QxC '"wEF a?I&B1G2p$Z b @V\KO8uRf GKf|8n]&/Y^|EraÆ3f`QBzkRH x|y!:Q-*L'M2셻&[qF!0ڗ>7FҴjkفqLI<, _Mĵ@0 ihm :yIV0+}J~wKtd%]^5$u]s}ghK_y@. ߜ3gԥ9`H4̔39qG$#o6઩jC}WBrDM Ѣ5Ǔ|p|0l^Veqrbe) b:]\%'[.!)aK;ۡ旣^zЀ=˄,`ɓ'`ۋY@ 4Nk4߭?P`< XE M%)Φ&-P0Q> kú zA -]{3ʔ)Ь9s-駸 s=K/g _DUi]HU&cu~:Y>"uT1r TԣB _&]r;ܗ@K7rXR8X.\v`Ay[{O2Y]'\W:'*[DKf͚5m4< o馛Ndɒ_}%ɀ ࡇL_*(J_Np[-ɤvpc'f*9| D|k&VhZ@o..tл~=. s^$W3`Hqb\ /&nM8q|Ͼ+ӟ^~?˖-0a¥kԷVGiQԿFo( ޴CDB GɣGVs 󠯏&G6ͼUv]yG.I ;(zw|>ʗrgRAT \pw.@ɓzk9L`4S}x}KO^xAk5jH/By;gbhHVh]JYjnhjnjj](yPM^_VA; 9MN,x-HCʦjT$ft9+*jK䇝jܹsI&M6z"篿/.H{I1W@yU*)q*JڛF4"<?$KAisCs@шټuG@&n&9~_2{YMg뤊ƀ8/-ovȦ'Dd2FqYih,]z#}Zj0<֡ ;O>}֬Yu]@Ǝ CRDYrC /v榽(iJ%SdQ,rAZW45}VY5{ p4lHRk8M --@ IrDz}p8M}_?Q$)5aj76xJL:~n6g>F w#}E@Oۧ}e!]q0r-L )5O_h>hɣhQEU<[|fZ4,Zu/>K  XV7M.k`C$2K‡6l8ms |X$H:8@*v&iI:W"PO{- * -kCO(!mPޠ#ં*4X4j19[sL%mNm@f@_h\7 铉ɶZ#_~]@3g΄pmq(@u]7y1wwbkP"@3/IųM_?tCCRF¡p4Ap^rݫJ 9KD䗏RKDFEq5Mm6ӳ=캮_SPX.GJQrUt0H :iU-"05 _m[6]`lAeJ}®+zzx #CH,jJpS^w3o>p/4 &pS6nƋWOxfnɋRtU Ynuw鶉ٕmsͅ(s` G,rQfE-ei HU r):k&8ce)uʺsҗzM"EoF\z7;XO@[ }6iN3wj 0s3fgܸq`nhh'Zj%)(^ %0Ȳ-/P L)&dbtXѢb>)Oz^hw4y>#h y\٨RzN.u?(ھ]CgѢEM;v,[:El([ϯ.n}ThĢ` Ѩ@_)+@Τxl{lkUG-jav6/J6_|q5ix{3 Ϭ.AZ.T˺T)mb/Q`ݷY&~NI7N1z>ny)9i\Nzކzײu<;Kҹ?kTwYܕW^a)SlE)$>ykwԺXh]5D@nz &Xd5X*()O1E=wޥ>q)TLwF-퓽F'Y orh.5*Kδk&sr]Zʭhpcy@o)- ״pa$ӹN7m[ OcqMRiW@j$I^ }ut S-?.rvޢ~5TVn>BR8m7zWLe'E^ !{62O1I"S-Xʃ' /GT|ԛJ}?9H`zoVeGW:{Pl,&~J%F%Q $GFZ}@X7S^})|q St+1j i0Q]7s1U4H2b4*?4y(ߔTEJ}f *]AJܢѨ~+V,_?zI&X7Py\.t2A˵BXmS0x\XL4)V5 -MM 7{<_+HLg۶,+eW@Y6>|+׶lZcYKVULQ)1}5E"R;`[ۀIP=R꫻΃(0A';h Ե'@*& >+ }2BtB$A_s7RihRk0*EP0,|M[ZZZP#udy|RHy6mF*^&gb6KeZTaԧSQ0`N$*rbWRR mj[gE$i:êM# ƮqlZU!<' LZ0.uDd?L # uʯ*a5l&7{Z!9"iy7mjsC")J' ׸qMNlK(푓Y>9n$g&Ǩ4$@Oe 0i$L6g [2鉶Lh{l;uN.@4x҂Ubͩ}g lF_[n)r鍲?\OK]jF0Aj$#CУ-->QJ ;@Ӧ͍M>)G=7 hT3M#*WDvPxz*M`,ţP%UK{S+|=jRJ0h8jxD:B{M2 ],S8pDwd-d]l4gK`}&a[3t&[ &@3D/~zRR4ni$a%EPUVo7[[e-N ch .+NψL #{Rp3IйSKUj@J􆃵$Ec}aô#ӂ@:wqZT-d-o~:s=imrJ}Jq[E?>ԥ Jrh)9kjCx0>!Eԟ8%ũ2p7Y+-Ȋ!ifPRLfBc6"3zbMdp(-x)GxZG>[*Qigi-IwN}}v!/zw۟:ץ..q\@&h4u%,2T`XQI"1Kgϭ۶ש V8mt]7A3"]oI |O}J}vT+b2xM{]Mag9?&-Y%juI{8MwzSLDg+f umR^ɤhwuZl0\GʖɖQ3۳RwW}]RAX}w!X*{ƺT/#>|F/V{U{w.F6ף@wĭڻHz8W Sߖm^8;, * AgB%4-U*a} 5wXc}]m);%uI9o޺>SuK]*JpCX_ԧR-\X__8wDEh,-d!y3zf H">[Y_mEX_ԥT-punN&ŞNhTV'خ}GL@@^}Xݐ%-X_T-ɄI͜w!R'ҦiUH ~JTu=AFQ:i^r`uK۞n}DϤ5%k2 JRL^Ϧ/7x%Yò/I4nڼEtM 6Hzv@=JPTI1S魴$ԥ{%x"fҪ.P%_kcZ%$mxC,O+^HmX *ʔ*"KZ %7.uRVk"TB>J%$APQOW_nš0tMc!)֊--b 0p}Jߞ?O;u^JEVQKNjD5!|ˍS +a}]R'$@WJ$SzL{r8xT"fYQUMUEӴoNYiL6~Eԯ-i8XٖM*aH$}ca] a(r0ݷsuK]z@jZf"eI5F>:IF4.>Shk@sQϣ`:VDct xhKaa)*y1$Οv| 'S#΁Sԥd{@݊ӴFW)@"}6v}۪< Dn^#V*2w ;uk#cz:"nԀE)q0uEQZ!9%x"K~_SC_r($-V)DɌe!b\@SqJzuKWef-hn=۝T%DžtnX˹uֱR}Q*oPIP iH\HQM7LS' h UZê-qC9;c}]U8mC6\( (_:)?L)>Kd"*KdҩBϧLtP#DU-@痢PA*aV) 06{<J+h5,c:ؖ$FBE08p /j?_ԥJ|q^An gnE) iouDBקҡϧoכ|Hg۳hDȡP8׵75yxdEKof0T9̗CⓤV }Oem*{Ռ͌'hx*Mt@|=xԥ.5$uqW^J&IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/suche.png000066400000000000000000000022641506574241500235230ustar00rootroot00000000000000PNG  IHDRk?sBIT|d pHYs  ~tEXtCreation Time08/22/07cOtEXtSoftwareAdobe FireworksONIDATHOV'ql)J\ME뇡JӤN/U?a6iҦ]0FK׋V"VH(q.$1骽ґcǖ>K f<3.aPuȁY[!Ңbl !YJ)~ w_$0pYk}g0I+Cto.4ٻP*Cr5ãDny%o?ty>E>rkAT%ѱ:`%ŭ: r0G>`Հfjv}FGHSpr=oq>h\Ʃ@k4̮y`|p$+\yC_8tSz~E,~s]gg&5{e=zsdדqE9Z.{{eE~WJ[h{> mdk}9`EL0*(ËhT 8]BD๭V!VP*́}F >A(x%ւGVM#h¸s11,-hn_@);uh(~p9Q.7"]z6NRF?U˵V{ḾZK^Z_8)W3,'_yJgs} iS_-^n/sit]GQN[lgeDϢ-9 oMtͳ`x$=t޸>牄HRIG~.W"Ja]QUqmKoC3ig0MaAҌC 8_!7+y~bJXO&0 jmR..D:0Dp3rV=-x@& EcvW3$EnŧL&flt]0 ҳ؜PT:'R 0MWU0TWwݒDO~E&Ύ&^kGh-߰6urBp6 ¦E0Zo6IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/ueberschrift.png000066400000000000000000000047211506574241500251010ustar00rootroot00000000000000PNG  IHDR /sBIT|d pHYs筯tEXtCreation Time04/23/06ktEXtSoftwareAdobe FireworksON -IDATx;s;O)ECC"M$EP"0W)iRÀޗwddw|3w.V6ɤ}5EeYBUUPUu ڶm0 0 0 +W^۷o,K0͠,K(i0 0 0gȝ|4M7ϩQ0r֭qpzbP;K}M hle9M\ލܦ;Ƚx k]x<^>c2> w69b1|42bc:@ɫ*݄?w o$?asܵpq^cO m0&@F6)RQuKOLHƖ+Iއ9I}gt[?X1hƮ}Y=o|ƥHT(ْ 3 }߀7Aʲ {>~M}`::B)m0U8_idwC Xx)cb2;s~&Ln>Ο6_qu8aibj$/Iͩ9Ϙ:%1Pkm;Kx-,ŅZ$cp~]'R^ck3ֵi}ץP1A墦OAr|k/>l|~_I2RqcI`|=56bkIF6]ؼ= *Ov./Y󵦏$s'oߖ9‡wJ]''*GblOg_y}g}+Ŝ{[u:G|렩7ϻ`2}>L/7gxڡþ-k/$ǯ_9gggpgggk|R{Hc>acxktlc}>B\_ >s!a[|袍$ghj[ZH|鯟%o"GygJzȭu`s?u~hT/Sb(?')rm¾5b^(OOOdI}0d[|1LV%$B96 6lhl H1 Hk H vBK,nۺ9==Eɿ1+IY'a~ހ6ML/E:@Kkr9mWO@MI0(dvɊynSP횏`|lB39 .LF6>}+6^T6/<!cehj$#896)%)_b2%#3s||yt}gѮo9"k.zØHcHgvX'~]NCo=~||l CY!xxxXKFS|%khley~:DcOX06TacuȘu?)$W5UˤqQ)5<ԅG )|*NsRc&о~6[uw245KCfca#xe eYB]b`ٝW{~ېtuJ+#*!.> m}: }()Rv@? O؟aL=t7V1]IM9يihb&4k#@[;(<>>Q}Xv~t-ʯ]utDWhI6lvcrvP\8kK0a:t:|UU-OFc>3m?~> aV Ð5MVۀf+Èe>d޿?al9,)( j vac0ǯ_V߽{7&aWWWm4oxzzl0 0 0 +lEQ?V>zUu_2 0 0 s(/6aaad? 0SV/IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/wizard.png000066400000000000000000000424771506574241500237260ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time07/23/07-k IDATxw|VggOH$신TTBh :VlZW bEe V=?'!!~^Ꮃ!:hS]@(Me'UPNu~l4m$p"p?=k?GNiAExG8Y4r.(M.^b kh%‚X*@/`)h;?t4M< "̰S |a4 4Bl)n?ts~kslI'\iZ)t;?tͿU8_ߖ qDD E \jk~Q:+* I=B4&Cq!pөDi'Bz@<4Q" W(?}4M;Զ?σ ge7'%)84!S݀AiCӴwd4  Wc)IZH葄l*4Mڞ#0CS7)H/K,x}խ ov$Q4M1roi6Z羜νq饹%&F+33]7>;6oHQSk~Zǹ 1&!r$A9j$i""9?u}s8##^b-!{twyʔ^=lK'9]݅3LCm$3t-4M[z2;Ih"Jll>nܠ7m5$))2_!w߭E̝YZ5q#1**ꩭu'9]ЂBE] 9>j,1Á `g'UKDnwCO())/,:,'Mi@ӴĨ(}C ;,XLC+xlXҠ~@ǁ[IءvDi/(j-2a2b 0hp&p)|GmYYȨ(4-$0+1jp]jMTr4M=?G꺾,LyfIIB7aW(/I>#ǎ[V!GFt&BQbHkM<" 0tc8pL7J˕LԽguuTK_Oxxo3&QScYTU% ILT)Ð]84\bpiמۀcE횦 3&Os"pQPG(*:t:rr54p ~'<9b- >AOZ14MK>)>^Q?p_m74N˾}*.L!DTM@6QÀ24O=JNy8n}M׋_[fwfuPXG;n*w 4M7P\3Ԙ5zFG :rz* mB(]~-0s,\}Mb-ӀgNB?%2 i!|K'|J$jTwqjn&/O"6.~UUK&5;4ʍCޯ3tiOe4ℜoP&&q夫BXXcƜMttiVq,hv.~!yrmgҤ\ $U&y\lܸq YY GX&`0Fv\N/kDǵ$ң61~lnKFHS5A(5xզy=ӯ'^_MӲ@j!5\(CRH8#c(yRʆʯƗ %t~ڮnB7+ё~*w-lܸerp'׵KiOΆ6vL(7ё,%--?:="-(O糿؋e XX% !0Hs|U ~Ug=h/Qk}ӣt9}CN#4M O.=T9XFH )NTU=|7!/.t]\mN7N]RMoְn&Wn] ltIWi}v(Qf x{vt]M T5p۷[a.gHx,G}.?/c}a6oUk5"95VEhYRYVD0lSpcp{m\szزϽPͱ'gT# '|x4=U]|C2SE&8bh ( =;d`*<}c3ݮ+zB?/4ǩ賎XakNٌDȥ4 :W|[AVֱ!1} dvSa" *8+CkR1J̢,'@=ֹ玶&֬qՓfoA@PLj5^idwXH)!hJ)QUUR펖4 NJDE]H Cm'f ys#WqϫyÑFt6a(U)k!###Rot"on50+vjfJ\j݊+!3%E qWHu}|~B,4$%%QVVFȖr2Åu.Hc!qzW\0LQ[5vQBJB>Y uݙ :%vaJf!p;@44MnGr<^;l :\fM]:贃B !8MNHH TJ)8mڴ[[|`)%I-{2iHI,$>z?B.⹟}٤{wY IJY@@&nE&A1;Ir뭷W[=त$:,ó, %bm۶gyf8A\Yhb{Rp뭷/Y$РA'|npl2n+$[!}z9jPk{Ft0ԩg !l&d2IAAȐRB[Ac5js5[p!=.@@`e7L?mvF4!>{쇻j'O>޽#']^4n馑7xz)lقO;+0SPQ zbˉd!@ADDW_}57pCsc&o| ]tQ,Ì6DunHI߮t?4QFB _3̞=fqAuM,e8]CTd5QUm.%"DQz5\رcZ2e>|ᇿ EaA+77͛7D)'^0ݻ~ߙz<믿ٳ1 0>Amm$55xU(v:v'6ՃA LZZ]vƍ#.mv•+Wf7/gTVVJ)RK:`D2e5 ,xoX,m>yo>iш*>όo"PR ePjd2Ѩ+NUlİaøۏ*ۂ-[\*]A)V!jBRݻSUU%g !Ҥ]6PZZʖ-[زe BTU%;; *++QU0ۃ럆ASHqڍK46hׯ#F`DFF`>SNJ^l&113. J =M QH_易@J)K);LMɩfɮ`6O<p Ip&=/ܹs<9=9::;wvi=Bpzj,&K/segiȯH>#y7Ֆ J0IDqp0fFE||ࡠ!=[w].~Y}Yy7믫gΜ0t oS]]M\\f߫݅].7<}:saq֓:Kȷ W N?jC /pŶ3ѥDk.$g5hhh ==>@aEOSn7*111Iu;^nɓ'jٸ;꫹PoOJ~sիx׬|zC SѦ,YBvmVf*_>vwqe(G$PVwn`n銒CYm5N);++koa32Kfܸk3<W\͛1#7o̜g**nia3/O"aLlbp6KWoO>ɕ\sD~:cܹ\=jL%Jiml„ /`w ]3Pm!QPtc|4HK')*|tK ~ d/Dzu4K3(_̙3 L0y摑͛g;;x3ihC]()$޼vm|{y͛7 &xW̼6 vh:Vw*(}S=(C2{2k¦a1+V>ӿorjUGuUVOF R$&^Kf9_ 0avBJɮ](~ VE ')-" VQM߷o„ nyf8npCFݍm<]2d$=W_1ie鯼os* | UPT=cW%`Da6h^~7*-saȑm_n O3$+n#8BK"6 s|k :syPJKKYt)_5H &+"YpSqz蒵'\#d\wlKp?0'>:;Yk΋u]ﰙQӴw#v_[6> 5 띍3|hihFaD*`W!,"LL_*f; `Hnr0>Ľj]..'<0^.۸%D4Khն_f*}Snn7pG8zD!L3%}t$;n_\9hnA8Cd"$qnD+?CuXޙa5@]:q IDATQe&n'5c}Ͳpkի?zB~ :^5+l:ZZkZ/CJWo)eKL شQ?)J@(i+3${_`^)Lˠ^)x'C.`zSj iﺮ;P )M<pI(I)de<`=[$''?1cr?EDj 5(B_v\pT<A}kpg\4 }4M{3tlR`6R|G|O XZ'$%u˴>q[ƴF=I' AF-w9~uof>*I~ؿ+꺾͝Al|_=%Ō'y, 9O L >M^45_^[ߛiNx.?;jaQQ쯯fܗɯ/i2 H #\zr>&Gvc6yq{}KOY:iHz4M"ٝ6QxR/oWev!hG%8,h3+B/<;. mo~JvN./rJ+n!;!}g.(Tr_!Kv峭a=HrUR5… S|mDU|/,XK~#^ {jCf?/t]owSA $Q7gdK׈ЎVGE`%h\4$G  8?Yz5.^zqjS1Jl(Ja~رc5j6mgf}uFPR~8 n{Aɇp*r& BQ@XMXʿEm؃rl;aB b0GP,Di?e_7||@AA"à^=(gj"-- Bc&Bv!%%q1q#Hl2sτpy|!{6>G{ 9C |0rL ;19wcrEU" RDs҉i&yٷsOjÞFiư"-QH%8ޏ4h.^{[f֬YxƏy I>d[eO~%UꨨraHHH ,, E$F/aHC?UxcWfL<q#5 6R_h> wymÃj Q=M6,7/t]?~(SKjrɏ LutONbڴi\z[zUe1 LaWqϿ沷 0dF7!@) iW1._Ϸsy2+==gyx m߃uk;2}߉7Z89&p-4hh*D_R^2R];\.^u,X0$aaax^ #a2&Fs\n@#ft̶}pXM*IL>ogǩs2{F>vΐZ cZqݨBdd'NdyT+n{P{ıcRGPdxEۏΩ i0yS/PNNOAg2t(Q\ver#}@)AnA#3K.0xo&˗/qY9MKDuX_D~]AXX8\p?OHIIb`2 8hu#Z 5Muw% $`M.d& `)[H)@(43H›uAڰ-7#NXnOp;,7(7cǎ3@ߔXhg޽lcMq={rE1rH"##1L-C=Ē%KCao=m1.HLUԥ݌7#&ɄJl{fc)^H(ɺٮ܆y—umlGe C {ijŖ-@0dX3f wdɒ%|wtuAjj*Çgذa$$$4|"2Q`Z_'\qۀ D{W: {V4D  7:VoQou}g3\h DJr"Rs0o 0l 4GϨ8UUX|9(~wĵp<bҤIl}mn"!l坈@;)YEq%٭=5Ja6L2Cx*%zNZO1w .2PVIwb5!=SĴk@ ,[IHՂUVZƐ'EQ0L(rgDzeHLnvz:0\jݤbptGpW" i?4Vrpaӂ(9t~'eҔ֥Ĵ3!QT}_y<,P%{/'/p(L&TUmvw/_í7&聽.]G_ #tپ 0"!4{MLp@]ן.F9HA cw9eW}| zLk4_Ax}Ō?,U]1biӦ6Qo})e ={Fka ٩>?FrX|txuF)5MK uT`!0Zm>PM$ 2{B&4Mr"2`Q~ P-`B@j| o=x^J4-G{?JZ%H4VͣU7JCeQ$|'n6bsaɈ}*R \ZIá]ׯ!~=iO 冨mmR/Eeu%S4~@쐣'MzJX:/^|h$@c:ORg&3 TddxHL0oA9ߌaOn?2Q8ܱ6@Q*">ς@E/4-ⴔ(#c]Б @HXǿv^ؘi(f|qC[x! +lZi_4i.QK7 wo M((p.vF݊?:=P:@M44{pY"I]MHҍmhDihY(}u^ (%}a ~~lݺۍjEU&24B։r*"@Я$(M|=s/*0 !1Pf}T\xmJ<=-Hyi=t2?n0W'w$9}„bƍ9 =IZ{^QVX$"9 T}An=T@uQi@Z )W~_w@4 Ѩ'3Q4M"&<~W>;֏S.Ohi>>nҚҜ8| PTCia-f|EsSa92j?v(֒/iZ뺾SӴAΰnTUOk7. #RB+ApqCq z6N'''$&MSɋ`Æ >I4'Ҝ,ViSz+" MNo?% ܺ~ ,~XV{ O4}M:Apc=!w!!Q9G"R5G7Vk5Ԍ {;̙"َI PGPz" aư%Ac^ZC?)GX=2>H;GO/*7vXc7n8nVy~CiR† pG| |Nh`kJquc%_TӴcyyUn޳a8ÿ44 e iG[G_}4JAHx"Xd NbտCyb5*4M;Z|>'ot#z˘o"J٢c-Mm<3Grr=OS33 Zr^ H!w0GXh6M4)V* YOQ2iiqT^ E5(4551|~FhdbfS,nup:> )$ۢNֲEX$=v̑P 8P5E4jzӠFU=fOHk$C{FZ@Tn#T& )T;惪EK>ٶ3F brh".vUhx< ST]k@Qω1C0W/uGKç38y \F-i[4h QU@ȖPc' i0Eloc)N=4Փ0~ zyk[o1~/Haiz#9#8P_cȼ]L0MrL;cnQYW&CcW疿7opr!BXqz)Ló{#Ot׮]pe)--Őo\K q|'9-[o"S S8)@9;㌏*\Y]8iqGDlL<{ş[sHrCBnC@&{\EP|7նc].˖-CJF{|?ifGt􀀞adGpz J"{VĒZ +b/@ىRQ<+d' °NB(Wmr3gNpMH1L @ÝO&`;_d .8ta˃^߉yӳAoA|?nX/0NIfeϝ;\{g ޘa yV2 -FZ Q~O&ՃDޛ3n;_!;)m%$ R~D3w?4cJc|}βң&eFjc^7?yg[-'Rڣ41G2T_φxQ65݇6fm4}>ܼhG>uU)'Iu[?7>`%5'^~ip%4n;]lXp!ׯ0 >" H-ީ8-9 6-ީ݄OZ[l[\[]Ǟ軷Y?Yu#uu(+p:G'QB)HըۋjԒ^fШ&w~=%zi@}%9KIcDE^EYb0 77!V̘1ߏ Ѫts`\ÆG~ @KtSA(lrS) q3ӧ-ؒhOyjò2}1QZÏ(~% /&9֥\wURHq(nm T 9n`wu?n[~z`df"3pp6{&0$vs}+0z`%_+8t4Q: n@-5M-o{;~}<{{޹qq73{z} PSJVB cPy P PE PUOE"H~G9sJ`EdxjfɕwB->%H!A2ZT.p+D2]غ+`3>nLIZIW҅BO4vʓN∾G zFlw.2.1*8 BR E*3P# ?QVa\#AɵSM|E?Dv۹N  ؖ IDAT*7 Dx&ǟ2yvE?ɧ?5p+/?!8efn.1dJ?7N~?T* 6%ݷP=BHʇDG$Rg_Wpxm.o4?]bnQJ樵^/S;X\\k6/'cʽ5t(" (Ezyw7+旮31u-xyl"HT &_Ȋ8儚/Pb%qu ϻ P$ ˎ¥#,S>Y!X6@We݄ ig w_^-w@;{@n01Q(b;q]]3[$+ (>ދd*%z[/?|kKaffw Yд,WK'ѵ,TD"=|J\'l q\e_:^AUmph$Mf`v"hyJ jb|ɒEAbD"n P'%xo> w*27>H$-7F'|ț?s"H칟5nO=r2VH 2Rjif^F(a4cGWx<$|_}iF.ނn'Ҳ.µgy9$t8N4DTp젱imǖ7o67ڀ+,k=BCWSvF(Z k3+4YF L =FeNvŀ1Kt"fP9b@#Ʉ#Xb..5pl͟8PBRCSTu joC鷬EյSK}tȽO# ! H IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/ziel1.png000066400000000000000000000034411506574241500234360ustar00rootroot00000000000000PNG  IHDR,!IsBIT|d pHYs B4tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time07/14/06<vIDATX՘hUe?9޻{vs)TR',*rA A(&$ X?@Z"ByE5W7ls;*'9>|!4s!̵̎1yw ]p[]wwIYx.à,hk8BJP,T D@A8\r>E)eq0 ,g!@ n8 Ѕɀ!6afj*YBX N B3 yP D:`_3C Fȝ[ &x9:k ΢s0aa 5pҁ Bcb?Q0`eԠ BL&IENDB`filius-2.9.4+ds/src/main/resources/gfx/allgemein/ziel2.png000066400000000000000000000034611506574241500234410ustar00rootroot00000000000000PNG  IHDR,!IsBIT|d pHYs B4tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time07/14/06<IDATX՘ml{a9;6vrKh!R(r7iJP%Q>**QJEJIC"*WMT(4JIA,8َvϷ;03sė8Hv=Y!ix !9 -ZY֠ҩsn&z.s< iw@QhZNCŅkpBJ)gXPhR)|v9C> !Pa }4`2_[Б@AEB(@{04fEa%L²`MP! a( waؾ  0AsU`kpix5m&Wa" ?w*Pְ&X[_uPo\Y "K`(AtVOI[ZSڌzt8$K!@pgM,ru@@s@ex ֞7o-c_~\!J\C OMYs<мzВ@u9t.A< ,TGA 0L?3נO67C%O"5![jN(eH`!vfM"|ξ'QxG7?XI#?5 ^IشXZ tH$w DJ4{]`&aa˝z7sQM:'  ( gnTGwšbw`"ГU8 ,\B !4 Nz40JR> n!DP(RT$I&yx $e {jXjսOJF`ajZϟ|ܹ Q-=s +W.\v]۽{o?R5mWQ.zL |5EJG TN̡f`.Zhƍ'SID۷o & @YÍ[6c% c]RʿlZU(nAu^ ,Xծ.VZٳÇ&" X[gEJ92 =hCڵkWvXJi7<=2L~p4XTL7zt|k߽PUݟԏvODL~Q9c =IENDB`filius-2.9.4+ds/src/main/resources/gfx/copyright_and_license.txt000066400000000000000000000043471506574241500250520ustar00rootroot00000000000000export.png ---------- Icon Pack: Human-O2 Designer: schollidesign License: GNU/GPL http://findicons.com Text-x-generic_with_pencil.svg / dokumodus.png ------------- Designer: The people from the Tango! project License: Creative Commons Attribution https://commons.wikimedia.org/wiki/File:Text-x-generic_with_pencil.svg add.png / add_small.png ----------------------- Icon Pack: Soft Designer: Asher License: Creative Commons Attribution (by) http://findicons.com download.png / download_small.png ----------------------- Icon Pack: Soft Designer: Asher License: Creative Commons Attribution (by) http://findicons.com Nuvola_mimetypes_pdf.png / pdf_small.png ----------------------- Designer: David Vignoni / ICON KING License: GNU GPL 2.1 or later https://commons.wikimedia.org/wiki/File:Nuvola_mimetypes_pdf.png settings.png ----------------------- Designer: Pau Giner License: Creative Commons CC0 1.0 Universal Public Domain Dedication https://commons.wikimedia.org/wiki/File:WMF-Agora-Settings_BCBCBC.svg Files: gatewayS.svg gatewayS.png laptopS.svg laptopS.png routerS.svg routerS.png serverS.svg serverS.png switchS.svg switchS.png modemS.svg modemS.png Copyright: 2023, Andreas B. Mundt License: GPL-3 or CC-BY-SA-4.0, https://creativecommons.org/licenses/by-sa/4.0/ Comment: All icons have been drawn from scratch, partially inspired by the Gnome high contrast icons: https://commons.wikimedia.org/wiki/GNOME_High_contrast_icons Files: laptopE.svg laptopE.png Copyright: 2023, Andreas B. Mundt License: GPL-3 or CC-BY-SA-4.0, https://creativecommons.org/licenses/by-sa/4.0/ Comment: Based on work by Baptiste Gaultier (@b.gaultier, openclipart), public domain Files: routerE.svg routerE.png switchE.svg switchE.png Copyright: 2023, Andreas B. Mundt License: GPL-3 or CC-BY-SA-4.0, https://creativecommons.org/licenses/by-sa/4.0/ Comment: Based on work by Cyber Scooty (@cyberscooty, openclipart), public domain Files: serverE.svg serverE.png Copyright: 2006 Andreas Nilsson, Jakub Steiner 2023, Andreas B. Mundt License: GPL-3 Comment: Based on GNOME artwork (http://www.gnome.org), slightly adapted https://commons.wikimedia.org/wiki/File:Gnome-network-server.svg filius-2.9.4+ds/src/main/resources/gfx/desktop/000077500000000000000000000000001506574241500214165ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/desktop/browser_waterwolf_logo.png000066400000000000000000000444221506574241500267270ustar00rootroot00000000000000PNG  IHDR@:0sBIT|d pHYs""awtEXtSoftwareAdobe FireworksON IDATxy|?^9v7DTC*"M<@xbj+ZJ^z+ >Zj* ;d7fwg;מِ}ymvgy~}![ܯR0 F u 01 d0HU1%\`  !Jh`?&b&b3IKS__VҨw#> u5|m(h@(P"BB5 8sॾh!pfXH\ҸAH&?~=Յy* Fc~dHHdT P"tBDP>,+hD(@4. vn8|TԻo G?a0zF8K. N!A9EL-#bi% ]dQ.ntM*mB\ZFa,>6$|4솞AE 6> ܂¦Y?㴡mi@%x.aƍűסٿmCaC`g; YܤWB5h!( Pd~I(| ߴlg┌KxԹ-}SnH̜$x6^'-1 @. -=y7-fqV (=gs3)A%'@0zP`R[}r @!R@u*=s0U\]hWcWxZ S/=d0*= W3*WV"o':fp}xE" 2炇"$Y$|ޠG!x~MT0.⚉-PU*PK,JĠH S#:<$Ua biѷe0~2f|s#;$rZK72g0~,(fh6L>T{Wrm|$T" jd#)H 6+i(|O; w<,6ӳan%uaHhHEjd)*m IPI8 , kWam8;뎘c?ڙ%vsdOX, GM\`BM-P*7K;ďJ%Jӱc.6XJ|Dc}H"J#i^n(ډ %6rg"F3|5:N4eC86 ԋ^tֿc ;xi pBEOi2?.ݗk:*<]#Pt ئ"@ JH'uk@d&Md)AӊP . AW5BH#bM7pCb(N!LtoX9xHa.f&5 &3rƼʼRtS$~ W9] XMN m6\l,ښ e"xJ89֝BZkM H)ivjQU4AS"|6!馝XZAwU6Y{tfNڊ"Aow&K|ǸxD4.Q|'䁟 f41"? n f=<Bu]/NH7YoTTi]&E4j0BBչw0+%UemyP%{+8@v_ lpz> @!@hcWEZ |}4`j!{ݶ6PN8'%"(D!HBMq淮%F5L,"*4à2cABTbnTW:f,T-)sbp^ 2p 5)^rR<[v<{8b<>Ȑ~'a/G $A'oo"`-=]7H_PD*QpO<ЍCc*wޕoA&3(D/a!4ؠҐ@CC ZTCE V h:?Bu^! PB 8hxH8iQNFŸ c P^_E$f vQ'oB<ƠS >44Æ5cD~L/_o QwXA3OH( ýaN:E6E%#<$ Y)'6`WOPs;vS>>Lf?{` TȞObpfs6)(3)De\R>@ڄ@ ݟ(Hn. 31N|ϝ {U`Mi@@%|A7jAӨF5$*3F!0#§H_&4Hۄ!Tnѧy}]44v_mLc` (.9 ԏ8f ~z V Ϗo {Tx d@hPZSP T84SHB->HT?lšfʃ3qpG `lf~0LfЌGJB7"#-}j˭X=t φ_,Zn$8d}ʐrGe1 c"CtAtAt`ՠv("\]9xwаgb7՗nGҬzvOFJУ]>X8TمJOȭe.jD =X:֌@P#J SZyMZkLpo[6M~Q3xi(L7"|Vf0 cG]|zg(Y/͟t{%}a^TB JM;oP"7e6׎g.Ag] ȅ ,ceV_Z3gHUBm` DzM58J&|n6f*,! XQ S \cYqH#Q^xᳺ ./(''o.Ƈ_:^܌>3:1{pF l#UHʊJ`g!y;Mxު1p7n`H%PԉgbڻNxM-6BagBk JJT/Gn) m] z6%!sD0N7#HMn4U{ހS,%+ t)S˨JeD.tm>Ҽc~ ^s+͌l?O KG`C%jۼ,謳3IB'clx4g7슙8 F*cFSUW=)~<3~;ҭݡTh3,ԋ{!]UkdhPDL>;@+7$"@Dy ҳIDk(]4,6V+|\|J.j#ٻƉ?cs PU(H ڠ?V#YG~>~|L0x~I33@s:|׀TAYON! ŘA"F]o=4~v후N yF|y2V>WIpM fI-Yndn)/e£Te0m8|_޿?7aǙ0%;CD߂~r?Pp @W(IS ?i{č"?}.hO0QٍgR8̀%9}~`M~L(]Y<\. zA0L}P 2~n@&P Q"]_KB9܏l-\ݖ23p$1:Ă` TZ R +*FP\`+zD? a ^4 O[_$~=O^:M`I5ҀՉPQ0`^ F/w,h90?ӁuDLS4H\% ,>MjߋY<leiF>v+=}cچ<ՎÑ[0"v^8>K?Pvn2^V6'h xM VP/Jo`RS|Q$ݘ|8*=gR\C\b>u)oy8.mjC#YW 7aތpɗ3~=:= \ ..0pXy'20о]̻GR+8prg,~AFƟ]W}C%kN^O'|$v+bK6fovش_{~\ qJw40jǻ~wkw "TW? n;2qp8#-b>]]ܞ\|*&43Me}7}fdxlW~*ߗg!8o~0=G塸quV_m(RKa!R/8e.ჼҌ>݌1UX3l5k LpЕ81c3N1+/뤰k1nmKU+ċ#?`W>վ7jnQ-oq֚ѿ1֣><He7B  XO7~}=g< =+nذ`ځ+pvSTm8wp}5-ңKt<FX0y*sJz?oF_ߖm  Ũ+۷lka]'~?+hܭm)ŧbrI.­b;3qMWjƲ dVq0Etr[! k},~DZ4?;ĚͿB9a|4m:!ױ(#]>i!2QurfO> Ǧ#yu>!ӎ1NT ZmUj*º˶Og ٣q˗~zrTvmJjH'^\Jz\Z\`;ľ(p9l^ ?\H6U\JlaĄ~^q_W߯Mae+Nk8X }N4b&^>2M)ŧ,Me[1wL`v| OMhxumenI?BgG~Z'ߍMH|@ܐ.HdClmgu)vjhV/~ɦKunLi_j:nX?ǔty6N!< 't$\#]{Obv?-` *VL.=AT{ة2zBߨGhks6тO򬦄2MA!ӳi]x pcM.>0mMe=>Y+뱳v [%!'J)k=X<,at8|֩*YnWïQ[Uz݇Y(h˶ੇ^ /wW~| ޳޻Puo@[X+gܨ諕7Al n[j_MgDNcszB\*\q ةbشn+ο$^~V} W sT '^3Q]/o =v*Նx0m.u73L{uDyY%x.8Bby;JoB fu+BK)ǯ&5r7 jԖͥ hf'm)ρcJM ,M 37`U rƍK%O-yO-yQUF83ծ:ݕ7ڪ " NsB Fmj׵>znmKs0gWT֍HD*@V o QNm"` YtG-]^C21ĝզӦ]nj EWî ~hX:UuEgg+^ZF ;R?S8Be(߇ޯ:jwfb5{6W;{l}YNpu{8M#3@94s5L~gΫxk{W``& l/tv S:S:E֧̽w}x=Mw;%|6v1Jܢ≰;3U;B~'x">YEe[t)d*l  faY: 麹i ďBJ`:v﯂N;_3~~u n[x_p)gij IDATFaU}Օ +AnU: R\1_*K^NMw 'Tw<ϾJ/3_T<1ޓ]w7 ty2@iH)(8΁3s :R_x3F34{3 Rdf:p=*:,㮓]+s.<ʈ' 9Ce7E ޚE$OPg&&L& =הRJ{qpvܘPlLЉ(2X%.nX&0$+/pe]ڤJ'\OmFĒW4y5)Tի5*{ ƺR6nQ̱F5_`|Zզ;3U~Me¶xk|GI$ӂ7t'ޥHE\r3OpMi&PJÎBPPX5|G@":ې ҫB("^:ᡉV9xG_Ʋ**9=Xޔ*P9xlGZdL [ύe֊XYg%kJ/ O>|lj5(9~ڮ|4? .P՟7^J{sTe" [zj\&A$}br]S""xw]`J`sX Or`C-cY1 o8ih>בftw{  T+-PqXHq.ԝкy;|JNrOWtϴRRZ}ٿO|BůVH=w.j;9vh\`*Ίha;[?#9޳ SGdFΈ}8PGECj0!+j{i2uyބ!CՉ#Ma6[`XalIHӡ_p& u.=a\!6m,vg@>+Wv᤾A'z. {CL?Yv3f7mF^A..YN-|r?էBԅP2lb9ؘpmt.5L&::v톧FEuB#͇w gYсDC y۷lKƒoQYW>װ/-[;$vntR+ @tklBcq Wr$ c'`vE%՟~TN8 ,-$7n2lt'ҟ̦{p6`ŏw=i5mWk5~* JU(l7X45P:E11A@ǃ#M8PnO*!aBL6(3%ŕd#7_=kCfl,SP}i⹱lѨd)~tǫj)ҴQi.R}+RFdxr ګm?P?Jrw(P`0`1N)n};4tv(:s4/*K)EWgZ7'E>TtuC S$: l1|LXJ yZQT ԒI%1N[F=1]+[ΨvעJӷm)i%k;$D5UvXjʿ'nYl3>Ya[*\8(Wl|FIbm1c9WK^`iwHΈoŠ0 J?K-͍vI\6lo>$DTUU% y7SlNu: =O{nn~v O}^^ATM5xt/\6?-/ƆxQ ¥WwGb5N0L\8)NPzpky\r.s?(6mƆxԽ o fЈDSzyd1U<o؟|l=|ц>(֔`AyH )ވ6Un|pZ@)& 2Iz!#QM??PF I"v\r CKZ[X> OFyϠsG\+ ^3RˏJr3}]i6\;Nj^ՉۋϮ  Yw,M}IL.a \24` J`m:ب[1H^ hQ)H١rEAϫ$AZ*}gTw#FYjcr`܄SҦ+/җ(yK/ѥAs83kTi/"/&L?xy1 {"!Œ&0?,g&ѴyaYwS2nIxݥKO&߀?>\G#i ;X᫋""6L2t8KLٴ[npowhp բWoM)Qi0䛽kq9}%{>Uje UeαBwD ! uHupƖA1ݹc:qU7"wBsf FFCխz,{5P |6MDeP2Z8I0tww.MzF&IJ8(pC񉖪~ 9uƆ++_0<DWx2'EjmÎPjQ$dGl(Fu,;_I~ zi[( Ox3/1vI8-=N \ <@-i""Jj8\_4d(DQP alYц6W+`3k04Z}i=\1oÇWh y>'g)Ocd EwWGֿ>ż^ kMD񣔢s}EQ@scn? L3L` \lQ``wdbɂ ~X+kee&1XĆ_mSO/N.72Y +߫)] YE@H,p>?Ȃ&݇l^pQ٦=[B7^,b̉/`B̽N&qoV%4zNgoNB'(|m-U8.{GR1K9V0kIBv? nQ9'YcXL_ӎ+_;_G 丁cpnɖ>m8۠GGItuvxfm#l|uUad&J/>~1+1=&@A,j]@O* oy:urEKsSp83GˑFx:G"Tug4~']ek,+#XTAF]- D5}=߆gj'wOeqt6}Ac =xO,@Q6(\vu=>;V[n( OW'47]w vfwb~1 =P/]m((o/"-fh@?&e_7EbFM쏞ꔞE'qbYS "y}Zp)|}Z}_Y/sK_]p:ͿԒ3 K^  ۿ+4{j_8Ξr?27l Ս廂 ֨ޮV7\>BUeT~AT9c pgX_^SKΈx̀ԾUktr1p [EݭP5.=- c }Vلp0+`ݝ"+ Z~\1#rҚI2.<*w0Jg+xvϨ1g5aOIuU-.>o*|֗~MWΎݨӥ S~An/1?-ox߰]\O\<}iyδk|wLc.Y{{4~w`Up[:ÞrƘבeG)#=t巣dvg#F|3<~6kZ@p"ymK"H*k1{uN;' ^twS9ui$X]aD0¥ʑmܵfܵf\5R8TV:廱u|KqYjjnMꃵx4(-n53v+l;1{kS%M8h9b3>/맳%yXZ,9Y?0v_waTꃵ:gG+-*3K&cw;?2dsC:jXĽ,W/Ag=`] r*Hbނ" ZZ;5DWoxG?v8X*7DSN{0ү oy4kDfC"pD !c41Ķ̇s02Ɲ2 {Rrֹ#V%A 7n=TRT|npfŷβ5VU5j <1>g<~zbJL|},ybqPt.3/{%xU*8/-@M+[trř 7WǮѵ'c|Xվy .K_$ԗ^`g ^''~3!hiƑZ,DZ3{a)k/úֻu'kHf 6,>wvcu#+̙~5LVwL]5R\2?XQ¦r]4gc57~<^]Y%nWTZ3g>o7xSqw'iX*g xI~PzGS bgo]U²b[ ^|":#ʘ7FzS'5! kǿԈ 7_+vZܽw4VڅW__//*Ն痽痽gŷ`2ԋأ.5pDs #XuG).Q,`$EfQVώƬstwwn, p}TEC8jʶ"tS;yK 'pZk`3q;>d'-"i"gBO F<%>ܱsW ՆmpJX/?~[0W%+vgBiٽV3ˁG|ưV˾Wٗ.Xڴ `OLTM/չr/^p9yi|SߛgL6`eԖtx. S%IzԷRK_!:~xy@`KPyhJ&>g}\,FW\TLFEel:zK.m@ ǘq%~G\-tYej)-?ɥLV >ՍƎ~\_?[`x䁧u}u-}?`zuG> 2b⇓^B Pp:sˇ78q68pϗ ρC18'Ǫ-<D.C٣<`}y#^Uw/ gL1*}G8dJ4Nsf;B1y_nU}/h ,'^03g@SU֠4Au\굘?m@ST>z~ 7*xTl;x&>JqW*PNc | Pm+'1,"ϔHCt~{|3̈CgKc/W a?a\<զœDuUj`=5jurANR8Nc93ȃOynR{&$ %&v;ƣKWFA 2F{ [ .0<=~{e[:Jգ"̞vTo.b~R±:jCi๥˱t?6_feg9_~a`BX]-]0vTW֨/ݤsOQ1pne0+OTym/߉7Vk.[UbWsMxms_ w P( ΍GbqÉb؎_dD2룍ffğ d#t+fMRW?@-3\93Ɗw I/Qm+-FU>g u]]QyY8o^ߖ?ez">[Hݕİ^$yjD?Og"H(&ȋy0WƢlGzܤwfuq3oE){j_$3\>AZ/d(~]UN:q1i%xeܝ,xFT.ԺDZX0:9/An5Z!?<(bS'EagocݖOpJxN)r=%6z=^p6 G^A.ImS`_k ߽XǿœVqoXtwgoWDnzj63Kt( |N:l^ [ mrܯ5J|$4xĖ?P05H|tc pe;6ѩՍ/K7b[y#pJX_+'R_WwҍgLAȼ1I[)9,HP> :(&ią/OjV[ F>@aʹ{xIDATOcF¬P^G'LFle`Dj{e6#h$Ls<ИE ;#84zIk[)EZy TV%%No߀~`IwerH+DWИx/b2};#I  %&Nrކ'`dv_ EZOp2qh.F}XrXƯQxЏ;|\R2L?/M4khL,.#^*0?*NC *w_Wy`E-tՌ:9U )/fwQLx>@-ݝ>GbfU]XCcT --ML9jE)g߉r#R؉r$Uc"_@#A-M|1O!mKGܨ=(2툴нAb?.EtF픹8(<7ˀ+0?'ߠ;m'ih/ ;I^u`*%nJ"RJZ/kdEGh1ubHB`0 @XXtP^l[ K9]m݂*1 ;]ru;ip;t\5da,zm#»ў ~'XaH NIAQE- w#34{c}$t姇ӀUP9jˬvp-X` CyL)&}\F*Yhܴ@)m78Nf[(!,FG.q M>Ԃ <8nU\H&/LJ1a$S L$&/S`c:I[sP_pQ׊ڋ?0sX8wdV4ⵃ<! 45 = [}}`,_(7ӱE` H }y,#o[}EPk`ozV@0 `,L F`,춘 #ea HY20Y #ea`0R& #ea!0 0 #ea`0R& #ea} #ea HY2 HY2-`0Rf2 HY2 HY2F Fj,@0d0) @0d0) )HY؂ #ea.0HY2 HY2 HY2 HY2B3Y #ea`0R& #ea`0RHY`0R& #ea`0R& #ea`0R6`,d0) @0d0) @0d0) @0d0) @0d0) @0d0) @0d0) @0d0)Jִ|CeIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/datei.png000066400000000000000000000005611506574241500232140ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  ~tEXtCreation Time03/13/06ޖtEXtSoftwareMacromedia Fireworks 8hxIDAT8œ=nP ?&-8܀KII w4K48| VxS YA0ÇG@Ĉ@u\ukr#ǎ<<ֵTСB$Lhb(IQ &zujUPJl "+t0B蚵:OF D((p ."P 6pjݳ᪰䁁͝?]䦠P#ל=Mg`vpȂ߰.nرGQxᴍŐ(84Mi(̗>A'JghQ A _ J! R, RR‚ƻˡ ծ  ־  c|`1PCY"PǪ>f#FtXRB J ث&7rC m1ي'x7E0ŋ'@1| fJ,@ (et"RtӅd>a F%{W(5i Hǐ kV{0p@.dCk}lL’:=ekAj]#؞A&mun cokN$|pJO|&~GQ2A'JghE16h ~:(E ! R,R R ´ ˯Ϥݹ؟ԅ ߆4r'+ y+ e (?: ,:I|7C9v!Im]1 MD0 ZU (TPAA=J$D!YEiBNfXYڭlo1HAZJr^" Pa!Aq+ Kxyr˙IBFCL2fx)9ȂؤiU HAvڨq"Qp8mvԯEC¾{(QƏnI|W2ľw! R,RRR  Ӛ՟ۨ ԯ ޖ{JBi戡W/R]Wd-~СQi'^>O*thr#ǎ<‡R1dQ$Lh t7Pm I3ТG;ӄ` $0VU(Qеa ;V҇o4i H=[Cd\D8 2l!PX5@’;,e)TF?a +´VV4P$ۈUڼ 8@B <+*L<0wo߿#:`_ _ " 67 9䚒bK$gǟ#-;vx;?-,޲iOP0ĉs(УKyrTν{!ڻ|x)! R,RR  ț ’ ̫կ@i@Pi9t(^ :H"J>0B Mnر)r&9)-^@9(@`( Mj̹3eu` AR8uJ:̰AիK~ИK Xkm֝]V" Pa!ޕS& YjQDMeԶoCʦ1" 8P4Ǎ72󷧃G! R,RR Òɲϩٗۂ  #@X*(ĀA:!xɍ;va-*34 44AD/P2IO@t+t UA%K0evA8MaJ-_:M]6eԪ4i ³ Jw(p… 8xs-?G*<0 ]zF JR [n޽}7Rd•c^c%&8siRKe^>=A'NJݎqX@rsQ0N1;filius-2.9.4+ds/src/main/resources/gfx/desktop/email_email_antworten.png000066400000000000000000000022571506574241500264710ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/12/07㟷IDATXMH#gG8&$kM'^ x쥇BCAOBaJKjW(KO{iғqêҪ1Ga3Ӄ`Ə^wyg>·`&/SZ:ϜT466l6LӬـsϻX,N*#zzz0M0l3 D"Ri5%eY4M0v;/@Q:;;+$Ibookudp:DQuZRDz455L&`}}ߏUTUB@*͙. QY^^s0H&!ZZZeggR;;;XVvZvK,chhAjVEQp8LNN" X\\$˲,\ ϣ*bk@.caa] c :u(P@u=F5g<<PϟQ4}燏>訕E&?},;/ey:˭_]"PR4>kmwQTr+, T۽]|ƝnRЋȲ)B,h4j;<:4)}*Wp2NWIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/email_email_verfassen.gif000066400000000000000000000131251506574241500264210ustar00rootroot00000000000000GIF89a sk{RZBߵ޽ޭ)޵ޥ{ބޜJތsBZR:d!1)J!֭s:B֜֔ӵք֔ԥнkνΔ΄s{cΌΗkūŵŽkRŔŭńZŌJc:sBZRJ1޽!ֽ)!ʽǽ޽cʽֽsҽk1)߽B޽J)ﭥ޵ܽ1Ե!ʵ޲޺:޻ ν:ɵԱ{۵,εZεNҭ޵έ֭N֭>֧sΩssӭ1ŭRŧΥ{έ)ťRʠť{!΢`ŢsΪΠk`Ŝ{ť!JŘkތ_R{k){{{1s{n{{|{Bscssss1{k:ffffffhcGZZZZZRRRRJJJBBB! NETSCAPE2.0! , }f HH"! )bmLJCTi#{'`ɒ4'4p`?% jr.kH HAG lb$'ӪPs*Q}uJlA(qaZ`de BW QXR7=Uk 4p ?+`5+T bx1ʼn7 _d\yk l BE4ȋߪ{)OB ۦ l+ۋgqg5Ye{Y_-(TBAk x'`~—abH!!g݉="{8`MǏ@)?P@! , }f HH"! )bmLJCTi#{'&ȑאLG*@EHb@ofP}J` ([etLfʋadiisr>؁H)pBG03#<OT@%AhKC4`"H9܈0Q6=,pPjH4jCr x=)dZWcȔ9y7mT"63w6Sy4 e 2Аi`s.h0!+]`rP@ ^)ab|'XDcR"8!xO!y9c[%?d\v?D5! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zE},E\FըU&QWϑX1{Ҽ8"G̙)4 8HByAXTA؁DhK}E%* R@b&J}Tk xh1ẹI6~*S׮5cT~tƘzDhB$E[ Xe'1~< #'50zA4  /׷轵C2!Uƙ~P*,C 14ف5wce(aUP|+pHQ:/NtQb0)hk;=9Wz8Uc&Y?NϕXfCTC! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zEf2uTT/%UT]4 (0&AU>4ڬ+< Iii0@! $CNt+0)g5RTA X-Î&ё Sd0hS぀ ,G&s M $ߤV%1Few!/HU'QE$0A{']Xbehh+' PhhB}{Wg'xvb#c48":t;9DYOPF)?P@! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zEf2uTTej2UPϬI,M0  xRUy1@vؐf-w`Xޗ 0 ;?~ "F́1XA 5*Sh- )6 2x*//0w G戁d~H/l0cפ@BmW4RC6at>KeHڑ9!d k;XQ#{D7&LHGf>W =MpgdHK_h@~5'\De` `%{Q~旆riQRv r,:t8X]Qŏ#Di?P@! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zEf2uTTej2U V,YfPV} hݻؾ ;RڕBx`@\!J P 'ΫWA~ $;}y32'bH(Q6ƫa Y`$@xp0`N&TDѓKܐN:Eƅ.~΢}HzDiԡ6U@4wkt.8 p~1Q]DeD.tPAg+=uuO^"6 Å(b,Y0~8$Bx.rHbu VWT>L6?P@! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zEf2uTTej2U V,YfPVeڥ 0ȝ˰^e*`Aw݉b+`!߾uv  "+1daĉ*h‡%ir&B1cADEF31=gNF+Ǝ"AP1mѣBc.mٲs21z*ZXOE(/ 8(H[ԞEZ,נ73 P}B,,h]އfmQw!+mh!bRxUb! j^CS8?@?D5! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zEf2uTTej2U V,YfPVeڥ 0ȝ˰^e*`Aw݉ob۷߮T3@`!v#@`  F0C‡r`aC' d3cLW< y"e 0cЙ#ѩF}"S?3ΜG:-zOq52<p EخdɶžjDx1#C$%3 ipC(@My4؃r4p=FWdm@i+}!hޅ"ʕb !d$zXCS8?)D?D5! , }f HH"! )bmLJCTi#{'&ȑӧȑ#yz4zEf2uTTej2U V,YfPVeڥ 0ȝ˰^e*`Aw݉ob۷߮T\ذݻ5-&0g@6 <|1D Z-69bN#?6}Ua<'[/8KFA1UQ8~*ꖒ8};gYd6u{=˰Ĩp8e `?Y(5UUOh80**ŝ$#1[Dt'+ @i\ %V / U6WI,ҋ$L;ZihBU˳ OM~srywpUn\㭘qN/ L&Sq$ &&&Gky.BߏaXu$˲, ^7e|ڴIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/email_email_verfassen_icon.png000066400000000000000000000014311506574241500274450ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time03/12/06Q?nIDATHKa?l+OP(KQ@^ :t^" "tC E4,"Bd[.y0h[u}gg֝NjZС/<33/8CApx5 ]׿}=EQsaD"zzzb3@UӴ"Ptww{h4OOO+A:;; /OG޷W Bd4S 4ڮU#SXs1N>_\_9{1;}'9}מxd35x2!Żc L;eoQ~Lƕ<`)@Hgkv.N!Z18 tѸ$B/!m<BEAUU\e2neXՑdx4߆)H)1MXX'oZa)D5!Tm%I9kݾK0FLeu[nDQVq+G,wQ~E<ބBM%T˲ uMjFMOUumgumKdS{B[~h[ 8A>M)vNT?tQ__=ll_p}{oIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/email_emails_abholen.gif000066400000000000000000000101041506574241500262120ustar00rootroot00000000000000GIF89a!ﵽ浭ν浽歵ŭ歔楽޽Μޭޭ挔攽ֵޜޥ̌愄{֜֜{33{Μ)1Μ:1{11̙1)̙)1)):1:)ff11Ŕ1)kŌcŌ{1朽1!1Z:֜)!cc1!!ZZ{k:Μ)!c{Z)s便!kcsR{{){scckcZc){Zs{sR3!RRJRJBJBB!!:)s)s:k1k)k)k3fJZ!c1Zss!RRJ)BB:!13)))!!!! NETSCAPE2.0!,! H DD=H(F& !$c A ) QBH#a9Ј͛FttI# qR.Hax(*C˗K|ToȹT0rH3Ld2H9:„)i&ETd'n]9I`*?5Խ0к2"DF`X3 dIHAf&SEF-r*'H[Pċ[5s~ν;2?)T<Ȕ5>c&H=/^a&Aɿ6`& +҉%ZȅΡhKVh!l! 7ˌX08c1DDDPѢX&VC0OR9z˳E?y$Kٳ! ,! H DD=H(F& !$c A ) QBH#a9Ј͛FttI# qR.Hax(*C˗K|ToȹT0rH3Ld2H9:„)i&ETd'n]9I`*?$r0кH&D dIHAf&SEV۷H Tܷd;LAYN_zyoi "-4?TʂHAcx dr1K."?ɲ~(J=@`$f&"<@@u0ᆅ`$lj( (@."#+Xb%}#h(3@2#7Z'hD̐R/X'te1dFL0x.L4IM5X d6pM7<1-Ђ(42 n4MszLR 'PB7]rKh<6A8H'4(U!CO!(LQꩩ Q4Aj+z@j 0 ;k:*b6+Km&-! ,! H DD=H(F& !$c A ) QBH#a9Ј͛FttI# qR.Hax(*C˗K|ToȹT0rH3Ld2H9:„)i&ETd'n]9I`*?5Խ0к2"DF`XineIHAf&SEVia`$YEnX*T,‡/5oi "-reAv䡩}{ dj1K.ܷ|ﲬɀ(J=@" *&b<@BXa Å$!*@."+X"#$yh!}1h(3@"2hcv# 7˔TY0x 0tY̗<좋.<4lRSM5$5l6pӍ60s(y-" & MƜqT‰#PMRO$ %p<  8A6Nh$J-S**6BM|P@:k(A @ú 4D6kV[, > .^{l@;filius-2.9.4+ds/src/main/resources/gfx/desktop/email_emails_abholen.png000066400000000000000000000036211506574241500262370ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time03/12/06Q?IDATX[lG{^bwm6qK%1+2Ā}⢂ᱏ}lyD^"Z!R5@$\9ƱlDZ^zwv8h̜93 k-O+ϟiL&B @J ֮WVVRTO}M> vvv68RJ}^r 10003Ν;tuu=ݻw)c.^k@-|m)={4OOOS,ijjb=Z1H)]͛8p c5T&!nb7;v)J)QJYq 1\zL&C4mL ֹ92 Ph0ZL{{;|\.ݻ) T*>3>>NZT6ettÇPV@k-BJqX\\dff&co)y###y[Mn.\@.j" #  uh ERb118c1u]l% ں.k#ONLL011njD}v;"Z̬8 us.mwt<ӃeAI4G?Xos8e`87 /8!UՏ_ERx:+[-^n<_:R0{?n#s6B*#7a4>??{'e?z>@+U7eG|vx< G1-KyM.N'~6k8AIno|emk[k?0w ::CX_UC >3pedH9~-{` ZJ:]~<|@AFT C)R|S*. )59M'.K "kuDZ "r3ZH,sxYJk\hU!> |YkIK6AK:xuo;ivCJ B ԍ1 1qatΝtƍhD0"ThW ;sg>( `%_rΗsOR8Cy ~~ (U#ŁG%7D$3bZEylM_>,}%n%(0Ag.c]o4A<ݍwNZDw>ܪҝU:;\zB#Cۂ۾{`O%aܤ*TVJZmJten~d6/ :;JOU-?1<]\K$]fʎQ?9 ^}6go{\7SfIܕfj9V1 ڿ=;Ƀ߿ujZ&+I8}g w}w]g>Wpxt"ќuBʴ?Ytҷ8ranps\]+@4`/@f_dIO E9žcqƯu[x*K 3k㊕!J.NIW"@- Ό!e4ago1t"Ź\ŭhJ]G%*5R $R 3i Y$'ñ^LI|dJ9sa| b:S)y)kWG G FF}Nڿ@j牅aM x&epi RBw.$MX^y4xҵ9!NUq=S-f5tEUեbbz )yI=HK "@%5V/ټimA5f{ )K(74]@a^lt=@*q0AaPEnړC4/Ѷr B)L >i! L6 ueX4Fz+iQ淂& ʰND0AԂ<!>xPO.<f IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/email_ordner_posteingang.png000066400000000000000000000022041506574241500271600ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time03/12/06Q?IDATHݓoUwf884M'MBNXyؕH $MWH QXW[Qi]Ti6JIq,*GuÑsw 3A`pi=fl6{xW7;wu -RkE===LOOV!hp5>_X B3q0x?!5zP:5EqÚb$=O2 +W蒉٣Q,5Ҩ[ 4M0ÀD h`Z?1zc}t00{MJfPuҷ[ 8}M!4qZXFZv"F;l7v~wkP(P,x"RJѣG9y$Xx$ Cazz4bH)y8s?}gK;d{ ׍FR"˾Q;oii>"U`Y 4M\%H!q/')Rn:tg<,1==źޫ`.i[t۳& zmu\%Q(HRѵlȭFH{rN@VxG?0 S*MӺM6;2Լév=ɫ?x.ޤP(0== |˲F vxpR,frrt:]y6N]Ǹ}Ldll|>$044eY#'!^NJ```\.E2 m陼R2kɎDd28sssd2m/ȉ${m\ellrmq7D^DʘI?CCCylj{BHmH XE<( Zctd{G#s}*m>y7nG{,|zZ!Yxq3ިUUh+޶Nϫ7Q˲37x: JU|w ("H$X~ȪljBD|dQhݗ9^D"RgRʠR쾍3~7ZUUVB,}OXy#jK\ZattvUv˲*dYzkwoj$si"b$O;n\CG\ZH$rEraDuEhF:n:b1lu]Lz xa& fFZoZ*IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/hintergrundbild.png000066400000000000000000010060401506574241500253110ustar00rootroot00000000000000PNG  IHDR5sBIT|d pHYstEXtCreation Time04/04/07tEXtSoftwareMacromedia Fireworks 8hxprVWxM%UMWuy5{gbĢMVB"@!Qb40L3hp2I&ıa1Ʊ=fl Y.Vޭ[yG߹JկãkG0V׮U}u~:V: aUUG5./>3/~[vo̅oX˵>¥ ߲A{O?ʛi}~}sw_?ԟص_ ve|?; 8̷ |~ٜ7Wko?uŋ;w:A{ >_xMt]t~#>k:36/)u1Ƨא;3?qkggb/V۴鸗`y fbKG%m~i?"PLvþ0v PvŮg{m wK:;a4 viÕŎ.1+<spЫPw3ƶRnj2Xuځȵ5]4\زq:U, N C@?4 /}] Af/l;Zcdâs, B aL81F֏XsY5 veجnبƎM٩kcݭU,-60ѽuI—Q$E'X.ǻ:` g`:k#lS_y1!f ^; hҶI&8:OfP-_Nֱ6k'DNߦZ݉id&8 n1csE~Rc0u][O&m t~l҆{a^STIMK "=Wm|@L |3Lin 7mp'SmQ%_Q̀cP;9rz+6Ѝ @*qw_6@b(aAg0lU8 ?~2(v S<L\<;AuYT3YX[iLgxXAv]e"[J< *MWOv˭v 3fi42mrhQ'4g5*'"^4gQ8fTGO>m$Ww#JaQFI98CpLiGJ"vq kԑ|SvkYɦ]].\.n!H1g 3w[ΝO6ӮvY.mݳƦ;q*Ƨ!dQ1(* 3'ȶЧ5-'&L`L1dZ 9vk>+6F2`f%1)ћq<2,°m5n6&+h҈ fR0UЍVM_K$JQvb־bE|R6tMud6kŘqlx0dX;tv0lj^ɗyTivk~[G۽\?m,1u/I+w~\b\=!9)E;GD:5beϮ\FQ lՃSQNlaO^zHL(T$=X(!Ƕ񘒛񩓸ȟj]M޴LH`^`l=L! % vJ:AbM5)V;5Or`z" +O9-ʞo5ʨ֞#I5%τ}DgWɣ݈+&Hyd\Aނ`WCa;`_D8n!>ˎ X w_MmGs'cC,n^ho5e)ygMC@A-${{zSLq&|S|[S%a>ʺ&#Hr1?V ߯)!j$v|,b&1>I/G[{ y*cƼq`3#w/8AOG^ٟe;pݑ?HmfDu%>-rړ};(~gaX*XWUEꅞXʐ-<S>/] }a >m '0hm~ + ws5oT9+^^$?m H >,< nw>7\ru9Oiw Lr;Nuȁu=vR|LiJyatCoh%KUF|5_?x=V0nѷ,^K ]G;ozeإ>׀2xs[">Tݐ{'T>r 1)>) {Čs ~{ƍ/c.>w^zB?9/^%FG?5{8R\Օ-^ 0{ҺL26~)sV|،胼ϮW\_z7<zē_2}_In_'' |yՉ;tw#;!}UmIo ӯ6Ju@2 ݣAFC7=OacS߂{`9Au@"bɜZ`a}QO/=+֖ {p5\.Pr\g< -ǥOl 7Wl $^.;LOC_nO2Wi}We>n߾?3fN"?w+fsV%%ɕ\:: 27Lǎ"W,_\6cHmkBF)3mkTSx][wȑF&%˷drgwNQEI. I asG&8M^f`A[:QOmNvs8Zv;mV1`+OU~ GltMԠM82!l+;p> v(6'fڲ0{1WPv 16lT)6FMu6dt<]q<θjmmjVt>*W\W#(]0pcBS@Sд$4M.w-ݬ6ۆ!}!Q*.) `zJ XfbybݬvFpmNl 2 .)rYYb3aVtVܷ|J:0|68>uaaᮙA2hTʠQ4jJh%KI_Sr@Ϥ(Չ{%$wuI_8NO8qΎʉ:I,o*ʧҢn`L]S$wv4k3rv-<5XC\8Z9Zh6>T>Lo4 !ԟ3B:Cd ! P> W@ qst/I%U9z/i,BiL']P   #_)WNoz fvR4z:^KhCg.CW :e d;$۩UAA?YL*Mꨨu1}zR%5[AGA[PڣzfZuzrRt* NGh/࣯Q6E>CtMd7Y/Mw _qc]7SGNɎdvlgo9џH dmHI#$7;,KVfDfY%1s8ALb L4ɘqH3%йz>ǖΠcN@tO:% liYthFC4YwBh -e3l֕N2ژAJWqa(V䢅PTEd(2ˆsţV8@#gq`3s><vTZ{%uf4Խ>a'^=}lP"Nt0 %(Crv)bpSS5b6=Ş V8T| =z[9{:M΢5H}dh]>x-MN J}Yp#3S(ƑǓ6]*vGD&\itOOMܾ㓥YTL'~՜Ao8qP2+͛? 8C0$$)p(9|,ك2k'}0qK>a~50i2'i+eTWF!}I ; p\ӵ\^ ģVܭ?lOjdx 观_'Fc!wRl\bm64=έ^[n 8czre25#vqՏ}R> رdddO5 5=uLC3gǐ$Đ+?cF3"l1)ݙwSJ(>Lȋ1ր0䆞cj0ueU86CF=CH~birc)T8F5'MP̻SyKqxj|m\*N"!юc75RʨA%E慶mYYNOWuh&S7 Afeh`a" SJx7SNߍ)';.P`wJ=Y%M.^uq*q *uG)9`6: Ns^ƥZQM\rM3Ḟ!yVcyi4f#F5!LP1x'l4A1{ M+'(&2N 9YnR|Rym5rn6tP L|@`*J"ag=Y.XftRFE1UBaѮtx+_OJ 9톧4`C:5+%RDC͞*ɣB+2']\< 2s3"1+?l? FW/ئqEZ4DCiq`IyǷTM#`~h FڰS8~KĬŐaV@##*?F ,Ȃ=-E>_Pڅ/N#>`6=K>86l1ԸqT59I `{K%ru@Tsx=r`أ~hV 9m> ܣ &OOb\G-׀.տM|/JDqHP-Gy $ea[\x s|5D5i> D:y{n]$x[2RnY;-z{eg e$g2əH!\^$sB(}pI>LbjAA=.9-$5I/(=2㫱~w|I1쌳geɲZ27'1 ܧhYBY>yLr^a[aI($`_lFyFjp[B6zdF>uzMK`Y|OcΒz6:?~s<*LZRZy DT&b瓖ä"\Z5xe=''-EN"GOJ$LfRfy pr,x>i fJkO<8mp{|LYֺF/z}ǵNPj@3=? j%6^-¨U5㗚:FKM/Y=EK)VK;-ϯRg!Z2-LRҶUU'<;k_1Dt˪dU?A=Uv㞿Aq*^+ZOBG/a5N"RvPUAC)gUիJ;rvҥK5Emjrۣ:O}Kf)k2^PgZ1݁jqNX[J)ֺR\) Ta[C*m7/UËWzegڵ1Ƽ"U9*ƌYe3ؚyqth&tg왥tV8cǩKkU+ci'aD$RtY4O<@qv/-zpJ& ֻ ~hԗ8cdQHEֵ$ؔٽDV%i^~?k2<!e!_Sbf,*f~T?ad# SBg|^vE⸆c{GKBם'lθW4NR r<i~p6[6_;µmR cJMi07&\R#Mԋ_@/P>OQoOhq'O]׫_C񦞛~w[_TF/AR" σv#\(1FP[)* ~UEW^>XKЁc(a ՠ?#nCKnίُ\TKfz/t5$w 'ItIW+g7{k2M.ƪ䪠'?ޛ^WGt ZM+4xīN7G~P-n~rP'jFH:_?%9YgNb::S( }%SL]2uԹZ"SoL}GآK.ٺd뒭KvlFkK.ٺd뒭KZlx2Ru%[l%4[+z%_|]u׹\"_; 'FK.\Yq6%SL]2ukbtz|lK.d/gUfl]u%[Yl=6l^E6_%[l]gfUdl]u%[Yl=6_<[6뒯K.zJ.,?ڈ!%pm*@}ѿg,7wa5{0fN]Uń>Y;%kŶ0:9(z.{^9 fʬsOM^.r稃Q*Vz4М[p {9 z1>#\s OsGy_/$TW*>G镮W*v[$Czb1䞱Wrt;(95h1 ~f$Ͼ"4 ^Y~ 8  CUׄ}2`F[Ckq%ǥ͈eEwzs i-*NjomWCP[-Kܣ|du bIM,ωylgSP5jk>W-{u/uiz>N`g~w6%~OـaEg5Rմc)b~WKryyi=bHijkћ/ |1_FeqeX>b>%aTDhL u72E3@'oC|>:=MA?ջq=.8NbiɥM^O6*mri_l 7(63 ~@Q$̍f 7+_<ʾPAah`2RA+'g< Q6~x(Ƣi"FӐWMBCiCv6x}V.E1H rWSYhA<Ґ>y&Y[~jqcr=WSR\?$gb'ĢQNӔlkW/W|o SAG5KcEpάlYj(T]HY)b~Řz O W! M!=NEK=MKB*UՈ.h~ѹKiX.c<&Ն{ i\gIW5 {Caѹ5g׀z;k;4kWEg?f'KJGXgZwl(M_ؾ3]smr/mXzAG]:~dc5ghd?/uyl[ҴX ? >58B. JM u&wr~pQB%_Ҳ> O-oM}JVwRˆٺKi+_\z)-eHz<[HY^(a%н߬`Ԃb%f*ub`#d>EcJ1kU|KcO `oRE L>/o {GQ隗!A=/lq^uSaPϬV-bYd5<,X߂beT]gDFX6ϙHK.=4SmQ%2>nH͝{4yM??;n4=T< X"o*W,U?qSfApçp:j9͸s^/1Ҡ>0Gop!U"n3;oޏlQ 7#z|tnbޤm.}کӦ u`opd>Mom< =kF5 l~pSg6>v0'oM:xN:CTϴkcjE+qs^ߡ7ؼ{Lm4Q lx>5 .n;;GT] U Nzb8^1=.`wiS?$@v{v_cЫiM67U5m͉c2A֭fv}\k;U Sukۺoormk~qӜւv:tv@+!5'@=2%h򵈌dͮDlZd2VJ*sTӷi'xjW##<ޯۯSWX>`Nĭ-1mkBSx]N0S#WCJo`lŋI/w[@nfd(6`<VLC@CG6ya -mJobD2Hrv"?@$'y#QɄ %Y\H\_  oyX92>Qbf9}81S _;&}mkBT~%x_a2d- !j]} l: @a2` 1fSꌭ{gl?:c|Ñcq:Xg97DYsNw3uιΊws9:+-l9:+6r\{~WgKqΙo8fgŹY垿;>,8ϖw>.Ͻ~>>Gy;I}goWGǐ》SXY0 b_ŀ11X M.`=1ŁSp Ān Ab3Q 90q?xq@:@)8(sa>`b \ he 1C(\ x9!2&wQ.  qf6@P { (@=0ۖ˿.0@MF,RG=r\ z ߫zf\ Twm-\ !?| 8:1 1 X_H7<rr@y r_B1 thb T1dj-aH(&\ S_oc@ІV pzB.Pz-%,»TJ.BjmX w wM bp1h輠P09`3 B r KKb@QԞ\'ꂷ 9@ ƺA?0s#6? y]Yuᄹ@ » y.lf(Tb... kB~  14Ծkt!'lB)s\ߍ /,x] ~_H [yBck1?6/pb`}J_hItH)/Dh@˿/P_HMH0U5H~ԹP\PPtG1CL 'HԅL qM Pm@ckab}a^Lr@RB\8e'8䀨 s]v.~qd.Z-zy15.x8]Fa\ț J 4>8:  =@2@X\@_;N=n @A4\e6wpsaB5?t.$]X p~aR B|!-?~ D&]P 4A_\8FߋP轠d&,􅡾@=H.iB;/? }!?_Z]0E8/Ww"elW.Յ%o -0 fa(/u_V;ӆ!MH#B`L(? ſ.&( ~)n.,ῑ(O7 ^gtAJ Ps/\#CրGA+B2\H5j`@F_[ e 6_ SZ]οp?(Ѕųa\ ;(P p3F=?64W\8J =_PnE x 0h9 Uwf{[-ss!n.(PXDkf_@5zC?B{uw@MrT&g=P3_ j_I|wC˟Z-W ?_|!tb 3@ 2wzo埑v9`Y2pl9p_6taN:/J;`ZB; 揽lt9(_~w!#??pւr/x'9 w@)y#.`TCLOOi?o?uiSGO&{' K7j @o?f|{>M O'/j6?+?H??hoM/΍#? }y/? jOߪ*/_?~?TkU#C~.?3od'Qt??eq_e{{?/OB>JD_fsB'4;7K~o?oz}=s}OyS~Gǽ]/*K<_%;wt?__QSfD߸GQ|~%y%jmYOoU9f=G'=ZOziy>7_?JzB.zzS|^6{?By?;ϓX}o?vQ}+džs~^:$sy?SIw{ -Db}>rbs߱Oy'reg5s&aW;ݼ?TG|s~˜zE9[k!zoq}ߴ>_}|>7ߣ<@g?/}~cC2*~;Oϳ1e=ᬧ9oœO4r?Hy}^j?9o9_^/kZo¬yǢ&~}.si}_?g3D<c>5Z/3rmۿ~[W1s8YK o! gnOs ZoҜ}?9Où_<)?뜷9Vs+t-?|}[Vܨ#s~O64/b39_q~9a:quHzJOeYb%9\O=I?iΗϧ9=_.7=Dq~cspdΗUSxfVOT4Kyz>⯞`:ZMt^{n_{onΟDf|@~o^}5~@穹~!wDF 3[;YEsO:/۸:ǽ>ŁTv6Kv:{Wgy9\>vb{ :joRr>~}:v6Tz ~e{?ψ?'<=pCOwu>r/wt!{'/OtvNmKsJHz}y"y;g%yQ?wnۂY!C;{_ Wy%:qDt_zKƻr?o-ؿ^i`|Hq?o"{M|i?&L繋 _y4M"?o'_Do7wZaG>d`7:ݮ&\#:S{%~Nޖd?_w#~G~뽒{~^f?>>? ?,ds4qQv@?]s/{׃|[E7АΣy y 3ǵǼn($p9EkQ)pѼƗ,|AnptM:@x,N8oh/{x:/q*t Zgz,=x}R=?s5$κP8g"ދ>KYKu=7gzfin/X<)@{ZתڕȟM]1A\.q]*޾BB>:% kO\,:5KoHk,@CW{xQ+T&%$xX<80d.3KJQZΏM$nE{ XV?\`{jdеu kY ywMrEr1'ľ'zu صE.אH8 ߼fFVǡg<@&ݥ%n\V3.4CfuٰUwς?qj8@8ǩ}YIT7p j]J K_JJj7(#Y7XKJu Z뙩ڹC*%Mg8U:"~4ZW>b#Uw|cr7;RJ܈1JM_#̇ЍƑh\K*O೹?n x\g5?α3>gNgO9hiGp] o#\ybH}KC3XrSx/Q>^=;s#?Â/񽰔ߍ)zYuTeF*;9d)ʣYg[ Sr-|x^iqǾ+҂4A]]9H)}ט#V#9wME:~=:~ I5}6IDIomw9]RQp5czN7s }=$+pE%M_e<N#,jDݳ #Hq?(} ^/3XSsO~1L`JtU[ͥjךA{x %'=ΙzS }B{Ď4r/9E佃-PTcge%G礞|j/ݓt=bAq.rwQέ'k=E*% 'vj LQhw)\6f: <_N8F qF"<ЗvUy($|Q9j`mڅ`.}N9?Q`#:0),3Ǜ =5Z,h1v)oG$HAu ҚX8s03|؇ߏ0|H*_{Ǐ]hwL[P=>!/>㒎x:ќ%hSd|\e~ƚL~C1BFS K(_่1U?b?,Gz:02w_g c?5?y$uG}⽦ULP`U41d둏,?(9h#1MQ i }x821 eh+q,?U?hڷd{~Xl*9Ǔ7L%rCZ޿Be2dkb\ \?n)r׮=sxʃs\&LmU.;`jPc]+P8Gf1?e1yG~XWN 1̣bS{6?>sK2y -pMu^~ K =|W`L}2Bw2Nk0^Q)UvL˻$漢yUo*On4'ˌ\^e]-x̒A/ +PA؏Y׀3WWZLh>%!ؐdkO9;9^B@9SGeD0VN)*; ^#{=o,J4Kn4yUb'7=U׾4@棚o(L )s/n.бsiZBs!/idžqs^B}>X}$}B'?wg.kyF&+@fdSTvdYcy&e{ 2O n.vHs@(TNz9n+/^޶s5{Gf wYdO^l j_/%n^kz=)6͛5G`;E1sLت bHY;sR"3~s˖)@/)>L ZZ l"{XfZEglեIȱnrM_+_f 'ef>Q'?TNM#QYʑf J?棗&}rIiVY[mBM$t ,l!LgfV&؏3k84-f̤N6qS"BJP%K.WpY_dc` ݃ Ré+[ꞔ*'\>dbgLIfZCrd%&~Ğ XWBz?^bh-g o>w|"֠^a]CDw*nX^`dICVbs6V1 P7ɋ-+^b_s;^eGx i3k>#_ ]?ǎe:^ſW-??ɿ}an$1+10#l,(EH'$kH7$[/L ]"= H2a#e^b3G̺E5C.o)جX\P)Qv.mkBTLx͝\EedtXdaV!,a a,Ȱ%%䕀  $tԩ.jۻ k]+c cr;M]E- >v9XbVX{:X^Xy2A&-E_ nuN\+z7Kn'!v']럊{iFä E m4BREޕ[^6+9s\3YSdy<=d?? :sy2yXi&4qb}u>߸*wgf8t;5 iq/+8V3Jʹ͟Wc66rY_UZc}G9MVW1%y)+]9GS-Sfj5cC W0[[^xVoٱīпWz2g9d-Kcz],H{Ygt~o̫?ܶ-rg}j5ǟL{XKXfjHFآκjg z1sKלk/l$[7̲ǙMn]]|=ZNiY$)Xi湐U:^K5ƳԞ-pLp*ZI\ |Zm5 !kWOۃY-1Ӳ-Zc1[9ܽSSگ`Y Lx\l:{kw_IY_y^X`u.kɾf;eW9}];9skFkY]p}zr59p=*̾$MNآ¸o-Bf\ &lyfuX5xXй#C>&g٭[e5o;l=:wQ>τ{^,XÚŽZ}km* ULDDf]F|cO'3/~Dμ`8vtƸ۔sE%">F [q=*xNVo 32t25S.sl_ 3nkζ{ g7{\%ײ@˹W: o_cLAߧj 9HM7VKg3EexHż {^͹ӵEb!a {^U2/ 4waӑ-DMs8︇s8װY`{ kj00ox9a{Eyv=Lͽvf\w9U s̩k>6] {e+g1M8Vbcdku0yQo _Yl!![糥I2?nq01ߒ^WW6yV\6 k%Yv`r}?Ig`jI[gI&g]Yү YlNgEs=\,3 yI.u9i^n^y.[=]!NەVb<«e'.]rZj13   .wa^G~..C̷l,a3{y~~*>č3>91c,ƉY*5T3&Ŗ wVV+qY\ObG%]Lݻiq-pVpO`R\%b"gZb%??3}\~&+fNG~~{lpg]ϭZc-T^疾Wl\#b];f35>ho9`-s|[<ūv.O쾌}ݹ|/+1#;>cκ]<ǂϞ^\ʆϻkC`ʾ<gD Cy1XϮ[_#6Ἂ}cT^to3K<2|?|? ] =L^߃T>t=\/=Vޢ6S}oRpl?K c{U`j94ϱB7Klzֶag]4w*ϣZq\W;$^W]D pO^Ԯ?NvsqlxOUqi9蚀6KplcgS[,xM?K>eW'ɽS7W0/csu~cWfg!W+i;'}=O%>z.O>7l=1b8TͺɻRcoyss*չѡ;{bSrg=XڒZ{,e7j݊͹gN$WwM3q e\±[l撘frb%SgWoUkiMcx)^{+\܅7N\̚}Ivbs+fUWQ;NxSyiL1l%9Xy=i%}+OYky (Û"K\q|n&d5~WWz-\U3 =aR1-%%ocs>kk4\KLE.ijm0$)kvMeo2b3cǫ>+w-3$m0,.|zkI7*w< ;Zw[[*v*<Cd3 I 5,3<:%rkbfͿ<\bjFsfGf9أIn(]Fuso}lpEڪn%>TLK;5wcز0^fծIvs;cu]NDŽO㬃˺.0nc8k280h{bV6ms~V5Ư1Zѡ9{r>mv|ĵ29_-3ױߞ3a^ag1!d>ΌO1ϢUSة+|OEͼq:<'9`qzua^!{`?ڴqvX79=Re9Ud{:N];V{^G {2++zM階vVR#GbUsrb\Vs:V Wǧ|ƞ̘հ+"w-~.ur_ڱJ̫dt/؎]m=j(얁ISoΪH3 .ZW/:T?zƒטx] %UZ?Jb^ۻWё?5t0|ǑToћƎqcLWF*g.d3-jv.[gYnJ8Ö#9<={'HYrg~-nOWsE7WuËR?[|k >vyS={bYzLGrfLn?t\^L,VS?YuUi e`[߽PCuNzW0#mkBTMhx՝yUŕ?BQ`w.QQPNLqd 2σL2!=T~[kծ}!IMOQ~]uz^S7Ly]zv\\q\y]ע~eQ]_܏_uM('(-E:w-j:MkCQݵIMp\~fFOZnsUOݦpQ{ޘrWN {cf*EX7pmoK߸*6\~3Y{Xkx{֞7*z Uq^=kNa 5X++\%g_ !Y5碦mȺEgpn똵(v?WYO0X?Ϭg ks'[-C-w>B:feT{ĺNxZwQ8[~ guSGz9c<`<^'y_(_%}ږu- !v5w>pӑ3yZho[n+r}{pb|Uy(e.~O_z[6Pn.5psj90ygj!ScO\m'is7;ڞ^5ޯk辬㔻5G%򖤫25usw[g֯c5rfi<-O1|Ď*%fuBآ|mCZ򵬼%#WKfj)OSSk]ӒUqnxŚ}2*lug- m 穞0kZUkQ~ S&\ {7fi{!CkdhӼ!k3{3LףPuϮ㽱3gs9JC!_}Mjs7n34-OCF9Zpkv'o3Szx/묓rC9t6sׄE1[RZFΒw\%vܳkּ3s\53mrs/q杓,}lc|-ϻjI/odh8Zn&Y};b=b}j[ٶ k\ty1r|=}4ur_ g,Qv24,Gs}\볎% I gWWy/89nM; 8ues督}}lj9k]sD̯y-9ڑ^wGC?3ˁsXg}w>>չ5^^e?X]evf%$r5uF݃>}z>MPYR k~ڵwg_y9Z= F7tΏS7~tc3Ќ\-jZs+GK9Zw_~}fM_uPv6뼚:Eq5\ N#Oy'a>k㔗*w{lٸyb5o8W7UV]PnWӲ5<_ׁ!Վd+'GWOY84ϔ~Y]M!a?w{wU#g\o" 誸bUya>3xj3#QfzgS%W 9Yԯ}[.4Tz}id딛uvƒb=Rμs833o_)S5<%v\-Ѭ3EvS;ZGֳgf-YD|Zjxs6b]5'Uw{ƾo_v2_g{<㫒X龨nPW4g#5"[iVs3M&fy'di3L9Yi~ 9_kOw W7.AΆ{5v6=8[Wc_վ}25u⼓4~h.bٕ>~g^~n6/eJek3󖌬vTX,O-轻k{v\7Z|KWmfޙ9j\S8[IghYSp~}ۡmg}|6g.]jkbcs9i?1 л/IGx+M[go3o3 o)u;u\qU-b8F6Wb|.Թ'o-O  ޿kGCG'w[z_b]{wj޿[\]}PAy{[۵y(ky2f%j׵-g2O&Mϯ^NrgzU{7o^^;.[ո%{u}Дm#r6N-m%kf#)O{+JNyZuܳo%ٯa}GSKg}̻槩RGWkἨ^9y)}q>h9vxKg4?A5k3m~Bem{һ=(Cp4& ?[r;ٞwկ9]=U53Q(~9[]:_õZs\ 3_>gaQg,8Yn5*|Wk\c sf~ 99%W fSYw5-SCYk{ygu>{5|C~]}:p[RYˍ^2Swp䢙ȷ+s3Ց X+iW֝9k9gg w x  4oS=4g|Mq%1[=[J#G3VW?^QCޤzGvq.26u&kf?Wv}ז>SN?);gM혳8x {" ߹*}=dm-b|m54ӈ{JYZ<L/.' ӥ89\ '~7#v=`}@sZޢek0}-W 4t_~4>CY~vYb?g3?97=s\/2խgɬXK2.Zה4;_کw˾=Wqs:{[u媿m{2Uk.nbҺ:Z?ogMms.cP0ϭݏsj}Krkayj>_AΜ]َvQa3|?_{n<=|4n<5l1K <= :j gWc֋{4ػ4s^S]p挞}kWq93 qR?%m3vp6nHoK=!S+8l♞S3}g{R\|x>>۲3([kRY ]\]zH0zGugg:z\]sQpF4^?ާymPv6b;˯)= Ǐ5op(Ckqy?ƚ-{|>ލQ=3<yzDws2]LOOKdhZ2"?kq9;;O=;y-k&\gKV/;ź[ΙYwL9Io>i3Q8;|Ms6>D7 ?g_==DBNNxݧ5z6G(1>Jy2k~A\5R9;~6-j 3<[rSyŜs|nF "p}'̿w6Z8⻓ܻ}͚^|36j ׬'mٰx9s"[KjGL [ͮvz7\6~{¹ڿukXW5kG}#3o<4lV%oIej]3;_y빊ڻS}EoVTVku6ml~> ouXNޕƺ28Og_c!YJmqB+yK4d_cއ7A;4p5h2?s6?#wkb^1ީ.~A} שP۪ey'$DNľΆK4uYW*Ӽq^3mM{wers}_8ONkΣֺʼ Ͻ7R;vu9|;10Gl{jSƞO4rmg;~CYػ0*:8-y[okarM=<+_I*i?vϮ9o>kR{'9ޖ8c kRw<-3Gv T'oԤdea񶔣!·bgՔLaWy[?PZζ8[ nnjƼԿմ-ihh~?89a?z\*ZGu1:[\-տ {7m-}{p3g78pKa=qRkgߜ-5 퐯ee`ͼywpq^քoЖY֖j=\q6ܒfjӱC8To3S5xڐy+v3gW Γ_Xk8O|}ՌGת_UWy?~UU}K&m֜,u%<x &~^ziv bG ).>p5p]뻬ʢVA?~> o4u6+o\#Fbdͽ;dVO%rk|R_/3-53TfgVGGKJ-3R5oMڶm;|Q6ڰiZŽƅf+-?[y5& sˡjƯ^90}*fЌLgi̒L5aS=;y[aq_9ܷ=go羽5Wr̟4 ΣuP_8|Mjr'Ѿ3xX[Ӊ ^4Yr 8Opyoɸ_?/϶ :C}}ǻ-=<ӻt^N{9g/Qp}mJ֒%sgYgӟnt]۞sj<՜ͳlmK6hYzGCpo 1ow} `d$_e}ڰ3oݠfh2g!J_\-rmQᐫa΂Z O k4ѿ;*\j^mY9\=璱'P%'}mHhg ormk+{de?Nz[Ԁsp5,\{w 4gÑ\}J0ck6PoK̥Kf5=mewwmၳO>S<~fNdjtOLΆg1;ZޖmlgDZrձ_zZі+gIOO ?&p?WvQsUp۰kFsIlƌs}vf2^kᘗb'ܻϕ,ѷ ={ԓdWIoQ]Sž|?4r7+o3lwFζpUlZD&_33S3jqOp-t<<.돲t7#wNyG/OhIGcΟY*b'+9oy mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 %mkBTWx͚i]U5Pq$j$@f2y ɛdbH Ԉ '6 : CNZk?UjZxzv_UuZ/V/ !F9勗 >X&|$!W|ʕBj4_"d*9>ӄLrnO;#yUf xf99mFa*f|=g2a^1 ƞ'ݳXw[v=[ҷ(we[mF;s5.ȿ |VK}^<' :3=|z4Oތv>\n{% io2})ԜX ]1N%ɝ\1^8c,N>'Q9;;?a /"0W`<X%3lb~vü^NdSbǐ4ŦD#z@#5<;f9\K/τhɄ5 N}lFdoG.6{.y=Fk/pܻѾg }1K\16eKs.dyѬJ$ii>'v95,ir2%dIsqd16YAb^F'y/f݇ ao`ĿKd8Y!$=Y#3{EN~/r1]XyüƜճ`JF3Lwq/.^2[wjx2mԋݸUM.璕.t/#{2#zN5mΗ| b__.l,`5ԽE} jXﲆ-+\4]ٲWVjs&XSeymjxF_|3sLMT+)ך-f5M,1N>s:b2.v.+WVε$%wdW_̸XfvK;`lc~gm4'#|;,z֯Swł .f1{C+_A]b;򮕌/̈́jw]TZDze]zOLvd⽘l'#[7s>62 y{8ؔ_ѿ޵g_q\׺pw}osYOُw`^[ۗ 'ƞ گ1E^*#!es2< -d!buelli6Mў,d9xyf04Կ"0!li6\$ޓ)=Z1cnR# .Fva[A;]k5L"Mv7lG U]ol?lC`|eA<q="aVEh?6Fr2NĐwmlvي]9qH¾+.^ѻF [o3cώ囚\63%'MwU9go_*մwS˟d3IRޝۏ; `CŽk`2a3;2C=1^lXbwlѿ,5|Y+096QV' rι8d,%)c{0+ځ:ط+|G(v.f6W6 l-M-:yRm.[=tWZ%ѹSE.`I[G=`7-5o3aۛx:D`Yr}˷b;qۛLްb[lޓ%WBJlljϾ=[6k'\QeGَlY=o]q _\N\zv6rg]0Tf!݋d[ +$ړNI$Wbp&.1MKty olq>GոWubm sLX\qrĝ!' \_۾N&}_'Lyƒ!cô`xW^ܻ{s&Osѻm؉Lr1 Jpƿ7c=)ПIצf֬skix!7n*۟eV[紶 xxN{gOv0e>]d.[zZӘ#MmIF>y&a%fkVSc3]-!*딓K M.& <`ndTΗ8ts"||gT\[Xt|g"֤:Ͼ6{7:oȎlGrG| k{dǮ7.?5-xvwa*ބͽ+r/\3g_OSӎL[ s6.w>SzHzaגY],2\>8CN~ΝjV:օzu ߈m9f.殐d=Nfa|NNޕ/#_>5sxj+μ*wĜaG,N}<%vacpGp!]8%~6s+l݌)s|fo;ܜW9̜;J[tN^&]X_һafցm{I3;ɹd|^=.۬njڿ;|>ߧ:Wq9 ̖~7dܜdF|gҽrZBFPnrO"6Ѕ$ciy"%|Kko+msu~2s/IJw'$xk0ޕw3LFyM0^]1aюL؏ERw.LYaݗw1ْ|4syޭykfy ;yĖ[P5u:?521.ažsp1a_KNp9.t.Wүm]Îv\-[{ޙ=1ȽwU};{^xצfGt|ϋ}GB]e0z?}i }[؉yE.=nS<,`wWK%"h{2Яx>k ?;sɑ,Uf;:Kf[ybXM!͒>Rdٽg?`E^mpj+RYGTmJG~k'K.6Yl(̷b9-ߘm*?JavW w;INmq’&113Zr/_]QR}%s1<>/~yu<\m?/{adNvN2q)Az.h wj6x[\r\Y.yGK}HIrN=73\lhFw6uJc8ѯ@=y$&iݳc2vafyu^-:嘟$hI>V}aI+͸Ȼ2E8w&Xaaa?&I+p 0q[dq0w$q]>^l+[t&?e>(\k߭| KswdNw❤;ivaVm=Cjw&t.KoG⺭3׌g[.AܻO>NdņRJmݷrN_G ,6xֱ{徹d`]y|\SlH=O{ZFg˽P;+mv_w \_CFu`(a[Ql u22Ι:YXBf52<&;i#K%b̝kͪolwalWu#=R3]Usg3sTǡgGߩ&Է]GxU^壴g9}MZ(z)`.|S ^eRꊯ>ɿ+u/VÒ;.gŵĈ|#mfnku?FjI>Nvbbl3sſ ;orB^-8S\8q& 5-ԝ3ލ{7:][VԳZQ\YW=?R Y[oϛ_Jlymk݈ŽX\~yily1-;],'ÿʼKw _3ߞ :K}ra],aְ xאc) "WY[y^ !^Z;|E\L܅a ?-0f`?2%yռ,Y}9K˜y&Lw%h2wm֜, {VVGS\.nll?Vc27 3]HWsyY엔11?5d82.qaп,|Xjdm˔_,gˉݘ\bi+q0ɽ2l"oqկE>16i}&`^KwŎs0 Eyf틔-: |kzř,rmLUyI͵'WTv Ŀ;wNdo],ڃau)Usu,c]։x\o\K]麕|Kc~L 8K0m{|z٦|Ks-6cÄ/ w ly:g^Ls Mոf&ʉɌ{%]lX+Q¹ĵD Ƽ_[Ŝ0&ޢd&Ǽi0~7|,a`s&ȿ%:عlؒ,s,+g }ڇp}Sf?ຯgpy޷|;Iבo[\ `{/8lEWxן<\ يsrlpoAh,po7wb΃9}ftwL6HH{ېw̝nj.ff\_ڎԩaks[64 jNJ],aՁweK-c|3U 7kB>Fbھ洸 C{U` &$b;_̻l޵K[~6|?#zۑcǷ<_WƖLT]AwV/C}Kw\liu{7xXԯ%zCx; WֵZ~GcS*0s;SunV:ѝyWʷR=;Y9Ӕ+M1Yumga+ S {M$q/N&AN`}z8^utŹs޵sםL,i~=?oS}`~?h~Aݍ3ke'gk]͘qaWr0jط{1X",a+?hRP]!> v'%wimFB̓40Ϙ<'c{0I{ϓb'@sCb\ml3L\?s?IsG.f0Y\VqYR&8!))r>׏|';pƽblz{3:泴"3BR|+r[k lo0>p\i}P8WV?: WW"BmkBT_x՛]U? * wHsCN 4BBB:"$K(ARA]sͲ1eOo97sCwmn n|{!UrQa:߷Ѹ<#uݸj̘7]\W*!7\: ,f*].a *?V&dzyf V)dV!6Ҹx(Ϝ2>}Wb,2 Q75cd?Č .zc=qsYO!kse2w\9X`=0Z`YԹjpVG\8_eBX9W|g`ߛ|8O0)S|' >gS'|Ty:pVqgRgWsCy;sdp+ΊuO0>W}>ٜI9r6)iէewy|?؞gu6bg <7r. e7|y~_ϽQ>}p[-}1Nn~_ȹ\ẉ9z$c-pwz51Όwhn<-hg{ySF,`YpqOcnwqW{gZM g3fvϻHE9W_sN>h<__+4,z&~p47-gp4;ggc6)g}<\~K ~# F-|Qgq_sN>\99-kt5Y4w'9L\8 Gpp49=p;O!.8Gl>}n9s=> Qs{ņ%:%y<;MYٝ~gg|DuWY mYwZ'2>M,Zu EMs7̅qΚZW|G]-c]Zj;Z?X9Zy`=clP@v-ʝЩ9ƪSii R]ft[̞sh΢g2Ws9o ϣY*r7ZS@6YԼXoi38Gp[cYh9生Y7rVw7㊱=էb=֤d_pө?-qcWlWهE:Pݍf0Syn%ݫg{|pk\ϸ.M?jq皎ypdJ*'ɽZSjf$OE{mqv/b^sfuŹ?7eYW&54SKy߁E|\pqeןs 6]o Zgoɮ; ]Gr g4Ybq]&B} z5΃No4Ýiäi3,_|qvvs^-~loy psL&OR]ZWCΩaF] = r"/.qy:ps{3/1 Ⱥ4̶2jwӸalӄ^%9rN{x}ΜqvUjԀyS򴎠>m|ӄMmgBVϺ ϳt9gκmuwT?EQ^ԈγqdX><~&XjB۞逧y}pFGs\mϙ;GCL?ۅqwt`v7}Vg=&Efзz:;i<ձpOk 0on!=K37CmW =f,]Z"Y<9Chg3ۧMQ6Yca?6c=X{;좬] kB{Oo繆L4 {NYE9{:Z?$G sҷzN^NX;Vm۱{|NWaW/6Njα>.lGc&n=(t;j>&iA֩>ݎxϖE?K`qXX9XFcn?Γxԙz]חvWgvPBVyw,_3m~o971ПgGgtWj{wΓxw},9lL39ߒ=>rg [{ gx^p/Yxž%wjuwa OKn>{vΝhNs;燄.Ścy7xsk>9\#5w^- y=SrԹGӄb gQ/]5؍aVW܃{ONû>~ z4nDp4isth8Z~f sc+c&GMߏK}X6^\=ĝgO.Gs{ێԣqGp4ڟQ?gGwX})rk~q͜Df^.,ձvݑ=^v\`{3|MzF{Sœ~fs6pXӜ,%nWi;=O;ԥݙp OџewqnGc,9z}4w`kWqp%WM= } yѰK=n72G#|0g8qn;y|/W\fIq׭`s5g9{Oۧ'xq5ƙ{|4֣{9L~sY&gqwa8}p~yENB;N5.|n&;J3{>S *{svg\rJ:晽Z{ONX7ӣmi2o9 r&YS>, ujSp4iy"Yf 9x}ΔuG5k^jxfϵL^D]zsz_K;8sG3Mt:ٮg󃞛p>]q^q*O G6߭yb}-ի݊&=U}=`nҧsG.qg=q4⒣R[T~v:gUL1WpyLӾ%k9LfxzYg~f}5FWsb珲km7OÚXGpX88חe^ Y{h,GkΌ: y"Э0Zy0nZly4j,=-8gh~VnδgQYu?cc7ьu۞09[f`[c&jRvO ةgy}4p5Q=)py:}p~cs=ۏu/㗭{BaVpl|T[xߩNץ9yrv7|nw gzW?IA2i_k55-iΞIkY*]:1?]^-Ĺ;N6ĝ4sxgxZ gZ>gڤi345ob{O[d<֝4iإ+Nq3YpG̋Uj:z|8]q~r{b4][;J%|@xG-ԫ-z5ǝ=Oi®hK{X =Z=slm3~go.77,ֳYJvlvsW0sۻKI8g.g}g"M8?'p~b闔yXG}B-2YEgЫ9G=CU;5OChG#]-99OC? Z~fs?8{|lru-gxVkn[bZjVg~݁o6קfp7;Cx:q3n+~믅E7!y? =c}k>-ƙ~8fvc^,#hjs(ہi1ցh\3R~&Y8kʹbM2' {#8McO^-t3Ց?{u5ޣaYzr 2iثpvvlK(g;=ο֟[_A/\r_z_{W ֫kGOc}ۛhХGc= sgѐ%~%g"|˸Epv9U+&+pZߒ;y`-v^Eۛ?i^s5Q췿""ygOK7vqhmc-Xs.s2yJ]1a~:~!j!GwyVZm5c9>5I2~Ύ}ckW8 {OiYS#O{q͝g+Rf7r&݌sS~f6;g3+ί_5SƗw;^S߭y{GO2zޓb冀ЫE]-֧< 9=ZƹN;ᒛ{lֽsG9p3ʙ9Z-?Xp %k|o5{d}~;nK}KjYg~KS}|{N/}ގga<ߪBޮ6ukglvs>漳g5 v uj|;Suϸw]=nZ^v-I0iNGO}L8z4ޯ\5{5(~ξމ-XRJjv{,tOY8;ŝo}PS/sbu&\u|<:¹)GW?Ir_WYzyws_;ej;Ϙ ̓-*gsL0g_﫡1_M {ߒYmi p6{ߒlO:kxN2f093<z6u[*~Mڃs֭yف^fuj>Й:?cF4Yz4t4qG3%?7OO{wϟܲƖ!bE-bSў-џz s[ǫpŚGl눛ղ;59u:Yu=E·#|GҊ冀ҫvjubiLv4gyX>|kjoUS2zy~w{}Os&havs֞L|~󊳝ۄÖ_}yB[$gc ڷ~ s5~ {OuЮ8 K 2>nG -gx9hwy.Oz)sz֎$}O3s;<+yl"خ4ѩ n%8{,wq*[yg^-wBrxٙ[g-ml}&_e~ۅ.]=`k^m/?ЩI^34OC7MgVfw^%`nl21J1<o 'ToTşmkBTaxݚgE? LfbĀ920a!G*1g J42{owuwuՉU՗>>؄)HùE%bVs//9ΟNn*#?ΟA.lߣyĎ=\ۃS,̄ &dυm $*3WYCYױbXzd!ޅ04c^iVSECra(N#E\4i'ǣO(\h [85rY#^RXNdy>uР!)o.^d3)'(W oCg]^P~fh%E 0kN7-mx3uyP4_q+ƣL 3ќťNC2qE Gh. a1OspImE9Ukg:T <3pOdŒ@^H¿'1!ca;M1 tmg_4KҒʿ{'>Jtwge˚R%q< u(+W&^6!ciNwt'tf<%,08w騳=%C50ӿ/_!@:KVT_!wIǨ>.9\a;$ұ!{y 7;Vtܴj\*++DNЄc ]j;/2UAD̻:4T_ӚG"FX|V6+ ՟MBV1kę!V]#g_gVYZo`ˑI)b- ?DW[gQsfUN;z9 3j;udFM2 "Cܽrxk,?&]^: pI؝T2ih\I[w15pOo:Imo]X}A_90"PLs)|c`A6t/g{܉l\3d'o]s(x%!3awsYXw.ضUvf1D87H{ܨ7H,6oK׵<$TY.%G0)%G '$o _t=uOy ȿbnm20L/_Hڟ`p\9kg1_ֵٵsfGQϟpΝ/! tmz7s݁m5 2ƃP;*澅ܣ73Mu_\J~5Qh:Wڳ']7F`7(7M[<9% ϯ'fGm6\7/D{<%;Y_oJΌmc]3l#V-ܳ :'!7m\g۾`] hP-[N'. oTIpgz)7=&gN!~5Q? tKRW|/o D%M*4cRߏGzlp lsPz8?[r0Ofg,j]oE}\D q?sSY5#瑡6̕m~:VPb{jXQoJ}C )BBnTߛt=)ݷ>Yq7~5Q?RoA*?o}*'HT^zcJH{{xj>"GO_o&y\N0-CµݵBv+ҫV.; Ϥ9*n%m_O;ܮЮ4h; bnT5z${mO7NNEQ4ѷOgTtcxԉ/)wJN}5g S"v_g"йSv!fϸ]K6`pmys`.fV܎ #bvmf}9M[^7*ץ_"N6, 'B;]Y o ?ۮ4*ץ_¹ DzqR}ѴkoH/'p)?ɽI%*W$$Ek\є;?MJu:α{_kW}hݯ8}BQ1o |@/Iò̏ZJz$w{='sF; SpCQy_Ұlο֤5ׇX^ A)nNЍ= 15W$Du _RȓƨQ>S$O֠n֙PwqEܰ)̻;g7}IIf}i D^ {!>G3=>7żGjR ʻІܟ=~]J(աH$nob\}9ߛ͜xp<9ndvmt)٣@rN]hPΐ4uw"n?"!}}ڛD6ȑvCoÊy.鼑[%Mng }T1yr O{`90CͿY_C/ߧl 5(1=%Z=[ CqgPhNdC#oHD[$} >i|7MhOD 9}/2h!+w b4΋-U>,lsgG9secsgZ6KNuӣdkNԋwloak >5Xm,ȾB3iWdW@SIt/=;3C#Ezsݖr(o/o֜)(?b+9tCӜ^ N/H_/B(TBϕCPJ!pmRnx ʸv'Špg`oh#9Z>q>s?78@"Cp:iԲO-TR NdϿȱGsǁt[$$܏h%?n8/xCn] ܉%' yF"/Ous$č_zDNÉ:#.ƅ%_x]nc ڴ C' 5'wPc^}3/r99l?ay h9j9Dr;g5'T81TemApR wS'qz?RwE{8N"f+_kS%N9{;zipNyHē7$/Mѽ }#V@J'`oBۚ}y|X‘ WGșpC7 yU :]ʑ֏-VX`so3^g;{nH1 pG|:1tEϜ,;=ݘPg}[}ܕ;ᨙO1(7bޏAތ{!K՝@醁5? )'~# W/;boɁYS q$'Qnp~PnԕN-9yg OuVT~0=O8 3Vzu$ߓ#vON7`n{!`'H30yݯ_JPYqDRufxЭHD: q#;CEBҕ 77_O{$x{N7wȓu=@R uxDžxmMu_;dW|8u7Ŝ(nƗ+'ga@f/ wC!;? 7{~ o]/x'Ro3g ֏}ꝑ M?n}8{ӿK&G;tNz+sBӒgoX^l̙f ESHGѡ$oL5kmrW@M'ܞZ, 1=lmkBTd@x]iUtLH jJ$25 6 SCCyý[]KX׽oծ]u=}KO܌?qďx7xK3Aa}~|P\77Տs3֍Uyvkw=QA]I7݌OS:`с Wj*Bgg ? 6g;竨 @DWA\L.h.@L3<0s|.傛A h.$7bE%UMB Ȭ .' R]"pAA׃&(z1ÃO|U\`U/4>$H|0ԅ<ۉs>\P:/~Aobumm/ua 3?O5c^"ՊD'K5b5N :.և&? Sq99&8jCj]b R/_ jC?\[XNL:(2u ޺AăҺ GJ^ U!\@\м '$+*Hօ|=t\@"S#`h.'D^"V & O_Q/kP+\h@#6teH!#Jz?L? uыy8[猪qOr T<j/^" R!3K8 -/E//_G&<@t傡 \~AMr g<=sv^{]UqX̓W@t |'/ޞ2z0!0Y.= j%&H YC?Ն#L868DϨ,l07̋,_ ` : <8 Dȫ ~z〫/nqzT_ o˃]pms@ &}!G$)PH" iMSH}&coO`0pwxh?F/_cDσIskЅ_X/0R}y1/`c=ذ _ \s$/,6$qуh^ {Ġ/8ھ*4 GXLZq@Q?(dNjB4]h&H@}A|/?¿Ɂ~ yh &uaZ`iB/gTP/^W\>`9 .zM#0HG0B}At_#8](,5+_1{e/}-}v+3a,@߫y'y5aض?$d]' ?`??H7lggjx#Σ4r O{1ymOO[/Ϭ{^ Iw_;TI/˷?SzPYc&~mǾ8;מW=![o}V)׃;7oZ)R{w=vgdΧy}LދB_~xѺ{~jΏxe0?z!'̷/x-y9;fױ ~h/bey4^G|G!/'} \!_xjrqc֫{^Y߼z}Lϟ]si+쇹!G|/霷o9ozúǽ;1w'o?|~=Z4q}}6}Hi8Ɠ^)W**A yo!7=OP|ܾ]bO2\mkBTfxUE"0?keYEY@aQA, A$) &5=é}ι<}?J4[ωLDN # M8UD-ZZײZǕZk=HVz5ѪV \]{h hM]k%zQz)eALOnz)Mumhӯm|MjаDz3v O^WG爎ZoL7kM+Ө-4zO1{Z_Jp#RG&EԹ+AGչ W,R@^b.?^!Mf}~ba~/^d'.nƿ݈}=e`CŽ?}~žwK97h= }}`gGO+^!;3NO' Jo/w6v-oݒ?;T_|\gL0+{+u_{WawyL'fa'n>>n}XQkK{Pq݇5>=do7cqDZ Uua`QwLw{1?:yEvl1Kcp޷~}oɓ=?IODKDD/hƾY8bc0[~ެ8S^{Wc|X`5=L's5y&Z4pGv3w0`z={ugΓ,ic3yz}ľ<#<2#9 zyc>/}]Tٷ3>\}Vf^>/χ^>W>O{O~fH3=3!{z6{]z? f!^sT{SO5"^W'za^!OKz^oA综~({.S}~G}~f/}hgwN>&'؟JEz}vKy \=~h`?5<^}ydI;ɟyTS_{WcOI)Nךza{qˁ:>; wy-ӊ"13ٞ{=G?vz${36Pis]>Yqs. &>[w\!No-3!]>;NǼ!w:${<=jCx-nMSxsϷv&xw;y*9Lߊy̾]fO|k\qɰλݍnF `o}ٿq? ?ΰ?Io/EOOq`//M<y73%z&:v:.wϫnsAq Τ5}w{}yYu[ne}>5喺$c>}xnz)M1^{B|gE2W~ g{;݌,^3^]ZzLp!&([ x=53=^(^-LzRxGnQ^ct;9=5cflw\3=34na7yt]^U>_=))M3kS{íb㖛g w%]x溊e^`x=7;\79m~;zNx=uñ y=~ ~^91#vy1Ç>_o(7b/k }Lp7eF<ޯ3^~=ݡ>F=?y;0<ϺxCz# }V'{ta?|uqqoq_meO|kVpػe~u5;`3=nbaO9T?1A'nU>{bؓ3Z->ÇSZpozb fs:ԍ>~# MRY={͞ Lǽosޱ2G߭eaİW~^ =>ᷘ\G};3ؿżۋfz*׿M3=~}:#z]j%R}ž[y%<ǽo1;^MO=;w,g3o1e;\?Y:?<.+[ks|kQ];\"{<7p>xiw;}nҜfooO`ex>ܽιÑuȰ, ]C>zy^>o;`z">F])T PAz?7S-^ֿM>F3> yW,'Xֆ>o}^Eo|g:T]{ۀy^sw,E+nB-+o3wո{R;>&>?aq~CtEyEcM4Dž062^q5XѿNG{|>O1y*1s=]"s>r؟^зyTW{[Pu }[|{ d~^qa'kzsk=ϣZ'=ejσ{[W{[_>H$C $Nvz N~' k}`Sw&+1oF>o%(\[op\7Zy6Z?m}Ȱfyeo<7%#Řð:; _=Z/TTro4wUodxQ1?)rg;SH|'1A>N Nu{yk}4?e㾞ܯxay2'^TEwоmmǣ/&d7A &8fy%y}=毸Sl; 3W%k|// |m:}O|'9y_OUɝm{[la_O{T"'ߑ.,*ǣ_TtB+{HۻU)swnwxϵ]-û<վ1ߙsAMsCmr7 <~~Jѽ٪h7[R7PE{Y_! 7\gחtrߒ/9Swzk٨ykwCsr[΢j=u3fy OrO+.W a5I?ϝ=k8ޕ\/r[wvٻ 9s<٨yYfoyσyrGg?U0ˣ#oż&wgi[wV^{] ~,/利,7cWm|~m{s!^gۀ:_rrWM}ZJ3{ | MЛa芹dzxf`gOe=[(/ϏAy~'}nn/[4{f/'jr"u2g-=-硿[ͨ^K~nvJ,0zz!2ۤ29_Z}<%[w~#osp鿳r>*C_Om<1ul*Nh?OPuS灯tיg%|~6on˾(sgg|={U~ޯb^O/k~5vx_k?j,zY&mkBTixWՆ11,:JHWt`ػDS4jL1[wYyf{\{x_3Υ_{swd@;y.˻.︼Oc};]?+29%97%s_|U8矿Q>gc\0{|XzwwglwfcϜNճA{߅*wSExovo;*#gYY;Zw9{ /5oOsaֶ "O:YWaMkw&85|5yE~3ߏ|Gg#wR5Ӄj_ux2?R\3c{'\%?wi?.0=βUO:̚YEzw2޲f) Z\ y{on,6%8!VmW_Ckn~_< ]jv{Ouj3fn9킬 xt!-toi+<'m_Y.os~3\w6`{Ws[ٽ2ޫf W2X[pF}kY^/S*o+摻}C{tpmll]~p?Gjǜk~s;|1r.:/9%߯޻[/fy_55f~#iݫx/88^ sff_ή~m/n:9k˙s&ϒ4` |ebwp7ZQ&v6 z9Eޭ)=^ai}Z]lIW{\}Ms54׹9TM?; s/Ś9p3+_NݬRd[}w6_CgmWcW }^]ΑϚr~!\'q^\?9}]gz1ۺ[ok|Z3ǭ>dvY]T|!UL48/5Iڷ oklm|9k8k[r^WϹQuw̵sR>#. Vކ=;!*154t5tCфg7˄ ]|=yʥzo6ϛ0xFhJV6-:%օ)9z5dCƾv;-qv7涬qf~9O\۸sk5dO5O׃Rǝv6ۂMs:5p6P^-p5&C?z4d-mqNxyr~SXɀE<y=^m]зDsL'ٳh{6i]3fvyfVc ޽q7d=y[:Oېr4KsHt͟aହj뻥m5W;z9k/voS{؞8qdM~}=o bWU/̞Oys8wsR>&}x۶ކgҷ1ԙ7uii?Fvմ9S!d?78GJr?/<(gyzݻqڠokmsPsIp{~ǮVZsu5/TW"!#>w9iIU_k:۸M>8|}Vy8JopšK yoַַm kvƾ\maijhOŬo Le#+U8r)/86Ϸ }l|ij2^Ξ&O暟= nYrK\ýwtM9r/.qNۋZ} ,_ocys:ҫ=*۾g픛'ӸWAG{$ݑ- h?JŚ]d|}Ksgt1{=6o[S&݋lcUܙ"?;IpMoaοu}?o_f}_LVcu7t6s}MvdvnnW:]50FYn+޵go閷)7T8Zigf|{;,sc禞7aعioKzp~75Հ}Щ o5.t[~:YgĬĽ`|{}XrʢEo^ <@'kuð}rfyۭm:_y|8-Arv57#O;Qx0sgKδ5]Dޏe2/< oκǨ@scw[K~x㴯76f9jLXG.g>oNpAΎo#sK"[xM9nYyJ gR=گfw۹j?B=Jnwhj[osSLkyJYIMK w-: lAGX+̽I~v_f yzfs]`ͱfB -50,:ol2sQZ1,ң-ֵpm1c!g\:?8~m8eOa^2~D6\sRݷy6sץWSXG~n915?kyYa,(b:k2=L̺fչpߒ_&f ?Mۜp9yOYyvz8>?T'>;}R7\ڝp'BwΆgx&zo5[zOOCWn09!`mf.}.;{ګv|ypoW]`rxֵަΦgYȾ|ޡڭ@iI?6k7gޅs?{g3\ft=?a2Okx M.gƹW֑9Av@M< g=c.;jz.vv6_땯lkrn3{1,%;(Uj{}7oMt->15ଢ଼}fxi*2LϳAumoK}'߽6>#mlܫ!4!k= .Zp!lUqiA'wܖq粟շ=kkm~|&s=]\Vyp,9۫.̷b\ R&@| p;)[KIpo_gЫ뼩uatjХqqV= <\32s9o5|foN݃R(1y޴iڱy35]Ykztoү{Szooй6.zyxSw3gyC?m/f_ki8+k9+ٌLs{ ᎓ǥ:.0Jsһ4w3<M[ߚ#ޚa.0l{{6eMo s-.zfy)Aʿ,Rf z822{}^ܥk9zַy:ÕTchu3ǃ<=gxys%#s]s9p֝'&r7k[3|yzvVX:Vxg<%7qS:swVw&zԯ6tZoFoNmϔ5X52H:IḏvWߪ.p[pDZ'[Kg4#w-mZ,d:p M?C7rf;YϏY[|}"?vwӔ~Fuo}\)ႳSp{[C\:nmoזTs.ý;l̊3lOl5߮zk5a(gg'sk]m؊;7o[x[УW|m~|rc sJ?׺O95gv}g<.Etjc>Mgf]Ǜ9[5!"C$S}Z\u^׿jw:vvv1z7󶔟[Z51g ? 25U|/'gl o+OQo9b7(:-{f+kMf32-`].LMr"ϥzI$M>^qq;qr~Hpù{l|mn 9ر>mvlfp5ai]"̧=Ր$?}>Ըug{c[FsNYk{y\>|.hnqZ7ӻ1Mcphn:ϑëLIs5r6\gZ:_I|ܲN|Tq9{Jwzp2ِitGs \u~D3=xd˸5x?wJ/'>r*}sp9ּY6ָcj7wtZJ& .1;5f4s*W"0w ].c^\tG*uWF]6p&2t~rp .Wxc=q7*t: 6ل?xmDpO׉3"zLӯo`|KSm7{0\z|wkvڗY놫ssˍ{(XۣZFM\ݙnoyhz+^ܘჺml欰pC/8# K#_-:zXl-yk̛]^jm}-ͺ g{#q r7g<:c`lsp7Ugm]fM/`~-z1o w^{[9`3k;p2< 4mϸ|l#;gaٻχ s[ឤvop:9sxc Usr9Y}wuvJ{sohۺBg3|t gˀsG_>"ؿX_~ eC[|Oݝjޫ}Ydynu0.s\s6ь|H1(CܟY}>u>7ZsagWݭKۚ2+5/rk]ykijsppgO[bͼuYqngCjrq$:`_~8H\n:9Ӯiu~ s] ]ԷeKnp9&kJ#dO}3\[?|Z0ې2"?3=1WAO'^U ;:+ՁFEֽsr:|N8篚D.G3}0gwmX`ߠj9k幫60Zeڼ&WYȷZev$Ue~*ŊY{}^ݞsT/-S%XD˕igֹ|;%}Q{8:>f9-ofqkv3: dG^gq~$SgJN],\_ygpFfxZ[[cjgPZ5gW^C>н&sxyr?2Zoy[>/!9_29~ `{`KA3~8EŮ.*vWN7:^Uw@< Z]0w:z:?s~>\5xFpr.)W$:&35w MۍngIȘx`<Y;mkBTtEx]i{UU!AmmU29H!Hd؈@rWߝ{ZUj'`}>U[UkWU]hawg`O_:2ӠK k[}7 z2<_ 2دŁ" U׽`8#5쫮]^ƫcuMh`=W׿xs [hl;p~;`~Gy ZD8^zdunꆶoiIvg: :6*nS PݸnucR5v_c ^_qO+GG5Wd+m=?E7o$5s ;{?ۖj0gj b478sߙ?}_}/kiފ}?{6)ww;?>݊};?};}b_O>ri_rk~ڮl?Fr<}yqgqsup_еթ.6NcykX?(r4ӚOcZnXd ROOb@cwZqDZb׍ Hc'A +c?iDZ?h~|4Oy }7wU9T.ybKKOb~Os>Swz;e[ܓ9ߩoeu4}2y?sLOb/:s++? ?,_La'hӼOsi"~y=#vͧz0C5?z?}^yv^$cG>Nؽ>{(<]ߓ{:/nK{ 8/ ^ICO z{}>_G}>8)8Yirb />^h~_ |Zc}>&;q/zmK^Xo⾥=7|xX{y{{=8ok[dp}?z}%࿱9O+ xϷ>;߉y.>q*45 DQY>N^y%g=C>{np^w֯e}!x|1}7ݙ.絤o_U|\|ihڼ>/Dsyg5qg؏y~D'Co׊y^ZY_n?%zL>? ;B>k>-]k}fj};=ֲcvtT,[4}_Ѽi}ng5G:0|pI9 ܯܷ /.{jAy޾3?P?o owf:y?Q&L/iW k~?{U!OQs3= #:6r?/p/r_9~ 1wEmC^zWl;Z{GS7.hI)#9E|Iۋz]39 C >0}P-'{X7"hbB*|%z]\h<޳}UA]wfÂίso=qZH:=~7r7[{o6I^mܳ:V/>7ϧ :~Xf n=5{{E =) oĽP[#Onu^&޺τ|?*n:˙t3\g]}>opot&ܟugxtrn\x(C75 22gf/;!?{\aÚN1qڈ{ݽp'Yc7:P+jwUvwǽu2kk}{[}-a~=H㼏hϥ{Ozg3;{io]]%_<=u;={O>wxsC/ .Os*=h|M}w}-]˜g*ޅsj%Oy?xG z?C HM~!8?_zh7oyznoyv}cqs}6ñow8|o<Ӌ|Aӽ<{~Cg9yYBO6a':Os-f?0.NZ1k Σ-ܷW{k#Y~rty[y+ <λ*<׉΃|tއ68kCk]_|?.yy^\?8bFnCccQsۧymc8n#wkk|¿a50 3xo;iSo1U>]eۚk|̢t^'鼌O ǽJw8s-L[=mwrsܖ}kps[*?t̾ dyYq?V~SQG376o )/fרּo.𿔽.5ų;Vs?W{Ms|ts]~^DPT>wnzB79qx 4ÑD9;:{VߓZ])nhogxax0p< m m~nn\>P?x,rf3ޤ598yxsn?SMo;eqwSenpw8}OxGyez\_Oqvל?п Ƥj<g7jp^zW%o%'{~+Ys;z|s{g5b}z3 h޶i^!Swk{Y1ہ3zֶ 떐ƣwމq.y;C~~9CL?m]_UN%3Gm^?jzy78 wx9s_yWs5㿟LOxxV 5\~s8'0mvM|wo71O+Gx4C!|>#Ƿ ëG#l3\:wmquzO/\јWz}ko]A\g_]ufn3zǣri RH;|;}.wԷ]ԱbwR3Z{Pphx/I@1?(y2!oまs}<7浾K= q?]'9Yvy/$w!BEȿ㼜AK?~h ?ϴ=ptNU|6G:h<1q:|Or?yIalm^u|&?u޿nomM_|6ϷmR?0Xyy^xhW^V}y~Aƿއ<Ż?}4f>ϥxOcr'{̷# zxy< c;PI/Hmh։yQ;9 k7mo^x?Gc7LG1oxr|.зkC޻J}3GpfNTIܵ,}Gol|Vۓ;cUkBf157 ~=Ϸs>ΫWkޟ; nWr<0rc}WKU1;x^~nQ!Q+W8]Ļ݃M?g{"_u;ϙ[[v9z>Ʒ^nLh:d=m?Q_H>n|@/Hۡ,?ƃ<qJgg: *~6(v9os<ş:'{oC8v}H v!5(Nxjzi>o[n|ƽSj%J(QD%J(QD%J(QD%J(QD%J(QD%J(QD%J(QD%J(Qt33mkBTz\xy{8!'M:GNHnaٝ=m[*UJ0xnBmWz%`7/ӯV_N>[<8&}v0qT `_xJo^[^^XdS`l3KЅ9l0Qt0Zitlv:^OVH{VeREv-AIB椯sC1;܉9~!0 9p_sO+yX?pl֍d܃s?em߆mmq#eNՎ{n^kCg!K|*7w(^& Z+w={gkx8Kuaj˼.Z[VymX65ڔ1o s߿?k}2w^v_3v?dmTT_\ډok+ևu3V+m;'ĻbHvN6=Olg+Uh5RȜ$zmF7ҝs^0nm S^6r|EogYs%|o!g9suk߹<_===4S(|6G3VF_TM?K/yeƍѥjtF\s\"빐kt }2?Ӱ=~׬㰑_\]1Di8 c@f?_؟K}?*b/u񤱻m~{}N*-{)Y-7o))X1=qnʜ^vFP19vaN4=X~ye,>ͻ1'~n1yUG@B? c ͟yO9y8C>"<;9bߎ|G Y_>qt=Y$?}!~J?5T ?{~|ިJg1{ sy>N9?CQwmzԫfP?_:wW˜ΟJr"ʸ]g =󣬭,_1cy<<>;l}\\ j3T8/=bm1t[cxU9"{zVo+{X73Z޳~A L ~8xyec>`.'.+ʸ.=3_K#G8'3T4o'/KsKgEssr2nQ<sB/>3jkL+3q\?w/$*5m+6G\?^ny[m?)XM7g/=-ָ/8t?|~[nN)ۜk{ۺ1aI,#g[] ;{\n7·#.߅yڎ%S{31,=k;򞷼9/ }?m,T{C}n>9m%=<ܣy%\joshu?;*Do~j жͳ&n=p?'B#ü&(ҳ=K#2ib= y;j9[/jMFYOz_wY|8Rkrܗ`-\ÉnM)w k%7}" 9JAE;suv}5_[w]WQ]GS[SQd|! (\_/+^Z='6rϿWke) O@zS[WŠڌZJ;nkVnMYvM>]-?s9*6wtVP-Y6ks7m2cHk_Ix:lHk6Y'_\_ EkrIםu9@q01%X!Q*[3g㮞x?e^_Wq񿀿 t?z8b!|<}&,.f kiָ5/?-~hN yb.r3$$1XKuۉƿ9:t (;;Yj-ບ+2:;ra?6yɣOQ}cw=+;S_=5͵4]M ~5u175ǃR:S"/iJb-0_;Z#_~Y%1 JジSjĞ+bE1&qjk&Qbx 9+/;;N0SgEX]h\dT] ż5F5՞YV:b{ͯ^](("ք+G3&|a[)>g36M>h/la^Eގcr5?p !9/sFw\+O+3~y 0Au6G:^J8hT-A 7A.>aOh?FDI99Ks#UBFIXK9X5^g<knI~pS/,L_A3b"9&/Kq0wgaƻI%c@Nt>$&HJ :*-oY9x/Z_u> t9Ot )]σAgr cΥWcZc>>Ȯ^#K<cki)5S4pbR,lLF&>R%Y3c=X{}pV7Q~ΡJ>AbEu)no"衑K+v=);==Ʋ{Y^:֍Qfٞߕ~Q>ᵑ + w$W'f3exM/]W{\lw}+և6~_>[7<25#uoiߙ8F qţO;=_GϿ֭~}no McaY߿U8UM݌utbt\QYxx(Ҹ(qsg~Gkg׵gm꬝~LXo)9?.=eM %|kv7S}gv> bX[G5Ogz 1!s^)E GoQ=M.SM3^$]G~/=q\'[hEI' xL(u"Q/b_Y+B%WͨTڕQZ>?⹜>ų> OR1DlKiw2VX✨XO8sb-1׉HmR1R+2NJ<mS%?3JF֠Vb?${vmK⦁wp2߀>tDt^cΟ}}=Ag_-ghMoFg_?THQVnb_C._zWkJz8/n9w+f}1|77b+TiOIR$1wXǹ7MD>'׍~Z1A) !Fqr)Rv^rmNLVg)gΚuYoq|Iw*eFZ-*2.3c3#w,'_H1H2%qS"SDž?j^ǯV#g˿=BANY_׬O^a^XccK!y93Y4E-ZDkMǥMa:?|^-3=͵}dFbf-o7ٴ G$C-EYC{Y[lfB޲z *i!,w33Q~)b|TsܙD'U2t[eq\{*ކ;$:J߇f@t%p;8~i2q\\޲C3I[\0'd1^3b^g-){mQor= Y,:ON(2B6WlU{_g<~K r?t}ߙ_+iN.3`KDwz,5,nsf=\LwCsTsO]렂nrK\\5S /t7SJ\=qd'kI.=]7On2񴾥*uYd dce5"ʨR5127 PeeK E2)0GK [.3LN3UF}plCSe$k4%}b.27R!s[G&b|O+捾InSt+ ҂չUH2qqYS4%bqJs!`unj{ BGNem9;'`Ԯ\WQIfyah* (h!w*jSK]yQ=p:!ɺY@Z|;VX ʀs5qɹer0[O*n΄5..*] /!ɚd~/&rGTs63_׍ȷ!UrewV!v}Nf9X/@?d/B>/67GiA{}2pgAOB753Mеk9b$xRM{."MD3ɏe$d,Wk]FB :D9 JxIt3^'XӢCu)&Re`!BiQi6䐳qy#2K{]k jCM3}5?Us#;!Be4Ky !J$H23wW>\Z+Ql3&A{zOrue6sp΅OA'm:Gr.|HfYG(LYRߪw#: vT[_q~Usbo'aEw٢.s/o9q,gBσ庇L/n9f߳+'p N,9REX$W<'xZ.:Q;T3sޟǹ+rq .We< "Lȡ^!dX`QeOjtʩ֎m >f-Y'GSj]v7}9՞*Tj-m5;3m7 :Hװj`"1=;]v}uu'K̺ļ]cDzU!֭bF]\ݚfhӑDw;#K$βt'uGKI7Tu .~Ye^ Fj=U@FkKbgzYsC{6\\ɮ7YY^ |nğna w.ҹ\w/gb',SpC/O'1`sr9D>c2x5 ^`\̹ d-}z0t1s*N;biT% 23#q{@Ygʧ s.IgG_}"P ;?Nl|>dϝc"@ϼȠ7?˗yݟx1ݦ^.z4 a;&R 33_:W)aCl.g;g;PGV+t.slak.3B_c9{{M5u"8Hc U`"MקQğ=R p#tqK58T;9ya{KGa kr]/kDn.}F}DZx0=is2M\E 34M'=>Qswi@T9v D&X>;]2+:r vP'|/&orS[Q{ԽFf߸{7f`Vt[lcw{ ݄EVYȮ9;}:ҴaMae+ng_teox%(+|o͢(k/w ~ANiKܶ+HjmkBTxy{DZW0I81  !$0%qrcUBBHhIhC+Y93?I$TUom}~Ooc~:c?E'Gt/?Ծힳ3ml:gٟf(:1bgc{컧=N9sG'OX;2>ɎME}.6ݱQߥN]IlDK|][RtzlFR\qvcck 'HmxVk;Qt7ӣ{wb;~(:}g[ ?1?#07Fk=[3ǝwIc?cpn_Mg76`>e3{H߈ȸW|rܿ?G؟>_~ڲ~?W/>oLe&?K ? {,fG>{cݰ7I眻eoO S;d?~I"Dާ)>{=eoG}9EIyr7O{^ gdolfo{Reoͷ%6d_]>>`uoJ|a)KBIui1}qZ֎`_>,yyίޘ{:o:Oa#:?V@{Qί#e1:?6ľ:? n{BumEt_#=H_H'KTSZwUj}~cߟ؊^{γ=Y튘{Y1_a[癸Ⱦr?8+Jo򗾟o*A0~o?{3fz9er>=w's~Z 3%{hoO:fyZdsྮWT1${7{ Mۭ=-8h_}Ĺeo'3`oO]Q__SzkJOv֋>RBFmΗhu+81|,H>*;[8ӹ8ynxL畯`/3}݁yސd߫y{Ly#;9e_Wyet~L~;?[>>a{{;fo nL){;Ξzti"֣Ζ#)~83$!o>%Io/1Bx H{iE7,$泾!w+m٫ooKzoK({[vŮnƁ3X'- M~{[]g=/ ]w=Xn3B3rnsg/|ύُv,ߏ+'Am&g\?g̑c ~/:VOckŸcf/D~?_};ǔ~1-m?C>jzzO3==va;yA{zb_seOz;W"#\ӓ}:=}Oo> NgQ}:5z}YÕ:{"1;>T zou}`L]۩_}ʿ{;eOΫR~n;[7Emvd_q̍ϲSmM)ƿLz,ϋyxO/Sv:皻 ӣoȬj}gzz#+>r[)gO{t~;Zϫ٭V꽷|?gokC}>{zy?f~O>3^{+pgVjφf?=(דnzgOۇ輋et^]Qfn;R{?e\*6=Ws{^ݦJ$5~yOIEMEEQo-y|K輐٭Ƅf$rvp'z~x9ٛU/YN;\yy4<0]36yƓ:OwLmYg>|P%F+yw|p{m}ó[ۉmma0?5g6׋y}aˣn˹H=or*ω mnqEn0z|C{{[}ؿswem,},: O&_;s/D/Os#ڎxO>cp9wh/)\GJf%u_Sr_xoO2kl?+'ח~Wb Qz^ r}cِ>>rr=uvyV^Jwqr=eojY>M$c}:˻Bs4SnmCo0Ď)bXr}ymgGX=5tn`v mso0d/'3[ޝ^}iI??ɾOqɽoW1[%%noE['k_+m/fLI/>7x=~4swt!]l;+JgZw͐?Wv^vRyʛP㕻˟-)s;c MA?zKo#{Ur9=Ի< 7o5^HMG5^͝PW>Byϑ3_Vy.z~kXEˣ7'z~?&>ƿ#W俯'=wZ~ΰDޛQCe}GE>ُ/irco=#O>5w4hsBkxsp/Z9yޱ`M/论̮~Εn9~˜~3;P;oQqӻަ]^MnV΍e!{~7wm^ 5/;_}83vs!㼘ڽ,C﨤>o4ﱾ$z.[SHMgީ3!jg~4NɈb~{ qOƳ-g obqͱC~8scxGo?Z1F-gmKQcg߭!ה}g $w{9sݜܹ$^s;5M&ssx`eq?q]-w]fE{ wy5}3;#dEQZkZkZkZkZkZkZkZkZkZkZkZkZkZkZkZk}Z,-M񄋕FmkBTbxTՑ'$:1^gDE@r(A8"EP1&F#dDL3ݽڻVjU'?>ZU_Nt7_ߧckyqſ|O:SC9;9v[? a7W1/|Mp~/WLJ\t<}p<:=_9Ԑ+±5ĮO|;S3r~}Fd\y "fR B&a:A8qo[ϒۯ['ӯN˂Oŏ__ _IFglԫ|ޜW1[ۣ=~'ݞ2Viq:xY!\l>GysPsg(`6|*yǏG(;B\3'y|4~cm>-fN2`ReCyJލG?cH?^1H`s>3Yʬ-Ex:xS88Y6[s5}:{1LO^{s%f%{Ly;1q:up 5]=% |yf-y3>D36ԏcp>#Ͽoyf?tØg>,h;NyЫ%rsrjyPriu(oc|h>59m[<4oss2{ p@YǗռ ^i1w5-\(%{Lf-S=sMخg%-:\oTa"pFccS>}_s⩊n/[[C{W5hKY e&yK=c;59{S o38ΙPzg9LFsU{} fr;٧[ηާO'8j΃Eg/(_QuPsz!uxSg; o֔3 3P3Z3oNG0qH!s_^\K{?{ޞYe/qyԼJFg5xKt./O8V4ylk=ξyz̎ z}z8x,k3r] f6#ߣ<pK~I(uejŎy !_i=Q0f.h/^Ak3xlFg ?'0u.H[0c'd_gx?"=RA~yY؟~N,2ym!.V2gkeҏ#Nhbp.3'b&8@x^ke'KRk(dgOuUO /0N#Qz~zj[ tMڬ-봙˜xȸ_?~8tlp|qWՋV<[ѹBPSP'k$8w#Nq B ZCh-urRm+،~np6yf ۫O=7aMh}V.acmvڈ3ܩOHFp׉?Zge]W}BTvd 9>"'mR k]Z]f]7hǀv=x蹔*59"mzg9M?SKY[m8-Lźr_ttJU#޺g7Ly':_|]G{/ Z񵈧tv/:k!JnH!ԅ/]t >|ޚǯ_wMrT΁~׊`kft-,b#?ɩBY3ۙ 388{JQ爧/u={fzPy}BUhDt֊>ua4LEnɱՔ.qOP#>5ğJ&}\;[j<ck@,Z}yuF7"^GOr|*_?Ͱ?u6j SJ捻/zB|W }Ft2BrvPaBՃ)]_Kw0cmS(Γ\ܗZ{fƿL=ǥۮJ `̐>YՂRGm?ԉX+%a Ky.N-n޶17`>b #L׃/l#H^% [8|5uҹ.DAu[ )]OF%[ROC1|nq`K ?y3yTRfiVRjGf-1{Ԉ'a `۬ØE=ѡ^5b y123!'&'W 1ֈ.z@=ୃs- u yf򣞑2SR+XڼFl  76?yD:써.X]o'6;b̉z&(=_Mr j_(`yOər缇lh!OMd=`թU{<zG_Xr%a |`@vˏ#A pՎ.,` J_y|sZ+in ]H\'\ 6k;Q?0"jOk!Ӽ8)dE[~mZ yFf >f(I3F:Ef>f D)F@Su*$W+Յސl`ɇl@v!kk~88#yu[ C!WL=ׂ8l諁xTvu˼!k*"t>:QfZϧ3_f?C&f g0o^Vy (s1'j@(ߊ,``Ks2+`  @|j@\#JY]9"Z@ˈ,d/:r5V0$+* >*vĽZM|K"IlP#stq |w [N^bߩWWa?ϼ4Skuzõl/31ǁ~)ѽi}&H烞| fC.Pk`PRwC{{= Xm _QU ᷍?낆Ĝ[Au{In^h!()ߋ߉x D- O}ujP I>3 @ PP+wA滻s "Sւg"> @y:wk&@{͙ |"\@K50GK.$C 0@gHjY??; ?[dnLHB {%2P^;6f4mkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxDZolBB Ђоh@@llbb}cpľ87gyHf>OLUVuGnǤ8&Ω8E/'U|x|㢊*N@ bK:8sv ځ,F8;c 粘4;cv/"阯`bAEK*X5e1Zk⊊5Zӥ,6걩b8->8U>b6B~[8}o7;97`F.?,N>BG7M8A; @')5`D=1~Oڏ#&!X+Ϲ%{*xg;F"|#N-Ձ b@4w_&U?t*!:H6FhhBQ2ZKD'ia?s'8.oDMF( =>Zwm'kk?ǡ"|UVAW7ps[=`/Sd^ߗzpCWۊ'I=8Ejoq!Loz >'S_(~=3KK~[B+"] ߃WJ)~S(*_#)"-@ЇT}xlL~[)=i4]~#!3u=pB,BL'P{oE6 WT4 #C w˿ԂP-@33 OQtCjy~a ̏=߭M#7@~ g<(h@?ޏy>}0eﯹZ^-oAQ?q#/Y἟sހ?=۶g68 g|9^۞}>}jŒx4#VooGK>_O0>ş;o^Zc^>|M#,w>T4fiߏx=~O+3Gw?zp4i_^v=>oz [ ~wh>o y=_ffa~@Kgj5+<wkXQu!`ϜZscrK}^`/hg?׵I?Y֛^PsKrO7t^n_JAyǞ# <<_ /JgFyY~T#[}3bxfחYa>y˱Y~CnB1'Bu{%+Go1cOxw]s]Yy}jOg|-Y~~ݜyS& "z>TVrҙބ>Ki?й{Z*f=3^a yҎ~)?sE-3j{mڳ;5]Vs,T?;IlVw wu]']o u>g9<ǜ霹 s.zN5ފ{x8MםMϸ)ur-d8gA݉E59yij=Dn\s{ ϫyհs)v3Nh^ zo^is Ɨ1ߎ}s>7w>pi~=Cg5e{=&p\ȷa[Kzo/F۵}W{k- &=8^]˳as^2>ve;χO $!1ߑ}cЫ3yޫ,N);` gƳxgwu{gPۍ0vvwɺ`y`7+}/>m|7ޫ{#>Yxy|X07g6stqLSMgu]=a6{0Y8"vo˱ǘ3}) ϼZRf濥szwS|78ϗ%63=h5^Q?U|vFohP'yM{;rg>oCsc EIs>vFynuz+=h#;pcMw;{ꀣ e\}%uwg}Pe>R6Vug:?}ZrV]:S=gُj?Rh봮t{i<!ŷG] .FK/cgUXse?Q uo;PcgRC.ٕZO.E|/>$9o{+Gy#}/o$$޵Hx?Km|V85s]oYzNttfxob3*Bo$vm5r+3t:oss9ط,?׼gބscw>9>RsӾgYa~ϣgu }(dpٽe}0G}7Kܧ~]Q[uh-K/_K݉{ r;m8Yi*hu8`|zn2zٽ#Gٽ8aO3Ts'KYῒxAdse|)[Z/JzwTy}j;SUoY<Kot٫NeoHWYwBYYsN/<+/k/ݞ9s)6]]Fs>A}/g4=ncΫ:O_]*`=ܓAo0}8[zn9 j:]M7nSw>yֳ͘o@Ϗq~0AMCs]H9S덮C>zwy:T{K}pǕ/qcOaƓ3d-!qnrs+n[t>_w5ulk^%nǽC;nS3Nuy.y'os7_Z;;뻗j?ϧ~鸲Z{X#y#'oy=ZSz,P둹}Wr?w@˱#caطv.޼44]pג͕'?u_r7 zkf.vɋv(%y̛j:{Iώ!M/=\f 5\'5]kErr?~މ8o5w%W:?%yN}Z:!w+¡^=/kOgus粊uA7.gN* ͟j`}}Ȕ^?_u>gpu3ֳ9yFּާWs_gߌv vɟ)<T߼v8߆zuv"%{en.<6e-縤㤷Nb><߃:IfQ>6yQ۽rpFa笃p:#yssoWsc#>Gq_ʫ9O<:R㭮{t]+ՂC]gu_:ancMZ?ꚯF[)o|z濳zN5L,s0vk<^(]^}FסC]g+WY=sLi 'IuN1}tu}th;R睹ֳ=swklܷި6Õd3¬y>idިc<?%2g{|9=rG{[ߖ`Myq5KRSIrl/3e4]ğuWA=/rFmײmfo^9^qg4̓~#azʛ@x5ԯzn5 =gPƫ9,י\g ]g4 `ّwv봶Wos.|G4;ٕ9] g}9n[@>Iةcܣ£ucڧ{REtKcieHw4+iQ؀ߌoZoo#{>o_'ԞSX?9ԡ\X6p3~3R)w]mԹv8=@O[% z.Sl4]o[$ׯ"󷚹澍Y%̓WQÉSݫZ.a;%{1_^ޫL&hMep_jm7{>vpƆͼab—guӭ?{TXdrfz sn$nwGnMu'qVǑsa> q:uy-Q9vv)_9oحk1s8geh:jkþMսZu3gk*|M:AdV%h8gORؑr$wGbV:'9{G*X=s>ݗtvg4]IƩDaW GZ/4{~^K{j?נKf)sџܽBMg'i&!MԹfuFp)\qgVJ}s̾JjhOsg6~־ǥZf4x=գzY{ fJ=8-[?ȸqg?ǣC9.{@]ּӚuAvÜt ?UB槄7nf-Y:q%_9͒ o/AQ-EkҙsfN|?{C[zyr ߀|!u&-^IE?cX/jL]70߼s@?Bp:N؝ xћ:Fge>[B'SnIncO ؼ5ş]sp"{"4<~jUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQEUTQo(vymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)PM-S%#O -סVgrqagvx<ڍQnd N~;0l[8x3g4OQq8ݗ6n$x("}S ; Z#[98-`$3,9pA`}o8xosz 32O{`mã{wr/pU 0: z?O޲_u^LR/+U x@#w!U!۽ǘ hП]k:ʵ'@x>pBt kc@_G oec(P5vnY' q ?H ܃I]dd,GnDr1|8 |rB'"%Mۧ.ĥ%՜6tkW.( Qo(! d?i~`{u`ۆ;ȘN9p}s?zzS_K-JUz.f!/*Im(̉>434fI1&i~0 T~ўneC~@,‘@Im3cXOf'cY SfQ+c`Ip1_Kf^|Fv9ϛOʎO/-LcJZps,ӲH<rT!bNgVw=niaXH ).Vu~Ɨ>d2j?=EGEqH.TM*:0忁#D/{?? t'LRz5L=PTB|!C'gA^pbJ3`׊c}U˿~p }юq#/27/8䲝#LXp#e2o?> Qe DaͰ\Vٛݟ5rAi%0,vy {\PC]hV?_qyQ 8@UdvGM!D{[C63" Y!Ǣ߱Yr_t|Kkٔ cKGV/Y,E^n]_JEԒh- (At?T'<ʤ R/%UT/)P dIlRR/:kLcz]CG.Վ}r}T妧9i~ u?Xh(}5 ;-],JմEG寳lOկWYܽ$XjAZDFB-^Ko6p~+6@6zupA_$5t}S??~A)gҍc aʯ w"חݢY%#_4wP>2.8V2=`?6x+(_^E].]ï׿^ 0ulp\g-pWFS>5"uO*}=A8E_Wf0r}k=\V{=ob/ށ]7P[+x?jKJ{ag[a{MZ8iO!`:8C NBuJv>7BCg 6}~F?Bk #ܸ.t'ƻ<Ux?czST^Ө BO8‹gP;_s {\) J4eG?G&ճP+1w ?hrX6ͅ-dϐۣz\e5]_'S@:@K9Yha |״YedQT (Y6QSڭG1O&B^sSfݙvzxRSKDb'P?eѪӉ'\^ /=N j^Yp羘w2lOL!zTaa'BC8p u#^eosؐr1Dt*U'ϣS?o~#cJyeVe#ڧǜh-B84T:IR(7{M/=qO*PaGƝ+SW`k.ɡ 5g?ݬȧ[0}F[O`:_PiTG])wE >dm)b56ɗg5 b3v23`}:N,_>QCR/Ƶ{<ǚI>[lɚv'/p8@b+%< dx3~C~:"]/g2ݗ@Ԗ}[X:ސKu2ySȕ{NXwOa' _Qp؄!t6h)sOޟ0R=SF}U0SetuXFUvyI[*-9w.Ll)\|i1#$}q垇y(E5"|;RN+C m5:WmkBT%xyWW?C@:%15_''("4*(խ[뽷[s?`x󩧞z?upOKZ kW[Wwv57n'ɚOVXԍºNמkD]띺B&5Cԥn.5uh\1l XTX`h* '$ {DĥCZJ T%5E (>LC6,#>62j..f&,;54΋ڀy|׺vXuq.n pq;GV@?'1D7򡗼Oha@X+s,@x{ǻw$7ޱ[s@jW( 9>xH>2{dċйs<`F!̩lXx Sϫ7?q{'xD&=C)C+P/Rt#dca$SCۄCfU'7 :qQ/8L_#=F<;v;r3R}N=ec6MaM)6z0׸7+&%{ERƐ{?_Ů̿gǢV ~F i4I!٩ .3t~,LP^:3(H iCi]0g!g◷7zJdGn"S>4y‰-{]auq!c.7~.Pw๭ &NsL}D_Ȝ Cu.7H9ra!G&Cot)G WԞ_,P  ILLCԅRz/=d}׷׀Kr I`b(6=d6񌹺/&AJ#1Qj^fEҜ( bWTn Q<шGR,"4>6;ֆ‚'kt *E._ / AW ruf6<9 _o/;#6Ȕ5z%Yj?Γ 2Q/X\0`ȔO\Y %&4B6/{g## H &򋰷_|Gm zE7[4dU?'G4A|{7_J |:,)ľ`&1~l0xƀ̹` :ɵ/о~B_(-/%\Y36 6(sF]HoȱPh_0°yG Y{s4~)`yGy}BqNlM󋯁.XElۄO-B[rDSl(GzD g}1x>M _о0zJs;|xFg"\HJ)3Y+޶>gLLt}/|A>o,h;(upR9HkW~@#SmxLxzs+  3H5b|~jY"p sGrw&!GxrfNfC=rsA;^LL/yy=B 3\ܗo!6}ΘÒrs#N?(/w1`9r@  žѵG&I;C&(=G 7 >| AS6&W0d 4}!+Gt9z7 76,=c/>C+W 8^1xM$ :xfDxΒ#& s|џ \o^hUfBBH{[2y3BI$9y&p9Wo` JBx.X jBj1"_ @0,K"U+ sϑKoOz?96yM!.9b+>`"G"}!3k&K3\`ysd꾌nV9!y&xMVor_* WCOvPr.Is3Dd~v y#gfNU,ba6C<̩78`jh}Ѱ#T L9[UE4|{B\p_4`:,ZPuoj|],5,E3l`z'}.NƲ+\@ސ7bfTaoԂ9E G0dxf/F_sGx{*<"12/ެQ N^ . ¾ Kc}-A"=;du\ %$MSXv. gL2R}a=7ПoVղp +^w:r{D#rzHရ }9 3gF ޒ>_뚝ĝaS207V_Ƭz  p *ઓB<ހv/Foh~5/*  Tq5̃3 H(MrĬi ]ѕjy!G~9 p|bO29Rυ{\Hh R ᎐ JB-/L`@=^Dߊ& @=#9G$_8"LYሲc&>/` Aio }2 Ra^8S$zyA)_`d#b^`d@sLG?A\I"-Hּ n_=_ 0YPԙ ߒ"׽j@o[U,ohܾ͋0#/rqk F ޣB鯀ߐ0s'Hˊ|Ľq/LNl^P+;'u@A̎`ՂypOߐ;r!g^g¼2@(rNjwmkBTx͘URDj@%PBMrpI!!H)P" R% ?ϙ=ڿ}Ι{<>{~|ފ#\(pQQi.޻јgᒎczYsl0f)]3^?s&0dzi[;C{!q*'K`Ģuy?Z_e0^/x3:9`A9P3 ^Yqsj,<.%4ת;A]da0 5֬n%dΌ&N]zm,9,vV`a.>w:̟ !nZt>acy^w 5nN޻٬ݷ=Sc[_ :^>74^S zT;`B'RJV3p{^S)\#J j'J,-/5V#fNĚkQ/y|kOhUxaqAC=⚓$tQ51ĭ3Y_Uj+UIWO1sY'SLƷ$xN5.'.];?hkL~@wqhy{û7sVgiءfq2eiہUbOuZtx)!hǴ߷!NK;*rZ)\;a%TIJp_Q*xK9nQG P5#WEdߊ2ǓO\tʚ,k HV*XKfPܩ8OIN#)fJ3 ;<9-;97bfݚ5ѹ>;Hjѿ}g}p:l10QρW@˱nS*51|M+>Th&eCY$udHZ_`,ġsN!<0tpgYcS^K05ݻ¿{UYkKV'aV\sw)5gJmimU S2-{8mn?eX,R{/:>Oꍳ5m-]UkF;r^m5S<}i![&8U{<<^]816vi(vb>M+Ovݧt2s)1hﹾ>;-2M%'ͼ6IkY,ౚn#;qOVojb07w&MEԹk_.7 h3u5ĹN#|եaqpMj]%6BTB]y>s[7J>nVco vw)۹N# ݳB [Hh(=v;w~j*g o\HYwKwM8VC] 8bDvJ;󦎩7տs\g~*7-[5qn] `|ּٷF*Sfvނg{QS*᱘;3_fTH9=AݜعQsYGhW_~wlS";!/pϬGl{m3@w}wKSi D}žmtttU{o*wpVﴙkxC5C{ԮՄZbΒj䒻,6si+ y pǒB6QF_O>[8WB<oļҽ}V:Il#m#ߙܱ۟9}\,8f!#wJi;}z; 4.GrXUn|?ͣψk.seS H{Gl-nA Ofc@#Sw5蓵##CpJ#8Oݛ[[)'pZr{Ij%zHj u#0oG$̝IgprY \gKu,bn][jܕYsx+eW麟P-洔O Y3,@RIR8U?O4_\?a ;b^}=}6ބ*6ОN$z{q#?9b36Kln[I'IBˊs86=!78[3l1A~36}'R3K(V:kwo֒c(s=RʳESDcݥ0g~*ѺvZ<Y8-wnЮu:Ip}*5m5kK !Ⱦz{ Z./N/CP?p.N>aZB6m|\:Z?-CHU蹬ۯ]3KWи ,CVDהNm؞A>- B^V uٯNO0Cg+B-WWai:ܿ\JJt>ډ'CK=R3L=}ukVPzG9oqݐ"6\jkwi,3fg؝MU^YﴔO]+m<^OFtIl̖;oJ)NSO6j]bc@SlDNLk/(%W:7,vbtνQG`i}Orf9:B^~S7kjz'i};[S&w!VXjj [C:pEΉ96fn(w]:\}[CBh"i B-ØoHgM͏N3I>6}$Kk"qLElNlA9%yg(9?yv8$&vMǡw-o$iJ N3ɷk$Ats^\6{.#Sݧƥkq#ܕog=w.8_5w( W}Zf98*sV>0-&oHwV09'}jc=c=3,\/tέARs?g&>Gb}g|Lx.v&s3,F{9/g,0[rϼK-(hO8-#~pJ?Z'kRKgb~.9_|ֱŠMɮ;s54oiwfى|is=obHlNأr3,ƺVoBc:&_OvwOLD9^2;CKsgiWZ~fwfGU?3ۿ|^}w6|Azs#?X?@NY&œmkBT:x͝iTw 8 *  Q#~CsD! EDM@" x9wU\aTuaG@*7g)͟4+?_-u:_qZof↿P9]Xg#7/1CW;$/N)O<;$נ#X*NVsq0|]3Jf*nDl${#lqA~鼶˯ƒO Ŋ趜}9%4bC? H΢? ;(s_Nk{(K~>O-3;V@(wW>bb.H}Y*ŽU9.-bM^-d~/Q́Jf ".V>3!gAώQ+󏿗SC>Q|b +2窂BFN9Mtߥ~'`q}%#J~CE N.}/[CNf8XulP~ߐPX.I<:䢳?lHm|g,y 3LQ5ϛ9?{ L/;Oy\O _Y6Xb"{>G؀ W>ѸadtGC;=͉ćO=~Nl:.;IsxPvYQ.)jχU1yǏ8:$`HJyH(z3>nj^A@=V`DrSG%N 3#.LH9gVϖL~f#WP9S=ƝbT/ =Va$( gfKp{39.r7C:r2>; Vv%:p<xYgA$aDW/#6̞p17zYopd(;xPfY-7 ('Qzӆ o\$ ѤSZ"z?UN3$;I}qZ5+һ^;GN*sj,Zn4 uo|lxr?%xrbCN6pA S>~OHty9fmF9*\fb;ʉ i^y~w4.pM?11)FFl4(rY;&cH3_V9.=q,_Ep}q /ea[anhD`ijG,CF\20c9S#'f>&41hrʗQPf+YJʉ*9p/(>;% mWИN.c`A6AXi$G_l$I@+΋ T;VEߤrG. d1^$F2zʆ7 O0~w>KX0Bq'Ss&8D8ay>g[=ssU)=S &~F30;bdQI1c1 (Ea& # 0NBwLg 3|!솑{0Ȥ2L(<i_D0~ògca`aHlpM + )x sf_r~wL| d?ؑ:bvC^S(:w aoO?,'~0B\(y80, +ܻK4VNyg\8( /`N4q7ә=;| :Æ3(Lh ;1:Wd=x滳:@z7zLt|Lk}b`G̽Qb(0%K Rng^7[1? F k\I1$3|HwĢ%x=O} 'Rw<2B3^ҙW3u0V{N<_ݠ s6l 3 Ljy0 ny9ݛSg86B˄# <,ÃwMxT|x('}ca L81|̦~b.Qq0q{&#.rzտE %{I3]dt?O#bo9$#4ݠ;иf?(};7t捅eó7,\h  z< 7{I?z!!g:vew(:µ/\Ysa@숍@$.XL'ڏ&cwnj7ğaaDug}+ xH_}<^xyOd_xOK nϑmxF\xw B 9By0fk;`(.h6peB p錞^P!fCcC1Ύ@ T L_$\qhC#,"q1[`y.XEI6L7Ԯ0]3&7bAcW @g? N(9ӻA` VgXJq6wPzCߌ8z! doٿ8c=5/?r?[}gKG~PyǑw /a{A g03d_>HH)3^#Z`&vѺL%  x47q<(GQ ޝ @ᾠ? [R 9茦s}' Ἐ)v mkBTb>x]Y{U}$$;mV @$(C0 "8Gɭ{OO%ľbZY{έ<|:UD~phX?<~6Ksos+hw  rO{x6q˿vyv,j|(C~OCD&GxV_rօ^9TWRi#EkozUA3IZynD@mӅcƟ?2\:ZS@34<3tbe\-jQ.PM>7#,@+\9DZ4 fSqlre )x,#Ec,E{+PI|:<<҆^9NJ.r .9&2{TS IJFOHzH#@3N7tãtj?Z-B.C"ǀ 1ʹ͡R/ˠfݷ}hXߖuN'zCz7@3C\F9iT?^Xe|QAbQ47Xȴ;zEpiɥ'D'V^3<@5a4ݙBo*n{w[0sToςV7zK .4OE(ߣ h7ѕyhOuӋ @uMTԃ6r.5gW &eDzA5SE6L]TvKX} 'dgN#!.:IG=xk |#y_D 8ψ/tP̹9C|eK73V93oB-Y1?̊l1Bk?9h._Heoʋt<@\ DGS'6vM@҃[낧oAϦ: ԡmT̠AZʋڌ?:?aD#Na#&4ꅻV5š=;k/|s9r7XLdo͉6?9 ?܅Knz[Ő~swzςiBNRˆPjOM 󣞍_/CHJ[?D9ҚOYeE&ٚz6|F 5å OKӌ>n_@<g7҈ M.>0{J"- JFg-/(qQM2ͯSf3#OFs I \f*!/7rWX?]&ֱ4Z I'(Yr ڠR]J);깠{>i#V7$Mx'7(A R/ɋAՁv'm̐]cF.DMH!E`*e\ם> o]D)%\6crQlؽdu_;)俑1O 3Kz3$<u^$O(O_1i>'63"ɉ*Q-gI &ؾS1Gy]Z~EHy-e-^6tDP.h/@;@kNXBх I3CJ~Ky QP5 5 '^#xq(# 3e_䲡Ks,_G$ xO3ٹrfy9'y?HFg1tf?<^HuHmu#5!_Ds4/3 W+C TR@kDP.8u97"8t>j /:`_N@}wDkBJ.H7HB.t =}?9m Mb]PrP03saE }Až=9ZkR< \jR.4Xb'oؓ!En_úelԀA_ ggBu_h%=C?z9PB:< ^ u/_MϞ 4An_SrRῶgdw٫`ZC5` _qjߑ: `t'PqEO0 e/ЬKО-d?DkΗwBi/?2E?'P7_g_;K  {?Zg?H 9BX*6/Oq<~ʿ'oWN濦tl?7p+=ܼ۟wr|(%ɿXok`z73wo1k}^q7V3[u??KS}<*g ިV]?v`7P&OPkv*;| G۵{h {L|3|yG3ҮsO=rpy;?f/ O O *״?{k.vsԸ?<_^??wLW,m IDATxr8,&bw3~YXD⇒rW.H$Aus"CDd~Yƀ]!h-E2y9d:W7NjrDݛ"۷/ 9lcGWFbWJ9ynko2 Td-~w5|_lnڹo!z*z?VKj)dN VcwMNl'p L8jl37Oe~/GNy5wl6>P*izC7}¯>аBecܴZ4}]S~[;qCzf(mń OJanx)qAqzFpD^-C%6U?ef#?E#gTG:N.ClL?}2٦u?q~MVތ(Z.i]֛q2_x7&ڎc~KEc9j]#-g53\Ex G y4"8c/@*m8<4`v5G!)n0ibT41zHM}eVQR헏4pNP hr""quCc9yIov>nzN1A8\r*wcmb퀿>1`v@/62u(1͇`^898hd6GSDNKۋ꣨|g6c-H`h2U#?\R٪/+qGZY nn!2mC?Ls@Վ<{Hp\¦kwRm:!?ԭڙ ޗ7O9Tkc']Y\y5 3K#an+7D] v͇|B}v'C7 7ϱMՓ@--5[6dΖ[UnHLhu:sT\Gqh_A^Dٯ!c65Fvy+/~p~?SС]a8M#Lca~q~YUK!WO­8;ʆd4_do hSּ? }6i.lMEs&8w\>'|"!dшph7Z:L~48as}77[ٸ7ts[ao[U(y qBUp%?/=pN<@cRo$QCеI0QF9$ĢU0DlL"$c?Ӄ#E'" la%/&8 I I2Cd oVqYɚ4] }0 sqQ%s_#J0xfO޺03v] UƁ[c3N.nwF:\D T %saP} N+q2,F%]m:7|q]Pܜ>Ĝ++>  1m6 X|o9Ya2dL~^FL|߬/ăe}< 3|#l7i?&xEm0(Nœr0'g'?W}x0JW?$Ŭ?O귻}&l +.7m??7׸o~,~[o+_Yfm.g:ڄPT~y:I&":;D> 7Z;~2 U7] Jg­+|6;F07n;|i#{,r:'}4>-4ޤ"x[0)_Q5C#|Ft"}9|_3ybNVv0`:e0 @N?:*Xi| xe7ٗd_q j=v7t!^^ D fˏ6'S6m:o٬|35=@>ɍۡ<;fș!w6y'<:3ȆHf/8Dd, o;bE)uO^3iAt(hS!da|VgJ>LP>pgȆ4ρ~`9YCpcƗԭ~N~3P _ROfC-B}z3OѶGęz^z<}_vl.ⳣ O#~tʸh< $"ȎlɘP pf&6cyCE lVZ>)7yRpwTa|5Q.~:V ^g}ɇ=8oY P s&\~5,%c@fO34%,]ɬ0 \hvjzDoW1P w;5/_*OnNƆ ϲp&nX*(HҔ};If =ޚoubŔ%Rh|`Ivο)sXc1JC)ǂ_ws]CIt"{>ܣ0|gO.ҷG1ܰ 7t= ;<0Ajx$ؓg\Dl]_ Y& n3 Gtp%L.6Mџ_ b_2ܘOݱM*ȒF(K89OocEkna{WvG> b >tm`Kt6w oINSj~^w/(\FKռkH 9aWe|FiA{TW|BKҨx&\R :2|xcslЦ 5-Ln}7TJ&+#on9Mmްy \ϗ_{ftワc[6Zƛeo~y-.O:)m, ز;׏%i lwYt#,;AVhr n]Yp,!+~'k`m\7XmoGg[&`oYd祉p86?vÆk;ͱ.eaWlחYC'k!FS's³lC|*뷇$ȟBt1ٸGI|=4?`_3|>xO>zsΊ=g3 [l$ X; x%?p Cs%6؟mT!)s`]8f}`̬8 Ćc>-H,O\UZe};Vy)A*X팕akng8Z;"SǭA1kvCg=D8U?~!bBopmPmzs`c=V;d%@;&j~!J`(ѹIp0BK7bL iUj|o/8OD\H*D])O晘~豜?f!a~!}xsжd3}mzAkx#W|Ew`6aqK&G@8f| %gJr{Gx `k F x1!3ƌbIa3J˶fyh<Շ5%%a!*=44gpm4;W]Ca6I pq`};?aX; y4|%vrnS0:.T$MUs¶X]9|&(q[8]^LőU8B$OdgZӶ_gxy#noev򕥍AꐲiN5mqٸBB@F1[ѦLψEHr`:\Fvl7޴`N('D&"n&jl1|m Ek-l~4lBXo/_$ϝwWHf)pp%Hى! Y>7iQElV@/  4N`.LsbEh>J1_sv֛Xc00 Ҫ-6i"@2-dQ>Xbg1Uٹea| Ѣ>%":i#a%sfOn4Ǝ+?ۧf  ?,:oޔ"D(Dk]~3(.qE{1. ՜}-'ʏPe:/O1:6]S/0*yO醭7jL_S{ ? bkj6ՍFw^@~¹a1ϵ$ ]bBT!+8"@=">"J8vw7L?(::c%(Ds4IS(C_s!o@c~D&BMqԛ r-x_YBϡ}X8h8_ A>nn)P?IΆK$1APUzg49G^Tg _t0D9 #6V97]J7\!f}*>#=:`ϭHA"F`~Ff>5!9O 8?`fmVF&zmn/@E&`n So2<7&}v2@ސax<K_a+?'v=چ/JXǞ6\voFf6'MS7M]*>O/{^D͆CoQIDwװDoJnڼ!`EmgL/ö>[&Cx堲5SovmT F! 12/6qt=;c^{SEWu͢!t;p}G@/BWnov_Pw05|Xo!Nd5L3ҥ`7X1Q }7KtG''˓lsΆJ}0F~l'tnnm3`h~WUǷ(Y e ?WMOܽ%rX_&i2AC!{7luL6%@Qqt/朖hiW>zSq&)ic%Goз8q ؾlD1

    ,(}8}̠R/W<8ל=϶u6tzv1B]t2&#DIIhׯwu& #"&X4?f!e |2\=1&E~@_cbHD12|6EN:Lya҅O0G7duI,D,W6*hWϠēiǔp8750Q r*v(755漁d's*ѭy!h8+ʦ r"붇&wަu Bf-!\N9l \p>f.Lz2Pv( Gv}D@`!@a`c 7ȟ؎BݨoS/s6X{J`a)`^+>|j,ʳn+-L"Էr'; ηtoɖ;6~g^y@S gdw5d;7Zoڥ!V y7ٵjC%2m@6vwlG_Z @_BSA~mw7A{s7؊Dv7l7LeDutmi[׼79dBze+ |4 Kúr䨹v&m叛zwކ@܋~ڝl JӜnilter~mOTviγrLWM:^@hL$~4䊡h?Ǡv/XԁPڟo`r98sYir5A us%'Y[e4m}gq o: -V/=7ǝ/.ٙ?X^9Y6^L1{Lfnb-u_ K ֊tE8.?6t~6 jׁ9q<Lg7O:u \Ntˬ^X`RHc rp?J0 w7ś./]b6 =ظ(cbjڟd\ON m%Ks IDATw:0O|g=G1U?qX >3Wg=,A|>K|:T3Ղ +dp=&G $}4D7 )H@oIx;6b5>Ϛcȗ%Rtd(U'"v2GMm>@Dԏ*CÀN=rnOQa|KH۠#e+KT;=O74 xvy 7_sҹ~)pK#_UGLa3N|UOM)VsN;6#]N hυ,J7J"\OcO-ܼ-[Z|ݗlmbfV61tfܴ[2nF"Kf?wpG7͝K|qx̍Ћ=bh jSu{nͧ +4õ];۫z JΛбAj2.#2濥+ R 6 2N~3؉6&q>|ͶShȼq'XLg/P;HpFͣç7 vpS̀r$> %ޛ'OAd]Ю ϯunL߲W}p̏$P9=.iă>( R1gep t/U@ηsq&v;d)l]PnC|6& g@5&9|`7B ~mm=@I…r$ nA"yRқ~3~R@X=Lr0 #9)nJ6Lh${!sl? ` xJOBD)GmAlp{^7R>6k!@6Xgcqe<5,H}Gq9?͑på85N"?g:laUzٸt*uKr[~N:`9nܜM .YjMpEpPAC-}ېCwW/%ظqf0ȁ{Ч]nbc3o/W;jpk)176[{ғ㙑Ǣ/ hr7%晼* f<yljũ2X,foa ۟ע@uL>ȻF*͠^G6b7dҍmݾ3nyG$LCaƬwtJN o7L>r9ˏ|G,Of.~8e*0⦨3=cz9Ώ3;xsCxv,K`"Dϣ3OCupFmezփsell;†#qX&2c)b.Kv87cTuОasz7bO6ByZP@7kF<-&(+bϛX:Ds {0C܅ol g^ۮk>d,ZM>D'3[˘I#~I\ 9D>7s7ن7lG7uЛ=|t!%oŘ0q o{>H?(4Il%0>%d vË>2ռ?'w'v,״7.cpp E봉*h1 %Ytd+oYX Rf aN_a~W6MpsOv'k :F*/f.@|/PQ7*ݠ0'3p$aϑq܁1d>~?y^M4B|av"?ϟ.q !Վ݆տɍem;zCUG|2ܯ]|2ݶ!ژ/AFKc|A,!OeɃN1;-Y^;6~]^_"CmFrkCde'68-W>gC|nS\MomU a}F %RIgѫ6n;Uoz7:mpЁêNӐOϮEOةXs0W󮋕=ߴ=A!~S^JBgjtT5Pw}8]ƧYtx|w~MV6NWeĸ ;׭d#Dفr+p6N36{* Hif@o*v3LE쨅>M7my3-gvwVXEv>ѻWZ "ҾaHG yQ"14ĆY8ABDfQΎuV_et4./ѳT 4f%#?>!C]-?rS\D 7|~`Xs :?>TSfWZ,`qY=IۘPKHߜe#GCxfԲ7L^6qCA?z.0UDv -De3jNfb-jf'X =z.34NpUaoBDs'<+ |Ԇ Y,q7dlpdwKYb`&`)4tka1ph.zm4- ā?M@eΛ2iOGn>0_VQ.B3U eq'lCZ 2t}O0Puz4:ҹaL(q;XV< ֽO I 6n{#y}aVC3T!Ĝct)_t ?"mPY7w;s\ h %Bdi8f< .e+BjLKcv=lޕ)߅q}eL-P;jwqo[7lh"mL>Mi6(:2)6z+fW_=ں lhS'(v"[lwc. :D N(Z>bm@sʟ=?:5C6djh|qb=t0ڋYWtAZd<֏j.^`Wf*xrʎMS34Uw'\2f}۰d Ty!%f8va#~<8`Ԡ ;Fp&ryPٚ30`\FAϺbC/NSC΢>!~a,I%hظ3c_b'_:jw[FgI66R|X20?[ *Mu̼ XN= qXhwsdqD59Wq Vt7cc?Pr tRJyo{ݱUP` 7ZbbHHϯ "1#acd9?0) GćdbbIo ~҉IFI$rU 3֟DTAx.1q :k~k3LXY'_z+ ;Ch3K2۵]0Ҥvcz# d3U7T%Z;zw&IXvT߸Ô'Coo/LK, bg|| ,t-j~I]SuCx>Py9nC[h7Է8c ?#^'8 Ci?Z7t,? xpٍC''[>k`BY烈Y*X>:fv7à< a/Kܨo}QѮ~W6˶kۗ Orua5)7rq=;$*nlcn6)Β#<o t$*6 ?_cQѰ *‡a fww}QRI*jno$HCgK@9iI٢ 1`ٵGU cxm]?0lޜE;rʋA7_nl.}+`žvng B"CYų]יH_ȯ x+`5 %eȕ<#J^T68X 3z.㴫sKحq/}K?O>f?j89i[rzb]6ua1\f]p>I OJ`"\t.e# e]wwk&ws'vT}>jfuǾ' \ Aq8Nu,+s= 9P^ RqF_%eӯ>@rLs1:[_ٍCMU}VbbȅOt@|X0d PIi|e0yf!|~F ?1/J_qs`t@n&>k/B5zszl+m``6*T$~" cgHp܂x7 DsUdd.b|a)\}]hT{7R]?L]-%?+2 +N9R]?A{'׆zτ`2fn7/'lD8؇D6䲋XvǒG_wp$)VWwS#̎NaqɎ,[kggꭠ'ɦ=-ڈv {^j7Q_e'{ [ gVwCm + d%ALX9Jm9ih'=&@#ޤ1<eI'7ͤDw೰8F7bEsse>3?hO]4QP|OL>qIO($9Ab4O _ELh7;OuRfY>א8Ch =&i8[18**$ZL\,Wi'|/YSi{2iM܅}O8*T%4X_^iI0tcW${̒ʵY  Y%{8\Oҙ>D>#%8F6nRɆާݴB?cz.m]!A8i"|?<R.KW,OǷ> ϟïI 2yq:Aq)l&T4J۝XOl1eM-n&[`ܲ'ph><2tS h]4v؈794E <@-ڠۜrw nB ^hdCiV~dWsenY5hf`` T=WM.HLU)S r,BFXo ~ l{$F?ٸ*&s뾆kCpMwIV ] 3~u6 t<2{T;s^EJFs\2TȹU2F7 \~+b'ӷLE>j:p:I;5SQw] -#[x҉Ӿ˅6=˖ ݔn vGE3QZ*Z~\>kv6rNVyrVnCArXn}r$ 4D%j>:x6em5`4ug_>_d #s6KY% Gn8$Eb}z+F ͭ8֤}վ~9@&)0P/IE l`IYDGDZ3\vJWR\wyck=/MkݑCI>3z-'NVc5y&BŇ=j o_ NɺC*x3ǥ^ذrOY]2]P^'V?sXiwa|q30|JGu_]/.dɻ.Wy׶WrDbc}@ `z#x(Qw_;cʧ e663e?nXR՗ ud3nM`;}qm7Få)O7rx*5>/2[cͷG .׬cdo$yvFk^8#T D> 0_b;]5ܱQǸ)v$ؠc>avm+`m϶*88uI{(X: pcg.$K/w ]݀On|{pxʮ3 g46 p"ΦDv}>LAଝ. ˰U͘~F|Αx-oֶV IDATǾ p}?C$>\Ж(Cɨv ȭ9 n&. |;;X0v&`XZ% Q6oTӁ _59φwvdVM=ݕ?k5ӷ8^j\>\7q".Duns i֯@~)d@rLj"V:k ½ 6|t oӈM p0qaPWj_lKHIXB'_tfCqv8.:*&f ։/ ܄ /c>!򦺺9}NBJ|"jD·I +Z92w Q?!Ɨ'BXϞ=l&6I9sOcK4DF}6wTg}"3-ѢmVvPytڐYZS`C m;IɸP̦ުHʍ&c!Am{nY@ & 75}%Veϧvo۠[zQŕu\o+$X:ƞ,ߝ`ytF >ٸR;-©67cky+Ad'? Z%opDz?~8~cm?'anPTwuݵ#ç\Xknm7l s4ϸ[B׽~,GD~a/&yvJоQO<,ON%/c@ݚTs0<6h.C α~kxcԿb}`qJ 0hjnS]es~/#+Oڪw]]Y7FGȆ8g~^W،C!,,E;X;r- S6>U69J?9v'{|V$둟癷t^r-5XvQ*bEecʥ4 C$MH1rtmٝ<71E&m@I˚]}8pqs6a7/mu?G$Z8 &ňVQ;B6aE?&օf?g]'Sᓶ߹Y$adQ{ԧ ӑkGEoUٹ>x@W9g&CnS~S x&ϰ_bB޵Km I" ҍV{KG7{];Dt+;YmD./7alwڻkk wal pKi+[VmSrc|)=gy9>3Q _)<Oσȯ~DxbsP.)p&W;u?p@|3>}ΙA 2JN'3cn2%v&W>8_3G +ڮ_`_}gb Ou"6űPz;]1* ij"ѺKr՝6#~<_`Ds}=c$6ٺM=#Oa99#1\_v|}޿ϯ viF%D~ o KB8s Ai:0} Ҳ-A UIJobvHbr 瘀]! m[mi_7+n@4R 4 g{F~U6-h7*T|V n`n .0kĉ O)[,Z7AuRv`z}$kf: \ 9ב,Bܸ\J%Ɩ¯8njX.nFZmluڄ6V/A! L}"N40}L0nf2u7Z̝®hțJ6bc/4 jӬq];~%ܨ֢-| !l'8QS*p76]ڰE`.)MvbVhsn'vtwI> i᧌oܙ{368-K|%fa"+ >&9~lW ?:*|@%Bl"|CG?me~\1P"5mK~,&S@vCـ9hs]p-7?~U#+Y$H61 ֑c&TS:&dA3)/)lr$D=2˞Ad9_ ʥyQ2Wvݺ-ߑ Y<߯TfQ@m^gKYćG?C5;^JⰸrfA|L;v^`JF!rFƘ#ce!'[\Qq1ڰݰ?9lMgLnۿf^ao2{*ͦ0m0~#z搝s^ُGť85m`{JZS&M; 0`~W⽧_Ss|- $|7G8lX tcHiʐ[I[we|M~zb}f"W])Ymut7#R ՌTXѦ;AW/pCpDA[+`;A# FS> ݤsd7jo[C巸'ŵ{0WsC?un N;kҷ20XDgg$<7ܭ|tÆ;u "8g|v9ޤJP2PO]AD'y|V 0ԡ-9 D[7s9oyw0 yh\st5yNtg#[^x>kxq̉tw@?8beŝm?P7g;;﮹l^b5ւt:%^V 9uy=R0 EDz+[L!"_f< ^jzz;Dݜ!"!{Y'>9=Dm%`UI $ u||b4l&?ygFc 4K޸QT)sa|WIPoQTpN(UVZȭY Ćms\D/;oiXx G1K<[ !9(L?"XNv][<&0P(1\U5#7ri#`>j*#yuR |Y9@vg 'ta?z58hY\5pi?:(^L:/;'7_t2?vgoX3+ ~b9L笣^cIXdu$,C.M\vSxt-o6&pĿ5k\>vkob ukF ' pfg;Bn뚆NM'*DNG jj[ StJZ|wm6t#p|[M䐕3R`_k'f{tj-.oH9ql8~ҲȚwc!hh+ܼf^b7ӑ{#Y# lsI r.Ux3xk K{je,HmoccM_ wN@yU F+y'=n/bS,@0⦁mb}g?nh'W6s^jW]҄l΁75a}q(sLeCM\z?'Fxѥ=IJ:\LWz3=]ݗC!R&pxe `kfA_d46^8VxȡojlTAHgENyUA!wll5JեAmfo4Aچ>=ݷx黌o5R>GfmʛMzwt]7wAmG"1-.T?_d͆*#m ^un|n8ΈSNBۙYGUU=[}su;XY;%"댕>g8iEqGL0R.ByOP zZK<#rQ~~g7r3owF>6fAGTa =>l zcXo~O *;DޭvůaZTcrnE#]p#k']-s>Zw0q =V|0qUs,Rt.A2|H$6dBc6.U LE:bq!ArSP?%LoG]f1g oX Gb}`š~~U' OwKc ~V_ʧo?|UOrJY0G-t, @v$MW]H!IP$9'[?YOgLw+vMkuA>1x&SvAȜ !t64XcqFJ=tw9+fv|:n˭D7r渽!FG[:S/š^KD>$ka'٣v揯| h^3b(А&3T}D/wVAXҾ'L[s G~vƟ3ёYJ= c+-zoگy IDAT aF\پ!΍.?i[rN1fϥ/p\~x3дѷo8DÖ!0IچhAAOn6C>>h&(8hwq$bռE$񠘪5 gϮ9V7w7o>Ǘt%W﬌݅F~'M?G[{6FI4殲x!}#y 9| @7$ugm:7&f__ _ݚzbʰq9dc mgӀ8KX`n#`ʂV >g?V\m'h+ 6.M0Vp&-NDI ) &Ɯ-t=\_7?pn7h Q~TF-B+0H/&7H dB]͞#yi;c:T8M+>W,!!\J$h=ĵFwhE4Ɵ*\Q'FS&M1F~ xO|Ҧs}51q 1G 3bK\Bz^UAC~: |.t3Lf"V|upNay3[e&I|t݀q -o LcϮ{l?Eճ,SD7`P[\r%weqfcKol8h|7u[?01mN%9aFQb #O$Crh=/fq %d Vv=$0'wddWNf?<ؓ I &2|M@&!Elڏ?k.FJh6@iTv2[On@7Xyd>HŹm .ךLcCK Y&=GkÎ(!o>gL+`^`6c҃!A` .؇ !^?k} pe3 ן/'"V2GWGQ`rڙNu\"lceႌtwĚzo}pҘغm(_f6FBEBixVue:vo n)Le&V>&>?fr1T\6-mg#Ґ7Y?N8@sigNhMg1|;A6tnV3^%t{kok0;6P#tt7E)*6ψX+ ry>lO[Akb5ZwC^׵  9aA祔E<MsϮN?fHjp)%裪+Md鳐Hm4{0KNYEnSoڨ!0VPnuoƺx;8o\,DC?|[#H;m- F-=Yeˑ4G %gol &uԿ#3UZj6J-YtQ?t~2c K*WO3_#3ѝ5+'zS!{7~>#hPV?Ja`&-k!ѧOQ|2<'uDgЙY<~o4_67yz#M Oi2a5؊C["zhw`&ܵ_q '.u7P6oo/(-?q5nmt_eo2#`3fg8Zp& v%b`vaM˾*F@~}d+qc34";߲/r> ʠ2్K: 'Ϋɛ߲k? *(_R?[dƦ} wi -6mwjD`/HQi_2[oPcP\I >\AY̲f~>^xp,^tÆ/v "q'v%+WA7mfΣoߍiS4%GCuLN:qHۮ['\T<p53%D(,>4[E։G<0aAݖ=R;Pd f[}ahFWmk 53PkTU_FBpi/)EL|C6ђ ^Xa6]BWWQtIAύwpu+#>`g)e>>4}G:뻈M 49+̿zGx0YEs"XjlS!:eŠ16E (M]WRȡ̀QOnDKKVoݠR,,/%{h<37u]I םKO w&dv?|^=7|WvK.fx%STSF]Ntq|!|@"yc{"x9<_j]Cl' x:&|#96M{-=lƹ%v&G B_d|~ K=-'c1 F $kNz-#vHlG|<a% Aɾ$ؔ\tYGzC lV7ްwI@čhv6il농~ȷn$ 6:v" ɓ2NscѦ8ւc+l~b 6p`w_^_bP'bLNi"7D_ˆQ3xTɱ۰ l`qQl'0dq>Z_ ïm;7hxEXv8'] yh3?}trՎUHZ}R):` (m7hKVN>C>?Z݀'_m56JBOs_A"lź:Ņh8P X`[AZƩ,㐏}s8'fpp󳹱c֏/Bpv߲! qg6-!7q܁A..oZD-%/">Iۅ pEF% cT}X<3rF ̎171:I:f@0ph76wKծ ݯJ~0? Ap g1Q4PG)AQNӏ:֧v)FYwgP,[fn'Ϙ %dbsD.%/LtheRQ`E[6eN7j$6XG22,=꧖_$Iq7d85N&v+;^mw`u& ;IptTqI[u3F= %z o7e~v63]i>7F9k dI@4aED\" X!?%1[=ٳ*I{u4p34Ř|6vd7uPI)NܖRGVន(jv+;Yˋv+,"qrћ`^1oYrţG.fmپ\%_nzlci:JY[I(p?L. FFluVjoXXЕnyk3^*O-?$݁-t[Wf-֐}oI@;{sx8֌4NFw+C-)o& B/2(KĆ$Mh62(:8Whvs$A֨d%4J{AU6 .J-[ n ')(s GC%B~b=gNX@q$i?;mjHef;kDwR\L{~7ȕ.28_0Ϋ_8̮Xj?ꇮ7}ZpQ.% ~358 H_ (toe64|Cfnc$ׄWKBPo{\3E-??'ِmg2]?5d蔒9eXq€\Hχ0퐠0J׭z*Ĺv~~}Co1Ȏ% 3G67{4op:be$ 2u#]AnG~ּ ,zܰЍa/!t 6FI~A`ANb>dx5lh9o!rE4E|2d* VؖN7˝5]dqk&{}MSe7n !AY;ܰ#ߑ}o  ԥp0FƜBMp8nّ'vĄu$ x6&E;l lc#أ W`QW?{qe3NP7D>F]?Q ? blWZ {i7{|e:wleZ۲)߬ d7x:ҳ"Eb}mTFM@D mq'Xxc nckr0|]sdFQe~Tm4tB>Q 쁭76˖dCw̿%؝7|])#il:-fa"mv F'AlD3?xD! "(d&VN.O7,b@v:#ҕE'ߵY(#iCȃ4#7 2gcxGv7lt0\s腋=~Ev0H6bZҍӾ}67߮  wv%Yx&zD,oM7,y޴ՙPM2%>·ivExU `?yх]K `Dv7a1B[q{׍\ۦ!sVjDrUg)]5 -a3Ťl=ӭFcKύ2356Ee*fI);q$tJu0vy@ǂ5ߔn``T> s#ï$ޓr ݬuάc7h(@*K>|: B4[BxРo]9ۓ!vŅeʼn)d2*Am)OY6(f]ae S[R?j.#wXl6yVk.|`7Ďx1,IThsV]K %;L) 9.mRߋהYBU.0dFF @vgJanGoG-G@S`Xr]Ad?Fp$>2y@uHAa. F` Lsrm5zV?#]}}÷#7Oμ7n1;g89LK=ļy 3lIKCCY%w$:eg3ȡש귱 ӡ?H?=W6lJIk7|XE}vy^C>zym/]a66fmOYO@*_?$ʫv9L Z }#ہ[QG9u w _]Z܎a[Bt5_uRԟ>ɏONՖsD3^ǒ[ɶн':ce;ro` %4ŘBa9v{[oz[ww0Z!v,&{"j)#&jN?NrQji_:\ ? 8V}Bz ߣ$_8ʠ/ " O}:Ī4O+f _bY?_D/K.\wg%- z|*Ǐ$ oCpnp~D  qC[%BQ$~.HFIJE^M@J(&W9R<.T71ׁI/]!JVcP,fڼdc<$ؒ}LE;;2!2G*=es6 Zk~>弟?$nQAMva%]&[d~$'ډ0Y:om~TDc@*|S*rNɠ/W >:Y',Ƕ :YY:  U8;p_ IDATk9tp)u4UXjl'Z.-R/'ul}2{9 V|X c|N$q;*!~$i@?%ݶ=`Ⱦ-IU;Qޱ&!A)IțX]qGzqd8J;:CE+ Do!ʼ!vw;/|#`Mya1B\W T5v_mv.q_fN8Ʃ<)d C"OY{)= tζ*D.'+;_;w֚&(N ;즕<[>Ovitӿ.Mo}C5P1iഐS>Idͷ|L||DUB-@0 EG.|yeMz|܆|Փv3>YOG_]Δz7|tT\໼,EM9kʥ2 5ůs]R-:3 i##)ޫa^edOSb}JrF C?(?*yAYU4|#wu \?%G$"޶{zۤ@(ʗ?|[O][mZ'xlG`` 'rBaK;iEoϿE?ej wv *(6wJ_ڠJ≣> |jYI |Sۧ;&9dPۺ&m\!}JѾU]C_?,ќϒ]nu,K93\< XA 5,J,۫I>^P&+&-gNs{!p5Qm7Ѫkq #GQo~$ ߝ,q*>s/͈U¾l?T\H^&;gדvm8c3vey]@ǯNݮ&>.ڵ,~WC )ݰJ+6I|{ wb[g'n?gk}qZ? 7mUħ XwUmQ}!DW btEJHC) Ȉ:X4FCzK> !`䟘G<Ԍi@tNvA9ȜHNO"ǸH@<+=Y(D#K\tjOӷDówۓ"qVɧCdɞpM5ev!ɹ»'.Nw-eaTz*:O> O1럻> JUuP(yowmțd7+ȩI]˺?ՌN-)TT0^\5}7A$N0IY7B'R|Ri#Wɼ&]od!hw~Jfyy/w$,vR*I<RO9;pԎ[SxWoa\?\``wO cy3㷕 A{2kWgzwf)e6,w &~; &=?p:G4ܑ;ߔBѺ/{uaL\8y wj@'x";]A|ZE~J,S|@bD1U0'<>AdW?C:<f86C. ѸI>F\>-݅u T'# ܅IGngT_3 [͔%~&r'')~3)SzmO!d29|Q`)+2,QX$ێ(5jf!+BZKZؚ3a#{j7y,_@J%֧ic<A@ws6[~a=q,|/,mϻԖn;ݭUzgй8wCS#"OoR7o<޺/E@G ,,w_䟘V_Hwt3zvlһS^bjM?FNd+d>T.}`ʿl*7rMʗ;.Ѥ+$`]'@Dt#f@>µ<ڇp >\A L@=9s+2_wc~K#hL2*A:V]_'(F˸Fu_P_PݯxX.;h̰-,`"acB}s' l &vV,8Wtmx'p^~#7ҷj#*5b^уdKN94 %W{1f?#L'"?}Ёqa/IcxWa,2|, #,cog4ᠼiyh_3?uKr5Y/ įd3;HP2IݠIv7%x2nt* _[(iu< Sa?]%)_Yj~۟~!UpR~uBbqNHY)7"yZMd?qcE,fG筯 敡_%x'׍|EX/F'Y>W{ Oq W_y"CnJo^y"S[7>HS$g1~M>|wn6?H4; R̦_ॸl fqN +',$1QaAQ&]*POR_ĭ>/ꋸ4#me4V> |_7n Nh2Px?|1J35,纫q߲T&IV3Q]29>oM>HDj u9lAL 3 #[|Y'gSS,ם'(5+NB:dLMl|Uw]vzA#woy {>0΀  @g,Qnv`⅁t_E Vqˎ.x0q>ȖvՁI0?t-(a+aK\ $+AeyaS;3p|f1:ו}5}i4"ӕqp5NJDI77d$Ju*Y/bNj㞫N~njaXXF`[U -6Hד&@lQvvp66ktpk`L \G+ A ,QH*D k%HLTu0!p~a{=-$0ۀ}0Uu32 p@6(ixH6>1?/ߘ\fO&Zʟ+!HfdLI1#jm}Ȩe!pi+?vXo}tp|.&۝4a&={N(X$'|SMIYx3+$/dwt;IOO>m}@ !87P Ic#< #|v75 C;J}enwxU2:H*/dXֵ~T^bw&JG߀7% t^yNe@U"n^_#ʪ̷ig!m CO_*cOu:<_,!': 9Snn`f7^$Dyy^pvP_y4>1J*~50gҗ8ꀍ# } otANOG>J2?tH&^O/4SOֹgEݚTyu!BR̾M B~G Cf&{w%]OdI _wyE՚ic6OU-{=ΏV$lS"osX*zwX`1^x`gKK$],K`/ Ґ'Xw3YP'7$z٢?y\_@6=~( _|q62YI?0_( 32)ɢI$CdHwbHSj$WlÑ}qPalGK"~Gg\qs @5 Do_|;,id⷏9@v2!zm!W!3;r.V5O}/8 /|$n%7@LSLģz"92)vVh]bKJ#pb-aANXXfrH JL>>ZyTG+/C5 \Ko Q /8ajTN_:LYqU偸1qZ1y5d0#Lh@^p]x!;v2՜"$$A+OD,`~] |Q$|]?/W)]V՗jr̾n×: A"DDe7"nS!IzmSHZʗ7KPA2EPG jl,5TJ^r#TGe7ofMnk87lL0,#=H%54# 2i*L[d:㲸Z:'JXn\d~g_P VT}aC>bF5mHuOBމ~.AE$oZ܌L 7 ȘimpT= e -l xU6yNaƒocOO7uށyO裏1w<*|pjڵѶݳt)\?vZcHwv:Sy\rڷˁ$m=22h.b=^Wl}'ɑ$?eP'8fҊu]"ϸ(2&d><~@fQ3%>*GW{mnAP&Sĥ0`vqH1¨mO wewpp6n+Yܟb2[{7PSm2kBtXܞqV4a]pw0c#u(|QDWp 5_Id2ny;&Rw + ^Gl8d΋˒KYCiIQY'I'SB/f]X }ݭj@3nױe"pYQ+LPU-ԻR.kJ ^@6hgiTSf8m9aI?BIx>y3y0/2`iEϊN{,@p,%gN/|$,g?8]g o[=H;`dAuWP XY3"V-s?:Qcy 54?ӟD]1v[ :R"p>rlpYX|wKi[x#E?YO[4tʆ.KPh2G }ܷL'd 矂xHd^`jLu*iI9T#dAىp6!J/p5(|j5\>OqY YVq@QZ@x#$v%GU'U GhZ_ɢ-5,6L{MDyͻq@NZ#]AvQWTRku"3OFDD %{ KvfJ^+=J}y71">>:e}.Ҿ/x>W'?*F8p% Ӄyz#yI{|uy. +@PH²|˧" XL>3:mCT!_߲oDPP50qrxS R"±m?i{)_,iNl5ްj7VǏ&K/bkG>7`H$m5j:\v$Ħ,Wjr2oKB4"7xkr#L_4.f9.̮?Cʯ?{$MLZuQ`A62ݐ"zp-.d"2'2堑T6]ȳENرn6(xM6L&:`] XAz,rb@?XIHMy\ZJ: $s0+26q\wz*_i w <~4,EۄPO[#q]k{p Sρ%գh_M~j8bf-0״03=@vv (}f||+m\yxsYN66_Ĥ1oE &s{DYgxnBR2*<>$x)4=%i$a)mZ*kyόC ց !'qҁdߐ{mBƒOdfaIW6oCEX#ѳqM~p'PG?n4NƍmQclhp#g!µ&Ƙ?|Ikš qŚ 5yi>8da/bM_($ߵ*F3Wl:&|62v_cW,r(܄SH[_RY pN|f] 0`š,ӬSr_^<7=Fy*{ju7n'XOe}@']oq/](JHq^2`ba"QU=MIj=Ҕ=eZ%.BIM}2%{"1Zj)ei3i v%!Jlw]),1y{|vFp<|&508d*\U IDATɺn71F!k澲Qy8,w`FF;=,w44`_|`(a3c =/w\}:#'V/.)o{zi$9UD+3 w*OɡTNS'Ըwf=b|{b[}I ܈8ga$um]?Ѿ/0qsXaЅ I696|ƌnq"$f\E01qB4d1CM^G Cخ7Y0n" _}]䒝Iþpdoee= L,O ef%Cٙ> {!M~b_Ol J`f~gB+;\ 1v?S@I[˞_{,x|/)nS9?$?w2|No0Ȓuܑ{?*BEzkKÉ ݋5d R݂n^וe?' .J$3ڟ˂J~"帮lVUXZ8/O)(+ta j[ك3J?};T YQȦ5ܝ p Sǩ^'ՙA^|~ bGg] \b-y`=z)ƽe"1< `@H/J>9M lS鐇0~N)w9MtZKOH']؉|'݃-vI,d[3jSrNȧ+SuEi3X߻W/˘)Y)[XOA0iYs{BX~vy %x9"8ZP||ب_22Qox b(G_o| tv~BRDҀv@=N}˅^o 1&Cr&eWz|1TYdI{SJOX7Lecn.QbtR&_6pí{eM)g!ia'S t~V:Y!8绺XY גd \/N3MY\W>x}]$˦{tj.}odgqE$"A0֗%V'pFhŋWgo/Q67l^<_.}&>nzChr_ !n4X I*q ky=0([~ 9JW΋IT kGգA OA.p_lW` 16EX gi?yC?& vXB$zES1#Qnj#M|V+'E;Mj?~C;I^>/4 nN8n~eE Z7y']0ZӉI’ ;}IMߗG>IR:,;uj߄< L_((K_YnӅ A>5/ #6"WPB $6mGyOt:xj '8J=ջ@6!͌nZ7C$e, jx(r/g2lɸ{dvf?=y3N>S`Pܙl7&oi)+b*;it,t>TfWP(qĮl~4Νt,/NQgq =vԏژ$c 5? _?\o4EXG,-o4ق)LGKSI@0tB%צKh֞CO~aO6>mv 4I]|9DR! ߭gC ?'\2)eZUDFD.`UY'Ne$Ԋ|I<JN nwаSe 2G8LϾ'CD' ፬f.f.8:lkRjs(lzW>,&Ip未ҿʦQ8%p2 s㦒?U>Iݿ>=ˎ>awq[D14PT5HHB2?Rp")Q:/۟j3>[ |cg0{!sFv]8]A5 B簈c򳁔;?Z~Ϋ݄xc ;p0?}OZP+!Kث!(7ުc k|e/Qcg'\Whc#QEalּŠuT/@fۀz}7!DA1ՂU1!lˁ#^+AũS+EZ|.jN&ܟຩ2) EU& cٛH.֢L]F$gĐ'uxn@qJܐl D9Q/:Go1Ek"U7]8n EG SXAL 8g",%U!uvl E-f|vDbyvWggsIYagɲ L~Ru ЉM7>:2=(/@XedqaG7 /Ni/>yNiIH~=,'߲"9C?}$׏?Е?aqWOL@"(!|OmaL s"9FAX2)#iSnk۷D(`O֓PSsi/Hy&-m3k{\Fھ<ѓ" Uf/KyIWe qKIۏ &,fGitF0ʿ6kbMD̐Ƚ~AlAN )Tz$s/Xp! |/^^ .qȞ|jSAlfNozŸ%s'OmA"?$̦SԓuRM19k|֫l8&q[TCum~9X?r7x<鼀q{;OU^:aݕ/Iv[X%['"Pk>6-'&3#8tg m~K;EHHí/o g J AW1ۻ+6| XY8تGUaöĶ <e =c (\+Od1ę u6Yw`27.+_VHRBw YPǀ3zB6zA)*t+6\>#/{[W=J47u69-L2pbA\{ĐK2$HwO&.b9aپ1a@؟ky1}`veÄ<ϻv▯*k#n 9O^iTWʀkȤ6 s & HdNذ(GDgrrXE, }SBX$Uׂ=8X#z -ܼO,P^Vm9K]O0Nx'mw1n=K?s*9e_ yb 9yA(],[fu"S1V~ &ڞ Oǃׂ@T2ɡ)8bDu8~7}>aӱM;\#ɻ(YljӘ)E ݬTv;Ӵ~͘ogŜ(-}囲3 sݖ(oӴp $+8Af^7mw;j#t;kvH)C.$N NsOm y'8(`3IEK-4DJbqjÙXԮm~)"9~bމ4l7aKuO>T~pEٱ xc}+dT]Ԗ2 Ia4>)Uu|J, XOaܤV0;Z'FTi@ {/\׵4rQF>mbeaY߮S/-LČ/GgE}#i);Ɗ^cZc@0;uwXvuERlC VqɈ}y<ˀhħf7 mא:[a[+3_lםU;.$NͬL3E_BRE6}sJ]_NjI]5>fWA+"}!b'.)5 w&qI_B#U o럄 [Ya9|g0*Z-}î`'x2]DrK?}e6Y6+ 8{qWނTgql<}@|ek92q]6џXOXAUܧ <wmC5"_ٕ@XOl_lu|]cir7\,waJpDwLG?,hAҽ a_~Fnu/ 6K[ [[ ic"!SRt D8vĚ.q ˇO񓠋E"ㄿ(R%H?)G6&`?eA_3L.ȡ]lW*$2;vO'T~-IԞPw8!*N42H|tAFΐ̑U+`je.H^+uZEnf>6'd_ 7(L- j}"R|)F7Ow|znS*w˾F'c~nU l'h1QvOćzYv`51C?f_3vu{.۩EO&Ϭ:|{G7}:҃T|)? Ov2WTĺ3<ȧIl~13َ퀅;hz=b+&QvF?cW#6./uڱhx  NK8oG%;|#]'{D˾zL^=|]_:rM>[D71J?s>$i]!/){C}aOA#^ާ~® "ZPN'uf?!"\FV"Α??3MVŬx4`Q~<(eU|#XlwgD @k۸(y-9:tj@D6b+_,K87+yawjum]Ŀ,Y}K6=j4 `Gd_;g_lWa ;`,7Q/*O1#B J~d^I}jb.f"w{&3nkL)l̵ Pmsgf3bdgüP3BIBJ\6s̈ (oD ;٘a2".fF$VB>\kj|saK7\c# W0dgΦ mFq̗?n`2GAcl$!8p -ר̈́?dhARn4ȿ'Y$~kͰ|R?` ”I~0I>N#=Vm>0'x'Xc'Tg4=gxqp3c?)8w}3mzM1x-1#BPq,_0}Huډ m7Ʊ?xf~mbC=H_Dulov!ݕ7(mP$8z $4xM `aM*D2iy̗P8?L4Nڢl7e/06OldMMJ|'A^jF֤UU/UpZBpfzWj IDATߘ*C\{8!Jߚ޵P7r齘"}K);ey*Ju ޠ|LŰ 8}qIV8NJ$T}eǘR/k7ߧ#!l)m| A¥Ff'x:y6'7=Oo!ij=gCWܳ=d',25E{C(L3Jw>1JU#} λxJl;akd_?3A*8HN}|w>a]A}jfq:mW?63k0W v6q Z68x3a٤vYj*tT!'B'4>dEFOmA~9a4Nе {0<_HGf*$ހ|͢E+ߕͦ QGYũ_ F[ B2q^Bfu^m4 7v}7y!}t6P+۸]CD:%(xcT@ )AR05 pGMjp~o aC n #rs3at_xs:\S:,ԣ1}62)u"ƱJ$2,_5O3#;ufttCilI gvn=պ<%Lp &G;ui2d 9+~e$!8]_C|%ιafR1da~VWikimmG  T֙ 7x>$@a _7Y+^FvWֲxpsa#;=} }F2dQm[p策 !&)\|pe;e4buN6:nRŊTY ,W`dnv`W;; ,gTA %顩l ^`!mSsFSFBXG`nwZdn"ullDgDu))}mL駰Q»5_}(8䕗| C:Ѩ,5fvbZV/ɫ& 4pTmm'ps v\ K[I>%>ՓlT NoE] 8UO}B{3]ziII| >l_l)78'~uUyߐyeU+/s<\]LGjl'W\(wς *H_]LQcj=ka'N]u*2/Y^| f*yem&Alүjb}Pʶ蛓o'̼ &O6jN5t?@z<YGTw2Qc)R[ ;KظBFƝ5,ӣaʨݠ ޙ\G>q}v ģO3o_w6?C{Gՙ#` fy[؂~B 4]3[ԅOEJS{9fnTp:_Zz-1't:~иO:6 J'؉ # x2ZEx Tڎ.|]~/3 xp'rxäE?ª\ف6SBЈ#^l[T7_!dHv)ҳHz;D~ۏ\ OFjAC݀T50X 7 !?ÛNVt& 'Y8<.ojMЗ'ˤovgJ5ҍU: _[v8Ju**N"mN IVX#U'hŅ^R__!\.CRb*DcBLlEЏwǥ&p0i.)FG ;zwGc!_ gz v;Dlr=E.\ Ro, &ԡ_ɠ$|G3o t19"a2P~R|w3W^%>jb6,B7񮢇gG&NHJMfa~qia+%B= v+c"Np\Ֆ1*dΞS&*_[QDh$Z-`0n'e/`/ @_o2_GQ=Vd$l;ӎ;ЯdQpdo.h/f !q(|C,ҽU&"HDPG5ᛤԘݜ?ӖTa&lc-A MF6[𳐟 s~rSԇ!|W,8ٙ*yƯtQ+&ʷN_ X6v2^LЗ*e2YJbNEџw;?8=2?Sh!q*wԔDouc#dpMɜL q}|<xhl;840h{{§>+:uzscAg |CS'L2Kf[-"$#ؾk^cڿʭKe©jA'J7S@2q{@ SEK16> n_թ1sd zm:ȋ:úGI9 ٞDzp~Ϣ%E (\| v[-%[n@ 18d/Bۙ:vis@x7Pav qApEg gB}H>姴/^.8=3<É(ȢVQ~26;12fvMae[IBP+L.wYs7ķr'|?`rqBm _4em OdrfvSZ B1;ȶڗ'ȇc|csI.xu:ex1:bJPpj+o+6Z|| 2ƟXdĒŏEQ<ض?W LjO@P2X ~;v HC/' O,}.Kx>Vww0o=oj1j73\ >-NpP?)@06ypR` &T@e$ Q\9b&z1QY;ٙG_ 0eO-Yx!Ų̮:vv;| >U2CreO~f}=7\DANkgaڡ7e~}xbMOO>v'X]٧>=d~uO#`XC lX__!E/?S,¶D=eco?~?WWt: BP35yiQjglP:y"u*'#ݶk\8?yR.hfȗbCݕᏹ+Y ebG u-gڠ 9,`.?p#ݗ囹vx_权CDUz&>y?6\|#d '.e]BfQ&}6N sFaW?50JO2,֡M=٭t,CO9+z`ⱡp./kL^^ GnE\9.BB%B  fQC:2X8(~4*}01X5L:@_l<:H7yʿlѶ֋CIn?\ӣ^~\cn[v۳l7M_qnƜ,FfcO_֮5u=\>9`A]Gש]X_%cT"I]O&bW>`GOUVMUfd&{YN#U^w%givu[5&-D!p`Wq nєv!$x$ ^]ىH&iEӹҝDxsUvF"xWxg.'%scOqhs|. qD6>cCĶA\+:G8@;tŸƆA 6` x^JÁ"YLٽXZ Dq`p|DQg W;_vav-曨3ޘ|m-_~?<8 'c'`^CɁSQ+lX`RK )Ѫ%3c&ګWcJP gJӷhzLArկ]!,gU哨m8Ec&r3&SefsDJR"vw}w~[+ܳGd %8MezC{V;}#cϏ\A9˹2^B6C5l(Xݾ< &@|$'9`D'YUmhquWFh‘87> [l 6OPsN[)THR(NfnW`3d#ue.u]S?#i i۹_DԣG!H>/˷S7&t/]GoG\[BП; x 4=h0@N/1d0qۑC{_^@?`&4C/(6dl\/8/nǬ}2FcvQ.󔍃~(5 +N6T0=vcςbsېMb(˙'vOos3B >|CW η_Dz姅ݴOf#H Ni={-(Iw)&]U/lwOm$pL-|N^|h&VP 39 fuaAExnj'Cq9?P08xc'/vGX;_Ƥ]n|Z['X/nB@br%& >\ęCtM64E\L%7}5U}Ks{+m<8̟$FjgoɖP:TI'qcNjtǓ V8'g6ԷNmJ)i2*͋Zp4 QP(bzW+c1 A ijfD밒k &zqBb_ t&Ga䟐u>MF,&~s+2 x\ `sW$-日lqxhWTDK{ s`W BuIAkKcL3 u{?=逿}dp]s{?6Ps0?y* ?{M5}{\kz(:xw6ۯc|MY}y O\wSp3ng:P&B,<6Q*i ?W+K?&ʹ,y򔏈3cI\A"^/Kf ,Aڰ餧<~T*djXD[# | D,'Cvq|iXPKĭ3 wr `"0¦ 0! )K0@#_,0uA? ы8/\$ʯ7foJgv+ےt\Vҧ%v/j' %oB^4Ut1X;6Z`G0x8>H'nKOɊ:foEGAN;}a7F&c&Șד/dfAbn<mTz60#`ݿu)jl/WCmO,m7LeIAº*9t|kxV2̫ax0`EC>VX|[v̮4C = D,'j0Vnp\ۣ1(mzRߥ쯲 \cSX\ʛ8٭t{3 0u}-w:dOA˰. sޒ<Ҏ e0]E$@ ;k&cѵY$T',`ߗ(+" XIs*Z6`Lv~,Ct9UopWC=?>f,G{Ÿ_}[ f `tE-q]wMve@憥2(/BgA2nPl;nw>;P _Oߑ%EYX|YA=&_Sw+qKn;ug8 ˝u@Qz]@a~'a6 פ5Q svgrci0 @9_o$e+{u<%SlfjϷM/ CzHYw]ɦW8:oJ** wQnp\UUXog0@Jyqk,ٔ8¢L!5d3xCY>xr焁}dfãm粛H8;!~}1&isQM}/=;*_mH-1zHz׸K ڢTo6K B_?"_ꗁ&g.(Cշq>%<{R5ÎoRHU:v/~ڼ-DsSjAL4GfJC'0c $~C$Ś+sFƤ^|"n. SFtA!} c_.q?٥H_A4}6P rS# IDATR2xzY?yr`hrgab}}w!kSaW鄶úcv:e/(":}n(twf؀}03`"6,ٳ)-jm7V KՑgr-C2`Q#'J_ȥr,ɓiz'.WI] v; ׽?T-yDW@ -ʔ6&#Gjד &D.r~%8]1$A#?S"iw6 ]h!qi(vt5˹a:#Ę̈́oÒ/pCp e3 @Y ȺMסة,P/3tS W_ckjґA,ToLq~܍K \M7.|jtʟ$|9W~pT`Gs<B'~5}=n.I_٫62GXU:? nֵ yC?=qP:62$]xXdv:B~+u XxA|  ~􇍚 ~B\ ? 9$%e;-Y2laG%|E|YgQů 1Q |XEZ-d#3L@k(3̆,g."&#?O!&av:ƦyY2/HEOaW yICE|[HDk| : U $t~8:G#0&Z $/mFvcf7!i+3#ȐzqtbWʿ#rC']z]&wv+_:U OOfd8|615Ol@ o%&l@Bfe^'Gft;D>.E?Y++/,HxM)}*A$oT&I@"&t;a4pzz)Ĝ :vݗ͜+,Gi+ÄYm&H>Y"70őLtS4w%b1YPp^prr?2jX%ΗO\Cn1h3x|e ;s$dh y':5 B-$c b{*~t ς?1,'Kyu6p5$Gt:1'^>-y!=DAav3?߸A~Zl]?$z|!0P>ܐK^/a"%XpTA TaoΩ5I֍ e;g=*Pt ,^.c Syau!'O~3}nNvݺ|?KON?bDU!b"vBł aܶ* {𸝯t},XԞHlL9yG^ㄱdB߁ lFO3euxg±8@U|q$1V3\/vD5 2 1L ^??- '*kg]],Uyeu 1l Z_:yEP?Dn'q֛^v<ڸ;E}?y|2SW=¾5xU^ܙCuc Nd;6q3r""\ҷ^wfWnK~+/%XShQޅ[){ęA0ag'*=tǞ"]f/hB"+OrQE.Lg#?mFV,ZU?g+M\ٟm(EʳQrxDz,/ls< 4Rn!^N>d3qnhwq{ -OOXo?2YyɢW']a,pn{_1=+̧D>O?aGv2'*NMCJ]urh_ׁ ̠lD=V3%) ,a_=C8MpT25vjh 7 G>.B%4-n[4wRC9tNI#ֻ8Yzz o/G  "+f/oXȿLD:yB ٳ?Zia8l3!ZUƪMIŕg' , w/dY](szvˈ>;y< 63Yl/'?dwI:뛩Z$. |-|F1-LuaLgS}w8g8㗯rwa!X9S|Ϣ̌m\>gC@$0Gsݦ_~QOt_Ɲ|]1|zQf\uӳrѣ[lDu0S{H_'K-g,0|LwH7p+`}婸1`_Yݶ_tYvUa:|#䝿lv2,B$h#:Lym M_K™7~1EGTFY`L":{Aw~ 5)eg?NPNtޮ)tm-l`ᑫibe/mg+qŌeSOALN[{姟S}p$f۰a,q{ ObhڟoLqD&,n&i7I:i~gAUOmLf|n/! 8ِEPv%)KwId:xO?%i{φ4R*dq֚ܵݴF')w&mZ&aLJ(? Rv(tv2ٺlK̄0VȫA!{c^oieTMh+؁#qƸGa=͓0҇6 eWVwҐ~:|S˪L r2!q]Uu'TȔIq ]?L4eD  f˦XN;z"_ϵ> Y~(6Efz1 ~rE#Qڶz~^%lGNl=ޙu&GJz[?>;b/:K]Fn@]ek0:ΣXٝ/`p Ug@@DЉ27mTPoLMHֵ}]G/I|~6WePo|7-]"8#ݑTF*.`T_M:b S>=ndtn6V&sǻybL//cAD—+(Ld1rKA2&ʱHQſHOwީl1 er)žALM0îaѫUм qkdHTd,u>ItY9{pbW`FlSq 9R>N.9pjG} PQ"]pe6]lK!Ar8`yaZz|2[Ნ?IeLEs+Lv3LJ_uR|Zfj+òES:\b7.wk_t RNuޗJvRV&$~b$21{e b4{4 $c2CzQ+$F=N7#D(|Ae3O+)}:N?n$#iat$ѦTk`nm)LMDx"$5e~K꣐sYj΃50QvT"/S{̣̯By`iȦR=7.c~̔)NQ EʶHuk}3H>GU`KbfYA>_vq E^V88ߨ/DݎDRJr=Mpp-*H4M"]>V[h[Oj_0\7煮O,7)*];( 5/b!?wʥ@!@.˟k$@tcZyd}k?>ڄ<U[y} S}&Je,#w2C>t?W lN)4ISQ=Z=uQ_ ?68r frHapOZd- ȋ]]vꠑ0a=xO5}cխV8mZHVP# $vQ"]^q`HǟqI%~V3 ʎHmQ˨z fMqV^gep0a>ʟK;ۆvu27!K'uXW}ʹL4:A#\;v36Pob}ڍ}_r"2{ &Y7/WG2)7XB~Vz 7N+:p/?vIrיSs,=Cm$Ɩ I@M${&ЉE허1DNs(A*@}3-, KM\VS%;NN'?||/],1 1zELPñ bI,:'hA<ƌw述S,"6i.ܦ5sԈPdzҁD3s9V̷ƪyKx.k>R "ռwWݦlt̓WѠa,SnuKwEԗ !.nP<-lpYj'GxA\>'&.&pdt29M+3|EmaA[|Yrr$ r>G\9za.|f"UuG9 ?/`f *JO}PP׋=;l /!fp&EƛC‹g=W]o' ;Nc ;l\l6DbtC6ncц]#Vh«ȭ,؇w b$η~<\<"2 AMoe_a%mWuDZ%Nj'w*#T@$xlT)ۻ[ھr,7ue]e9qB~d8;% t/+ df"YgB)Ν̬CfX |/g/{I9oG+}3 +bٸ ivg\g0ڍ̝jEo*B2˵KOw$LNtb_!Ҳ~dGEO@_h0~'[#{`+( '밍ϪKB'a?c|=w S#1:br0.us^n7t*,w*Aٽҫ KG KCsO- K2`Tie) ק0K3b> ?kAj d..1bMJqb?V=gx 5e2XC]l1aV3:ggmX?%ɱ(_ȳ(yK>gy=%Ͼ1;_3 3yqtkwܲG5ŋ1C}H,M.0`(xҎ#yp¬?oX=+xBUTO$<eǎyX(⻒9vȧב0V\N3m_M cV>~O._k'pRLdQ8AE.'n>\CQ f1qtG$_G@o oƫ>n1D|C/pf{T6uW3}{7%Ŷ(^UIn=d w-!<:/!s|UX@*JV;HR]Z(cf#NC. a20V~cKW~q[vVc">VŅ>!gkHb<7Kl[~@Ƃp!Ə~XDUj$} P>H aTr5p/ڟC' _v__IDЗ3m ۸ŤR3蟨^3+leIJ4kp"A9k+1'{q4?k± ٸw.4\geL>fb[d$kFq N)fICh“o'=zKF'8I{eb*6v0fMaIiY\QYnI;gJ1&\6p : 蕿T{{ AX3(_2QYDIԮ\#!c/n:]?gҜ|!d4UuI)8*Y"g:^UߜK'GGA8+z'}FG7+w~7Y?MhWWsҦ=uބ70MȒ@B|vˤ$$d')HCC~&dK2wa$4><<&Jorե*RNY Fo:RW}:\ {ѽ|~RH{8^גrc@> !U mh_nW{x5iM|l$}^MS.mfc-V+}OqYx "t0G<^1%q-M1j0,$OlWq3 =}g|qR xh~&i'! -}{쇙A#9c/X+G IDAT.4ijn⤝nH%13Gž1 Q6C-hǥ>,bB d΅P&~KNn(cbRbCAO6_ &<}4*[vfGj 75d>x*?rFp2)b_$G23^n> JI ,M"ӳvA&oq .dJb-d NPI#Jr]zGC ,3?žMzc.vIqGʮ(; k-*٣ +pIg6qh簅a*k6vaF?GY1a;PڈFio0~91[Vr=n`{Ozj=GX)cW堄HEmo[T.P&Nr|SI i?WAg:E]ɍ5v Q,iV,3T؅~35o4nA7}^$ހ2Fo;eW|7TW=ƀ4>IQ{۳;Xa<H"&Nޙ?85}R}yZon o?x0"Ā(j,Grxq6.]_ϵխq9[&M4V6tϊ)k܄IMгAĽĆ:-0^qу3! n\N5J kaWfn`ƿ]-'cF22l Wk32>I\ y衈ۭ㸆f 0˕;>ڄ,]l'#Ŏ9LճOr 9fAqao0 b;dS " j$3*qm^"՘sYssq `KPIn2qѪ9bcCL'ҽ ^U?p;S̠O~wCu_,-.v.Ιu:Ou]ճQM[zR5Bv:,j0ZٳM_2wCҥjR r0֑OJROA<2`l ~4c16" $hq6.OV",*{['cpD7OM aTgpؔ|[ēOu8z 't=7O_I܁g~ɒ@\+h=70Pa68&3N8Wbj> Pa;i6.6 fb j>7d{a0tX6=̮s4y:4d\]nN";[6SwV8Oc)?||(6=3N" dY X>K<7p B0T?$'8j/G,5W&嘹c23σ"NS.`b7#rLD~pV6d;py:ybP9א> ^]]]y9J=z#wED`A3 /Ux1HիXFXB˦ϕ)VC}|juSŒ6SLxb?"W$!Um&)IӾ}N?nرeAxϫ^[V~h;x2˒we(gl "n!D}u?D ݟ_â۹I1 _ؙNŸtz21Ɠ PU|F _2G60N:vpL?UΔn%][:} E.l񈾜 O$]]-%b Um$-vl\VDfr?0CLCd4$cPIi tSbKqcrĝ ~/H($#*}b;( e( +&;IC6;I e<~ n-DoӊnQVn˟)}1Ȅ*h1 "OqItMRD;5`d\S6L^2+ pnB""W鳁@T2#1rOd;$-# 4q1Z7 -8WY1K)3)0D_ҵQoI 6TRExB\bF9䍭 &r>9NxKp嶛 7ewjjMħt3L7/׷dG rؠ*]!,pZ2G bw'~wRV26xB8K^8!FnkC<'r _8ca2F'eB_e'W+<_1}5 `ۧI~ek/ڻq$ $HQ_'%^hGF=]ؘpf!4`}b'H@GObO:v*EVV%{J #:G["R9,Z AݖucppUkSyp^iYimm܋Iruӯ!峁 ۦx+월!x0^~యwܹ޼{oZUKW^Zn~d| 1 ] fLʸ/ !p {sl[/{W:&>2,F{o,W“} (xy[)UY7$fdyB&<^> ~>мOlK>(0[wەB/L 'c$*> BDŽS=ǓR 8_3;/f,?O%^#YnJQ{H a 0SFoۛƗI}Pge_iSQ=S<0yyq!Fw\PQ s]`iR(I[&I*n\KaƐb 9">NҠl HȻ{JN~ebHFڔLA#O:tsQ$\Y%+J(oŪ9hﱏ :2O@' L21 $b|u׆{r n5AuqV~o'na+|#]l;,;zg !їey}]Uvmew:X]蕏u@HdL(FX1V}vNj\ F\^ηWGd'#]( N"w*xV$Ur~3!$nrm;m^f՝f_\aYlcvމg%)?I 9s&ek?FD) Q}څ{3L^P8]_`"C"`׹f?~8Fqfir5Y߮)^2fYYGN>-=u^7!ou^L`!36 >o*=Oͧn[r^u mw<vX9.GvN9SOPqlVB˪~jo(ľvό!T[/$-κђlK~Buo63gImXSq2 'Z㐭"h>~"!߽k k܌'E CE{C.Pb4~+^ QD ?JvmpO8VVVnW)@=Ӈ]؁CeFFXRcoWZq7vݪ~C3ex-h5B쿦l1(P>\ q;\I2Be7,OF܆ " 8*b o'9+&PjϢu^dxNv]؁$q__'EƢ˲??`>^x˗#\@OHOaAxC#a.v;C9mƊb;#{06,*ΤN$5yD`?\~CUFt}G'sR,ChS7 qYK~2DS)lMМRF&H=3_֭U2\v |SB@#LXb|޺0H}7ĂJ4ˀv3߾ ?˗O'/@`Ťn·Y07kQ#)"3K]ǰŐ0*FRɼ38Ą>5 %},Ɉ(* "O/n~U.]0ݴqڵn,Qɤ+'b\M* ͭYT)NYWHK 8y­S@&{uN$WdKvIb@&2~BnAJ\LFl"Tm<qߎ%9>2%VV2۝G`O3{[1\%>eNJ.6 Y%Q :|ӝq 9 }^ޒ?rgO_9؆d.~>j^yEWzu,Bq~VNc?Sc ܙKG$";r߄&<'cy'dq\~0βLC+|J@S'6?]:#2q/ \Æ|`)6$xd6'V,(|pr~F]DD&$/q6/t/LMnsj[S>޾5 r@|[Q쿫_c7<)cf[ʝReƟ/_jAI"4yC"0Lwl]b1bovC! M.wSX%%&$ʿ+̸E#|/ئ+eTaFI8%!D` 629d:"tLaH kۭgګDv-Iz~+;S( /zt]] avȈ5@#'װG8T29i8q]boYw72w~Ed1bq|)LKy7:Xtߦn_ɃqOߗa߱C+rﺓduq0+zA+zApLv䩏!o9 4d(+ oY,4Gz 7ퟔm[ϫ/|&4߭y>&}/f@}3X'(."S~/|tl$b% 9k}3t_f`&th?6XRO]'j42 ݿ"\DX& }?NbpU3pSo4o"9l>5,@3 _NE_naSb`>Q^IS\l|IM>~~($[cab.,t"3k`lv~# Aޟv)l w[ipEy EuƯ5&vvH l>~gR0"/!F篷wP?!zIk% ;O?IfycEzWh\IkE(d kEwwv~?@Ì,;\9h}a}A*_Ka)M~/lY|27s?]ƈƠm@WnN"M6A W_@:-?-1'˜s+q':7D Ā-lC99ʼna|s_:=ԣ6-4|vñ?ĠmIxЗM/'$rOpǯT vمK}XnuN+ 035Ɏg[Jo~>k43w;rq5~Qb"yrב.; ^,C/xL>{bdJ{~ nE_>dp~-t:&{<욯.kwe4YIR>vI{'On^)ߨl*o\AHnJUR$CRǶ6ϩDTd ;+h )A$I?>#vHW8vujO aLV(Qp"Y$jX#OKoolEeQv]YD> Rkv!jd7/ƹ7|\Cą*![  scЦZcj IDATqrqK&?v9ʸ[2(u-†~1Z%ŁcO1)C0~;3{]ٽZb|$_A!OVOCmV6sMc w qؗJG2$Di˻9U>XF ë|2ϒ${?a :撻g<ݶ5F`Hp `T/\̄^p^MH?3c拿?GPWLK&|ޓ'cqՎPTBEŦZe,/ƃd,._Ї_f] {Yw5p #7J6#2jvUәxn?v"쟘<٣+>m0a#_$3\aBN8YFH&^~@v[;p訾J1ݨNeߤٚ3hG)A؆/=&exc`QvV٬ Bo^{NƝlk~|5c˪҅Wˌk#) _Vq, >G\h޾OxfV'^}*8wwMOPt|-nT%ZkAu&K،NF2_z+)==l"_^O*/tSE;]H-z U]WԥX}{S7+582f#@gǤKIcևk`붰I@8-7ɓ[+];׻t`7k|Q T$U?v4eӇsV.}u>]N;nNS/!uLN+|YLQI|E ]'=H?4[vמtMOؿ/%~ka-;_ MtO3z`)?uY-._zˀ(5!,_+c*$M 3F{=Bm>CI6 '4׵Нx5%2M"v2+rlmծ_E G 6l2{U$Yp36/yh.*o`(GU&p<qT؅ls]2iǕVAeMTNeJz(A,JU/`~C: ,NV.d {k%>k>[<57 |$6H48N7L bbx~cɗ[P65|ƏVq?YFHPvҸo,o3q&macחǸ17pC/p68qPf v&WNag "!cqqPCl$z3QIBa&9e(7sΟ0>:bug9L|QWK._"{%{8E`2IbX1@>O8Xf9v)? ?8/_?hB$H&[tA(GƤmu؜@Io]`&Pē!mU0deNW+L1'ʄnON _1_ڏS'[C=0l_=l,bPFt>9b߃%s|N%~/ܺ3^j*VzXnFWzw_+[c.}o'8;_T^* Ue=1fۛ 13z0^x+t%W%I fp"ÇBeb":l͓81#mxn9NN高/%(0d6(,|+ u|RU&nFqҺJ1x=qƕ_b)%1C)6*ڥ41d܏r Y}}L۹͌2w>QT2]wK7O`]~?@3[9uY_s"L_A0RL>=U(dD.䮿we6 Pp\1srJ j etAֶ$c8UzHH)%7!l,nEؿ68m<ٕ"_xOV~h|Kl,ID\AV,mj7Ws?>6茿}vE?mJa5x/w1:.l mETжG,7qP~`W7g> '̗z]X.|DjɂN$kD/?/Z]cVDLҏJ=26 : <9t2d[r1 ͫe )f*cMuXiBGt2!_Co"+;6pu{َ&bK=cXnĭ~Iw[S8ǣ(dgߦpi1X"œ?&MqܜbfЊQ[Z@L٠?tNEX |;wIXK{gC+}Pm3ž')MXFOjf~sDH5X`̮'p,t-1b "=ϭ[Ц B7lg\$D^ER^SxxWFTdo *+nf'ލnb %m(.H_~BaSg="{9&1+I^ԑRm9O'IK馝wQE+ؠ*dт!o:Xʓ@E,iXfur@HMwx0(R[,މ z~K/vl ǵ_ KR7$R4%!s[w Du#ڎz_q}.qG5c|8!dݸFB2yp\[ XrƸPv>Q]O3>6 XgT_}R=D`LԠv(nE0'وt 1hҠ2gD2@" b赫͊Xb"hBNtڇ:؍1~ p) -\vYk¸ @/~c=6&Ip_P `@2Hߠ=H<]ǠXX8Zl[xbedo^?| mݙ#}O0HWr_R~:1Wn?S6WL,P$+1پžx/$e>nP- ,gY+̖M㸤*=n/?1G뮱0hKܺ|mY&d< 2q%Aռ C}u ϻ{_䛼Gz_p=b:Hc+z/$V,cMrG ? %B̍e(Y%j6(c1@u$]=ФLx|'>柬e]:"NOuX Vf.%6W.a][w^0do2s胘$gL@gmi^V/tCя۾I&CQнueUEO-ҳ9ޭm6yW-5;ǕBњn}¸@:8tc EdlPĐCȨ`LX' surs6LZ!!hT[8>^ϓ0+Xܕ}l'UDⳀ IUY8rμr+`VپHWlRx"Pp oeBqq1h~"Ft8~po(j*;*;y/N`ni_eД0$/ls<5Q:i x7'[}(tplN0^Tȵ%3oM>Y}c1)4Ia_Ϳ EI&f~'*6qЇ DcG_$l+G?vJr’x~V?'U@>;RpްS?_*r_9B0?MI"8FN*ү|>WS^ WSo|v!KrY8g׎Uďga?pU| S60w'29Z,*`Xsc0Vqxo}EI1¤'|3}:*W1 _%N&^ uiyRM029(d(d=^|suJd1֩$ g򎶀.`u7_I@-kwZ~@ b_D|Pzϟoˇp`Ą~Lľmoٶ wf4JIc{FM>h^>YB˜y@K _FꞒ*CvH ;'vwSvX!OIc+wbN4Rup+18&a#TYcOA J~'plvY?z"yS0QkQt~c=+|4ҏ7 K8%+%95yFNݦ]kiBBr4<ѸJ6_?3f0)11~6ct8 _&pW8ٳ&@b9[;Æo^n+Oy{8s"94`'Y*{S#cl d}AV@2 +JW(t$#`PA=Z0+mݮHv< OdI۹OL1_:T܆ƙa42.'E@lJȥ춝~3`T_Q]#PLX}N}TyP$C$|(Enct,:%$n Xn~']g|5{8/< 6^y=LDmKL/x\>ŅSVBGmUЇ=y'ϕ7@_W>Qg6M鄵덟Wݎ3;M`~NFٰ s}29Xv#ԡuXr/C`Mt!e[KjGd?VF&Pn*{ 8) 泆;\}^m¯ E:@\_ 멽lD'J32;q=z|繊ϸ=%dvrˤk6l㶘et \1?G HJv'x\%jI.M-",2u39c Ʈ6 xe+M˃Id; -]IQuj#R  CH,M`iInEOK~ĊM<{8'89}'Kж}$J=. O: b8JBQ$N6_+'6!U 9ŋl.iڀi im{&_HFo bI,_7xva>YG X~3S0~/rIݢC8'Jۀ,yy#+j[gl?JݩSm=$ Gta'0 %S=Ɵ:~1#_"p2U*1%az,?+vo ~]+ 'NG$SQZ,"JvT?,Op 2EI anv'3 Y/zTI&")š_Q kW78Dt3 0Ή\.[EprC&8cL0/HI=8CT 0` \ ,K{S%e EFvV:N?Wq8W,n׿oO 4|2qߚ/zd s:ETNg$>$!YmY!*uku²,#gL;"V9{4mj$p0H+8?o+U'>%`La;eL"EٹOe^itd4^?+xsڟթ[EOٸLT6.:Em[&Fb%yEc _enq7$nÐ܊j7 =rŭ.I\qfR* lmMWhk$Qq@ #:2BH tdHRz}C}N,c%[fJJd7gR/H_z蹋97A2v ƁD I"Spi6/^"Apʅ8Tj k駉l@$0 [bq׌rE[( l3JjF< oULB?<'@N0qŁNNfnsM oddjĂY9ym],Ǥ ?K uq'q0G f~F7uSr<+UPL2}V:j,uLVaaMe&ʝu-g2nvGHGS+)J6n/pmlI3\m-O]s.Yf8n BRq}/1ˢ]HdXb6v]˺=ޮAxKsrnKvwWH $/ N Q_K%pH&逅 FkZ&'A0WH &Df /n$Ү]n&>ڥtkۉ7Df&EL"5M3Im1OQXSsuO4[KOO,,ޯ1_YVWIu%F IDAT y9 ơDe %ybdēf%KMΎc+[U HtD)2qq;Y~My7n f}M*N7=y\Uѥrny*  nYGh\l.T|^q ΗDk19rb]Y$iύmڮ ӾmGI:'0f'$Hj9OƢM/_6~ч&{:aLNpZ"p mE"U>kяn:nM7G1 fS:z}yֳxg k}.`jtـJA}ގD+Kq&8k?O\Q_W{7|7= ]R% ,~n߉[IiL=v`lx)SMB~*Sd[?4#O~_7CmJKַ Ƽ2M}pA2 v,[\ӵq("AYXnof9b1ke]B]7q<,:ԃI,:f 'TUMOB$ e~$=j Hj`f77$yC8065aYǘ8B͓@8%c}N1/`pB9&go9E2L`v$LbO1\r i?M07wJ~[M329cR~OWK6!?Q5Q' #=$$jD+I`O$tW@&ఏZya(a$ȋN89HTɫu50f(KS<1fDA%xDwz$%7 +ko7qM oYq,'A~|دtJ>*ieD"K"?Qdd㎽U]dLԘv.6f$僈`%Ī2ƺ% T2'(,|tcXN Wח;_]տ>MخH΁`6 )1uw ?0;/OHLT/ugp@UK['H+퐴|V$,*&h&7d"v86e՛!&s ũ86cLu9S8J)Ta!ԣ-'B\`s6?'_"sFǁUͅ(p"i 8a(tAS@mSuV"Ox}tO|nۮd HwbTzY:>EX[Up9"y#y8>#yGu'+;/ıo~'/?EWF!mk`Rj\h^\,&o I3'3//k Uf $'atMm;7ۭN*I8xw[jGmb0*dG OP+^NۛO /WzNdS˿jRt/+eL`&_(\d"$ CЏgiGwqQ"lr-d64Gq.A^;W'$Aݭۡ,u3CVU8TqZN?rXɫu+ A0pSRS+h@T[-o8~.yc?^_-ωbbi>deĶAݱ,?~x먲rn__dd:VdRpB)oˮd+;ץ8 .d·vT]ްNp^~''6A~&Z!q' Ik-H.MZx{ ZB^8:M ʁ6Ņ~.4(8F OӌbfD 0ΰ 9HH֔~ ;;O[X@gt^]Yj:ژ↍jb.k1,Kbm0oߊ|$ "3í| Guņqeb/Vq&kPچ\XI4H}/W>9 Oewv7{U:Ʊ96?;|_Ak|:F"Ian_q\I6Aoq3N4nxf|k$ /"dq07$)dw LȮTID_?%RN~WT:NփH`I*QJH/,N6_Hh?#/ GKLeQ~c99]oJk?_ߢ@&M4fXl)ȩ oi^ X67ƿd{/VXYYi I$7[mVv}6}N"1 fgdժ-:ngcPnnc׼?6ς>N15VP}9BG#C 5O@<_'<8uHtPC+HHm*pTRI:g|J}pҋT:~h.[1in7w  /B鋗¥2_+vRV6\9d'62qbqN9NB&>q'rit~v]-] A~|yP3GW)Dݦ8_?Dd~6m%7k5 X1$"c"[d2;B;Ev(H!doDk-ո:R+7s{"HTd@4/n&;cXl@L%NjF=C#$_*{TPl¬{v-mQt4TkPLt-ee|bϏ?6YF,OT';уx8_oq3YL޸1qеf<į Ax䮿f,?j~A(7F;ݶ?# $m+ /-xi-9<{e;vm|l/'pu 0Iy· ]Ģ{ µT`2k!0JݮX#B~l˶7ԲIwx ]9,WInofGo8JQ_]I*Fkg).Y oEh5DAϓ;GWuVtH7٠c{~'&Y K1ئb?)ʆMvtUNr+W{ۿ*Fw ZXё>c#(_u>G-s;~ãgS7V/qo% ys}"hEИa ),BA&0&=Sd,{VoLb?YU@ldpZw]6`3|a/ H|3LSlhbqSuyWJI&]u4vs2aejش \-{o~€/4d'HLnv8٬I_lpˬҝwcxBB@@|aSŭ?֌3O"9"d][w BEqb{~[IBfK3H}U<&pU |NJ{e Ä0Y/x8857ij6J{wBS QXwpqF~  moX IE}BLTpB,Q?;bn}}@B$[g_0wf٭[IC* )~ΆBsm\`b +tq5*Ee BH~X_1ڛr(}^kZHDr:)n w/OY'q\B>h"7u)4YG}^) > XX_}e:K"_b+mp  XyMIS~Hdeξ?y1RXXP6>Zrhn^W>w&ƙj%F/FYfRMEm}lHƶs^NCLrVL*<9II! 2?DeG0%[<8.D&uf~,se\&~6|';;y\Ϯ3ԌuFr/|n ܵek| Vwږokbȓ#;DhuY[YTr;QI QKb,}3ߧ8]ҡ+z9j^-\9x[P#c[J6iG8XcIn Flva^Jle>j&Q^rj=VSzr*s:BW&Wg7MCys6hWARycSLW9w> yv9mql%(ltȒdYLuן+&dE:&+ΐׁd\_Ɛq0Itq\m\Q<kYu/N/Bڢ!0]y4̠K7@}+LgF4[cEP>&KX(+Bg࠹,<9pL|J k39FE@%Hx-X 'rY4?Nvazuc]ⷊ3#NgYIq۸9!&qxήSǦAw<ܮϮ w$ /JNp@_ifU_ :p3tö#8ԫ!k~~K@͆/ttTLqYM2H\4c2ɴǸ]} (i5hϗ1 sjLdߝpwJd?@_ǬLt $L/be`qWm/\-F+'lʩmϘxryq+Yö$r8Qyql-#yNGyl+(O\c=l'{Ad7uW}/1OuNW_ckԶpq0A 0Iڊ࿑Ɉ)!NN l9>>3EHtǶIqBNNnr Y~Ba*V+D)8IA=q y8 oݶD\YSԿ(mت}l󊰷(3~_"uf5mI +2ɸkܾQmK;0bVu\T8m3xҽϠv8$p=^/ȥ#Jo.'ty0YorLq(?O_wOrmj[ Ӄ7ijJc(bqQK:][#9߂mN1:6ŏ\&csWVͷ-qj Ң/2m%#Scb>E&VA&\@h?1.>I6c5աr V01BT?[.`1fd=sW• 6יdgcw/7vh뿰$JIL_ cS6؞'*#F+_U^MVW//Vd|fS$kPz u״E) ~N\{|;NI1V9Uq.b>a-OǢnvޚY8blm=8JVՂ_2[q}-\O(Oچu4kuʗ7/ gu IېA$a,%u(j ::OX`d7 ÉȆq 5r_Nt׆`^|!N xlc˶wȉZiy [`u391!@ sek>I&rumM'Q'L&c#p'}q"ϕ>u;rOMWLvXQ.g'VE29 .BWe3ɒF&%>ǫb9ЏdTx8VFS]ES݇e/@:+xqlCdl\!9s@SAynzO; nɛWLܥ0 {u }I Z&Ճ Ke ,ΦT5&`D: ۸ [|~l1/3>?Id,cqzvє zy%isg^q$_\ v grƾ'Elyިg|b kcosq^|ŃXwSWC"KA rA`-A,Z;_Xv!Z@w^/^`‡b,/;Q{gS^c{tCH>s2L\|>k" glN(倊[jSq&#vWrbӒ8Ufrl.QWfdm־S9_K\nq.F>#nhm9*pSk6,>~݋\{·9" xqts\>|Ud!~r2 8ԁX-?# Ycrhv-i˭dq{G_/Wd.}t_-:˧E<_S5ml 0Q/m2| P?8;,>5Q5ښ;9b?$}/oY|_8+9asW|vqI6.E5MTh)үCT_^'= 8iԞf~~rn):W7߶ېMC-ygۈ3kgU/_Ӫ/Ǧ%׊><_(u&rǤz:sỌygQ[e{^|a[6= ՊdTߎ YpXJ[HIiN> g[$K2wv1 $V6;fnQ`&'lraS9}='0_ dV3Md_x"Zg ɹ=BOjgb" Cݪ͆m)6}q(n Okn sqI$pˉ_x(ⴏo$Tu+}gt<8 _mq9\$v뺿&dVnQ_bp&.U BU̕ Uѷ-&fdgϦsl\<5'Oؐ*Vdl/l%*QBMPv86Fк͹2'S'|w}-ή VtqQnj<ϰ 5 1+z1iχsyҠMC߸(Qr |n+40vz`瞙D#!h8s"8*")e|rr3 ٿm}{ǵX4qInGM۟׶p%gV% BڔĮ"qJdPS|8 N{D!l͓nw:Š,ۣCz|E,]"mCSE*b v _m{~>ghy:+VǮy&kI@&Lg/aY#{ўt<=Pi/פ'ks:H0*EK~N I/M NJ>/(Y%L b6S g93.$r4+ſ+qK%;O듇׳_8*]Y&8Xf;:nvXs0n/nοƈϵuHv:nz{zuݽtⴅOҸQUҗ@N6HT' sVg rɞ *%rĐ*◖_a_R]l/VjqV+:2vm V2$1vLC*x>okT1f藡k':ǝW|(wNvp{t@c?~ #U%QE"gwv-^@Q‰8o^8@<9? mkAR`CݽXIc4vh8 =+2IwI{-s9yY)2$2\yU>[\3xng7f'?.tWv}Nk{p.\s\SП\ ߯HYn;&nBvF4QG-Ŀm'rUZ8 yYgvF13|퇥c] a5f%M(k`SFv[))lHn#aOD*#nJN=$jP'xK&^7$u(.Ap6$ZAw :1f 삵+<\fXm:4A/"R} Hp< %bqd9 (on*/fK6M^a۷;jg?s0<$α_&}X6:s MmBOIGWD8c$&p)O \)Ƕeõ/yYmk N$]TϘF_KF&_fnJx_!inR//]҅Kz>OOO}D]ʶ|Gڐ~X8O|ƕw ܓ6bf +_JΫk| 6z;˓ w1nŰ8:lX=c$yd'rH[Ɋޓ>_(_B?HoS))᯷xWVk4gwi[JIխ6d,C8dlmKrF}H  e{fV nIEc9Ls$ Ի'K $|.V8D]w[bqԾ_A,kU߄<`^l&L;0À-ϧ|!/c^`Ou<޲>w>n'7nU/UUnsmYπvw֩e?A^$۾r͎*^] LsIuIƏ1Tg?ۤ 0pe]gmc6jI P\0s$:ڕ lK#Bn &VP"D|tf n}1,`/DzC&L3M3OdKYf71vO_KƁυ>g3>_׿ Gg^G\or<~| cdJyN4LŲ}peyb-|ͯ{i? Rߏx 9٦]жo𓫀I*~ h-`jV{fE擂?`i#>+qA:wǀ jϽvVl%SЙv8fg">J˓<7gҾHuJlLb0|Է3b&  d'4u`B:cdt_͌Se<~틾]horOm_WЧ?Lyqs{پ^5GUcپfg 3V6)C2q])K%3&^Fڛ޴He짡, ̑jS'0Ñ0~#NapYnT'{2W8o?lsz'Q.E9ξ“Sv?Pz~__ 58Cu_.ZQz}˨sH'dL;ضg}`H"6}Qhj/s8^ ,-+7K{1i#nv(M-=i5.oxxqEW?I&㚬W^  =)S|vx:~;DO>XߝN>Y&\ίL 8SD'დv];~O%{2w TOt=ūu‡o[Oo_Lqp<7q^uR廒qsStΫ%UAi[E;`u)ο ,bWs ]F\/ :w ˌ6U {%So;;?y v {?&rX*GKGw_z~$}NsQ}I^x)e޳~7)d8 xᨷ.o/-P2~.S lr|h;/2o&T}U$=HtE-|꥽#MQ|._/$ X?e?gAQt~? dО:'ԫky:`{7%؏%H ʾV?edYWJFef}t䟴 v!o,-5(3. 9XmKʎ煇q-x(r^@<5ЗeU}_6.^Щ]/cOl=ϸH+iu7 L;Aqm5zIgX0:^@T0b''H362>B>LdC͵Q-?A]XOݾ!Ep+w *O}BoҎ^m>W_%AqXoRqӵs`;ϡ/hVs.4N~uHHe%>_m%$Q_Kn>5{ : +F(>|͋wyl)y]0鋝;\ӷk+Afԩecd3Nz$n'oyӮr,A땡}4tڵ'ڸ:}/,AJ)x?:gۨ<ݛd%}}gqVu:CLDΌhU#p񞻉A8;'ZwAc7/9? UVAG%'ӛeV[e;>Հy a-p\O4x"A{Ĉٞ 6c> -Ѯ[[k_izzgo<>A*|_ya5ˡr/ZcS}r|bT`P"hJ&ymd@ϸP.I0)akܬT C'E:kUƝwQAƦc38p'pZ { 툗 }Nn.oqԩ¹ 6cP~NeY?^~_lv>@Iy>-z]$Qn5f$^ ́>]N6?Q|]_4;˂/~a_V| 1B<]K1vROZAEV&a FJa!`kǓ-mv͡lʩi=$8xuu&1sy:TM'}bZlk5b?j\'{(f@ IDATQwƱlj.S:>fA F}R q@>e*QmbrVM {K*[I%#Yׯ'Q/]V]៴YgeExV8}<վ;OR1=/)s}熫r*ʽ2C}G{"nE^YZFjUZ?s#FiR?׶--x],CO_ya>d/d N/Uy"1gTԊ8 <<EG%ʃ/Sy ŀo>aþ@Nlj}Szԁ5N,Lp ػIw^:Ѿ_8i̧ M\/كG}Vg[66K~>T9yG7x;1seI?tԱ˒}G{./*}z6{G/gƟuR?ˀ)>Wo.0#C5z?8y-2޴fqP#7Y{@eF&SG(KŖO5q= u= }r0Ȯ,+t2 65r+2OͲѧ 6>s迓 JME;V?$_͡+O:1;Nc[vcKct,dfop+u3Qs'@O.ģY,i+H<{==(hc^"ni3~lw׹8HЪH;Oۄ8_h\6YSW83( cܼ99ݗL'aBhh}< ,0X R%V(]6Mbֱӄ)äW5艺g'Ɯ>c9DqvD9>o۠jţ mR5On*)XQnOm㹝l/ \O -)Ag3(|3gr _=uT0Dlڣ潌,8_xD~ rgW/<w?pr^sM7 9zvGz,6P,3{']G6\n\irTrR*zND*ҍv8I2!N}LJj*i?%߂6Np.;']]U/w\Yc2rlG;!>U2>]$du}H<$o Ut?_G*>h`BPHxk8}eQɃ"YH||sr֫.whW]DZC:-Еti1?iC fnS=}^,!~=A'=Q- eU+drX'* { g} <2RA;/6c#is2<ԫvt%}Ag$ͱ~TI6Sw/h`7<6F}Ií* :|Dy4dǥqC(ΩMXĈ/vQ &czD&КWMFA؁9LDݘl>:ѥ3Y E9 :yќ^?Ix9A_+x&.G瞧/~+p-pA6&uUM1;_8JǮf?nymp ǽO'6|V|้y g߅INq]OV| O LSY5D>JʁUu tN@@@WHI sTz~o>9mYL,Q<ƿ6σ4k'öRgq袎~ªߝ ; <֋/'`N}yU ]#q@hiR׬p^Uy`aqx~\I"P;pJ|rЬ>:#*VkIv1.Ce.dcE7Ko09 0#d$:MFNdS;x'^sqTo19E#>X-7//+'T/,mfY eIv.|lVp@^_*y_{7?,,3~ri"S#lCFeΕ"Y?;b%=p6On_ABlR ~A5 I#{N8ӯԹQg~}͎m; :6L^;-n.Kw6b%ƻlm~G6 V9d_H8my] `r."IfnU@Doǡu3>'V dCu"ݮnK[UX Uzcyj)4'YL}'$?i>xez:9޴Ap(ۅ6ھdIx9@=}ȳ_J(t5A6Mȱv '?76/H慫UQz/et#q}y_N-' h@+دG۬1o+hOyN>,y mi[ƽE kdZ |L//TB6$puW8X!w?;腅u`7kUqrY|efn}wXhdīζMcW_^\H%d!W/F l=!UO||yr΃? oMaDD>?go=U\>Dz?.^YDs{\ԙEs-#aG>SSy֟գ ^\/1gGQz_{IA+pgbນN]Ƕq:7e>6oPڇոZ5(G<;n؏ Jj>^_|=/k[*Ŵ"3qU}'w}moGmtgvm֝G-@*'yr~)W΍V ‡yj/i(m{~OF`n8+=*H1c~[ 񔁠 h3O4,Wz"1P#S>y \)Jg'Y=!BLχvmVɭ2wk}JG/v{>>~*~+eT$ٿ'0um~6Qy0@g{۾9nY \k/E?Mʣ1B>c?U !p/$oL^4l@pA.1[Mx)IWE HF@~_<<5JA)'szyĮXV7'c!Ctq㛍ԑ%p^Py͏[}n8{=u7gH,f?n?({CDQ:o飾ؼ ˶Y8QPūqc}wmG?W,%b^NfI!xv&|:}.^?>Yxf #:J(O&N>۪Fx!ǁ$ɞү5|+WL,!9$ڵj$Q3aqt6 7Oiy(d۞^֗BI ]%,nk.'e?B.Lku]q;bJ/9c2Ädo{Jnk5ξ<逓`#L`<W>|>T-q1AږSpg;\z+ U yPU@WfY^eH_E.|@Hl*23xbpP]J^3W$X!角@hpr=XW!oYE3><Ѷ '}؏=8mݜ}AqKcQo+z}VMsRY_l1xCvntCdaz@;9, kŸ;R]38נ!) ?)V l_ 8r'HMbKXTOV/bGe = H(;}"޿CkGgMT׎{cwUߠ}yϩ6#}?SV`^O9G{^ǦOtmd:fv=d ~Kq"8fWy -Mu }LJ]N kӱ̙dt0MWN2xʟI,Tmףnr?,yll+uB#d8S5f\%6țjSਫ਼=syhmRWF鋟1BrENs g}+E8+}:<3 8=g2Ì?]V+J_g-;1E\A_!80 }gHLTgEpkcz@.HLWi$)CPatsH1@&e4)yĨd˜u*珵>+ #rH^ KzYBUv=Y>tu~/ɹx@qdT4<&8`_|w>̳/̛|Wڠue7e8W>~N~LAwu۲$ڱi߸̛ɹƧI?(jH*[o|Uz\+=F?Oɧ)8%6u>m62-Xxz@-KlOA0YWgkUھZ f ;[AbH~ƿ۸}Jړ$u? }m`dzt>)3Y3{qEУ1G'<<%L^xx,+7D<B g@RwBI'p*VN|R bU ,en=۩LMFf1nWXrDg=&QvO/y2{m,_ϳw~{^I;uy'ٵCNW}e=l187W,ݏ zgj e.L[y(mƿghc=Ou1᏶Gp,qăo=8^j[0 D~ /8NseR8糄ɗiOE҇X/O 2oI爼œPϡ~AT8#TzƏqAr@<v 72cұFzBǽNvqtN ]4XO6ա />YIDAT*bt{֤7/Ԅu֤,7#ܨ 7uHtii#"-3M&A5L uܐa""}F@AI,/(S|,">}A|PBɤ#=ʮ~v:v}scnK7aR:A.]Ňdx {_{8ǽGLe^_Iv3fC{uڿ}QJ [k}61Ϧןfye#2^d냱ۇyH:31Vu~s4[w>LUIċGX gq;l ΞB4$@>"0Dtt?&|)L\Ah6O8ģ f*<t"L/o9 ~dX},~# ">Oywo8)ى]'ݘ'= [vVDmoB|Qr\黪?$3/Գǧpg /9Kz'/!<dAUs!W>^ld\"=ȁX+_wvP ͎v{x2 +T|K@T,&uV25)zMPۼ<+ILUȧ__ox.q`)R48/N6Wyq?qOvW7ς'ZZm̫¾ѧw+W!*%J2r_)ގAA_JX #8L$W&$ۋ6t41ώ>c7 Z_6m% |W'u@lߧ~@2>v_R'O|XgO2MYY}7)}Dԁ?$[x/>`gWsm)mKA19 ~ߺ: >yCӦ,˗,5ƖȦow&t98h[4B?z}F ǗBn)XF!|2'I0{-W.:/KHYHY7>Wv1Ob]WM+> | (@L=r=b4&3TcāX.!8$==@|nrE x tY+_qR7G[=N+}7ÌFv'-:} ZU'$TMNw~5iDs'>8ī~u<ڧ< ֗x)pPBtݽV)➖v\kc:+>s>zyme7{'~)z ; M> "VNss҇cY+B$_hVbcxhM,PҺ j9fA T`.lQ2۸{̼ŹԌrʦOo?AKooiOxmfym(d 1Ǐy

    Wnmd?ރ6>l{swi)oN}L;"M~(wx9~>IK W-@˘wQ? $"(J"Q]ib jV6Eρ~Ρ]v>E"~OQO>ڪʗz9Ǣz\_"r>_z=ކc |l:,8v!ps W@`"갯6xb]}5$2֧ >/ΉJMPL\-㤎AgP 6}>vy߭K|Oڶ%'/ulb}uk֧}}终O=sbꈠ es x $ށ>':>n* u8l;>TK*b|z<,DwruTBktcL.NՄɼ&+fd^NL]ѧ'L_;TiCT.uğ>̄jkAX1jRQG٣gO@~A ?Te;Lq&χ[`6rfei=geߴFCyFUBj7XW}S~D0OAŎA:(irЅMF|B5mGieײC,?'|4ae]12h._VHG}767$@cG üe^|#ccf_PEr(Me.ڇrgw5"N~^ٴtržU8 ~#p[v'q 5"x8Ĉ!Kx Zi/\6\:2?٠.wG1"JNq s]:PFJ_S!YcV7_UwNoyaBz!i.ި?}s;!uq; pG zN MYJhτWd%'8cMYH1E+lY+mKz.U>^lPnx~aav>5l@np򁮝=cW'D7qcF@gy?O:9wj6! uHQѕWйQ _O䩞Cv֩o}e*؎o\e/C^Yy\8"M!5d4Q%SpMi>BW9A zc0xMݸOP"_2[ ~7i;Ѕζ.' u^ob Oj>_7ԿCF݄NG7xgDyʮJsXUw|:PЁΟòml:oic`V&@!ur.d@Cɑ0 RïrMQ.z?EmઢSI/x/<3ԍ'Q̟<RL:{}IqGMkA ~B$py_'g: g? d*]}߲L?K{'ek:.$hA]]X>Ij=5}h,M_QBc`;苝7t9-} :q&FP0M4SA_󤷪`?[̣ۭb$o*]&Oqb-}~[3ٳSxybR0Q%'ٌ?Ǭ'MoNB^^doDFtBiK#lR+qUePN*n qb^r=.A V\!+S؋ iWQug2*ڱlu7mE~ ' /sa>E.|J_i.q2IY0yi@P݉ yO]OH4 6I_ Lu gn߄`>B*W7%BWmw'?Wο4GHU犁]coW*Y!6h/yho0k]Gqn+#c'ИaG<|3%_#J<a7>o N_h_qÔƁN@s"8 ;gQch5\}[{NV,7 W&-DzB .쾞eA4&30[79Ѕek%/!aԩ t]b.{0i`՟rQ?>.7ºaC:^9^o &4/>\`J`>g4 WHu1ϲoӎlj pgێV'J>tMJQɊtS>N~6Z~dk%}*g7_I^}B_ಡ_q_Wyr* *^CZC)џC|̋>tv=^Ve¤\Q4W@*R=/p֡ -'(G%s`]`9ȓ K)`hOâM#NS_P^tjlm+Oo9KSsrvvE[a5kBE\» G9,Vr~_!i2>ȫ TB1cJOabē g:3+]&'~0#J׼/:ka31E{B+$t^7 _6rS҅ Z/h'I?ND4&n>Wmf+|^A~?O_EoWvDzŌ+?!aG_Y;ӓ-mlWΆnWgtr dO۞U pzTqqr))&yqÆ. WeQlMǢ# /}Nή1 9HRȗ$-j^0`K&~,G*e/7>h=YEoe^W.`C3}X}m҅eVz񰠯./+<6+tz(mxQۘz<LuNu&O{\OYhH8Ijq_70^=I(#al \`*{:n.̃m4yRSsŻ :j]r6ʟNvo(82?=6&ڞwE-IwFyl<>:?h#n)W]֮o9f2u @d_+Bi+d 9 #Y]`C'H:zг Jx=AEՃ<;͗b}K׿pG0B0ۿ݌r%WoܩPDa\xjϻ Gqpqxu(7m?|k< _F_?tlo)XsӴoi#"+8ȠW}dS+jY UQAQ}I2VEt j@ρGV+^(cEDtEYmFWD"D&Zk=>z/{mzF2! ||m""'AJt:E@#K^$0La'0:i[xa* [\C>Ha/!tI0u!͈et|=VcT=*U SK#d^^=ˎ#kgɂ  Q}8d\3SL1,P+ړԡ֗|EEev;@`Hc@asq'+}%i' :++!D93_O_^ƁT+ Wai(C%ÓntusgS$( _ H ż}}QL@HP{ղ6?zG_Ԃ%%@IϮgRJ# >u^77NJ:5Ȫ$uc?֭O}tS) hg @2u7?⦭ZA&&ʭdZmZXᩝK)%}{0Kͩt1*"ׅgjhu^vY&duq}1tLB{aW3*{ߨQ[ڝodNjq qZZ% -m532 "7 W[hO;0.S]M԰uG}b!Q`@ Y:x'}`:XxO LJxBE#ɘJ/q J|ܹID2 ⥠gXԾad8) Q1†fdO]/:{OA1Bg-6sJ?$f8Bgy$e&F22U&)`c4"#G@3dРW'Iu iXDЙA]B0^bx$9\AՑ|wA-Ĵ;|JFN}D 4 *KFxV>m88l($$&&&/h 8S($YM/ȶS)E|gxms 7 G2yex|:~aבq:ZܘƧ?=l8ͺw# ;E( BLw(%)$9t5OۘȺ{|@N%ீ:JmPVYQ67TҀbTۄ]=i+Ǟo?{EC'ʎk>ًUDP3PʖZ rЍq@P7_4x8Cxhyߝ$F:\)kucA!!t8x ϒ% AB%R\bxŹ=+Awt'd2m @҃"k/L$@ -|b= -ɖRrUkAzz8uf7Z_IBw f*U^Pv >?>ZpmȚ"{#Ɍv&9gSÞ[[V%q5Os0zs8[(hG*m%ngT[[8ZڌoE (և5w4'͑VGO%)'P`+ClgC$> CxfAQZ8mdpWCEP"SʘSC͵U QJ8ZYK [k'켣W!B׆9 6Xܹ"+B F1sZZdzt_G$U%dInެ48Z B8OI I&,[2KHKLjn=_bMj>g5/eFi%tADHj5ތ0-! Pk%K_2TSA%# V1 x(e8kYJ`D*S}s]< KAD1 +fGV`[4`@#ī83 b2 A1,K)WD° .R;4b8YLsu<*!.廸nQ4@>N)PlO~q*=>.N cNcbv% a)cch褥J⦉,M+T\;er+MN)zc<+s;-׬~ *--BYT9|ˁ⾽1ZZ<1NWP E8W- KSʥqej~\H|AS0FK lJkn\eGH*q-V؀Z-Z.rnZZJ(X{9UA5Ξ^3O\+ Z/fi-7}vvᅴo܀1C{s풟JT*'"T+e\Wx(&(&hm@&"Jhv5}@ȋJxYH^ڵxBnkX QʾCc3Z f򟲰CKk;8&˦F4&iT$ahl ?;˂hkw` x,#vvmN^Ǻqͪi>p7ξJ~Mq@fvQA-s {qS)IR3*NHZ(ccJtn]^ njy!NBW{X=tgNxkR2!5,{}*'Hٗ%x?KU¨@`s٦ٝZ ҩvX3Psy!NF(*VS6o0QɓC cvZN#ZLoMs1v*͋!r_g,?Y;G\e/%[P}Tb 2^mw.-*D#RU#)&ȁ9u-J1*50(Y2Ҭ_~^ Yh.s|r72 mBvAg@8U^  ׭Kp4Ksd{3%N|O~:WL/5 H#PsGS$fÊ/fkf(qs}t ecg?ǶOIٌh[m\6ݘb uVAy:4$Udi65m5 BVlLdb6ޙ/rE Lwyqeޖjւ {Qh-#PU \ZHvfy x(Fv+8R{g>\[vqsF*(ǽUgyX;ر6oz+&#TkVzmҍZGL .p|ik!|+a>!*KM&:uNBQE$RJ1.%=y&J!lXS_@;0Se:g0dL܋r9}b"XV)O6{PɁ:&??Ovr돮W_y _,u O`ZZjRb8_ib6ٺf xx\(Ԇ2GjrYH&a}0i ez7X;5\_^낹r["Pt 36f!d&`Bͮ01gb1+.D/9@c~10M[%4fX½_C\H9{}B>H:#asmf:9-aޯ?C\.`|8y5SrPm+̰fhMmdz{ | KQ>8WX9qI/jJI³v vH13>&Ou M.-_7Cۯ.`>L <"a0-jZmEĘ>ۦgL1L uh\60<<@oj|Ef5XKZc1}Es S,PU E&@s+YO_t#'º<95(XP]'W0f/Bt):o H]wpΰn<@[(m ao'`\@cLWy8Cx;ų.[v+PC0Yi5̴Y>kw]u.Iy:62ߏ]}wMd iڨG~"8 'ɓrZ @D Pلf CAr`dؤdĚHdjBSy(cOug˾}9:q~  b Q10͡T FW耕, L<;f|38mESxG?ͨ^nK]|c]O_+]<^IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_clientbaustein.png000066400000000000000000000105151506574241500261470ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time06/22/073tEXtXML:com.adobe.xmp Adobe Fireworks CS3 2007-06-22T09:09:40Z 2007-06-22T09:23:04Z image/png >9C" IDATxOlSמ?v8$/ ^K 3בGK2W=6IYTH#ud3dmꦛTMhjGJE؅&1!Y8rc_+_s~s!f[V1[V1[VWO <(EQw݈_bKcRïZvJ^}? !۶/7PE x#*eW]UFOD !!R,...ZB֭[˗/TUEu>Cy )eBNS^y>!DǚoWWWC2c6}JW.\ wa#`Eۋ}&hX 8RD}x\Nu9/#MP(?)4==xuM_41M?m01L?`_Y 3Ɏ6KPˮ(] ^Zц* :ضmO^{?s!!*P|lss9&00~v?t̲AgPu An]TdêQ%P\]ᖷm6gΜ8a(M7Pu I+[m[aɮL=@l`N^UBTU+nZS:zT%?`ǼwnE18k=R뙇v`Kg3!(ǟx +P=C++j)n(;RrXjF6Nvٿ^Qۆ4B@cm;~4 +++tvvO? <|'x'|S^gᙾ)ܹsB쬧g8NUUTUeyy_>wtI} XYYaffƩ333 ܹ(VPUU4MCӴu~M|躎똦IWWa|0 @׫7gΜq$:i=Jd]i333;wm۶i?#PYG~˗/sq".ݼ5+zwYx9ѣGyꩧ8W^ʕ+ضbHTX,B T*a۶8q2~۶s!i`(@ib)>cTUezz͛7DD"={h4Jww7.\466VUb&U^"Ȇm.UMMMKr90Lp8L8\i,rnx%I0xd2Ioo/xSvllf̓L&5UlL&C2t3 }}}Nb 3>>@UlIBQvɓ'I$NY룿f:@KK  np–esNH&Xśo IbB!.]ĉ','UӧI&\xp8 5-5bbꉺW\!sȑu===9rV 0JxT9Ea``Ç(DL&2cccqz{{,z\Yz{{ ÌhYPl4x܉t@ea&PSN100M,㣏>rEww7+++N R)RB /^IJ,,k׮&Aid0t܃x3$,N:儏;ƱcjM$NXnz i|Ϊ8KYÄ{V*K{o߾*?R[ l5 l5 l5vKKKR)~?~@ @CC466bvfzzzZwyX4nOɴV"u֮|", T7I/W7D$4448$l:`۶M8B V mmmD"(tuu}v">뒥zuu۷oSy-{ar9pvttRheeIQv!ĮBNOOsƍkWLrΖDss3Pvٵk~_Bz^B3ꢫ.T*F(?! @!|RR)ʁf@BiB͛7ͯJ>Tn߾~*oxd7sDSS``0H("۷E$q.ohjjzP"E,P~/T\ꇓrtϮ4=oTRvwht 1!DP(p-&&&I _I---c(۶mN"kvtF2`W;KSWDS^2U$yB@YXX/,//;+ݵk̹9b ,J0thpGvٮ' yHV@,H0p&^USIlJҼ"m:zA/Qs< x_zz~zF[F:wvP+]a?Ax&/ʬG`){@lo0ȫ5tL * jW+,!Y l50IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_dns.png000066400000000000000000000175161506574241500237320ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time06/22/073tEXtXML:com.adobe.xmp Adobe Fireworks CS3 2007-06-22T09:09:40Z 2007-06-22T09:23:04Z image/png >9C"IDATxyTŹ?uea`GE %Q(LcFW7?7`7J4l3efa^9n w>O=Ꜫ~뭪ުRJrAi٦)erbiJ&n)e-% R)ٴqc>zji CΊ0"`D\J?O65N3X\c=W nUk.0)1 YmfUUqJKjcm:ngOv#NDT+ꏾVUp{h X2M<tF++%3g{BUlG]{ACC[l`p'R?g 1,[b[ %銨.Ha/SugzHbgla7 &?eKl+j QbOߏmo鰤Ȁdi|:˖خ%%%ɼy0}zb\;{ )I_n9-G8ih(]iOvRɲ%=6?  @2#3]NէS-ksÛx&LgM%W,;lprE>x*X9 s7/Ƿڥf|z\gE%uiA~~|@@$v-dRbe$ˡ:3IQnۃ+x*-Ԟ3l@VN+ aMZ635i*,$vwaAK6c'pglIqᝌ{/~eϞ,[b{XP}Ȱً]1ұ0AL:b/B #!$bZzW9> )4##d$=Yvw8k$LS.2ͽ{wŽ:[8zyHC(&IßQm M2u.(z]tx%"XPOgRRI5RS7UQU 4öߚ{q,](Ē8Ĩ(:vMck/%e @" N'GN|?L|>3[} gN4I_W~yuϤ{K넪3D)AXkw"sNһBܭ˓nE'Hi{9"yG׀Ҟ솘lϘ.]/b >XB߉Raa#]C\"v{ZBCgL:}E`H",hCbu98ׂdhxݖAhÐ(ZұG{йGY aQy }[ 0 rC_U>u"vg8a_^\=8pS~GW(DqCux5wІy*Nv8 CR1i?U3A0G]2ѠTd$GhQXYLh qZ EZ{\wQ3iӍWHCCPtbZeVHksvTGhr#Z 'Vna،Hk$^K=)S,tʉ^n44l!U(/aTh鳯44`,A;pyrF\Z?R!I} peb?"?#!36cԏ,tϷ"͗'^gsݮO*0;@-qbX{1m=0>w.2JǗ IZ>JGkxStޗ0>ZF*zR;Yn: y[F#e f3&+PbC` %^[*hN!~Tt 26 Rz0$y\ګBǁ*mx}Đ"8ߎ[ˀ_5} zYJZ(m'cz1e838"B|"!R}6fT=䤸;HYZo{ZUsdLo; ]e02ݘQ@&Zmos=Q_y,G (ZGH5 B0(!=c (0aY'ڎ$3nF,Ϡ9@ i,D~(!@D޻@[j콘?+@^,tʩ]K6/L`Èxa=337 ;BHRI!1L=c'mBHj89 oN VC8, f=;saǿ!${¯1dCЉCIA(AfX\:&D$LԀzl@?TO0B*B ĞxiQ752;(Ϟns`2sh-R""%[1uH~2y5Bx[^f:<+/ ?SiIz]L>p%6J+zv1~ۀ=cMDZBFш%'4T@оga3Cav vw`n*is*5)c&tZ59jH=%&]̳+#WJ -t*-hsC{w1̰-ݗGn8нt~4$ ~sUnܹj)(\>v~c>yZtmeUy^W'CQ׈,+"S3q sU 5F$$?pӁôTEVKZ>;~,Qp{Nd (~{EO.uʔGM0xj8Z^ڐT[S<}Z{.@s='#w[ <%'H #desws` TUJ4M\e -kpbsP~ (KRA/T+sghw}6MSh||]qat7!yeoibJ ikĤgG"K^/͏+Dt_cJ[y 0͍'"$$@j(Iѿ޾O/9W6_@AP>y?RWql˅sB"-r޼hoN(hy &Rҿ|OT^amaX 9:ːRky҃iFEQA GP7Iǖ9e}"u 4TNL Ρ]8˺ tWe&)STSxM&0Ӄۇb Tx[G*;Lvqf;У7ћq0P/ߦ̃jɫeC68vF ـE1_:6'I{>=$ji{of(՚S,AUg{וY/aO[auv|{5(">a—n2n0{ [sPb7epBH 4`QkLb/;P2[jkƬ!3T\@M'ƾ1.!UeVc׊p Mp U l`W)߃u=!$6AnRȺJ=iXوy!,n^MA8җ 3d9.[ +94 Ti`򴼚Fgs+Pf;$'Dh6=i+Hk\) 5(haw)"_{r,}X~6$k?TO.-akȾٯ4>Ѓ4SFk'%l')-q 'gh;[_ VW}'(HW t~%YAFVGhXhNۖ+R<=$݌[6Wo|s$4&^@UjWqb9=} o$uȎF0X")&g+Pd2*h9~Z}qzOrR^~uɏhk[G(ԅYKMb ׂ1:w.Q-ShD.\b:+hlE4C ;r&^r^!0}|>TSҽb3/͙ȹSwcS\<3_?Ȅ өc<\"BVlFd\8yFʷ6֏w~36d03I)7@w;1sx"|۳o~PX8*1[ W|.݉:J6/nW_ `'_|#&^z=GJ -DZ !j!ʕx8 ~%X)؝݈% +׳yѰ>o-ޣ?~*|%fyB$Nx1N=_c .6ر [xwopX֓t,9VY 2jAIf-%]컮bV. 2 郿{`˖4Î˙5[?_RO>w|%ddi r(-*`֜ٽc+*gsWjD$$jAp䩯aIq^klɰyE<֯G h )S\z,Nv7H; dı֍f֬=u~-/)+ O# J«DqW7Ʀg eڴgxhl<@y3n^{m x_v7ں.iY PN,9_ضn:f@J!|OJ+#QY@O@Isy#+p0/qV[}B֬YhiIP믏L.~󥘦7j%()V-[^fƌo̒^~Pv$#QxMsէO%Ίbr.}h`]]5^2=y|,_~ c%HN{?&'gG5{i֮S7 !Y+Wٞnb\{?&yy#S~WqX^SO7W'N-k{JJΧAr֬Oxs-[֦(1}sweoK✬i(,LKSTgJgE|YUUs()nU\wvRf?b9rdeKKKb9QVs4]ւl;`Nd}ro.LߌÑn$/ot'x: zݺ.R^iRq4$ +;>Y|"'OJz&; 6l6E!h=ClN$u/4~ԠsIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_emailloeschen.png000066400000000000000000000106741506574241500257540ustar00rootroot00000000000000PNG  IHDR@@% pHYs  nIDATx͚ XQAs0pDV끩MM\cWM[7>&lMh6$mx4Uc>q>#I|j%' HafN2@H]$.BuP(J:w߲e ep o00+]4B!pN(b͜iʷ@N#4T$@"%H03 QN"#:ItoFDqVH&Aj3$"{wØym-Fף7;F-CwBD$KFT#")MҥKHƒ%KIWD4,+L8D2pE{`..[ ?ϸu!( A 2tfY/ 6:~ *u.LWFܹ`"Hq8&8D&' 7 )P.9NH>Twejz>3!0)4쓌ܷ~[<@tOl) ]@Qj_YZ0x Y"G.\''oMs9Br1ǥK۬}233wnj6XڙGV gc%} _!8DNN+l1p`pXu9ͨOKs8l:.)6&Bg([""V+ O"vw^;ʥ;5\lMQ2B6ؒKXII ꈊrd+ }D.yJ;OH\\\"x >7zjAp'&&nObPxl̬))FBc`ykA~F."oTܾ~7}&hu4olp QMTz(O$C 32Iznʪȸ?'9>(n3]Qw$'b $ˊ]H3ꐠ~œ{񃱸 lYYim:9?)=19!1ё 9WҖ[[ÔɓeX?E,?㐎x,>bLݪU{GbJE^A7ZfHrr2rrn*ڐ_ŖXZ0)\,X3bӳWc[ [n߷ [?[6rq??8B ^(x=yx|0ٳCASo=anfd.,{_KAASvvMVբ?^12֚{P% ~.Wb!gL}woM6[cBBSJ p3'%7%/GKM6`@W^yygLEHhOS'0Y5uZsvvS\\Xg4VZ,4RR*lW,rx>>+3KӯW^'֯_OvT@sp@ϳ(,R?3咻 9 pSl,#.tg5qS} NXcˢeEje؋eFwaM WCq#%,rJ$': 2SSR 9?~ߴL欬&x]+hG4:hkAhXFe#s :`8ǑXk)>9^{̘1褭ڵ@BrR\Cn,kսbFʋPo:B<#fvvsRrRo0\-ZWުh749)i~`ne(SSb%%foTؗcS"{,)5R2T$b&$N@X2o{L‚f̋5&&6FG7h iJF+Cw!!92woƝĜNIuP͒@ި7d1j.Tj*wii)K6&"@S$^`/Bp/kV۹d;d*~RKY715i$/3f଒hJ;Ԛuzkc@`߾})04Vx&7A50!b}LWII XbJ j5ޯқa  ]bs6lڣlWkb05){3Kd#Eau\ P0 n Țg;m&e&)=z_+`T`OpǍY5mQkȐ|e$0L,p1fUeq*e vwÆ %T2vSO=z#ʷ)jcPϞ'N cD, x=dK}dÕJE]66mdl8=}tβ VO0!r⦐*H^4.DQ:"""E$7K%"FW|m sƿoƛOR,~&~>@x`E]QjUaňbE)0g~;\by`ݐUd6< 1r 9<ُxaGd)蘄Aӈċ<raWIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_emailprogramm.png000066400000000000000000000070731506574241500257770ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time04/12/07㟷 IDATx[[OǶ{Œ @oE>%%ǣ<(Dv%Ql%qqe3C`a{Tg {R jWVZM033*Z;xynY{ב?|裏<*J)( M@)%R*!(C>*eBs~,_!BM xGeN:0.!DH߇iL&:/ud2`㶬ּ>zDy`0 !DD}"FZ;]A)`ϯeE\h9;Ԏ]r2|###4 dY8hgraggSMaLď?wy\R-`!l&Gϕ1,߫껻Y9GP𤬡P\%z,SLT٬_+p8.u]0c !8Bc 8૮UU}]kv攫 H0߇y ſq)TScݓ8q]@a  ~B_fϝu]Xϟcwwpl~b!J!@4SkȵÒN:%Iz H&D">mFֱQ9i7|uR:zzz044QP5J={v& :+rrl Ͼ>'޵va6~w۾ˍUUW!w_XX8kEN144sUQBsT tFrmnn"cttXKT0(_Np8)S(rֿIçN,J[oU5*48\Ú'8ENu\p ߫"[_k^Zunmm! uߩiׇ% ,4$cmm Ƕ8:*w)Z/~, n8%<6===^¼BVVVp%5+|I[Y":B]R]h@2DPӧOە!N]loo_o1.\o_&P(瘞ijÈD"xqGBvVٳgV<t9< ks'19b``yǔR??)Ne$IܸqDPu]rDq}J4|"R^%-OPm2 Ţ#gX^^FPDw% pαB&''9ǏYWWWQ(o5BՉt:|>\|0M~-G#Oq0??uLNNl.ׇ^{ x$ ܻweG]BL?bBZ:n֖TR4T*;wTU066,,,`ii HRH$- l&40::T*lnnbaaJK6L[]BL"4MԔ -,H(0 |GJۦ\.l6 ۶oWlCCC<lfi4MqΏRaii abbdφa`ddðm888@>cp]#G8~M"@2Ǯ\\.Yħ )8XZZchhX]סiΝ;Wu9*8 [9R @7ooYLOO#L ?lc O>뺸q^J6*UF)Buxz \Çe4±UmσsW_}7ol*L+hVՅ!BGw2­[Yt$b}*AhwHK#YׇT* |w-M͜Gs^4 T 4͎iV~V]xXYYi.S,N`@ .D"$ tuuA {QJqUayyX.<>򒪪sݲPPe!cooLX[[C&eY`UB"l3X,۶ BRRQ,q׍OyÌbߚA4x{zX px)XiYr077LOOcxxl6O>d޽{x7۶cQqTEQ*t]yFF5 qP(W ȉ?/.rw=midmHb1b1: AeaaasLNNlll??/1WkRiȲ8Hm4w ÀaD"F i֐}GT*x7Y*sg4͔m۪(~zH,ʳ`0X(݌˲۶166 i8wLm;bY(iAڒ4+Vcakk BsbZo.2/J3.9C06GbBCR˲%vvvG}R4aY֕B.uh]]]bqrl6/M<˨[nuI۶߳,k eiTUE(e?dfb9}$IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_emailserver.png000066400000000000000000000103761506574241500254610ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs.#.#x?vtEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/08/07B~?SIDATx͛{l\՝?>m{C0q [%ewٶj+ BH-KWZVvWlRJPjV)gQD RHtiRi'1y=gI؞ǡֽJ)TP*n!Vc?!B{!ĮL&+9B! lB| wB_^90;;{) k _NRc+9|ֹR ](t2\`fffHf^.nLD"ru BD}EBB <% j !bB[ƺbzz鷁g" [QB< +tGs6Ћ199B|וo9PJ9e% hOMl;w|~ R15nh&ĭBqO6_|*TpוIUFȨE:/YoYiҊ˹0?ECN pTIJBWѮ;Ky<:thBLӬ$ C\׽{ɜ7r؎D.H2US׬ PQG3ق&H6{E2BGsu188}}} j!g']Zu>uRە@@Jy a$&E*K]{oI| m>߿4ݷ FFx|ǵ̛RI \I0<<OD(T4:"QtuQRsiI* {IR- dʼr²-r3E,¶/)WD)RX%RʹOֹ@ MLe 0ԼVa׮]O 6&}q4!sy=jP,F"T嶴N2yigy"05׮IzRed2%yϯ~+?cl\lwTW ?e;TZ_cs%_/ghK/[6.FGGy-}jҰ6ڢB)GM,0R+4kZ;vnX/Ә-sܰP,nȺyYÎ7mēO>В? E9FM?=Qᶛ%A &@T<44Ν;{tP# #h-VˍmxsȡՉ7GFF,߲e ;wd۶mtuuKN_Pm!]C3^t]u{sUo6jߴ|ٸq#Dwdm۶m6FGGϢ/Nt8,wֽh)7ضNƵq.{i_v-[nW6|M>o/cgtM]+/[ޣ> D^D\ЖyKwdB*_|Osc5W-BضݻٳgΝ[iIc#%Z47*2\=8?b1]٤hX4M8ǏѣZo7֕Ν;8/cݵ'QDW 67o􉅏@J OUD"RJ #j:118k/YDo'*g9t{\E!N~zcìH)d2yT*E3|#GK/-~2Q,Eo~WljL]Hhe4م̱ brt:abYd@qpV%ryi Y^d)z ~Jnq|t-R),Bu,jRi(BM_*L|s=\&I_Os=AGIwI"@R9X} >R6/K[z͑ P;Em!db1bd2I<Dz,\%R.>Z$ "6ts|..Ru\6Eg֌P?ގEV')aSYѦ m, OGGjBi É*]>gm|~sohb/N_wln/ @^AOc4Y{MdY@b:'?hf]M=QT*aheYw/3zb _%D4 N\՟`/wleBE}>&w@AA[[drLR0 2 x!A %pX&W CVec`9TÔ5d֮]{COO E4MlۦZ"4M}]|l]UqU ;MIxf%%x-+g2_8qbD"a8S?3;L,h;VOzğoh#f.?A}$Ah1E]ed'?ǎz>70#d^?HS дH41L)6O$qLC`qSxR± 3ytJ/?kgΜD`Q4jd22\.{ se[A W[쩽ꁎ#Z!_GB^|ś ŽFGG ǫd?w^wC̭⶛'ڡF3/Q(f|S'7-ڥ^\%/y'7+ziai`0ч~7"?L&?fSև/nDukҗ;[zH)5MK]رc%+++ٳgbnndR[Z^>w}i=u]SZ=]\cG4@Oؤ[Qiw}2ydyPRdfџOJt]S? AOFFFax]}~~^LMό]j҄(mM=cڍN $ĺtS"c<ϱmϏ?7{/{V*zĿ6Kꝷ+}d 죧JU%M]Z)X<3..4CӚE}hj022r#\˴mk i=>T*l~%kCrn+]*uiA9ܥw۶f'Õ'mi`)R|G 5󃹿bzo}{na [JV1r@!4D D!҈={  [o({k=xyb-)/-D*w-| *QN48g/_\A5ya;S7n#ۓ)_y\ |m}|0MYvقIaP/z5Ƴ>b+ض@֪BT̽Z@!TM)BH盘[*_ |HcY"j*9#*m_ ۉDb\]([(m5)҄,/ieStvin o$5͕; bTUZVϑHsJZEowb @A @޺$nJ0ܞ=7=0=222EP$SI,`_³%gسPYKs,k@u:T*̊E~r{H^HgJu2qcwNz[m"Rh,"0R阦^3%8mm_X385*F3B 4Ma>RJŰ-Kԍ'XywxASHe}:b֑Tc;FB ĶmR9󚁷& itt5cM/q$lTe]Zc2 < ֵ۾Kv8'ĉ5 !@  MgDZ[u)|ln1" 3M^_F6 W2I2ԑQt (@)]wX8zԦ}M_r<ԭ4N[9a}ulJ@]kHت(A4ZׄsS\5Dp1Mh,`yŦ)i i XN ڕRۻKW ^]Xia%U+jw7 ]{T ϓOW }q!Hm 32F(_y.K4&2kiUр5Qؔm۴ǖY둓;t˿h  ,)X,d# 'bKh0P¶Kܺs'/_O5$@(x"2=>Jʡ9D->]j 9rBfco$pήA)o"W5!hMHY]ö-nj'w˹xb}ډ$`pK(m5kC4W_Yp$FG]75yM"ƉKC~kM8/Rc Є&ւи8n A8g߉_JtѠ}&H$7%v tL&.2}yd p+"EzR(iB! n{c{>;UCy+LSSΧik gXOFt6,b3_23/1MH$Nފ wswB ߖ%2"\[s--a'I.41b_,Y+iDQZZZ'KAQ.n{.t"H$$@mD"A&K <Ab?|ɱM 4"iy_\u]l&u]A4){?/|;++dfà ztt==1~NQ9u >la @oYX_X,fr9Jb˲puF Ő`lqOm(.Vv ^J8o- 蠟RDtv ɔH, 9Jh,D(d-sPd_Zf[ayyH0a(<2 q1M˲cK{P@$k"󗶝_f%;H[oiwo;]d-/07$B`;ma4}Ub~>t rQzr^9@/2 *R9c ̡iZm0 RDp!y+@|湋N/- ic%qps;Lowv.m$B'7wς3" _yzfff(ttt`R/hmm% 199J~tY.f_ PtB(WV@@=@_vI\/^E͒߾ۻV2sssyX}R عs'X 󇵝;w|JӅ۶w ^Ç-[wô|5 {m%L1,*`vʧTVޱcO{챽JbOD]3ེ^sLp 3 abbt:ynىRJ(v}4 -H͗u}3[\p&^9zh?33,R5.y^m ㏿g}B)wiϞp8r\7^sF_.ul˦ c?;NV)%H>BPmT| \EQXXXxO<FFFkVѫ(? MMMb"k_WB IJ,qG)'''/|\͖C=[BlSJfzzzjhZKض9q O?/%UyG޽Bi֖UO۶tRuvݧz_j}쯣y !D<MEJI*bzzZrqqb1ѣw wJ)ih+LNNR*T 8 *w}wG] 4ioo0 ,ˢP(JHRb[<7ZӴîW4Puaii qoI|]]]#c۶nYRIE ÐJ':tO__cBi.J\.SN}-OWO݁7[~ ?ե<mIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_fileimporter.png000066400000000000000000000046601506574241500256430ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs.#.#x?vtEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/08/07B~? IDATxm]EϜw]--l`D%!h4%!bAEc!hBL4b0#5 A6ҥ{}o眙޻wwn Ksf<Ϝ9#;ہ~c~;o\_>33]*RD4^bÇٶmvn6=!`zz]v!""2Ϧ0}!Ddɲݞ}YnO4'3 *ιNv>_(PTh4$I[UfHj5x"4 89GE+ʓ=~yQimz2(8|0ArQ]m%O9rG9G-">vԩ]ܹ;w0 䮻B !j8y,*vX%ہ~c-?[=oJ>l@6Esԟh/_;v/d{߱(;*\O?<}=Rv?{'訦vvZ&o<ΌP6k|E\YWcVm"#ኂ[ $J4qO\[C6 ^QTUmU\i&4FtwPU][bʰ_$CimZD2n?G: _%lL֪ &j]j-Vj^EV&XMԩ%iQYQ/5W 0š%AZe:*iHJ ؒ} si0:ə̲OfϞ={'<#-o0y#Rb]x!&+NeUp 3%sR "&ON2i0Fc=kn A}`|r&G'~\3E zx"osn!u0M,)pܞFKB'd$DF =1FUp½<%ihg5+&_* %N3b;]2M](m_M35*nd$ġ {xE`ẍ_b?'\ 3JE  L^&2-5i6->)uMJdd X' q$C/2q?כA3zm5>S)fJ(7f97W즈5&(<^G1o2e;^:dӮ.JhJ\k= |j߱v/g?# |Rv ]]Y6;'pM-sMPF+M{7`Z 3e+ڰ+xxlw/x* IQ<װ-s [yl sCvpӢr Y(y]W+{t+r_+'ޒ+>?]W\woc\Ϸ|kH-oU@J@7zF@1ٷZ찴j}Cϭ:Z \o8z(q^_nhP, ո۵VQVvf9$!3Vg~;ooU@'a".-IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_firewall.png000066400000000000000000000174631506574241500247540ustar00rootroot00000000000000PNG  IHDR@9 &sRGBgAMA a cHRMz&u0`:pQ< pHYs+IDAThCzxUMH^7=lz k/(PTDPA)"]t4 dHo63;wSLfgfS~k?}1 f !,VK5Dx+&0Zdm#X9bg$FۛLDkMtdM8wbev5:!?/x* Ї_OYxkA?ʳ<~Чg ``|`Й?^:|W}/[XPL:7Lu5y Q#vQPw8H7820"4 m Hv185 &5mϢeoX~ $UE0_Wy7_-9 j0yu{ ;&m'W/o;V}  :[Ӟ('[kfӢM81GYmQu]?P_߁}SScyR-7fy#gdX5 sWP+oFkf:5[ohj&O߿v&/S#x4lsŘZ @sىiӃE*Z˯?*5d/Ïf`~;^*AkQ٬ yX#VPySS0=q3pK%# aƠ |e,Cb#&7ӂɘ &лlDvhN$B$w,+mo$&= #9፼'Xe"-OTb9yCL+ <"Od33m6^jmV´S-Ժ5Yc а &Z|߄F*> <_ꌣr16mfjQ(ø^M l?KF7$V·A.'|0tr pyAs2!MJ`P)kG+KG+Q.)UUjՍ  ZԴ(T%RumEUW>VWjP0ZY19.wZVIhr׾sƆj]M WYTkj%ښ:ftp}v^UJǪf+UUWGˮM(Q:h"HK h twhnSN?<#3ݓr_w{ஂwm}h{cN}'=f>tN?3;9/{|;vNHD`mt{^zȝϙ^;szlvOsy+S[ <6=󉹧qA"`6Λ1V1h).X[4#d$3iW?ٽgܳcOEEK&'G>Bw~#;֜b(m6b굆b$P,iX'N͏F݊C O'eΠ~'DTS݊}ODzZrz#@!%Ds-\z܄؜|DyI@qRԅ=җl8|{rNׅ(( ]rRy8{WCcӵ tɎA'"lE/~є)\ J \?[*?Һnђk~YSC DD~ Y fbJ8 X=^yo战Υ'/$G_N9񽿧1[r4ʭ(| =*7) )}Mڪ4lA}S%u/~sU;u +]>v[Pd+E'&|#q6č>t/yk5,yx~X榲/.uŋjp}TGyKW0@0u0kqv@; cK072kWOkplÚUK|.+k3^ܴ?hݜ.p+3wPNx)ue NE=-{l3]WBgu箕ۖر{GۗtOG~X>}zhh40zA͂X=!4b0FEijYw.nGby|`UɴdEfB7|xNAsN*-lhD-w* =6kOBy|A6yhDoU=J>˂JCc?MuECX"E!Z ԍaЎ}8.2꘴!EQ9Ïg>M_J#L-`{tf)1M249bq2tJOӳGsbJR%qqQYi)gސ:1s{A"KS#1ZZSQ?x5`:7Դυ%仉:f7OUsz]sEM:7mСUWSG8hs.{S-~:6<ɠpe_"RC0d]|!b$BrCSJS'(:ôM wzyRJ`Bk`50`VԍBq9*x8(#6uO1E}8-{}vlk5ttlCfBp Ġ3'gɎppvs sRB~`GzVӄ.-TdT5m0C~6Bw \o0Q?yV͞Ph?5mD^!H>=]Bih~:HؕRPuh;Hn*u01L{9<)ho=ˋ\zz+Soyھi$Ӌ6|{d#0?ت%$/5^o#r'Κ_6ofsY3zstcݕwtz<6]tL+x貓W]KtvJ{ہd8\ƄxZU&pŻPe^?'N8-:r 3fk7 ئS}WF\ H^%?M7OX\Ck$|U&]0ph=^L Htbr91N>:P>pE'գ?y-.$bI`Knu ˆ A 14CtFѦ^z[YЁDJkjdТСM)c4q>gA ?FBvFWN%|;dkkfQO]Q0/uBq M:´W9+WOv?Z~Ж1QfKh\wzL}yȚ1Ӎ2` 3X;F\(KI.M}5iQOKa'RȍUPfJԹ܅0a@T}R؛D;7xbEuP{cd~P  us fmI$Qek1i'"3bWc„.[d{"K|kKbf+ā4@nwb17M敶ܬ& P95o{bRM>s\=(au3#іCKj)}ʔ'k$rߕ<.ĉ'ƮMzK'΀Ho雯nr/JǕ&']II>[4=%35tK4|<[vt<f;rH[S20` $3 ^$5B ݨp` E.T] b6uy8N[^%׏.gşJ 11(;H, MXN2&j QzF&۽h3^ ܽx+aL"/\^qyjwT۸өm"m=%pM8J뭠ڈݠUĶ4G9D=<@L X:+щyLJGmDER{k^\]Ed f:a cSz`qVuL h/M,4SPiMs*?D]:`d x)z91 ;hRaA7V#* gq Fa#T u7=--C!k1\3pxM{8&GDZ`:n `>nNK׎İ[A7gCǥN |R #RoT| b!*U;+ jn1ga6,hp=Ό`\4^Y˞7$.~eU7ν~9BW[C7朠Ν7O-vIDATxYyU}L$EHE$db[q:8D^8K#ı6XERa(Hq﹧CrW.L|]]߿Z%",7^n4kKk+R?Pw3CƏI^_0o=xM7|z=!C"cF? 'C P NKnm-2F?)ҕ V~|o/mGrqK4'/|[tM brP22-xGWrEdt-bH Z~^}T]?܋=Z51vwq֑&p0GG-hYI`9ĬdsX,ο:TRՍKl4R▌$O25A륋˼rimx]DF!co?͇*EŗL@)Rd %XIHԐ'<Ͽa DCrU1[0& ZU_EdDh *Y1.ƺ$M6hh+J li[9q:N=8Ej n7 !cƢT^oi]FDZoRj*@iCuCTv"(s: U4uq6$ROh/.30[jywuMm :bʝO`,0֑78Kwrys0{zW[j;iBo9vz~b3܍kZÙ SF}{]~=O3 jNWqhqfZ(!e{?r3xevzpq>˽c\wgG-}f2?w?8;SO{(9蘧iOa "5)Z@< 8`v%0+s&bpA>fmbȃ@8L!IgȆ|(MB qop2WEefpM2qVpl qŦ0[?Ō1P.(wuGHf0wMgbS), ?"QEaXaRKvic=B4K% HyY84cAˬ(ۉ%2GH%`!Ozd:㽉Sl)v:\1&%Iz&%181Ok' kDQDE(s7WlX)IPֶ)+~ 2iȳ?rP>IQf)8#I$#1e3 Xj177S+E_Yb'>` % 0d:5 b!-|9lrҞƎvFjXR,sh6hQ.W(K~qoA+Oӣ:S!WD\cUc.Jp\!IPȄ_EstB%`T"jڦ9 K(50wN48g3HHJev&"M1 n:M($zQ7& qR(FkafkYdXPdY1l4cqFcx{FW5}paUr ,d\V L\G0 jP* T^ ^ qƤl"Lĝyz`/z w2BkP:y=IfZDIj8$ QFkۄ*,3 JܴEL/S V @=EX$-K\@qg~gHqRź2=gXC{ $!IoQX#. pBK=ڦ ю$`)` *(Gڻ^ 5oWnf@** 4RhH8([&#Z~ (6xڎ!c666X__RΘ/J1E2<[,w>5C,LiJ1?@E&}"un Xk7U{v2)e]N18fI2%WQ{#yP׍Z^deAKG~gyޒL-%yfy{v sz=Ν{G9řgv.:|G@V%rU $XkWը[=sk@m8$!S%lMt{==Rcnٗ $# _ ,5&ԁ~髯|ȑ#zZR)tVpZyT*FLn'b3jZs$b(XX_;۷7"] )qJqHpSVm+~J]-//Ws`vOy rJD\RdBU%(jD KK7:y$@XOu};Y|[; Vl6vfIդjj6=[mz|^gd[YY1JeW$7d'ZK2J)%pۛW>n~87 I%Vg:@>M  c2)8:|\׻'8us֎oFZsiuvշ-N8??ޱr9N犻r_ٗZ@>jwNԩC_/Օ+W\v^x˜:uO|DQN?`ҥKgϞJgS|Pu:ȗSJ)h4Vit_#ssOJRe?K%CC묱֦$?YXRq>SK$,ȗ.\{JΝǏ1f`DDjr=,t:<,--}k… Je[@RJ"?~\R*<h*+| ".93HsI~n+ $@?;,,,8D$}{s=TqGX!AkN>ӧ)H{jʇ\Bӧ3g֚ܭ &?~\N8!yP&d߭V~Hިjr}QV \avvz>Zu7MƎv^3;;XXX7< \ }q8f"zTXtSoftwarex+//.NN,H/J6XS\IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_peertopeer.png000066400000000000000000000150241506574241500253100ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs GtEXtCreation Time04/09/07 ZtEXtSoftwareMacromedia Fireworks 8hxiIDATx͛yTŹUue6f`DaAdQ{]wM\Ifěś(Y^cbj\@bn6M11F$DDf^Ωz1OqOW׼>[Glގo5\c@Ys?-o9&E[1f.Zc,sfܽsݳ@Y Z/Ź;yo 8ޱ#`q/FHA 9G7pa.pPܾTP9G^lr]V}y̻ԖJ7fmxxOIYTh}: Q&U fy5wL}G[ &>U0{tÅ';皝s3qcV[6mБn1l4Y#bB:jφw6y}cQe#lHe3wg^__pn]Z:Ȣ#]$jLdkh:tHΓ˼,:g{CLǠ3!:gP+<%Js0EXKWwѢ+HeMdr+'%pEh F X ֗(V L׻.N/(^V*-ƓH!XTP);G>~%#m> S~<twлr9^$u<8!A96z\,W } }_ Pt x2y4HXe]{v̊#ZSqi6?kzW)TCUfۉ 89G{|] O@h渒8/@a Zd$N: s@Fz'BhŨ4R'䡿nݿh+ܱ\:k>7 ϽkdQyu]R7+<`O* ⨎ѡ.B/qc|z«o +mIUTߔt㔕ddv$ ~8iCӒ_G{ū'e JBH\ju Gutvfd?8.~-+'pXz˷S$$s: U>|]{zٴ%#8e恌9nkkn ,9o/#&<;4 ck:!DZ3Ú-;9d(V`X߅ z:@zR<7/%unFG ڿv&Lxvn.>{2$O4Jz4~8jC[9 `.VYݲǍ߿_MӦM#0 Rwp p/r-kOKs^fvd"Hb)=",:C[N !1RCKPhz!i4YJpVp&^[ƿ~ANz=3_9r@vBkmuw&. Cx"x~I\wޡ4x®v=]D\БAYGIʧO$iL$0`R~+qj"s;B\Os<6-9WMl葉㉻ce\qa<ٌnJ:~u'N<@ЊlhUkַ0On愋M)}oZF}d`"0N԰@K8PlLa%~LX{G&CKeٚ&c袋*NMۻGR6NcM`u3%˘zںL>(E&sYL^#0&_:LAZ ϘT/n}g,zn%?\[[ki 80N`]3quL;1OMus1du1݋56X?rB:^_e{DQGG@rh9ʟ֙N~ky8|VxXuT_fkkqч Q455 *.TGJ( i{]YL/Si,kR|hm8ѕ p5ɯ?< ΀s IH\Ea}[8 ҴuYɊdCSW+}$! kVѺm7& a^7^2+R|:v,}.J8'@8N$"NU0!Ax P)imӦM݀W| @1#&]zN8e+aFDFE|f4CοKgƿ~컬zc=D4N$p:r3P }'3kv7~!/Jx-Au:ɄS As)y䩵n#$6h˘ΞɧR48j4>~n^_`aE%ZJULw`a;.I%\tTYs=Wֵ%+xݢXa7yUɀ _+%SYF*03IOHM-|i{´a:ÎÏ&PjʭZ 0Jt/8voK?tj;s$~d+*& +&ɢ/u]Y#bֻ̓/9Ǹz;Y2t"Mfgo;9k˜dR86Q Z^tXȊp_>놇 ]@U=^.q `Ɣq8 mko5tWSS&0uꩯ>UG*HӲlgw-[ٹ0N. DbH{),጑h"qqךմK?CKGE^uG^wJe&cr}4̤$$5jԧN$N*HQ"4!B^umFU>PR!Dɬ8)u(*Pߵ m Tg^C\.*dS t:R~D5u:jSCJ "b%ST'j:^4yaE[um-VH|@zI/|Pd2*{2_-T)% A4$ SXYɈF4 M॰F"P0hgU@uPC6m ZB0΢N B %O2O *⹟z -3H)_3e°`ѽCCm++[bI * 4k$$[E4Td@2u ! HUjtt1\jJ tl1Q쏏>l/ D|E~N:U=Sfm yaEAR>dF~DoTBӬs µ0d@}_(B.~GÆNvdsx#|/ 'I R^`ghcZckjH>񔇧Bxq%"PyӅ$QD>Q <8ϱ7&c%L 9DB!pB  J_GT![ik X*2Ry%B!z(ݻN ߓ!PP!PD:/%xR*4T'Og}JѯH@KƆO YXj|[/_p")(XwVl|7^[_A p}fss.^WHvom__ D䧏[Ň ~"E`^s-0YD־[PITAcFݽ4c$hBD!ɕB ا *O*HxUz,@Hot; (p-p;~L()@ R]!@C$l |)K*xm{^_ _жvykjFߕC #%¸T }xrǎ?n?}wxoojGj%*lnƐ Kh mӱN;aK猉kˣz~iwxU⍭c6vۘLږE0s:B 1,ٺu-߾ﺣ(ү7fn>2F?n'J TLfI6L@^jvfnd"9 iXz'T44fn5P?|vndl` Ks{h)ES/|s㽧K6y/ۼ9/wX\ɻE7IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_serverbaustein.png000066400000000000000000000103701506574241500261760ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time06/22/073tEXtXML:com.adobe.xmp Adobe Fireworks CS3 2007-06-22T09:09:40Z 2007-06-22T09:23:04Z image/png >9C" 7IDATxoG?%Jh=ܲbVd$ɹ$,$||CKȧE| rXD 3 x ̌X [jlQ6_|twVIb[ى@֯~ՍRJ~h97>@3XB+`O*im%ﯜ={000$Ph-ꫯZ&!t:MZpnJ.]בRBP9 Ϝ9 ;BCvB !|a-ei]MˉZlz\Agc[[۬'\O>_ͧضPvX\ooU#@K+7&GZYl#) eH=~@*˗/EI<BB(H5

    䓚יUdf5iQ.줻rҒyĬW:_@0s\}}}|]]]\|Gj"5iJyp LLLP(봊"bReYXT*w{Nh.O>K룽rAC݆z{{PUfyF ,a\.S(( eLDu"a`&RrL\fee]bVE ]59]󬬬H$y&X> i}멊tN=Bg\RD>' ]k׮8S``` (XC:&344@$!311i(LMMiӌ222▩"dX\\?{VWW{ϛ ( (  󌎎d2b1'( XwT_b,,,β4322aFGGoFD"R"`ff;w0==iy&"… s-yY.GFF\@/4>333 |׌155E&aff# 7nܨIΝ;uBy@,#sUגbvv!D9fffP &''tn,CQwZEQUUFtm !/HXYYR{O^re o FњgNF"(HMӘ:9({T< $SSSnCd2'&Wy&PWITi5bq8*5+G<|w 3<<쒭zyEQ&HH$֡*1~µIz*$ TU%eh*cccCCC(7npW%?;fgg4WuEa||]&?#Eappp8;50h \p}bhhp8.B7n0??O"իn7u9\᰻!8D5}UU}Gi׫87^ p8n;px-qwZq݁Ư^#%²,ŢKdq`_mDr\]k K岻w<oQwˋ&@H.#P(]e?feYnr#"MP.av焘Mm0Hy àT*Q,]"aPTmf#"TN{(+LӧO&ˡi:W\=% B!ׯ_' 9?a D4$CUU677Y[[#Jf"X(\pavww= fC! h4gN"cݻǣGx;Jlw+0 (ŋhii) 6 #w%Tit1"@mW:!O?L*BUUd2R)4MszX|YMO}+@=' dB޺ue666H$rg&jH_*\RNm=Ѧ¥7ZK dI;H;uWo} xu_9"&df SqL B7ي\w,i3s5:iߊ\W}k]Iuj˪k"׍)mvt݊d ny4xB~XZArCzw4HОڊkeN,b1P1FQK(Sj¶Й 2>%*Q_۾}tO٭ino mθ\k011202h!+%]\;Cی˔*+K(Ӗ3ӷmn~|ew34j^u,¦ i0s@kRXxNzctLsFozòJ5_ЪRfxdm'Yo3TYws'>aGA@u'$&2?6عsGi`[^_>}xŵ3(?сJ\Ϟ)>q~6Ġ^p4/XN8'4W!Ah+RD:Ok'H <49Y~~h(]7k@Җ17 )A AbP'eb-\ >5&ہ%m~tu&'β{-}}U|DR fO!W0<@ 6HjG%mӀ Qv;8wfWFgzǂ2h+?_Uu=PKRzP>D~ Po3&V|c҃#ۖq%_XSo_ M!H4IH!B<19Yؔm[p,_e]K!kZq4B^;y:o[W϶- -K5ʘ X:V|xt.XFK;Sl'J7_MD)2 L`.X*hp4m.X7^> te[yʥ 0s!Z,P ̰֐/P."sgf:ho ҖsL!$ 1k"uڨa󦮖RyσJ%X*rrN~bDam;xhvZ*'1jASv5x)km}2l-3t3e8}5VyJ2RB*ؽZښID@dB6Zr7A#s$[7sR.py2Sϕq# )׎MЊ±KaaKUkS'Y[U@ *#ҽt&M!_ #b#ilh  ߯G@$SSiѐ`C5AY-9L6"bx /&FMlrE~<6ɋHjѯJy1T*.ca}zM n[,W ǔ  %[Μ;M_P05zxn/S.iHA;JqO080 t8YK&"Ic4Ҭ?ƠH|}=\A|ՉTm\BT<A`^:e&peq $Zzu%X66F%!g 't1y |&!qQJ#IJ,|_P,j Øm :btAj/.@{uer 6'"w ;HhVW}X#.!B":dyh5hN/ۖ=K<{2ޥicG;aBı%0:=TԬ*|VWUvY e60|Zκ4X|R)JImd4M>L`k_GvR.r|Hd2,q23 !ҎCpF@ѷ:w޽ r/}b|$qx\GZ1ćPMN X8Ãgf羘ﭗُ70zzr^_k@?"#k3z|/pEAmf'iUk$<7XǓ2<2=Og`к |04ݺm@>ϧ ?dJV+E#x;!9~1=}X?Ԯ W:;̜6C۷{|Ucmc^>:DCN F1f]Rv 9^z_ax^r_NޠkD&IH-^Z";:ΡF$~^b9_?rb˗Jsf~㣿wmw 7Gy~E"<8؏ t;t%B-(UT<Ƭ1fcf ~Bw7GvH?rc | 4Gbnť;ɜixSDrͯ~rs.OIa)XAsQfBn`(]qXlvveysgمGb'PF0zҤ?M* LEjIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_terminal.png000066400000000000000000000036031506574241500247510ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYstEXtCreation Time08/01/07 [tEXtSoftwareAdobe FireworksONIDATxOLS[?e4: Rp($y Cp1 \&Jč&.01MpFP7, &Njt4S52Q"jIh {f??-}MNʽw}{=ghN +Z츚RJ @=PK;€+_j:::~Rn2M4͌D"rʍG{J'ӳݻwٲeKŋ+"g>1괵FO|ҥR !7@Ӵ )J)ioo?@& P boU "neX}}}8NΝ;W:)ˤL5MCJtuuՕ񺔒ӧOǩv\uXBgg'?Ng}6Ru<ƀh^v>x^Rn>̛7oPJziooGJɦMsp?r9sfjj]v%0W^EӴDVU'2'Nv9y$---twwsyy.Im~;vt:|2@!!ϟ?Otkk+9r$q.ߖw\Y A޾}˅ 8{l"aō7Bcu:::B!޽{{ebbEfggB$KjP".vu 4P(d@ }غu+RJ:D0dvvǑRqFz{{|Ilhh@4EJISS<Eu$Xprx!===x<v܉s7v;?& %}v^|8~5͕t刺_ȧ^xBd˯^Jחhb1Jm\eff>Z* ZҙT\B cR&J+ ӠItVXON !]LBbƑRMsrpWXKeEU+ п٩jUj# [WJH[#`NWD:gFV PB"ZUBY+v@NEdJ|-(@iJ#/HǑփRNu}OPJD0 0D"dZ-D͆fKrc;}"I8& 2??H$m:ذauuui^)zG`z, Xsn˧B x%+RXrk sD N"E!  ~xD "   BtWߏ(UOJ^q ;kkk鹼;vey3gFF.aܑ 1r.ٳegB?z pt:F tCb0r``0 @BB dffb0>}?acqcƬL8&áCذaMMMl66l7|@OO|N*++q͔aX㏉jN{{;eeex^Vz F){h4ݻ{o>GII {opY#'55>$N'yyy aXhjj"##18~8TUU9wzZ[WWGbb". N]v~~K0vO3ʕ+YrJ&.a^h?''GWZEee%EEEcJ 8nժ~---,\?_?~|qu&3Y).ŋ:;p6ӣ*Y{{;vկ_`5SFCI{N<)lINBÇdjjj(++f3UUUSVVFyy9X, z <_8v;ooXn&hAxRa"LiHLLzUpTVVlfҥ?>UUUf-[Frr26M ^ z.xtplqS}<(*0(pPWWG{{;CCCU,Q#(~x?]S2@UUIRk;s UUUԤ f$ݻw#^jEԄb?FU8KkNhd``@nIf&>Av 3g엛CI 4a C|XW-L fFK ` UV~(pb$?9O>^Vk7]WR|-؍vni5i? 7_P=u|NadTq~abUq4t2 Q10''yO5Ň@ ЩhCQ∋;&?y_O 鶗z"&B!Y BITEy~aP($kB6c d$ydsp%򣘔`hh+PFgggt:`ٲe2L|fNEQ0t:G"?ƶZGӯ9;Ht22tX 0! vѣn^Orr2555ttt~RSS(//F b]~%iKHȑ#P]]͑#GHKK)&'5x(='NxNDž dS0La}E ~ȿQ" 222ON*,_|tEدEQ(((@Q,Y";k$w}GCgp:SzV+iiizt:--- n PXXHaa!̝;jRRR`eNO{IOOg``T4zmї/=pʒ$%(Bvv6'N`M_G=XZ0s1rʫ6'b Exb@XO0#_mʚRXiKsM1bBMMMTpH })vŖQ\?vn$33)NV.lO,S;z( ?#;:;;!ᠿ?';n_Gyy9ٳG.X,^/!f$$$^W0͛7Bt477c0<OcZ@#̚5KFuVhjj.< ڻ)[PPPP7T"omO8_&Oip4Ws,2xaJ՝h+07\ۋT䫯"[&,sqhjmFbb8D>P#ÍY^ Sn*gvcppV~Xt=/ޡ{L'fp'Zf9\.hjjb˖-c%11Q&=E/77EQhnn`,yPPP@vv6.\P!s}0J y}=:J[-)IvSnGG #Cc^/竪̓!xbIR^VUZl6SUqK%yTvjINNpaajtJtNN/Ƃ+"Aa4|.:x;$%%]eS&AѶZ?^E~mmmf*|sϋ9Ol]wjp̱b4b 馛/8v}l t84J-O8X0y)*  'BKŜ9sVguT P(색8OHl/-^oIF6ZeHQ,XwIWW6m܏vpdGKz<\.W#oLh?`v`' w7b8MQƍeٳ*23ٱAQ7zn빝cG+(lR=aa|,/FVO?j8DqIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/icon_webserver.png000066400000000000000000000123221506574241500251400ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs.#.#x?vtEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/08/07B~?'IDATx͛{tT?3W&I Hbv^]]^Wtk5(*W,DLL&dfyscb&$Z~s9B4Q;x\q Xp8=G +?Bv[%g˅CJAXfFc>o@(jmy||(/r 2"#L!C|kB'f˝רz3IzK eatpU^RBL8+0P$Xn46 ir}-wuu}d@K<_ݝx#L"0ʲkٜC~8L! #piZ4Dfr&c &Th&b1,mH&}>l}\.dYb16O.#)r(J45M3Y?0 Lopت(.O/w\C4eY>!6{FM,cب?dNOt::*EAhhFC&@$rssG%r<B!p>i d0D"H8 e+2Zc\AՊ`h,LMF Ri@^^鄜(PX,me"ɓgJN|,KZj BE߄_ itx.h; ~v~)lق'dBV+VMH$qE! xio/aswB CEQb ǰwPUVQ]]<@nnH΋c-Cڽ:c$ $1.JI9S|'/S[[h3L̞= 0a„ ~~Ysou&Z;C$:IB/nXԹ| p<^W@nnmlD2BB***(..!߾kO'LIVŴlWP]Cs e59vGCs{?w kcẒ>J<@L&a 뮻VVo7%hhwywkhPڥE$A$ `8+ +IؤBҐ$ MU 6:wP'IK& SOtY;s-*7!ih@IHj=H%>+"<+HT5='{<7#  bS'6oƍQ%-ZoEb X/GS~4x>nw Y/lRKz U`1$b{K;k?}4x뭷6m ODxؿ ؒ0h18 !iF~ H$xѠ)uM:1)Im)r4TtSpN=2K='5㓊 V  yٶm[baҥ<tMdg_ʁi4f/F)' DP'{i? =(62+NQD~am N= Ⱥ3DT?5@>bAutްgy-[ OWWW*@DMYE֟AK{m!d]@$>;"D ApsQ^ETALhq ܄ t!w"wF;vҥK1cƠkdf^kK0"_G T!#2̎(sшc0(}}Vb" hdLTJ)7A@i@*3hDz^Sh"**.jf)]$ ]G)sڐ,ih6pV8/&K CWP.}GZ/xH$ի),,dܹL2 m،X{j}֗av5hB#ϏBHĀ4ؼRwI @'Ew "%baܹ֭ĉ뮻R׏0‚-@ h)ޘI<>>ˇÝ#/) !$TEB):g4n\$Ri&`Ÿ\.>tc=UHfI7NECCM航*[%^wV2S,?E  BdH 5#E$e)mxG1ck֬IBi@$!PU=&9p> # %cךۉ-i-I8 ,кcΈPTL+ȩpcf$" Y/z+Wp¯!?ۘLhUʁob=CN=&Ns~2ρХ6>zc[v\zOySEXh YG1;CI$9VX1$^ӧOGPi \{cZ11~x(h z}*i:Jf73(H: #ʤ *gNX9V=^FɌq u!izM"xDF-\W"),c_p~[*97n$Ja BR>AT$ -a½d%0{{{޿,E!vZjkkH9jƒA ]0tgUo)r\_ $Bѥ@K)lNs^yX $ԩJY4|O?y^ &D"A( !ӦMoFnJgg'UUU̞=@ @mm-OcHȃɒMO; MlxVG)30ChY/sEVv50fK6c(!۹X)K\Lb$ekShx-!kGw4M45tljD"hFSS---L&}/^G~avEFF7ofռ+[iSmp Ӫ+1ٶ񠕭F;6u"p#U#n N2O㑗 xS'ޥg 3;a/w,ɰlKdJ+ X,ܠs_ 9sPWWy饗`ʔ)w}̙3H$Bmm-`?3g,GorUxOt4xqL/h˫s-̜9rΝG4ԁw~U*xb1?Rrqtg 7pÁ۷^rHKF#}}}l6z=6܌nJL&mmmc1?-[|z>jeRX&Ulnj3!YqLdlقݖCUX0j,&cW uIR9tXV0$QUUESS( W]u^CǏpWp r'SKldΜt2A_IOL246#OMŇs^&L"%9n,X  5M3,"bXD"!p8!kFTWW*w`ƌȲW_ ۓ !ֿUseZ̜9s̚9䚫B۷tdrտ!̿vq9̙3Rm )dI [Ct' +kF +1-,X0ZrY) nJ^xA|2,k%IJmz{{L:˅ʵ^Kee%{졹n򨩩IJ~~>\.4-Z[Ȥs}tw5?J@-(3q~\1yldӑO|,%K3`ǰ(**bL,c޽!{4:t:[lࡇ`ݺufV+D"~SlղͦtڹO3mk=XR^}oX98V ?^/b=`iл{7O&[TO~!Z%!izx7(-=Jشi h]fM ;;~|bvo?qrhhh`ӦMh;浪 )((pwS]]zv:t|`nn ==++xzGۯw\4dXYn&F 6̋FMν{^uuʀK:*IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/netzwek_a.png000066400000000000000000000011551506574241500241150ustar00rootroot00000000000000PNG  IHDRVΎWsRGBbKGD pHYstIME  66 IDAT8˕KaHӫdihk;uʖPp BTZ$@dK@B'nq5Kr{gx_<.r_&?ZO1N_ǀA uA|Er D;X5_ia<"qˏQo!J2GTN{G?지q2Fr1fP(X_OY Qi>K,v]x(5Ny?J+@T5-t4۶aXQYz]pRT8<>d]麎(뺟g8h4v!,BP%a"!_Q}?\.$+""!DnDDBdF fFӁy) "()e08(˸9&J%bfj'JunZW՗~cX|mi3g.twkIEy%xxN{^̙˚(+a`8X,fdY=h0`6(mio caIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/netzwek_b.png000066400000000000000000000011241506574241500241120ustar00rootroot00000000000000PNG  IHDRVΎWsRGBbKGD pHYstIME 1@IDAT8ˍ@c[ Jxܤ @4HϰņP D4s)(0 `|Ӝ-6d6F3s,a? @aql inRx<& Q^4"l680!Pՠ(yP' A=|߇y tBAD H-⌈Bf_3tn!TUoYW4ߒL;X "54Mjz<8H{%2Am۞H)uߥR W@M]e&O|@׋J510BNɽ0& LLeYM| a0Fxn;IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/netzwek_c.png000066400000000000000000000011401506574241500241110ustar00rootroot00000000000000PNG  IHDRVΎWsRGBbKGD pHYstIME  4K!IDAT8˕jZAsm?@͢BPppqߝ`iA2R>B׭BS{VtaZlզ73̌(܉*~q]\ >G#^.`\oP/cYzMt|]+ l 2 z"6m}ŢbA,kd(Џ|>_>??W,=u]K*ʈީ(Mbvd<#"VVN5rhVAџT\qxeD%(RUDt:RhC_Lٕ{x<mdYǡj-&@+d}өcD3"]!Û( 3E#ih(,{Q}V%bEny܃Mi"RFRn7J=F? m┩IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/ordner.png000066400000000000000000000007401506574241500234160ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  ~tEXtCreation Time03/13/06ޖtEXtSoftwareMacromedia Fireworks 8hx5IDAT8.Qΰf)؈ (4 *xy'oRTAK'PhDV${30&KN9ߗss~ VP KD685j5ePzv|%}ײ#@.Wn%UܙIiMA#N3"u֊ތ`Ӛ 7̘W`ݲ}3s GYpuERuR#┊ ]ozbL Sj޷W} vwL-fjĪi,Z/ 4|Ƨyd3Tx^qRA*.V-k>觭c_r&'{᪉Q߿SW.?i/iY:I*#ikB<\DW6Y^ 8{vCoEA#rCnY=zGqT;m;I+.+F2H1s~.8={= DK(uׅW#3Sǀ%5м ʏ}{-vu}]A@R{/ ~‚]rmrwS:AjҳCPPmPiQHJl[wx)meH N6؋4%##H`n\H%x~]lL2m|o;\C&mQUSCuhRC5t{}}رBR'&$l33 'ȁЊC1{ ۲w{"@V dvPcCŐ"OJ­:|\Ucqq8?:\={L]Bm~ SSw"IlVFت4,z:#@;l@5@QmLOg&+7|ϭr4XKT4m4sJ9jSg' uWTk챸8ܹ@%`ZָcY;-YW8F{[:6jy[c7(7S9r %L3)뎀PȰbsrf9J3!7ʆ=z)oߡ(]#=Pswq+e965ǠPy!o05Www) \dlv8CY)7zxM@R.Y^rj~r`.;5wu}z3u߉QBlӟ`Oςm׳W&`*֡\P ԁCCZvax_h+X8NhFfw6K߰,B4)J7x,,L ԁC# |xgy42}b z0662p'DFF5X70ϟB#ć!M_>/W/ӑ͠N̙wEl~(hE#\.vʧ'˲^|U7;wTjGcAX条<+3dӁ׷lsB6ߍF |2Sͽp@8:vT^_\ɿ,zeY/[ŋKڪBHy}߳;7]Ө+twJ]64vMg}cd㾝^=P43@H8{e-8$@8& i1I;4 Hځ_".}IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/peertopeer_dateien_klein.png000066400000000000000000000015401506574241500271510ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time03/13/06ޖIDATHkG?3{?hr )o|BrEB7+}#}g b.xIrݝ; - wyaFXk!?pVVVNGiVٿvךGz߷AcOjmm;`u0}F08ud %5v$Z[REV|_ ! BTfݾ*%|r~lְq2+q& DX j5Ve߮eڹ{^Z`3疙`X (0Ȱ:%wI$`&`R,`ӅO.2㱵2לѤAP-Hhw߆ey̳^~?Anj?Leb&tk1ba RF#(`A<\70=sGG`ySI`p] GP*@(tz31@m \> (I=7?qIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/peertopeer_einstellungen.png000066400000000000000000000100131506574241500272250ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time04/12/07㟷tEXtXML:com.adobe.xmp Adobe Fireworks CS3 2007-07-22T19:05:42Z 2007-07-22T19:14:54Z image/png ux' JIDATX{lTםǿܙ< Į%/ [FMiɦ)R*5iwBӦV7}J7nVY6-JɊ%]&n,B16{3wc= Vm~OҽևwY>w&a۟fr4Ő3ˍ̦ʮ|6-rL=L.9%O3aE>F;n F7MҋZI=S1^0 t}Ylv\պo~m(w_D7um߾}ˊ+000&fsÃU+(2^w%+ޖM_wU8V&&N-~*cG}9zTَǍ"g!IV6TѶ0.LRp2*A8G -rK΄.ڲߨo,~f8q |) Ǿ;dw~R ,BIRV8G:6}! RERP(|& ʙLfꚛ5ћf0<B4#GKK xGU~)b݈TTm u[OA\`o??_Ơ@@>o$ mmmnBvqFYu;A)-E)$IBwy;y; e~yIܜ,"j[\ bZ^[['Ґ%>#R B @:.H2dfku8W5$܂8w;P8cBe460 X>0dY ~QŎٞg"J$PH"$I׾u_8n?}4~- 4AYv)J@A -[}uPdp@{tJ,ׄ -y+rQ!6 trճ/+_~4z.}\>V\UQ^0 Ǒaۑt_~ 'mS X~_eUfo>$&Lq mî fS'|~B3gc#NӴ۶mbZPU>J$eYSwv ~`ڬWMLr %%p@&tGm9$! hb|/`lE]Ahݎd2:t{] Lza{n>^`{{k6M;l[AOq####RjlڴIHRPU +>T`j1~=~[i9yI\oo/4&CpuP e1hP("L)r8 e+]b~8eE-FCz{{q5b==磳UUzA3ދ&Bv @YPveq݂!#i& XvTYa*H$3^lNuk798|OO٫J.> RcaXU op\ .kٮ !PSSSH$B[6O2xMd"_PJ)<r(lY4隚v4 v[-AMn*[ZrF!y?ꅭ;Zp펭 \]o~|NwsgLbqC&\.pd׮A9k 𝝝$@Q o˟2ٽe]JD2) V,&=o(E$.Mc8͆ k+H;fȔP+ې(sƋ'ַw?}ZPT A,,Q`2õ4rP_G+Ky۷ }RC `O'QT g_6=H+MP4lim#4M(,_03>>u8|>  Jo_1f1(l>?s?}Sde4|&\|~F-r2ǏR֭[jpȞ={Vx"K> +(][pK cOؚIs ;)*-v>>i=`٥kEFO'MMNh4 Y.cmmmΝ;;NN BY TXW@{*ܜ+&Ҋ|]'`my^4MbqEQ|XV!RV0p@z- @J=OSTfy eMԗa2*Q:Zy2R磣Ԏޚ^tTIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/peertopeer_netzwerk.png000066400000000000000000000154571506574241500262430ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs GtEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/09/07 ZIDATx͛y}?UmE4Z@HI[ ;~,`Hv_eqq?l X>/X`b FB , І4f3wK?3#$9S=uoWU|r?+~j}_-}jR(>WJ}JWJmݲa;Qsxֿ z,$[]wy3 JOD"ŋ0"PrI${8z!G]ǯ_}uw]- r:/THذAkfZײ|-,[&Y\uG 1˰}Mw[6B|}׮/s]Aρ$ ]b4 7﫥I*~2:!`b3lhe:T`j w@ 9֯SаA!'RxX[+VqUxyxQq@M<H@JBk-"Xϰ*V$57|\iBh`%(0t%~? SœO&yT ]J={Z] ӛae!\ ` dS8dolBHi ʕ#"_l'?SAw )E!6iT;aJQ0tuz[(=M>w''VX(eE!" ;/B+ Q"io{L" &6 ! Tu_DL``4hԇ6ʥe6}%WVTu"6o"#c{Jy Gwss͏@m#$\Y|X1a 4#VkJ-lS0|WH82^ө0#M4jaXeߵ>6\&D9!J*U_"Dw&vhEu>Dt=\;KUFsվA4Xȁ{o%:h+d&ڵc9Q(! U< & ]MwӲy<#mp<:gk `x>!+8lcowp\tp] $,\r95*p#8=Klo!&IWFuom[>FJsUlMXoGs0|@'CVٰIm+mk!ۈ *bs%+4ągGr/8k/GJK S=W?}]:xTW`X|T w][(.5װGXrȝPMʙ`9ѓJ}µ)f]s=]x^fm#ӈ;  m<?[|OkH4c  ;ϳ]߻m<Ɔgxj0JEӔo =M4]~H\bfBzt= h!,۶i dl"Bv$đbg FVK#yBjB>2p<io*xV',s̡e26m ZD1j4-f0q Pޡ|WJAv8Ӊ5 N%0vR}NcPOxnhjs@Ӿ ;s$s wg=9BԵ+xl|څ[h3gG&\.֭GxNl&j Z;6p!N`S%Z '-)q{g)GNNԮ7 =~_W 5}B N͸kN{Ծd;Gݝwu:t_DQ\7_ulfϮ_`'AF \Có W`6vV[전LEy(tT5pLWظ)ϾͳÓF_PL'\~N4%<켤u!9!KψD8}s̳cU,9:ތNH%l׆0B -%?KTY*UYݴ`N&g8L`PJHWLtx7sN#׉o A*1̼ۙ7k-nXmضM*\.0wʛG^ßwOᚿfX'ڔ셠Ÿ1GsC\R~O5w'$N@e:@sN^541ZPhHCj}Ysԇgr)>;h__?[m)4׿:Sg}9hU]3^V#ǩXܩz+j'Y3 JD6x2FXè20b,ISO|s/qg\ܺzOajijJFvE˫ R~Gcr棙Bbq#0ujj߉)@Hᢅ%; 楗|򓭜>Eo/9F&'0b}U\cŲ(V$xʭ**2C5 S\AB,Ձ53^9 KhP&ّ&F_3uд44l>cgp!t]yYW&2AGCMNG)&6X Wxl5^(HѴpPS ѽRi1pǍ*4wLx=^QW:r&oEG)M(%1 RJYHC׳$[oGۨ4GgqٵH# iZpEZ%0PNMn$F+n&o}QL|Wmto]B RUY).!BHoku/[>pw|d|P  LhN*Lמ%LYy7EJ*pC߶Q5,A禫Iz}LJ兪< k"e0՝8HŜ_͋c'(_b?0Yޚ5pQG@[% NĨCQ( YK%/j_Dߖ;VeP)N9Vu;|H|űs,Z7k!8sRR=|$&k%PLlN?7qUum@, 0.;Vo \q@F2Gj9 _ALC(@YL =2B_ ~py9A(Ƹ P)ܬk; NnX$KkǸOt˂uUXUIt˦eQNO"-_TP07YPug10bqR%: zty%ehw g[h4!_yR!t|/ϼD"!֯p8Du,c6. 0”UbһR5нkurXpnj9_nYO{~Ms,|gw\CoJj[cTUӼ|V*H~\RZQ'ꪵܓۉxKƒTM#;Ќ jgFȜoǐ}[G qӬ ?/.yj@?Az8ˤUa0xggPkfɴ4+#կ}Z>3x:J|4?;4${Zu\)lDlr5?Oލ T|.qo9gChij[b 0|pur+Pі.Eh~ "3bxoc:LX,Az{+&p#Xr4/L]I3cTMZ9>:Iǩ{?\P@dNWnυy%:x*yRsnM$ڠWشmJcQNMyP083@lrBZB˃O A矿u/[z9 $wGggѿV<;2&x,x@#f-P3dwnƑ4p#uR[L_v @+ ƾ86nGidx')9'mз/* !A$YN2WD? 6nk SvLp0\(!PN2.~Vxgp"&8LƍEmSödd24to׿{VCq8aȮ]=<0B%Lm}h\4!4Bz"Ʉ:w[ y īy\|# 0.J۸Ja K:Ŷ, N+GPYC@].>Pl#c@("@gplX%$Î/ pwځݲ0g! W;Ix!`y"6Q;`OOL-0! `Q jAjxG (ƍLDwȭF05`]!`}ZMإeѶ`V8n͇I B<Wb? ~AOIENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/peertopeer_netzwerk_klein.png000066400000000000000000000033071506574241500274140ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs""awtEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/09/07 ZIDATH}lW?~w۽tmPptP҉n7lK f\JF$[₋d8n6:t`+􅖾9R`<9sI<8M~ k=nPK a6ٶζs.^tסZ:/GPw4Zk03RO\șOZC:ٷ/vLJĉb7 xϰ_954֔wI Zk~NA;;m^x!N>Ϯ͟hBT}>~VAȚ#lonf+əh JO@^8mmn{I{5<t5"C5[R(P PH#4gKNϾz ѪMYo{[49!jXc#cnw!TzYH/V@)A>#Mtf28"Aa#Bz&f m`E@T\&: 5y (&UЀsLؿ`kUWvb_1a3{e2~X,k!Z@NBQQ~9|떛-r$Cg$W8ض\E$)g3B!卐;(,g$޵pLt#Mg\3k=(0J) NJBm+L/h7O"@:Ґ=^-!78Ha`aB3((F1dwQL@tK&No@6VdPmLSbYKcf!d};#H Z{;[4$F}3H]Ct_VuQU$/ԑ.hnړHM{`4'P* FҔ.0 Jk)-/[Lhq|H_:D\yQkyblҡZ~qXKt_-Ep$4/S9d{>e&*Sr8꣩a"u3x[1rPMBU4 XDZ,2^Z#+?<8_z80gVz3p1)\!== a~eXKBGDjb_yk9|/M *`v۵-Kw1Ku!$ݿ{S gcBZ.܉3^y/Z[I'r+I)0Me~lhrҡ#w۴g`k+o2ݟf8UR%F a-_Ξ?gLHl`xe}mZMki)e= dvB϶{`v2C1`˥"?^F7ᖀmuL7$S蘎\!-d#)IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/peertopeer_suchen.png000066400000000000000000000067321506574241500256530ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time03/12/06Q? /IDATx{lTW~ǿf<؎@6l 7aM(]R%jU#YinJU6 VۄhmvCVHPE[QZvî"nL{eƾJbN*G pO3 rcy`$\˙)TiS'.t;|ɝ@(rE$Ig;G+뚮f p!IJ+l0wwiln \._n $IڜdL?2 8 ^oWi.M9t(o3kymxj L~/35ټ!@UP.X0ӻ`AiaZN:ը(fEQ(߶0+sĒ鎄 @1 ͹k > MӠzGj :;;892uč{~13?rcW`Jj:%'UI,s`o /C@1|,TUeY@k^࣏>F3atfc.EOaNfӟ!D8C#(g]6ڵKy,|N_8^['bk>wd{q}!TjCC~ XlE駟`˩09XKZY ק@)źu`&AWW6l47i6 _Ns^8pK|ס k:nOUҼ99[y4 O'"D"LLLNMM}]psz|BE)弁fێ֮%~: ~}a$IJ>V^ʹ0   AUU㸿t@ `Ϟ=p]<Ǔ֮辷nza`vo:._ $ $/߿wyg( YjUiQqqb1b1_XqK^xsIy4tsF+l_ !B.s쥉}曻UU=@:::`6!% S([Ryap'%---KĢ7I^gr/|uQeL] 5G6#AUd躎\.dH,=822槟~}^{m7lj% ÀywaFPU 7aI>YBHaUMζmA5Ma,#X'CCCyĉ|zg: RT !8n$ D^"I/B#Gzz,˽x<;,YdYF< H$V ܊(e"hnnF}}=(3躎d2 ۶U˲λJVCPUEQxHKKK8-Q mSSSPBx!CH$A8h۶ AJ`YvM\$z̅p[( >䓎-[\Uյ@( +!$ <ϟzn;vu.ߦh280 czŁi0M,AӴ|"09ʀbzmx80&PJa6E0L-cppGغuYR a8q!h X ܆;w l4Nczs\47HܦEK|=IUUE<ϗ+p8EpaѶm*l6, 4Mp\w4 cPE;hBA3,P) ?=UU뀮렔Xw,bA=zt[ww+k&,kMLL`Xtڢn^IENDB`filius-2.9.4+ds/src/main/resources/gfx/desktop/peertopeer_suchen_klein.png000066400000000000000000000020611506574241500270240ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time03/12/06Q?IDATHKoUsfθ>Kb[vh* $(P**vD7b" V]X?Z))6H B"A$\7(%vKܦ{<υE:BH#|w q]fnn0111y^^ޤdžM%i֧~wٓ+[+EKG4H]Xu5U"LMM,VO^O!r,A '@U+Ie?S@ =6q^c8.`@>*e KƱdɽHOG0چ[( (""}1^=A$ T7Vj{ (׶`ߏ@0dz 48i>v4P=D"*mEҮ=Aض eaPH#tH@$̦jZ~m }n0]-!F,QI6Ӹi?/|Q촃'˯ zT\7UAymYz96bL|Dkڏ#"87t]?:#1V-;O@(0jUT &Hgb1lll@4rֲvK7R &KqBHc[`(TB_ `~~|>?FaJdYqӝD dYslnns~GQSP(oAh1MKAh4 M`YVcuuGFF~SU59G^iK[.q^EQ9>AuI\X$x:]wEIENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/000077500000000000000000000000001506574241500226265ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/dokumentation/Nuvola_mimetypes_pdf.png000066400000000000000000000225001506574241500275240ustar00rootroot00000000000000PNG  IHDR>agAMA7tEXtSoftwareAdobe ImageReadyqe<$IDATxb?( F`dM#h FBbFFF߿؀r@!^`=߽?/ b=1 |b$4i@ï_~0ڣ`fff`ccc`aa%/>}wĉ MJ 0oF$+++= h^~p9'O0ppp0v;v, ("0Qgf066Q@?[e˖1_\˗/W^p(TDN# `ff6b#851 32$&&2``U ""$&&!-@$5$ExNN΀!WX҈ &HxNNNpez*JHHz P N(N T|NH-(ţ bh$FK0 ~~ ???XG( Db*n@ >Z|bͤD5.\m1P<0⃛%g`gg ILa G QgQ< Pu8 K؊X D `%6s %0q/D;Dxw*`*qi4̜DV @n#p4i.r@PG#p|r@QFD䃆Y4Ho0=T}d@Tm䳲1gv~f`WN Y~ȧV# XUr:'7Ã[ ̛10~d'"'AÐJ7pxE>cxi L &6 2 @>DŽ5 R ~:B'T2DN 48}`ϴyJ?Oݸs100hV/Ço߁iis> V gO`># ?20lpq30^pU3}!U#H|P.iN7OuO L l@>QvPg0ax xn>ObF @Q\ ,.tE7/>af W^/?&f?0 %z|rP0A1l1>}(RB c`qxz|Jr;6@LԈhJfLC#!r0f``¶, @HM:& bFa XZ@5% x!z m!oVw@9_lG,9GD N ' tޏo(3/ ^#^O`z!(/F 4`+7W!q.6VlZRREU!x.n<|>'ț'#J_%'09* F|[ l >dajc`ar4% h r9@LJpJ R![ޑOn @LxRCf%hxW\p .H+3𰳁@&𡼌 \H)S[!Io \;uWjUDCm (SP#|$n3\. >4#,lc```#/wNgj <ޓ7"@T ջ~3a'% YJT᳸ | x#@T ۠\/ _FO *xy@[1@E>%@Q i,q2c6[(rGnA(tPD>( ρ ^302#000|z|ÇoEO @t4x#df=X0Uǟ_ \woi%l JN 5u}_gO00ܸ VC0߻k_l/=G!{802lZ? H2~ /?~b'  -G2pr#oe?\ .iP|r@}Q u:/<+8OT\, W2y<@5&@Tr䃺w޽eu-?/GnA?88 _nd'B+9%@Q$le6曗89!u 0?d|< B0|tϟDmCDiΧ-2s~ *AߔEJA:oA7Y~ hD~clCX}0_)4"{l of_s? W ~..!aa𦏿?~00Vcx3!s{w @ʑ߯_aq'8r^k2)0p3 A =dag{ 'iLڑOn# Yl 0Yo fxይ?~.`)AǏ-;` I G2<6 V#yCb]cJD_^0(s?Ndd`1fx 7/1p?T]'@x.`HΚ 432|n=7>dsXX$"}f` -fA A_v3F`^ (}x ⢐~?(>&V ** ¼܏̿:^ə᧒+`c 1$)ó НJvhFPnߟ8!|ው/BfXݏ]LA/? `nOW]hЯ 8A~\ ew0@ᕙ "J'Lx_~#$*fxrtс+  vH`|P͞ :b |Pݏo'(p2{3v ʘ i>ݛܺIxC`@ rH@E>Jwܬ.zsI%f B:_( "g`xJ) [>|~\@tiH3@V|[1e~.pu_``x%*c]Dɤ4w _W)f3ea۽(7 ֡`Ts޾wLAM7F uD ?Q11w[W+٫o6<l (y&?t d yG>%@12X䃆x~O8ol| O9_HP<+@&H9 Dmx2yXvcxrGgt : nm $Bn V~E?|Z'Go* << S>QV9b,@}@ ly }a`Vfxzj/*- ||𪥘ᛔ+h0TE!n 00I1SPe+ T?10I1] @K.  t6VU~/0{QrӽXLl73׏#w] a }T 3Id\̋ n\XTcz@{쉈9i~r:@PЏ9@vS"\]G/Y"XKh@k45B34&re?0/4n ( g2# n93pK0KJ2H(*q'~޽]_wo3w ,l?vnn`[SSع`b`_Ɨ_` dxoYA#30l | @ Jo/`g=×  8@TQ^g`@e_~aaUӀA/W~:e_"#k123p/0G#r[}xM?3 94q`aX `)O`l t*9?`5L B @`1\ !@A @_&J&EQB M:G. $A9ic࿉N@)~0t-^ VNDcA/`BNm1 IUF (|!p&Y壶h "/atQ=TфF)K&DLw8~P5 ls["=m+4=z{#jpXug`WY&10 00svL޺/?1|F_10|o 5%~3Tn4 xP)7`a\!r)+T" ,1?ˠwr;ԋ ]_D B HgӢ* W4|`D5@+ӕ 7.2\;r dL j@A~ׯ }g7?4 H>)3|Qb")SX0gj&`$jN`I@H# Xeufa*V/Y#oāRo0gx]ˏ O L$3 u=*]\[MP_t!%''MD^-qr8(_>a`|q`3qLw² މJ3|ҿ`l "t3h> ab!L u~30}cHX8^bng`߶} ^Y0sd!. "Mz[6@/0 -g2|Gw= leTL\9s+iߠ.8g"|sA tБ F#%ŠgeKm:k`ρA``6Yeႎ5U JMPr$ (S@ ,WwQm V2#ۗߺS~7g"h7 + "5<ܜG8,j;}y lm n1bbcr,0a1GN07vHF>hŶ$ء`PQ yzKX^[j: QY 2bI2& OX2<à9_fȽ}e Ҽ< R ##aZ`"o}= 3Lb40_rFρ>'" Lrrr_,S* KbG'x:h;MX6,b`6,"2u&Ӎ W2||3'#5 _ 7NB.u_QM ( %--rW-}+𧼇ܱ ?!)R>eՔTRD`P[31i̩od>c o?eن1|Ue`g`g9Aubhč/-* ʂya9PK*\ǐ >}_;^J &PÓ9#x4Xtb?.<u޽a`/axp]Ìᩚ`dW`&iYK Fv`xv00ubt@G> *U40qV *@=Qr# h 9X|7Z֭ ;`ӎ epԛᕲ.((WqЌ(;ў,)>aK,W5y`KPP zD>zu7ѫVj\Gj 7Ar߿15e3^?]<|0L h3p 2r#c`h l(f ,93UcAx|"G3iq Xȉl{11޼X3# 紭93zP#HP@>OZZ7f`@/`v'Y>IIIpOKhv  -r?\ s7.f`݌ f`߁]NQ O$x 3A9Tt?L C&`"6`+dϰ1Ṇ1 D/(%bFQY,Ox<Xbd%& J 0H[pq2 Z5 lK&10xVXa0|PO`"+Hyf>30 (#놩 o30WFPğ>j(޼ouk1a- $$$FdS  `Z j2e`|45Ϝ0_>e`,`3n gM=6 I))p>5JUAE}BG]h&h200} u +x(LRᄙ{( lЮ`p0D>%@Q\ངA m'2? 俠9zy:h|_&`땨,-K \j,#] p|@~(2Ver Bӹ6 &s~[_^Hz LB <\ -|]/ z| ZPp [Oؾ[~r1|g+ `g0h :A Xh'E~bv >2O1q`|dfbdg|PB3};"Z%@QHʤH KI~}В(`[4)V񣑏tiID*O""4m>V>(3)mՇMC()DNd- > Р5l4fb hɣ?0 0Uh\'7Ѐ0 ^FD>%@Z0?OaB:@M;|4+G#z >V3@Ϩ")SM} hAMZ .]?@*#z&j8hTԩS _|G_BAELC|>z(Ν;AyR@b6N~bx0$c@a*!QhȂl O߅/ׁ bNb ICx D`oo`nnO=vh@t (I= PB&ڃМ@1n(.  Fa9 ՏX/IENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/add.png000066400000000000000000000100241506574241500240610ustar00rootroot00000000000000PNG  IHDR>asRGBbKGD pHYs  tIME 8^IDATxY]YUܭo_^e&xf4(?D(O&/H&CC$&8$,޷w9xs{{{Su:UGcp_ĕI7 Cҩ s{W& 0<妪mrq~|W&xck+&LߕBs0|t;UI0dnaw߻yX<|` q. p6 ygbD@P.M7b@tKPw>@8Mc!txɉ@N\$a`I;jt^t4#A 89spasufJȭ2b`LfIA&!8>p޴l9\Dſs}/}sՐP}*IogwxN`=\jw3a ^:d8NH{.P6J!?\^j_6po7W+Nf9[JrS#ɐM{$}}V2ˊEzyopO 0I%nL|x#ÛaL4{ eN,syQ dqP.۳Oy{~!ThRi=OI}|22L-|x8 GT%aa-D 8a/ºgH&$G&i5p9$0I)4 $PPiBm궄hH ٤!аV҇]F2HɄ@+Қ@麗NR Aٲ1UyGU8o¾,#P~'1ff TiP$Dk좀&Eҍ@6D.P3TR) -T(Dk2\,? 2Z0u@j<4`MdLdm@@kvl^m"%~xwr,PVkKJ]@xBlBeCu31( 6/fCm^0;~wu@y !BِD"re`,U ܣK1.\UP66e7eX̮L-̮MթޔjNzc5V Y.ypjy$ܺ?W)q=`X1Ι( \|kh)!(Yf4ߚ>TtU|Q\zL(f/=9䩣If={a8tP? F B-$<>BlQݔb>rw\aK_cߚ|ϟȐBFսx`D3Wdٕ1,%K}‘X=Z۫j. o_- "'M;^UܜgLJy, u񴆕 EMÓ*ɇ]Gk'H$̮ąҹXn 2xV6>ZA`L&-,;d`:*89"<"wg2 [HgC|H1hE糁/orl 'lR&{/E吨}R  _ ұ U{@(mDWBBS^ AOTrfalXXLxR e A"Sf_ 4ᄀ}><$𤳆J4Rh}A{ZG$=Ac%Ż;Mٝ+ͧ()M`l5ƙMK CC ٬ غ< xҲ'Y۽1VyXMks+mmhRD8HNWe! D ^Afv7MY$ϻ"o5.&M%|ytiRn9w 5RvN2 [I2~H6 Ht(BF0|d9Qr>鄤h̶)X] ڶ"{$'|fV5L;=}ey=耉䀇ŵ"@ ΤO^Z xGqz$Z/P0 T YY8_B&)="#Sܞ)7bkFrg]?Hg9aznbeg}SETxPҬ5{w]&Kc /?}/f |-)с4Gzl:)SYW̭麙Зpzc(kxg+|J«de biB%?>(Dg<6W$koܚ X/)nϬG oOң7 ʂቺAHʊkw1;.{dC?_0 )yTR9@W.I$O ~thbjY1W6Twޔ'SemSRhdwpt& O>-kt;Lٶ1zcw86sk׀M=@J`_G9mI4*/#bĶs_9Z>Xҹc8?>ĕ7s] lh7(.5,z-?`{Oi{ e3]ow~|so<׵t@`Y˷UVΏ} A72bP)7蜸Xw5]܀&':4UbDHX0ɖ#Ϗb6Up/gm p GiS{ZkL@]ε *I}KX5ʗ *mjz *+ZѮ=Ca]Xfme H/K"?ŧr쇑vbe^fD ڭec^g~y~|읪MX3+> n.ےU,|SGSdS _T^hVV?~R?_ןzoD5.\O_~`t ߓ!lєCR o`+ͥs-{ .\mf_cOJf"KmO l+ҹ'0O1_gi\Ք}{ȷ|8o#\`IENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/add_small.png000066400000000000000000000027761506574241500252700ustar00rootroot00000000000000PNG  IHDR((msRGBbKGD pHYs  tIME &{|~IDATX͘oTUk3a PK(^Zb |1ʋhLOG}61/D_'Ãbc"E DpΥt.gˇsiZK'dr{ηZZ׿*F[a1ENto/9tyV7 _ont]dGV6$ԏAJҩq8&U,U{kHYiOYS/ͤ.# <8J>k#^xݝqRܲ6 q1'MSx֪5ɱYXR&}vn3|իMgMqMj$1HB[aAհ;^ *ATń: q%Y *0U owcV:,=_ZF,xBVN*p |v}_#Xb$&(gS>!1"P+ T5ƹI\. <uL'VYc`v=z 732Sfnv۽ˑd;p;磾Q-U$I[+!-X_/Sf8Y׎ɱQ%vnns>q*obLbe^,V)cR u MqkВ!CU+g,-r%Y5Tn3,P-O,IM c΄9MrPrZу"}w^d<钑:nka6}iCSDh!W<[Y>k""1nU|{G+uYΏb|dxg..wd<,ץ.1gu]@NЎq{% TYD#<:ʸÚBGɐ)^өJ""۟Vџ>S]7N#8*b*FвwkN}h|d!}R*Gjf K1,mΫ.PTg!-<'KsFuY|މ>N|?o+\U`E[kA% T;Aa5]ZkiX]5}i S>NuiuMPS ޳o5= =8]"i$val]BLr..s>t)jJ Bh*`5Io,oK;2{Ъ _ZjzyjMT8V'$fj"`DŚd&9@St&y~v+OucSW,jM억I5pj]Tw[tU3^/^ԉU:aMt89MEq`d|m jJC&XC$t:0Q 1*ATń&N&"H\IfY*0Y W&jc:,V-YO}CI~,! `` XLmPMc#J1l[|w}UZŘJoLMAe J2nPQ9-8nmwF2d^$ &T*rfrnҞ3 ӧr!ʾ)E=pϙP֥}&N: #HuXPÆbAZ*&D";c(r,J٢sP*\[ } ۚ]g9?c`ωI:ݸ}zz7 eu974$~ +q>uyCqzDT637\* fgYGGqq]SP0,[(v; "pzE=$"r}˳ދ:˗n{ +1#XV15]C}/pbfC# 'r欑Gv*8ư`~^u:l`6,pZNd=c X(](F~8|z]TI#Cc!_aXwߒP_[g۪D"H9}Z%vQ-ҤͳIENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/download.png000066400000000000000000000062761506574241500251560ustar00rootroot00000000000000PNG  IHDR>agAMA7tEXtSoftwareAdobe ImageReadyqe< PIDATx] tTfddLBBL VQ "TʡbZS+zPzڣhi=bA{[/##I,Z<:::zuQ@Z/86"^XS1i}Sz 4 @ "@00Auǀ"@nID @(@!f N$LA?dh D9LR6_# @\ R?(D "!N >`("/)ƙA1;NP༫b h:U[|Yk HECƠD( k# (I@2~ F0s+Q{Yd|ߓGIr/>CBgmGpin"SI8ǁ']gP3Sd/L`|G$ @y@>@(9pOǰ>@ (p^QC<vkBDFnh=@V c)"@ '0R&@ H|,!>T,@ @ C>Ĥ,\r;Ea@-{ej^Vu0,&؅L~D|9r0j 6Uc Q Ӫ#ae8ʷl/?k511o.'gD4._ňA仉 ';\  |g`IU%^T=vս+Ha`kFÚ+<_o!hwZ^QE 3(xD)*[w yvDKH3hbnx˪/zM$_pw|ώ]!Ay@3;S{xN."QTw0{\,[l?&Cu5/)~;%99hj`OD}b'3Qܴ r:{:i>xB[vl7<}: $8-FS,;Q$JFVlM[hB~&-Ř'. p @viB/ߺ&|_?Q-MUS@/tZ"_^j|lȈJdsr}a|ZQ \#~1fJu mr1)(GsBI[bۜ 1&g2Z~u/rf̎w:F#Ӟ'͇@EY%u#g($30X.!xDbJFAx!oXf1!?y:  ! LcJC]K5^6_N '&/6YAvF)v=$~g_oxV!2TϿ䂙Bb]#_MHdefcW-Þ rnt!S~[*r)FOW 82cuoG݉CmN[acϱq9SGNZW8x(6B^aOA1žz| д1r|%:Jd\" ى3|d`IHAaUB.-kp rK Kl"_M QY +M ")) ZKt.<1l*Ů|B<:})s쬒>c*/wl9_7%g)%;Yd5R˅:{zzO͔8G,2p`b[y|^]+煮ksq]qq1~x"2 X'ה8 g ,zq=jM⮢#"[XwoX*kQqyw26_ _=hI/RsKŸaSAc{ zN\w?HjM۲)7}Yuqy찉zpp{Ŵ"kg~S{#J`0oӘyK!C5]^V$09JV_! @mOԪ6IENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/download_small.png000066400000000000000000000025501506574241500263350ustar00rootroot00000000000000PNG  IHDR((msRGBbKGD pHYs  tIME *(=IDATXŘ_hUwfvnIҤOV4%ijEb_,BDŊ/}HAK񭏥) 5JA0IZjk6n3;s٤f&&d`fߜ~|**.`e/_)5bYn_zڔ:]]]E]!J@C){f+JF2Y ּ1oWg+p ERQ Pd ) Բ1 "S\%L_);AC] ⌝{yBRl(rp@FO-b,/|͍6$I@z/#Q)j(W k Cqkm} 5Z|Jg5z_Gk/vg[J(o8g8} N)'˝un޽x)sOd-B*0XÂ]l6S,ˍrk*N#_t} edvR.ūR4U\(;pD 2ԙ&bC= yJ fb|ZQ}͍l,4u@C)^L6R挖%z<|^bE'"Zt) ݊JQ/P6'):v<%F-?(f)-”wUe"aEr>aň D"ݕRK"GTa`& q:6'`"_{d \pbi7(9V9FGvg?c'wMՌlhyc$=_E^IDXhIc;l^{i;^•uu& =ft_͓$VkX/W!a")[fǖ^.:{{NC୪1TUQsﲮy3-̂wnǟCcHTt} L5Bi]9^>xKwC %ct}C2AUn` O}ƺN-ɤһ-;vڦw,#;} 'hL#AS7A s=\GSOwKgP`ܸ{T~J/_IENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/export.png000066400000000000000000000140411506574241500246550ustar00rootroot00000000000000PNG  IHDRTTksRGBbKGD·( pHYs  tIME % 0IDATx]yU}BY8@jPFbpF1pP 5cQ3S RCEFTJA1hL!|ĬoK/o8=ݯ}KCU];o/gZ?4i7i;2@@ @@`>v[Oe_",F~,"y nq4-K' ӝ5̽[O=SO$_4Mx=c$nN UjZ~RJfFh\o(=붜0TФBZNTݍh613i"g 8 A$@0_sM̜_7?@`7\ ѧ(M<@N 'GY*2)oBlAs@Bd 2@uGc2iiNRX f<[.ܰedlc{PoPx*b ]y7MhĜMN-zIϓ$r`tg,;c匉cG xB@144Vj=8=Gc'ކ _n5V~c-v 6s nrLn?M6T'0?w޺cK]Q6:d8J& 1]r.dra|슟Ƿ#mY69ΰ̓ӑ1 層}ބgDuZ e$I͔JRg?~G8_oDZ2=/ \\^`:1Q̝'F9khFH-i*K8_;55wclR C5Esו~ts>s zTxR$LRDH$5S*ъVŹgZw[q;=PquѪSARG'*ѱ֏l2F"Te M3IÍox|1/g_O!&7yY#*=7YН0Ot,R[w /7?i 7{1'kΖS TjX~[pׯ'FsƲWϰ(v/KQw'Ң>f|Ui#THD"%L2,u:6IFZ =f1yv qU;7ff S!TdfyN}6gыƦsT2FPR (Μ 3v$o+aj7.=uXhL&GKɍa]ιK^b|.][1ݴm܊a1A*,MɜB+jA2MJ1YCJfs02?Y3<јCg P?A%4Y"cKyiz< I\݉+4um* EJ1v*{b&Zc>Ÿw|kt!l  &wf7fK<-zbs厵⤷ A\\oRHNm4HRJ6#S;4uً~3u 3k߭ff|B8\gL4DA9Ys:Tqr!3Ց `檟ca3FF@P1;$ZscYXKB[h+s:_9; *AI( Ch1LI)np)av 3׏D ź3}0s<I*!*VNIHƔDL ^ǂ.U׮ "H@kDъhPoDכ72lRLƐrҠ6rߜ/{N?셐]A:F3m |FMdK agfmՁ XÓ$)ZQ(NPЌbh%s@F'B)JPo}|#D%QW"XexDEփjJ0 Q +B]0}ָF(kBx b mIrn%q3 JIGJâ.1$$|f>kP HFHR(HRBX*UF2P# , ϣ0m2 ,5D ff Af&+pQ @E~i+Vw1j"ﳪP#)ʿ\dg9Um <!ɦ0@a".W Jb qlL+S` 4 A@FjW{1JZ~\M>R2ڏ}3L/[VsʎYN. 7- OOr%&sO:8` =**2t(@1A(3#DOV'ns dJyf.2+NDjΝ۱kО?]Wkֶ]|sz2N}P.3-n;5'3>3zwDZP`@Hǟ~z;Z#F.l,;4e@X #xWYxӝ]Hմ"^RKmIW=e-07JqwM3ӎ?AJ"[vfOh6NDiW $P5m[s3xIOO~c V)D*.t\|}_c_%Uvm?)(X f4Xرr"?t?zV~{2R&>2Fba( ̜::x\.eV(@%T@ YI00xxNXjW.>RI39_huFU/s;BR0"O\)( G|³j}gO"Gv 蝀/>i@&444Qr"dؼՋ↯dkqPuh?JYfQ"euclZ l&jb" Zw?x .9V?4t̘QbI ?"W-ӓ.+5QfIda[Mh&@O~0;+>Sy1 ᔇ,|Nt}F,!x p&E]Mqo;',k|K+q NlgSQKtf'Fg׉yw3KO V@U1۟ވVÛY9X0oN֌[f܉w/ӣaތgTcnbԩEGP6SoO *^lpq@__9!$iZ8Ϥ ]emaÆG:f+GJEсݜxUA..;p9x4Ҙs߇3u24- ' J)h86B0{W_pGN?C ɏt"nQ7ӍEUVs a"q"b4MHitkSZAR."@^D<44D7o .F"j0s≸J<$lӟ3!Z;V+[^jEk׮}0Kd_Lʏ. ETt;1'i`…q@bf;s_3P c's,.%4M$ 8ҥK̈(h8iݺuW_yИ?mR"1h6#p)CdžN͚@8C0jq٤|k]vuD4h±=8Z3t({:q*zZAZ4M瞻K.9%Hvc @O:imp`FN[l駟F`E?$4CZ@Z&5,t :eez6h 5+hם~ 3bdcf j+}CyEoժUWs9h[M44v ؓțy^B6 gԂB_JyҧLeGE0IENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/export_small.png000066400000000000000000000043551506574241500260540ustar00rootroot00000000000000PNG  IHDR((msRGBbKGD pHYs  tIME /PmIDATXý[l\3vB&D%%ڪE-B*/Ԫmx"I#Ym>p+RE K6BBB{Ξaw]{ThΞ3f/ >:ۘRmnq0|nru$*ma¬9m"s4_¨S(]_.G>gH'zn5r]0ӡ8QOpoGE+^>aMAuaԌu-*ϼ_X OPsMLeϒ_~r El|]'rnV (LÄ0(Jml۳;W|o{`H) gزu35' |j'jAlJa@^SJe3E"tur¶-?A+M(DQ/H $| "-l4#{jYp 4cRb`6XkAkl,FHivjJF?ҙM[^6I$A`ĶmulƲ, cPJ#՘%j_Pyit 8h%Ydbu|c|0~ W8䎥W,SYpZåe7+5,O<rOXb-d4T34I\u)|Q,^ҩ65A%#|v|(DgB6&P4B\̒I0luL|B/(GN6G˪_jׄA s.F Km\~֬YM5&ib_ LjG~.,*%M|ۉE)ZTv㴬HZW5DZ+rln8?|y˵UqRض-JһOĭwY6s.|&KeQȱ'0X,U`fÇfpppc+i_kzU/]\ə!FG(y.mO[xeY7Tx~ Í7X/W*bdddGwڵM xӴYV&n oX,Y122rハ=؟ZJt颔}q1111{ԩٳ穹L&^\R ˲mK͜wnX\xwbhaY8:_4$F"IENDB`filius-2.9.4+ds/src/main/resources/gfx/dokumentation/pdf.png000066400000000000000000000075101506574241500241100ustar00rootroot00000000000000PNG  IHDR>aIDATx{}?gfc߻׬bc;Ơ@N UJZE4EG MhjE@j*PHhhSGUU&*u1M?zwwcfί̝wX;w={W!J)^~ew(ru5Y燿 zZ?Թy2)?`@V]XeT?(n+cC ̑鏻?>[`ՈHiC>9zv^A盭VD@`盓ٱ?"u7 pKhFGP7MEQ:q:|=Re"x~"*w_fkQe dx-+.7s p;p:\G#0&X{Y:-qQ4 .K3ZeO uiY{,5( hu궺)Ŭw=MJcdv (>E/Rvwy0{͆Ɯ%7Mq" us &3 eYS,`<ܱ" f9CXԓyN׳WA׬tLJ)3F`m5-d ,I\-JQ&R_N@{XW4;h9d$ŕD]$TOڠb:/"ᇡ}-/#~c?+vLPO*,Ph dȹ9+EѮ&d E㏾͑gF@{Z} s 0@uD\!>SyOWbT "r?/N"l2E$Vs=ȷAD_h/2xؠlXƀjaLp×{Å mBClb;Vӓ@ pK(潳oj)01J1P<\iDKMF\67*)_U^?E.n.xm"@?Z+((DdaGgHX=MzX E[@nJ58V 4vRR =>J.v**$R@!Xcxm-4%$ D0|lp[Ki&{oP$R AWMr[gj$"=JӕKUUlvEY˪Dy\OҎ6Q s 4 ъ#\tSdLpzWqD枛93ZKl}B"ڏovW0JJڮw)lH'`aJW\(X^]ÿƍW[,|~6ӚQn]PVb8--5W&?[6oN~VJ[NJR 3Q{_TI2ؽC :á++(H>=Ps҉ڇA^:,f+uK)"kJ2'dw-kWhi]{@uFkRENO[yD7or4(gѻTgopjuoEvMAdeT{l ڎ5/3U8/Y#xDlDV#|}KɯnP7MԔkd2Mh{vGDZa`EKn# I7gű *"k3Cxì@(юnmTya U|)pr?[N:u]zB- lj4g%7v ۾n/[8<"Y=jw4R'ލ?z?R3[Ǫ\4͋p*D[5 g(QTy"̓Vtd7Mlp|Uwܺةv:| d0xEIZ{ngL~Pږ:szue^a֥IE ΧSn]z@,L?LZy\+qߝkw u@!Dr2߽ tjkԉDXaٌUki!UZH1,ΟӬFYQ{H)JODR'`e$o\"^D)k+ Pbtïe`#oG1>( nSY]]@ƕԠ3)n weI |F`z˦F+V mp(;X** O;jjЛH_r:}@/Ze" ^~Fz퍒P}"(1-Ή nd+T(н0 kB)o`ۮ>m+QJO|+EŽQfCD-3aҗVJH)u).aH4')weUj-y24nj̘cs:<}pyQ/s.t2^xcb@`cc<@19& )& 0f =& 93b c  b c1 , , 0 3b@19F1 #csb@19F1 #csb@19F1 #cswcJǾ6z}Tf|xgaYm^ Zxp[\\̷/7XXX<ϋ CxG6^65L0^r!쇹9\0x"BA+7 / 2==z*4u]mR3gΜڸq#===FkȌeYD.ui޷,u;wie5\\.'m`YTX <\NrQ8H N*3ښjMo^||Vp[7#ϟ?^76~{^Gͽ_o=3f},U0uz ajyD>_BTBa^| [];mۮs[e~qn8.]uY|Ǘ./xt>bϰVyZ,_Oo[`U>mż< 5|}'z<eeHŰ~D q獑w<`"Lz<׬6+;Ő5|pdI8 -ٞ %eW+x"C!Z1S̱[)RIزSι[%XRɥZZukƘ'7nGSϽ |FqGu馟L32l.BiWZyUWۄfwy]w_k;׸#$g8̙`x h']ņ9$|6<ò.n;ns_~39\o?ym ; e˓}Nu˹Py 5<6XL 2 ePV kG-6eߴz p]^ДiD۞ciSfSo9qv^^=aG ,N^6 Uo~Mއ{8t̻76!qMǔBqPCncmcXрܷ?r\ Ql&A*weKJ1:d;VMTKl&O5^vA[פ_1j_kmL)-Ǔ@;lI=AԹ|rv\/{̤9 -ҭߔ`HD3("JPMKuiC<Αvѳ0U'½(=C@a.͢®D#sZ}\ ÅXx h 񇽫UlivE|A;of"PQdSk wgbh-cnsz.y' <=l1s:[ZpK¬ðZZK3rgxm_4B͉EF1~-^_3joWZLw~y^_5 tC!"NBX0?SE#/`R;S⋿VӧtDX"",F8ŮjT\'Fb pZ'*`/) yEd 8OxFݐl];H$S5w0;?$J vnNsDv2Qo݅aZG'mlF+C5F8Kkq"h g޸bKRN*pU5+m "i[|CqQPlgQPy#v LA$o@xȉQ- 'AymO|^PT,RY:8kð|2DŽQZ(M ޙM!'F\d60BGwcF Y *e+1!dsjzE 5q:/;5n0 ŠV6uBF!PrWplpͷa;faL0X 1L4ɒVnfC0Yp#5#uqIQ:?@g4Yx͔%鐫ZT# JFePԬE<,ʅAJ&xb<1s: akm|Ρb䂌! Thp؜N#ʺ'q+@S3D~ k]aԡ:cޠBG}jٿ M&ϻXYzo^"t)D6PoD @̉l}J>L Be3@,nЇz6%b#Y6JZ pyI ,L cèDL>{XO+ +Hhٝ(2ۈGMs vR=0gڞ|cwb/W zt`@%D"[3NBUCOJDscOkH"{H3ܖ@(? '[ʏ@]- )rWvڻO0:''IOۈk7^Y("/X_xbӠHy1d''o֏ڽKY\ISRפ(yBWgn'shl,w*d7eA#_R_wneRhU7s$1"1n-S\NpL O4B1G`U+wNJPoX'i(`Uv%c\ sDp91^q;PƚF d0_$c_!f/)n-xa^  u_bYOWp:T ~C㸝€}R3?[.1o"H THV6p%x$e ŧ*עv\ԈLB޺T1 / ` pj6:xND4u,=-xaܗ'Up&Tv+O_jƒ)[Y6ȌT婡G* <^uLꫢYOBI7M"gInUT'%>IcXPbԥ/3_o~ t2mIGWҝJ_$޿zŖ _$T0XQ"6;f0CU$'c%vJ,*n3^(=(avΈT.WhBj9h%)?OunI1T_E]֠:z!1(u^ ծ-<*3ޛ$R’U]' iM?DQj{g; 3X] 92̾{""Yo ^ȶyji3{/nj<+!T7Ҟ@'e)UJڻ"N+QkAۥi G~qvCCE{GD["hJ")VB:& :l.%2Hk2W\ )OWUG41EVbpf&&s |WPВ*ڽy;'`B98-ii$!U꣑DH:t#2FAt*Ǫ'5 %uwӢv͘XYОe tQ1 "OE8p/iᙟ{x5v & /UǑfMA,uͯa,܎: dd NYihMUB_lc@gb`I@f<<颺 n(s:r( 6Rϖ\)ήùvG߯Eצ`QBcRdPU:y6|H0sPףZ?XPaUٍD @5ի , @J^uLt Hu@h$icCK)q$)wfɺ `eJq)B=K` [`\I(m 2koKm}2pnӖG} jP3KNy*`',rSRdaֶ\^s9ӼRYtBSq ( HUCpb<ږц?JRǣXRW 9y`BÞ1h8䌮⃪cvC#"&)zVabi& fH+6&n!ua!l߂J!vB!ryN&m .bRƵI;:@z |1)@Y03-pVUv9p,.DMTZȜ J.(VŖgU)LUj-Mt"w S9:u몼J\$<0ஶ g*OjVM>wG!JPvu?EwpHA5)S/XP j5=s8-ҽwo6*.qDRsWZ 6 BF>qŠM^-tw:DJgp(>o>Pm~6*44w|ęԿ;Z^, z5Q@<Y7Y ]ʠ6"o:UP ER+AZOM;33kIԱ P! "q;Xp@}GU$.8S"&Z(^TRMx`ׅ{ ;ӇsйvxX=Y@S'կm)C %rfS[IR 8>OљO0 8~%:bKKpuv<ԀF%h?䶻4J.9QEzJ1U}|WʑQa]xJWz)/ ,P] <“ȳ:f %!BCj?(+w)=m!]љ!BTSMxv\|(6v#d4I 5/wV#[#HKg169yluŧCU|ʺQiU$U;g j RjQ8C]эT2ض6m0j9'Xjet/ pogt)td"QF 8%[C9}h?ϞH8h%S+#P.U,toxB $nnk@T/7B2g{5^>@]@k2Q/m@\Oh= ^d7PkC†չ1Nai~-#818j mRfDDWkBlGľfsmG1s&`NOE4mvC֥+恙sw`hDa|]1ϥ z>Rz=S뙮'~ 5}#l5t= @t\17z"NtJ u%7ͷR-L}8*O<b{_38[Pvgq >'W,sioӬX6z*+a"1$=$9ni!C;:){Pmp"9 Nf+MPFjbHO}D_OhU;:DCG u[o{@]VUsݝcz?N1)@? !=Fth9) v`. T65OegBE+Sڅ"ǹ듥ؖΧ0Wvݛ};!aOlgN.%˪dTg'?\?QGiE,<5%;|wa xzyu{}u]oFc~7?m0[>[~[~[~qq?qlθ(L4mN~*+um‚"X̽ GBEvn83Љg Pqp /Y'!7W˒`nb]T,:=#(bB\-MmV{bhc uχ>F}BR T\GVjsH1$iCCPICC profilex}=H@_S"A;8d:hATQP Vh MGbYWWAquqRtZxp܏wwVb6e$bQ1^=L}N_.³92Y>xEA _bKGD3&M pHYs  tIME+8twv~IDATX͙[lUgfSv.e,PhR׈h"F b< D FCDi$^ mvw:3LJR^vwvefΞ9w%7444e=jv٭(J0mii`}}-[<ɥI\|ݻwwvuu=d4Gm۶iڵ7X~˲ܹMMӶihH6H s!,K,%m2v[n%g۶EM9pB cspq .E=qJ/F! xfh.̩MGFb*G^z'K p6Ǹf/c4H$x3ģ7]F1x 5:+:JЇȁb0eoU5{ UuMBAuLIɰn;iaPq$)9!k \Pkrs ¦9H).-u4Fqp. aEdee$L[,"䶷zyJIVf.[6CRZtB }k:| LJSfiV#3.H槸]H/Gpli` 07$nXgN;ZtZ6t{}=5 Tւq[|ha |1bG(*,[S?+6:I̅g )vG+W˨> @&޵d jRy2M{w(\?F~A0AQ%n_gc/([0y'p-gMN96Hu_3ن6 r{|>%%t#U+})g_u\p1^Kh{?-d  3ВP}m蹹dy<\;ѻ~3U62(99g36N\D")-TT-,4;˅(S' C蟼CΙ W1z@$mbY`VtYO]opiZD{Uk g.$%|%rBUV)NȒu. o2rkbZϢKu? U=i/΢xHazgE?S^rF6Þ[?6zR`UR;IGoQEGQpR?J8NФ~qZ>=NϤRϤZ;PФ8<Q'RI\BT;Ġ5?n9f~~em_]iÿ@???|?C?( @11|>>|SSoYYl@@yNNxRRtUU[[y]]{[[|aankkkbbsffvoos``xffyhhzmm{sstrrywwztt{{{////33;;!!33772299;; ))--..22==00,,''4499RR[[ZZRRUUVVXX^^DDJJNNTT\\__ccddmmnniiyyrrssaaoonn{{{{vvyyCCKKFFDDVV[[[[AADDVVSSXXccgg``eehhnnhhhhqqwwhhjj{{  '' ((""????""++""%%4499    ##''DDAAEEAAGGBBRR[[HHvv99s I =3F(H Twko~)4='"${K,-S0,tpe}.+/\&uhר#a1f@iN8s%{VWdPϳ[^OoML_AB|x7Gyz;D`ZURqг$Y>Ƌ6 E] kJ*ز< Qj94Cg5!2?:nryvcXmbl?p#1?( ''//%%::**1100**""&&**33KKOOZZ__SSTTPPTT[[]]mmpp{{ffhh~~{{AA[[KKWWYYaanniikkdd   %%)) 5544>>11"" **)) DDvvrrr   ##''DDAAEEAAGGBBRR[[HHvv99sdqZ[\We.HF c*21X1I3 4JJ,MO@)f%T+PN&g-<A9`B$=lh !_?EY'aV b8>R 5]^0L;G7SK6/UQ"m#D:C(?(Pp H     &'#  *13.% ,:CD@5)*9HRUNA2"  (8KTTTz---jdUD605DX444bbbtdP=,  ")4CXdddLLL vbPGLrTTTfffuuus]K=3*"  *5?IUffffbbb:::zdYEEEcccfffuuulllpaUK?1# )9IXdp{mmmfff666okccctttCrcQ>+/DYzcccmZZZu^G1  !5L m:::p@@hN6#  &:S8TTTyD7dddP=pU<(  )?Xt00###c@,#$.mwZA)  )A]jjjbG1"'8yyy|HH{^A)  '@"g aa---vYB1(&)0>RI +~^@( %=[x ||__v_MB>?AHScss|| R{Z=% $;El00ss ;oc\Z\`dlw` ::xX:"'<X$y|y.fGuU8! .BZW-fkqR6! )8La{mlO5%5EXjuVhL4 .@Ri{̱qg7N@:8778:PRĝ|~eK1 #5J'hBX B:5/*'#  &3r(x}bF,  &:R\3T>5.("  23x|n~v[?% &<UfF+S<1)!  "~UspZLDYYpR5 ":JjFXXmC5) \BNt[E9OMdE) 4?`lpxTpD4&  jC}eJ5.KGrT5 )Ih^d~S9(  RArU9))d|aA$ 6]OPXy]I2  h|bB*"+ѩ|lK+'HUZG@InL1'ڸmL0"DCrR3/Ũ fG65,QEW7 LFuV6 ,wX7 6pT7*0iI)* ~^=#'t![; %DgH-#-ӱcA##rTdB'&,K_\< !zbCbA')cA# =gF) )Cw[; "{`@&'gG) =hH-&0QiX8 !~dD(#Ò(qS3 #AgI2.<3R3 lL. |V|bB'* LdI8:KkJ+]yvY:% -&sW9! 6evcLCJPu`?#J:iK2%)تlP6   +H0dUS^Q3 #Lz_D1+lPjiP7$  &?\w{kcgeA& u tZC55ǛpkT>*  &=XwuK/ H1q[H>0Jqp[G5& )?Z Q6 gtaPIPbweTB4'   1E^g/+^:$ Д$yi\VYrXqcSE8-$  +<O o}R:& !ףPsidMz?seXLA7/(" #-;K^6WƼ<\7$ E,~vrr_ }qf[QHA:51-*'$"!"%)/7AN^pF2!  (&Ɏf0{ph`YUOLGCA@ACJQ[gtp?`<*f+u({vplhffinu,E2" =2ؤ$sf9FH3&  }K޵%<H[;0% "wKП?T;2)   *bt(K;5-& $,vTʜìkkC=73-)$  !)0ABgs(K<50)%   #(,/25566665530,($    ??????(0` %      ,5.,>A3  9555h kV>-5TTTT///\=")=222tUUUbJ ZPPPέ eF3% &9I`qqqSSS]PPPgggnnnjUC,)C]paaajjj555 miii:::6iJ+* Os00\tttFFF YuO+' Qw:::]žTzO)%H}SSQ+###>UyJ&AcX2! >ìmm=pB01pIIP2',?.stt^1 "SQQ;;XC?DTcgg44 ~H  9}EEodh%u(U !k7 5k}Zr|~W,9 aswI# %?_m6h=$@ `qhE978CU) =/l}xD7-'!'lZ4Rl<9M},M2&  NG scHU* 1Q|l~r*K* C?ZiKkrn= #:_#q&V.]KrJ 6JP#5b/y C#|QU/5?`/ GacP M' 9:a3%o7\HFa2ưq: QT{? 'V5-NpHHND8#D 1LC(sqz9 1@L! +NG?E"x9"m6"Q%+tGl_j6q< T*3nDvfn9zh}G$T0CD? q[ xA NB9V(  +S9Xw7 M>5Q*(l?@sVJre+ $mg<*ê!\4 & _i5W'`OC|!gtbTIA;5117CT#u/U'dV}C~uld^]c tp!K#G<:JHdl4N;2wG,-1ʞ'K2# GAJV5*  99osijsFW2)"  !02G9W:_;c;d;d;b9^4S%?*!   @??8???( @   ,3-B3<\\\O;w~~~vv{; 2iGG>#sss>>u3  I~ n5*BQQpp x_ #PDDtUUe++ B6Ray}t. ;4NXCXtu'S"M'Q'>?};X-!UUg7c" 4rt8 HFMY> t/$R  qN&?X3/p9q"Vb*s! Vd?H& |{+,BT8*d+X+=T41]/R"T!;0x-ͼU ξc&@KV'e'4 9HU4z #>X^]$ MsEaPQFc A7R% <{,3[d#T/ 3aQ'W[X#f4bB+ :5{n$D~kUE;32=UVr(  .{VA+{yHZl$  lB @IɲUv&C! 7;|ôrB*  ")--,'  ?0???`?(  @/#24 U###vvw^urrvvv & ^))llB&^bbfDz3!OyMiG$Dz' a`/m CXGk +p?}( IOXi=Vm17?$+DCij Ic ea7aXo X ?#%_DY>6a6/1'NUs'Cfilius-2.9.4+ds/src/main/resources/gfx/filius32.ico000066400000000000000000001255261506574241500221140ustar00rootroot00000000000000 00h ( 00 hn"P8 H'00 %p  F h(0`wwxDȌLLďw@wwxLLLwLLLL@D@@DDLLL@LL@LL@LLLD@L@D@ LLL @ @@@LL@LLL @@ @    @ L@L @LL@L @@L @LL  @LDL@D@D @ @L@@L@ @DD@@@@@???|?C?( @xGwH@Lwx@LGLL@LDD@DDLL@LDLLDDL@D@ LLLL  @ @ @ @ @ @ @ @ @ @LLLLLL @DL@D@ @@?p#1?( |LH@LDDLLL@D D D DD D@@L LL@D?(0` ::a<HazgE?S^rF6Þ[?6zR`UR;IGoQEGQpR?J8NФ~qZ>=NϤRϤZ;PФ8<Q'RI\BT;Ġ5?n9f~~em_]iÿ@???|?C?( @11|>>|SSoYYl@@yNNxRRtUU[[y]]{[[|aankkkbbsffvoos``xffyhhzmm{sstrrywwztt{{{////33;;!!33772299;; ))--..22==00,,''4499RR[[ZZRRUUVVXX^^DDJJNNTT\\__ccddmmnniiyyrrssaaoonn{{{{vvyyCCKKFFDDVV[[[[AADDVVSSXXccgg``eehhnnhhhhqqwwhhjj{{  '' ((""????""++""%%4499    ##''DDAAEEAAGGBBRR[[HHvv99s I =3F(H Twko~)4='"${K,-S0,tpe}.+/\&uhר#a1f@iN8s%{VWdPϳ[^OoML_AB|x7Gyz;D`ZURqг$Y>Ƌ6 E] kJ*ز< Qj94Cg5!2?:nryvcXmbl?p#1?( ''//%%::**1100**""&&**33KKOOZZ__SSTTPPTT[[]]mmpp{{ffhh~~{{AA[[KKWWYYaanniikkdd   %%)) 5544>>11"" **)) DDvvrrr   ##''DDAAEEAAGGBBRR[[HHvv99sdqZ[\We.HF c*21X1I3 4JJ,MO@)f%T+PN&g-<A9`B$=lh !_?EY'aV b8>R 5]^0L;G7SK6/UQ"m#D:C(?(Pp H     &'#  *13.% ,:CD@5)*9HRUNA2"  (8KTTTz---jdUD605DX444bbbtdP=,  ")4CXdddLLL vbPGLrTTTfffuuus]K=3*"  *5?IUffffbbb:::zdYEEEcccfffuuulllpaUK?1# )9IXdp{mmmfff666okccctttCrcQ>+/DYzcccmZZZu^G1  !5L m:::p@@hN6#  &:S8TTTyD7dddP=pU<(  )?Xt00###c@,#$.mwZA)  )A]jjjbG1"'8yyy|HH{^A)  '@"g aa---vYB1(&)0>RI +~^@( %=[x ||__v_MB>?AHScss|| R{Z=% $;El00ss ;oc\Z\`dlw` ::xX:"'<X$y|y.fGuU8! .BZW-fkqR6! )8La{mlO5%5EXjuVhL4 .@Ri{̱qg7N@:8778:PRĝ|~eK1 #5J'hBX B:5/*'#  &3r(x}bF,  &:R\3T>5.("  23x|n~v[?% &<UfF+S<1)!  "~UspZLDYYpR5 ":JjFXXmC5) \BNt[E9OMdE) 4?`lpxTpD4&  jC}eJ5.KGrT5 )Ih^d~S9(  RArU9))d|aA$ 6]OPXy]I2  h|bB*"+ѩ|lK+'HUZG@InL1'ڸmL0"DCrR3/Ũ fG65,QEW7 LFuV6 ,wX7 6pT7*0iI)* ~^=#'t![; %DgH-#-ӱcA##rTdB'&,K_\< !zbCbA')cA# =gF) )Cw[; "{`@&'gG) =hH-&0QiX8 !~dD(#Ò(qS3 #AgI2.<3R3 lL. |V|bB'* LdI8:KkJ+]yvY:% -&sW9! 6evcLCJPu`?#J:iK2%)تlP6   +H0dUS^Q3 #Lz_D1+lPjiP7$  &?\w{kcgeA& u tZC55ǛpkT>*  &=XwuK/ H1q[H>0Jqp[G5& )?Z Q6 gtaPIPbweTB4'   1E^g/+^:$ Д$yi\VYrXqcSE8-$  +<O o}R:& !ףPsidMz?seXLA7/(" #-;K^6WƼ<\7$ E,~vrr_ }qf[QHA:51-*'$"!"%)/7AN^pF2!  (&Ɏf0{ph`YUOLGCA@ACJQ[gtp?`<*f+u({vplhffinu,E2" =2ؤ$sf9FH3&  }K޵%<H[;0% "wKП?T;2)   *bt(K;5-& $,vTʜìkkC=73-)$  !)0ABgs(K<50)%   #(,/25566665530,($    ??????(0` %      ,5.,>A3  9555h kV>-5TTTT///\=")=222tUUUbJ ZPPPέ eF3% &9I`qqqSSS]PPPgggnnnjUC,)C]paaajjj555 miii:::6iJ+* Os00\tttFFF YuO+' Qw:::]žTzO)%H}SSQ+###>UyJ&AcX2! >ìmm=pB01pIIP2',?.stt^1 "SQQ;;XC?DTcgg44 ~H  9}EEodh%u(U !k7 5k}Zr|~W,9 aswI# %?_m6h=$@ `qhE978CU) =/l}xD7-'!'lZ4Rl<9M},M2&  NG scHU* 1Q|l~r*K* C?ZiKkrn= #:_#q&V.]KrJ 6JP#5b/y C#|QU/5?`/ GacP M' 9:a3%o7\HFa2ưq: QT{? 'V5-NpHHND8#D 1LC(sqz9 1@L! +NG?E"x9"m6"Q%+tGl_j6q< T*3nDvfn9zh}G$T0CD? q[ xA NB9V(  +S9Xw7 M>5Q*(l?@sVJre+ $mg<*ê!\4 & _i5W'`OC|!gtbTIA;5117CT#u/U'dV}C~uld^]c tp!K#G<:JHdl4N;2wG,-1ʞ'K2# GAJV5*  99osijsFW2)"  !02G9W:_;c;d;d;b9^4S%?*!   @??8???( @   ,3-B3<\\\O;w~~~vv{; 2iGG>#sss>>u3  I~ n5*BQQpp x_ #PDDtUUe++ B6Ray}t. ;4NXCXtu'S"M'Q'>?};X-!UUg7c" 4rt8 HFMY> t/$R  qN&?X3/p9q"Vb*s! Vd?H& |{+,BT8*d+X+=T41]/R"T!;0x-ͼU ξc&@KV'e'4 9HU4z #>X^]$ MsEaPQFc A7R% <{,3[d#T/ 3aQ'W[X#f4bB+ :5{n$D~kUE;32=UVr(  .{VA+{yHZl$  lB @IɲUv&C! 7;|ôrB*  ")--,'  ?0???`?(  @/#24 U###vvw^urrvvv & ^))llB&^bbfDz3!OyMiG$Dz' a`/m CXGk +p?}( IOXi=Vm17?$+DCij Ic ea7aXo X ?#%_DY>6a6/1'NUs'Cfilius-2.9.4+ds/src/main/resources/gfx/hardware/000077500000000000000000000000001506574241500215425ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/hardware/cloud.png000066400000000000000000000167361506574241500233730ustar00rootroot00000000000000PNG  IHDRF47NNsRGBbKGD pHYs  tIMEAu^IDATh{yegq߯޷2=3=3F!6!ْ$;8Blc8%,$&}` l;$X";6(1B+Fh43[*|}cl9t{~nox\~_858S15ՆGpݯ| p5o W]pڞ=UHi o=ztc~,ʗOJG>v?yuCYEQHVW~|e8nsß=,[3"!jRcLMb}um;ok) ‡?< 7gϼvg&DYy&jVfG5T LeY`~~ᴅD)!++UO?pG?y+ހ)!/'CdKc/(N=jK"B5{uUV-ٗwŽEYHw׺x>?o?j)]C~K_}yAtQvEtssKw: Q.]߷{meEd&RM68tG77=̆Z!Hdaa&&;\Uuxر>|˛/qP^[cvDw⬙&d(bsS '[[[zNٹg{U`!&J;zvvvrnno1۽mzzF"DH$p`f!9.$;v}U}m%\2߀~eo|.0Wm|Y ]p}ZXXܻsmvU^W/,;jUSg7ka[E#GA817>3u0;;WH`fa "QD0 9c QZX:u .{Pzq>)7Z|ck2͋VG?O?cqSeYvD iJZ^[݉ZeAb icGJ!U bER(E$H 2RJNuJZol|n瞻'b_֝v맞.8݉DnҜ-ZN:iq8A?5=eY 9d UsӤ V7'd"".e!")$"L$掔RZ׵UJuv}p]U'R;tt7?so~d.nKB~9gy֮v=B(s9H $00"6bJ*Dv%() k,TLG)P`NIGth6=c{OٵkMo;C8xE=/VoF_|'nm7ݴ_g>wല,1s["\JB C\V"PpDDu]s.,Eb`@Z$(`a&P[;"Q aa 88Ž;[ss۷9k=;vΟc8{.l^xNpwʊ_wwO||b ;E_s &fLDDr3sw (b[911110*y&m3%7QH (, fAZ'&mffw?~{)w7Q͘^{-zW>늋/~;x/9O!tBRDJ)bQDb%!NfS#s'3y8,K̆"K 9ܘbS%wN3AU#3Sb)_ӗ]|%x_ \V{[p߇K_ozOMM_p3y铓ӧLAXDDEX2t,ffljdlljlfl<k@̪a@X$H`s3)#D.,DĔ?M1bVYΟX>^^tsy/ye'>aDݡ?/9gGI h"N, 3D fj@nNNˉݝܝT&wpPQ5`8ᰎ LLd,\A9[\)/nT}jk1L 7FJfn>tbYO;[7{ԧ?X[g=eYtHL<*,Tw;TLDԤѷ"L&t Wa(1,n,@8C`1ASr,܄0ڷGiU +~c;_=cl`Q8V 19i+Ԅʑ @n̡jps寣@wgɅ8zBhSbnx w&:̌AN,4D)+F(!{ ,[V/? "?м&c}faL 03\2X La0wpa0 aJʪW5g&7c,QPBr2cO=|p7^&SSS>߿j󊛀hڼwN>w3c '"|&Z, [߻#ƈM3"DLdNllNdf ZN7s$Utbaf04o$;܅LOo?vd'N?;OwRu+3צ!q͍p3 8{F  w`(9iR""k"++f%S) &CNwB^!sZ.spr`bnjD <3OVfNvˢM3HxvCO-e+ԑGzC(~ ob;n3=S6f~Wնe-Rb dn8[3̍F(@U`bNt%ш3XN/dsJIISr/XVan6"0k.olow߽Gk_E]S3äw}jME,,)e|,܍Qy tZt(" 74Q@2HgFiU3XVvIeYpD$@llGdk4plk+˃pxWnqk߸%=:~SvO0lSEEH PMjC@@1Kb "EDQDDBQ!d`;R'bbԈܷW s#wnEDYD E3CD<A\i(;y 6D|䑃+7 /~%ą=|>xb鸗eܘCc{L0 13B(eQ(JX f৆SEJHʽkLZҼ~ܶtj%gs89ʆD"Bًe~ PL'"5c~~v.ꪷ + /vϽw޷j!!@mHfCCpQC:'-`m2LG1hRh2X2nw,=f$͋2Ze~[:r2L'3Սh۲cǎSS=|?xKN۽{O%'9 QfY:myNwn!lI-PK<ԨUi(b$Pp7mJ蔞Sb%As;'m'5kΞs"=b2b/A9! [eF F+v{呇"nIQd8R;;5~(|=L-{k33م|۝"ɏ9~_ ܳm"AEf3o0:mf Fb$?Ԁj.|svR)3nN2+y4 M )%!ZSȍ f>3CJ)kILif7K&b({1ΉmیPV")`c㚛2 )y]'L5ƷaÖ_r}eQ;CP8yQ w9<Գ=x^ըu1.Q!1̼vgVrumuexw|7\qٛƛ\9i~Þ={o_/zO{70mdg"Smu]  ]M$`ADCB-!J$,6F07ͣ3"Vwjnw٨n{:p!m Nfw}C>狋{k.;sī/||%:L]3|0bNuU B #gb'㈃T;|@xXg 7`䎪+UUmڒDydfV -QtP̽ȑC՝wq#~in_{.-_U_UW\_\xɣ|!?+~^/01 [2ͤiǻ ǎ\^y->}Y/}}7sC;SϘߵkϞSwJfR~'k뭥D{SӃfS''M766G{$=|'߾~CE,jb "L1DEYw:V(n+綂?z>o򴛝@plvOLLMNMNNCeQucc=-,VWVX"{{R/ɉI1 3xy'm.jpܒ&6СǪ'xFojX}#i֞g;i6:xыko?zoom1{Ốfmcn@V~\k}"ED\rEqdwbnzz[{jj[Jl#䪊^oWumueR-8S:R>tx^ ߻ׯg?o[oUQ5!&Q5WUm)J9x-Rt[-=%nsOéCHĀ{auf^  /kA]}7>nM@I^v!~mT *j{X58|wZ!݉3Y]f:uj`gj_~_??Ƥ>ɪIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/gateway.png000066400000000000000000000325531506574241500237210ustar00rootroot00000000000000PNG  IHDRM:.mzTXtRaw profile type exifxڥiv[vc ZAo$$KK\4uW\X=_O|;~__wi|߈xL1}7JQwJF03ٟ0 _q]O<1fiYܬf}EʪV[}wǮv}t);3.v[v?M_A7keJϬُ!(g$,ȸ)tT| 9GeN9]XW",'Bs'ܿ7goR3׼]x@^ԅ Ot6b"<8Ǹ~;ޘsv,=# V:>ͧ>Y\cνoQl{BVmŰiN2)M*:(иsۺuY"|w꽽j/+7%vc.s%;;nbg gbVbm>r[ t,S ݌8 z-3uFPR^;C^)\׎(b}Ocyc6"0;?S}diR_#{֤`:beIqMgw zSr yrD4i̧2h¡Վ7酛=F5c'a)G#Rȵ$aSa~-^WWHֱMA[OX(f_뱢<3 ;mz*TZܵ:˨QH8(;20D B54՜egfoDhөa,mx >qtF*a kne[a_(P_;97dBv=9kڞ9hk,g~З4Q۬KG0e:"n'b|'>ߐ dV0Ѕ ۩~ɑל<([q˺'35 ҡXn %%z?#6ĺ&XUhR;kNAni(]-grޯk397%v %. /6j *6T_z3ه 87urO_=N:'-=$-^D0WC3!^t{[xepF~v|I5l\'H <">Y4s NNJ`s)j!H۵,$$P@tb( IH50ϔ!-?GB)rMr;؍5n^~q*srrƸ' e n>Ě/g5Q#/|E/L2Q!)&]ZTvڈ:e`2od$3>[B@=FX$;< dÑNצ!5R#@*oQ".f_J&rR`y} zENB*&DʉNdNgl  = f"DOYaEX,TSԳM*1\}M7鄃jܻbZXܣ4-yKFqTYfU\- ǤZ]ºihH.E-6 ]M$0C߭|4 X7kOby"%˽pO>S7h+ i$nѨr.yE@ -*#*$k`dM+$]3Cm9C9ܥGm j /ٙf+@+SvĵͨBOS,~抍؂Ҫ G΄H&!b w{%@)³DK{VqIܯzR!w>oXӍ޷Ցgq6g"}TQzY^YI&x/~jw@xJ|w@ w7Bb!'|\xScqhV(t&nzY&F5͙H /ŽvwJ ؈Qcl'[MqR^F3L b01{S'>5+aиk藙`MTP(_H!yްى(N$0+lAzlrH<-w <+(IA [/@_ E݅xNP@5ԴĢ( ">G3h-Gĺ}Wu v QUGY6ә yQhqn5dwP0^ڬĒ2"W֭uA"rN (g+6YIȐ'ZtfI.hX+c` 2!?X*ZPkfTXuYҹȫ $Qh+ n^lL*Cj?! $,-T0 VV-v+!(]jr±z,aw0uJgs[ibo gF14K.* E\[mEFNǩ]u$aQKL \濴S5r@J}:n\4.%ߐ@G&P7D HkD8%ݱRʟFl陊Ӆ9T?](-Du\mq+h6).R$Ij \t j @S ߈"e}%+EHJ2k .KE:l<{Kᆽ}( IĒX)K`o7+՞˔R9ЅPrH@DP{J`b|DDGPAχ( ^gIÅZD3.zNB3X@ c)0"k"b>:Ҋbȵ%b  | Dtw3Gih 6ӥW4!UHVt(I hs=ʻmZ<}ֺiQmC8V5_JFvY?ńwmtw #>1AOvŀ2ѪKɇRkZL@K0BEᐂh`xt s^y}[ >Ogg IP\h.!qn#{)U+:>SGPp&5AqSkX3m=8(Lr:2\K>Ľ0QEB!E'$z蜑KCr!J LD:'P U1@1Ci Ҧ 64,AnHGPFGBY-Ө}L*JmvhT XGoxdʄvQj&كZ@kD*\<ŨHt k6Ku:G"೬:Irտ=W@녞]y!m _nRCQtN=T"UмvaMLBgi7?8C;WD"0b@98jy{gz0#mx(KEi;_1/t*>ZjEW!i"u;#&^L5m``d ~=hv49Bh OfyŸqbn`tB 9oiCCPICC profile(}=H@_?E* ␡:Yq*BZu0ChҐ8 ?.κ: "ƃ~{1@,#J zEQ#n(9]gys*>xENq\hW(c~itۡ=쳇Wֺ"H?:Y閕wYEg}ֱ-Оy}L&qmLLLlF8|>x|rԩ{Q LØí!*oH$w=k. eeekB6 Àmll#ruh4bee%WJzlp W,p׫/jf d (6:NJ)WZP(BII (ʦAl%b<Ñ~bnS>m0XXXPe!I&'':bM0  a|B8c6]k0d$TU-vh]gIqԲQf1aD"ٳg]] f~BQuEeȲ Ji[m )-]gL&TVVڕ>X,HDS7nA˗3.3hL&SζҤ(T3;;eX,X,p::4ӵ#`޽M0|N'***Pg .KO!j$f3n78msY@ W544xg6Ͼ>\~sssxPRR9,//g}ZGrAhYQ%4 E<J)kE333ٳgk.8NaB!TTT5Ͼ> I@)E4xFoee%m!<pJòl>W,p8 BF#$&''pETVVf!Lbtt@FMMZL_~%V+jkka0V_QQBHֺfVLt8l.L&`Lh4`0"xǃ>~ P__?PO=@ټNR%_󨭭,,,l61ݷlY,|>} !$$ʕ+٬"Љ'z2}}}z4ٳ###zKMಲ2wJ^QJqL&l6DQ(ը7UC#p_~AII , (B(-- )~'m׮]ݍzɲ 2z{{(XZZB^dVkaPZu~wMB맔69s[ yUccc,X,ߊǣ r[-0LsڰBU꺦j(Ǵb#<BЖY"[ hΜ9Ӣ(J(#Zf^UUS% B~ .\x~ݍb*;wGQ}|˲ؿsaaa?칛*Lmpŋ[VukZt:{5塬A2|ίT1K. mxҥkOڤOsںfL1 =\zEiUU5*xG зS[ڴ) XXNmm]fEQeYDvqqQQ,0A M9:(ׯ_a䮜e fEQDtF3 <<<܃mzEiVU )P}Т ô޼y ;D$ )_%I$ $l:(ͣ1 _KT oE~JiDvl 0C+7k标dIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/gatewayE.png000077700000000000000000000000001506574241500261602gateway.pngustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/gfx/hardware/gatewayS.png000066400000000000000000000060161506574241500240370ustar00rootroot00000000000000PNG  IHDRDD8 pHYs B(xtEXtSoftwarewww.inkscape.org< IDATx}Lg?/eҠG+ P_ ElV W֦b/yC9_NBZ^WP&zZS@޲ "?Yfv~Ivfy~g~<3<;zemƚ'HSe$))7+eԏy Q% X,I!Vu8 8."m0D$ 088Ö!e@~#Hrrrbʚ@fXb"X:"{!fٽ{$''C Č ZTrr|vbIJJS;l j[J{{ם;wd˖-Rl sOOOK.~_Ȍ3e`&HkpڵΝ;C)LN =q@5(#nڶm(`lU+7tUXX(FQ eKmf۷kr۷G.JڻwY!Cn7|krIffL>]nݺ5q믿?UBDʞz)C->}Δe˖i"$ Q+IHHf@t:eʔ)Z(("N h JEEi& 4,FYrPghihh`P֯_/!S2~xN!A٣/m?(ׯ@ ڵkԧ7j2~<CVEfϞtN"##UڀXj5+WdvQE\z駵~ Pf̘ŋ3=z4zé<}vJov<t2yy6m0;CͦSW7`5kB2^@ZEFDdZI&3W; oݺEGG6 EQXl6."8NN'>q\}~:׮]ɻMq>]!P`WW:uJ^x(DDD lerss}IJJ϶F1L>ݻof.X:342@p***dڵbDQ1B ;v lGEEIsstuuIAA̙3'|R d}|3s pܤ@͛'ȬY@@233%&&F 3h%;v_Y'kHڄp`00w\vECCK, X.##<Ν;ǁz*,\ŋc69|nRRRI;"FjuWWn7oV͛7r0|')>|aҥƒMii)7ofӮN^ խVUWVww7'OfӦMܼy7xSF͆^z$Ξ=Kgg'Gek> k1** 60evICCw{UU555Ǔ`֬YɷB/o", ۶m޽{|RZZJYY>}ڠmB/ukkkkQF6l3`0p88qwYt){9s R^^|駀Zp8n7 G/ۯ$55Uɑa7}Ym;W4uӫFqK}Hիկo$&&ٸq<>GTi祸ֽ<*].WH:::9a455`P}x)T>qDHFJJJnKuuwX!"rqOɓbn߾Mvv6/^{(++#===é U/͐dzedd&!dq8c.3Ti" 2_T ]xQ'NhۤJ 0-**ҕ>a(ٳgL4U~ Ign֠kb4%66V.\0\'\nvտn4EyswL fΝA)))i.dXg}vo ^eX=ࡇA\Rfsb2$""BVZ5"sXS[[$$$h˒ Tz4L&,\P C ^yc:ďyieX_#%%%&If<rС1Aյk״什G%X ❆9g hg1 AUWW̟?_q@Zɫ'|2&!hyf-D DQ h(:v옶~" aJZ  P+lRUU5TUU<ĜAT+0a|g#c*++ &haJ0SH<3|;$D,nvpJDM QJe IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/gatewayS.svg000066400000000000000000000104561506574241500240550ustar00rootroot00000000000000 NAT filius-2.9.4+ds/src/main/resources/gfx/hardware/kabel.png000066400000000000000000000130511506574241500233260ustar00rootroot00000000000000PNG  IHDRP8jsBIT|d pHYs B4tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time07/14/06<~IDATx͜{tTս?g@ *P^ b*X WʵJWUBŖZIdY[<*!@@I ! I&93sι Fo&3s{~qL2 Ðz#a}&}p썟'm 0ՆGLSQ"`jX@L;__@ ~Ӿt@!945 6l\P4 C(E?~ݠo )rx qۂ046BQؿv A`"4i f"m q8Xj<=M0, s=]k܈N ·> "GL&*¢JS@K[aRG䇬dXwu ,Orص {ia(^ :\*.w)@{CT8!!;<d c P[j&X3V* $G Xh\/s(N縌0 5 v3@8'YA[=x/ H uP>~V.πf9Z*$páp4~JT0li ;v x 1>=Ppopd5tc r~ Ollq([̂uPЛ HVj'x\' *}Vm?߇O.")_=;ӂ0WբtpT9ҁL k | Ft/(v $(DUpBxq5飠`f''~3lp"} 6ՉEW}3 O:iDy}!uA u:A:x^'Lx{e>(;Aޝ Sh{$QP_ >Ib`a"8$hB9po JsĪpnCF\}S\DV2͙!DJqbUp+0?\`YuoASЩ*&VXq-@u (i!o)< x'6eDZ;BO4pG ^G>b J<%r+1;(두<} ̆;na3 _~<r!+9Ղaw<8)X7QEA0v DN XV~CaF)1@]W4EZVQb>PצH2߀}KRYmwv X-DDZ2on{p`X`vNBI-22Wx~ +X*\$::{wM>))ǡ,<  KNBlT'9J)02 z 0KO O"ع)~!4>ʹz#t_Xn Vd]Q1o<)* &;̖B7 2*0y  tk:|:!ZJ?:AON~8+1 T_Y^r^ TtXpؾ6y!opL>u,{mp < P> `'t8ap" J`]Op9 D%H6㴸4|]gBL| nduBc@L_o*AE!{جaq"L?09[఺<#zx|ŒnJOb{>䓁^f)}bVC# -ѫqQKr0.B|HybN"ar(x4ܴ#ĒvBZb=W M&R&ئ g*#go"[PTz;LcX`]^zXƏ8vV #*.ݰF|6de 1͒$zPr(+71]: CPfUફ#uNG Xp,ƴG8 6Y`@>wd7 ëw|U@o glEׂB0Hݧ$q%Qe Y S`0*NLNˠt0fu@dr 7F Co' ]P& otP]ڼH%>z>-\%;@]8gZ*3à)覒aLƣZƑ5Uia{B|6끰8ĪW> SN0 Tdl|揁 ZC/6!T VêWayU[")@@ȟ Az 5l}TGhA0t2,-*-,V#iZDuIVVQv@u \7/|/xL`!n~Gs:=ȕ`g:\1xt%m6of;< H)ڛ-+,YZtU u?of(t]PMdjk"LF"vpս0„יGɗHE$46@ (?7mhY!iO˜>02:)d ƮXVu~iy[!2 V&+QZU)IqF shO/ )Ү+I#L:ICf)(zGiDGJ ;ˆn5/q9RO"\ViVLʟ ! Ά7M S7h! vWH W[8 Uz\ {~%hB=O"Ǜ&iWRE]$Tv]g4onZp&MU\;P_ ޑr:@Z:EU'٬dГ(Jkb v1}"饉I.CUkF6:+u % "݈[0d ~б=d!oP4wkvCϜ9)ٓ';\#ňD"b1jkk_x ,4=E3fx1==FFx 2  \ @5׈s*Q]Tn\P9M=R]o7-B]{H ']\n?~ C7uԟ6믿>^z'''{`0K.¹:Z΃ PqFrDGL.ݘ#q0TEKeNb@3g˜^0h1lsʔCÇwڵkl[nURRۿ_)..N1Ma7.[V DDŽ>mS}UЗ䘫 ՠ d̛7oرcI8&eeeUVɶmoܸٱǿ0aUZZ81M4?C"܀0~!Eqm`Ʈ^TY m2?s7> ̟?رccXd߾}׭[1ͣFӵk;^oZNNN̙3s0in;wf@Ib|.'8aUj1c{n ;vܑ#G'NػbŊ%---zwh;wuС?q.]d̚5s]/FP=Q5_}DK{.80@7pLdݎƍ_VV~Νi}W# }Cwر魷ڇmw↝yJ:*R+4a W3 Ԁ7ZcuܺJv}e/4 zWe=h)˹ܺzm윯g9.4Y To!6TeTuR| J[&v;>Mۦ?ҀwIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/laptop.png000066400000000000000000000207551506574241500235600ustar00rootroot00000000000000PNG  IHDRTPRsBIT|d pHYs  ~tEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time06/14/06&lɈ IDATx͜y}?^}w%i-q(p^u7R"o%rֆT%^Cjk㩤6Ilǎ1`X `-hfafZ{<{L,aSVt||ib/~/qw>7;r; 8haq>.y_xgRo|/y vfo0> ԨRylwPJmPJ}8o}G}tlS3|ş/_!U??w 4eCxa'fJy @m;\Ŷ-|,+\_DL!}L .׏a}Ŏ1Ç8t: tzt)"2%H>D̔j/꺾ϞUA (#UCû~s>NNs[[Q4J*EP}}T!=m63%Nq;νx'9i d#jQ &=ÍTrզ|լ8Ct::?=]@`=I |5h x!xe Vb})[uNsŷ rz0ѿ*_C2 6 `1ͰssZcn~u+]v@?)4 6P(чXL|чF|7^r\yzncX=}..J{/+g3CDE@q"Ey!L./` > v5GvS"*) @AT5E"ABX^UDjwy|My F1cwuAޱov vra#"!(Ffw1'M*Bᅈ.':]fim:C? 2`P(@#KyaԲya]TU YyLUU21w2v9=gV2F {|sX6>t߾} 8QvJ<:AhBMH FB EuoQʏe#ƾsQA7zލEyxᱼqY\;>qB>p<k@@JR7MGH0 P*(@FT=*&Œpiͥ6BUa/*+ 2RfdQj@h!\頙)Q nXoX/z!+7VqQ>B}ɇ*JjUQCc*M @iB7Cf:Rf*ڄ#=BB~9!DL <uyZ-Ւ&t{8F.BRCH#BU2(݈^!`Dh:/B;a(i&hEs72QpMv, 0 ~2FZLR.UfdHDkR& °P5 >Z-t\H {F5A SY@USU!Dd嗚yhCh18y&3nj3Rz*];!0݈J{;E w J)6JQBf.dFP= D衉'qP2LT*=3RCf&n愪F~; X!fI}p:tDcq4*4%CMZBx&fh|3J5MGruFM苩*Ys8ˠR뮻K)K? Pa 8)O~/&V/.7@az^ĥW 幚rE項Jф6e>QLԛ*%]T + H \oe#Jҹ- ?#. 6/̍i]wCD^UPA@ R!z@B!0D[7 T+n~*w> ?IUl"F9;ACA* ( z0`F,!O>H΅`z~a7AOAEQJ詨$TH "6*S D.㕊T)Q,.$@P3@rf{l: ;͊Da7zэX!cQP" *".k("sI袈oJJaV|LblljvβA'ayU4q -;/ER@CQk(!dx1BHx{2I-JT&+Tm}K/?}+39o&<&PXƉuh|%0xDC(5-de%ex1| Vu9;wmۀܨضmW\qE_~ù_i| [^QEE֓'FpQ}oRڒ!d]j pB@F)EZJ=Io۶}?=̏*B a 0^ьFF#B$(EqMK[d]a/ H{@mRK5M+?k_Wnc`` BAz˖-[r'/cRB$Ut3BJ q*^f!LXBD1X Ņj}ǀڶQR]i9÷mQ.c۶m޽.(Rק7n5LTR=9z4A+2h!¨Bt5-,z#hJ,nFpe(PxϞԶ!?i_ XgFڶmկR.1M\.GR}^x^y6o?An )[:5r|t=DB4Lin,.[<`&"%mj۶RcT8_yW&`YR )%Jqh6m^{5nʞ={ؾ}{VC\qܙc&?z@8q!EWU,ҳӨZ2-B/Mkz۲lj)_K)}apx &ST–b4MΜ9C4M<ݻ뮻B*JsGY!$Yɢ&C+6pM-ggڶ>g2P.Kj58yR233y|MOӴmZJb޽\{ aaa\TDm7by6EALWI㥕)I <̵Gz[FGN #G$,˜>}u l6BOjdG)EV^n1 ˲ڵoT*v]?1^No7WB.%]KBL;Jb"NVPa$g_gwڶ]4K?d??~bH&j199RJǡ!sssI!zR븮K\^i\bu]Ǯ]( TW dcZXSMEJ=k%UV (z/P۶\*څB!./"FӴ|'''`.J ǡX,200,r˲hZeY,,,>\nK^qRk.vڕ;qB $8$A{A,t~A4P۶wJ)dY֖|>7 OfppbHdrr41 FNSNvZRsssT*.L+W.TULӤT*JhZv),zݛ v;v|UBXLzf*d^?_Ub@mRi7eզivyyU6 fff}l6˺uC42'OD)Ŋ+*6 jJ*²,*JZ5M* V u#&jBXeY\}ٳ5k֜VG~|x੮~4fUOKX`!Yu OR@m,KRi)%*=2;;K__|jJV#N'rhh?Z&J1;;,vիWb jqqt,Z_)%~AQJQ.i6A>/f׮]\tE\J ?5H1&* j+)`}||ll7Զ훣qS**꺎~z MӖqa,RJfggT*ʕ+Yj099I.#0==M6E ###E* 3334M\t:뺜>}Jt:M*R$)f K.~lٲ%Qk<ߙг?::^֮], (Gʭ?<>66m{3B[t]߬:2vaq瓨0R*vZ4M̙3u `ddUV:zGn dpp0wS(˲255E J100im2n7g&CCCvm\s5K SNO|%S[E``ޞ F':66wNuB?RRH)h|&@Ӵh\4MzRϓuMT*QV^NٴiRJZ4MdY,bvv4gnn.Q*d2\x!DvW_eYK2qsD{yopUdgN$@sB-g<xgbbTUU0MRRVC)EP`xx|>BOj'NjXbEff }T*Bg}ߧZn իWs뭷Ia`&G>LJb5Sµ< m/Bȳ`$}oG'^5#C6˟y%@=J4M !Ĺ iy+F bZ- 144D*bzz0LR ˲Xf }}}9scǎv`ʕSSSJ%\u],ˢVJjqO6+V`dd4lpvZ+ vkמS=WSvgժUH)Fu"n%~V1>>NZŲ,.⤞?uSSSA&I>gff&y"MXv-{XnJ))eq:Cv7Jm{h46:CPHRпr:[loK/`kFXF)%I0??|XB'&&#N3224gff$7kٱcGo8v8N8Nq}w>gy^-JõZ7sRـ&&&7 n7n:ϽgK|iĔITu֑fiZLOOt(\r%XyLMMQV}L&jIV^|e]ƍ7H.Kժ☔2yH}s 7\MnKNgBHhd |+ضdn"- `^Rj8H)(npK:ZW&N˲3 -4mߪslf∜Jfhv^`p?yGؕ+WrwrW.ZiZZMBl4M3VÖe\WǶa~/͍Fcq \߸}6IDATx~Ƕ !vmn[GbVB[Zvј{饗{4isL]!۶}?+:St:!Lli]{+ٞ pi|#y\nmQz7v8ky;m۱8hO|T懰l}RjyuW8r9?mU˲}oNewD)6Fo.WJ|׀K~^P>X@5l^n^ܴiӓ9?@IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/laptopE.png000066400000000000000000000120211506574241500236500ustar00rootroot00000000000000PNG  IHDRPK pHYsodtEXtSoftwarewww.inkscape.org<IDATxYl\ye3E2eJ"e[6iٲ[JRA@钴H[yC-Pp>ڦ/IQiخ45q-Se[ KCr|};$]g*C[w{w`!C= ؽ{ ͟>Q7sPn!Ϟ:qWsZm~C b7''N׮J۷o/㸏Vc|Ϛ@VhqU{|#j_3 cbZoOH_n5ܼ)vx-g'os嵿Pso}o_ ?^rէ)JeƟ]?@n{?ΰ^U+ؚ\ʷ2ƶA:g"oN_wI]R.&!iހ-|^9~od_ȉ3WٵV>sW?9÷ JR 'R h罹K Ɇ H N<.ͮ.vXHlrWVf>v d?ZQ1A*D^ R2J r bk*}N_7-BǬ&T?C[}cquA@Ra8t*ļ K SϋADT$ 5e&OP.kϝ+^;4JkT2:scsT(Ҡ>A#&)/0*Y75d TbSDI|K3B Vq,4&J b4FD4q Lj-Zi:&Nm̅#}lBtHc (6he0n{VZ A@Y:*H vBYF}bdjlQپPڀh-JzJR(0::(]XI,k>B!&R (44@ A`È "CDa\[+Pa ɛD1$(OgSe:} _R<H& ~!~?zA@AHEq\4&yc0&D7ޅImE7{ZA2jiO'j,~)?}C+/I`DRPɾIAIŏ$>λx`˥?.sKҩ[MUʻ"4 Ad0]V{!O G B[TQKeJbX ^H+4 W:( 5Z%nkdZ 'S\ {c*Ar3 4,Ǣ/=*І(Ү)`jJgqUX[^f$nFf+dX(ڊ%Es.I1&vEQ)La]#_L$f0Т1KK,]Q Q>yXϬg:Q.V)J-5<[a\ K ޴S (&E#K"Ɍ#ugB[@,puf?5m`Hq[YtIhFjT"(e]1eJ>+Ha{ B{`ɷ.Gd X2⩕PAA-Xʴxc-bE`չp:튈r6IO&.;@# :ur (VVk=XO4"X{0qY=@)Ɂ373=pM"5 =5Y#iSIms1;YwXeigI6>T $s/tbV^uK^jO5EM$uv -}iMYY5[K_0&*J3ke1^\)Q[>v@;(/u]c2,9P J4w6SW(w}QI Q$CZo9u ~Z9k6D NPJ)%p;TJ;vl&@Ί^H.BOJK)VǬT"9DcfPg3JFcXmUAXeBpqpH_k ].!-wraX;}>eFa|&l׸_}bxsrrT!.+T0tSkQ|?G+]h̾LDzh\ ^u2tsWyzo@9l{~|_RJW٘C)m4Wen5 7m޼'mٖEXZS*?-kU۶9q}q۞v zei׍pbDimڶC׬m& 5 Mʕ>?d9^C[> dk~a;Gz G¿~guK|7?K}fxyCp?K/ ?)?|;1c_< |ݻO,o?w9>Or{x{K/V*/gΞ[waկ!__Xg"#X&4ބQx|+6Pjy7e}v**Ma`Fz۶aYϜEkͶQ0ŋ388@}fV+\rynvOlٲx>6mf~aY׽~:s n۶ Ҽ},˖#ZW_V244,slٺױ9{<"۶Ù3g €ju1BDN:I\9pl%Zo(0B${eb|Ƕeb|ݏ`6v`tt+un(._8}afqKd"* {fltذa޳Zի׸sv>ZkfZƾ{eFGy8Cu&yI#WĶl0TL]׭\x)eh.Py60]j˲8CCC}hT(q>lpC sYA?mLy*9I( Qɓa>{^n:?Ghhw%g.ܵojm=ajz}rT h!:J2b%Xg/=JZ_ls8A楗_N9Dk}3_G\ +i8aVhͭ˚֚[6lz\cL;K17Mͺضٳr*ul$C̲eYWQ-{`58<5y[b𦬭ދٶMPsP*cGw~>;6o4,SoyN)tvR7&'VsJmeu!krEMyw=8uߘzN+Mmpp qFPJUnڥKp!YIImZ6YiawrJxٺvn@9\fDXDY ,?ſGv V]N|:YV Pkk7X)vU)_X|d s͵wfAJWP`j\.Rj͋3咻bZ\\\VY^%]1Kɒb<5۶ZX**޵H  ת50gΜ!Z˞|u2BReKGGh}_L{ k"G5MPB^q m^h]΋ms^_aef17  image/svg+xml filius-2.9.4+ds/src/main/resources/gfx/hardware/laptopS.png000066400000000000000000000024031506574241500236710ustar00rootroot00000000000000PNG  IHDR2:l pHYsodtEXtSoftwarewww.inkscape.org<IDAThOH[YkXG% 2a: uj E7j, 0g0tMA:J]4A0H,Rӊ1 ME}&64ϐ.ysy%" 6Rg_R*}<lpx//9%%gدz|rg@<="N<o-"/YXXs]#*/~awc1.;ZuGFQwnuGFQwnuGFM:H&U2lnnfZ׺|8MMMcf  S q:+ŕFT;0 kD||墾, &";EdG)t8A8v]g'Pe @Z?9n8ooq$dNڂQJ]3tUͨ=yh4㡿ԗX__gjjNS!ti,,,rqㆩmoo t׮]3gϞDbddWWWyuڋ"VO~V|H(t+b1MTI])%׫PH|rZoEY`yyYc X8w… Jnݺ#"R)M2kkkV6mr`܏m?͑H$633H$дE4}~~M+NFFrݚ @__;NRtj9UwV#5o>-\ rp!._\Dd25::Z@ H2,)$I$=Y:Zܾ}D_YY޾7WԘs Yt$@<2",7o|{*nʕ+Sijjŧ *p8~L @;BvB3IO3~=~]Q tJHX|A̫zIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/laptopS.svg000066400000000000000000000200501506574241500237020ustar00rootroot00000000000000 filius-2.9.4+ds/src/main/resources/gfx/hardware/laptop_hover.png000066400000000000000000000125031506574241500247530ustar00rootroot00000000000000PNG  IHDRONahK#sBIT|d pHYs  ~tEXtCreation Time06/14/06&lɈtEXtSoftwareMacromedia Fireworks 8hxIDATx՜Y$yuuU=.X䒴eP|mKI ´a4 > C  H+0 Ezȥ{33}UuWם!+{z4*+3+;T"$q޿'?t||aaͺ/SO<y&<$y#pJd+mI^6Qx㦕Fl% i+TkO%ħfo`'xf M^odƤ:nq+I]f;`>Z`tPx1E kGJG0iP@d"R "l6x.7I|v`TW2*&u=86||UG;m_#'l7n$4[kUs\olV1x:7:Ç;NGl \MvYkݕ}L<ևpWuFI)jF4yG r l5XŊF3uizs+KpOEn{'޲1XVJw=㭲Q=$ &ǣqi3- 6T**bŒ~y&Ƥ3OO>6˝+pg NƤ!O0{Ttsb MQ0<~xrԓ&eĕ%>9SaHRh,i+_a;b}m=fphD2_5ʔmN,Yyͥ+Nsy>=Sa)""hދXx 56;˴8Rhh|>ReFeKţ,p1ܪ ׿A+3Mv@+mqa-8m`:ُX\h9Ujjě:4Ym[cw|P j_*>VfT)I} XFOF*}T`}*WY,.Q kC@lZN5Z):i IPᝪ!*;4lW>v7܎~#ʣD4~;93<4P&h,TQ>>*tiAOwdF\Q_F::ꨔ=)m۴&A#sJa0AK(wl_ݞpz\GǏ?Ë/|JAy>T! %ĕbt'WWr~85Vχ: ѵnVH}Vf4j| {Uʕ*H{<;uO̾F1(VBb-qʉOxqeí}'׈ULOtl*x F{ly+HkPDpBZB/.i/MlAyݛ-̅^|8p}^D+awިNؼ#F\/ѿ`(SRYdMBB/Htn`%E7$a%"S@Uk| dɗEdr">s|ftfقkpnEz9tyTQQȜmq JOeryvг=kUd(+ ~zv8Qh1Yh2Z(L#A2[4>O;m_{Q;FjwjF!Rj! >ܾF}Ȟ =caxN$ m{,z}3)"Ap8ʕXʼ2*xLA5/n.ؒrsXoKZ1uc`Sj ~_pz)3c>ZVL0Vn-*AvVoizic[\Q#YfK>z+|ԺUa;6=@p}~ o,~3S4$T3&aօmc;@> V׹h4 *̕*ܯIAL>wxrfj8f;`H}bqb8_X%Aq; YnZ+ `;)qaS, {mԢ!`?@r|6S`x~M7/ԹڹLF*3= =~PDagY%SNjnW%*"DiTjX 3KkJ) tEDxGٮ2rًj4SAx+m/ Y)cuF-+Q{ӳK#+2 (QJ@=NSo'6tC Aq / R k GGi$u.Iۈџs^@FT[<Wi^~Z4MBJ`j)b@FՅK̺BO[ISVGݧѕ٣B?t(YqthnHlk! hqY)êpN({65.>g{ya3,H\Np^x<܏7^,.\,DBZDF{[R:F+հcӳ%'?Pu*T(ZI+Kl'[XoU^GKtVÍ :äxU?+VOS jr!!2}ԑR QW:؞2|# /K_H|R:]7ywNޓJz"S>DZqJ桦xϩ3Fsʮϟui+TA8ls 6y'1yׯ G딢v6Y^g{xqގN"WL')eNVNq|JX!u)?`}e4-(jPd(h\˻{>L!.ǓfTW?杠 ^|UD~-s=cVHG{ ztl˝+ 0^"gxfY2!`>xu#G("堂B'.u -jxboWڽijw_k1"2 j%;^]"qrs"wkdj7^˫]ew7."Ej4` w/j^J|R u0,W k~1_9qT?bn;dxo^Xȡ=XEORRһέ>*RH>'H%e,pYoZ k; ֈ_%Sb%Iu?1(''n1X' Y:CTSV}O^D\mY+E2 ߩ}Oz74K"/)1bF^Qc$Mh@{JJ.uuDdžQ=Wa:;أ10|YD9c%]X ҡ tp(Qyđd`@Li#ua3DgKAp-wqCm/Ij5O;ILQ2kZjX[yg}QoȬv6mjAn*tⰒ&[[7[Ah0;xNp=^6_t׬yڴƫ'G,8q~+ni)0%Sհˉcgh#׀_" ^b(4bc xճtw+400,:#[=6z8{N\ىld]q ͂)@^ kgۤV{%y8M9qpPGh+@_xmֈ'OOȉ -"0t6:@__c{T;q} x8jx_ޞd?pyLQIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/modem.png000066400000000000000000000073341506574241500233600ustar00rootroot00000000000000PNG  IHDRP8jsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time06/14/06&lɈ8IDATx{p\}?dI6O8igZHҸ>24) ̓ ItDigLgک0Ԕb`(Dm\,c,%վ􏫻^I9绿9s,(-Ln(pr!|Yx/>zߵqQ;7NS)\> *4@jj Bk͏_wH?&6. |@$ŲXug}k^cAcx7:qIWE!$[5=]R}5İ. <xQI8oQq(U4v*֘H$CP^=\R~zNB)aimqSwF_N'/o\%"&\V<`pJfI?mhE>WƇӥ/yPi+Bd"CuĆPC%Ĝ8 0Faf_ݡNn(,Lgf|RW 9ӣ'x?_DzV4x<> Dw 74sRJf׼$&~1_WUxznNJ녂=u\f_ßqX&`9dڶŹ%xFM[TdTϕ=K47Yݾ~=̣ҦG4'?}ᅫKⲉm{+ <=XO:8X}U2<< k6rWzfǙwv߽r|Vd,Ry J% ͨ9C:&͢:uK)-)D"3H çX޽}׮^[5=^߹\)R[  Õ,@3DKc֭Xj՟zz 5 !BAE`O__ M"þ}ĭ޺SW8xBhd2SKl.\؊TiĒoaeҲ̈VW \&mJҊ,qu}`E{p%5f ƹzX鳫h4iuUU0X;qna3DQ|>_yDF8?u* uJȕZBI/)2ZB,Tf$ɢro@G" J-iWu >oΥjűQI, è* сi8Ps.KiϵD"D" GhB1 T*U,5u*B,#H$f%1 #>3g[oᅯe8g@Z&!Nj=X,F&!HP*5!  y,t:ѣGuצ, PSԂ{[˲0MT*E*Z.vg! B4t]'piΞ=[Ϧ :LT*Zceq2LMFᅙatM+B`Y\.G.sJRݤ {KDi:ur+4Mt]W4 c#he:Dj˲dddBP&_;57 [E1HuZZZf 4M4M,*Eﺮ8'O$Lk{_1=J0$͒祔n`, ]IXiZ$ҼʥKx+̶>n&Ovh `\.Q@MEiLQb]wmذ~zǎ_5]X|>bdwĩ7zM8(Ï?^X^ ܉{Of*eQ(r.R͍=wbrrR~zbXJ{dd2yĽϲ,!n ˕_XW4M[Ua)'x|.bB0B{q^.BL4E-|=,F{@?55ťKfYb^ظq#===a尔rO>9P)10MMM}׭ӫJګxB癞UeFww7q3r:Z˲bO* \- ^4Ms]RT\B0뼥X,dj.MZ_0dڵtttefoO?]011qrqj޻ʫ23qd՜|>dYf7ƪVf戢(۟}T D{{ƽ6r& ishBguPZZZf`ahB l庺/^x/k/{S UU"K<+XUUZ[[kn3`s=wEwwøetFQ֬YC4Eo uտ;(===nJ)90yi`l]UV{VI/͸(N[)2,/.pUrǁm###wҤ݌]׹i9t]P(xYrRJ[رcߊp5 ufWנj֫?I8RʁW^yeYUJ L={v?n|ܺ}BqBZ;3 ?~|%k=l;s-D,{SlEQ !xs3f͚#ӧO?[KD]C78qi7Wl' 24) ̓ ItDigLgک0Ԕb`(Dm\,c,%վ􏫻^I9绿9s,(-Ln(pr!|Yx/>zߵqQ;7NS)\> *4@jj Bk͏_wH?&6. |@$ŲXug}k^cAcx7:qIWE!$[5=]R}5İ. <xQI8oQq(U4v*֘H$CP^=\R~zNB)aimqSwF_N'/o\%"&\V<`pJfI?mhE>WƇӥ/yPi+Bd"CuĆPC%Ĝ8 0Faf_ݡNn(,Lgf|RW 9ӣ'x?_DzV4x<> Dw 74sRJf׼$&~1_WUxznNJ녂=u\f_ßqX&`9dڶŹ%xFM[TdTϕ=K47Yݾ~=̣ҦG4'?}ᅫKⲉm{+ <=XO:8X}U2<< k6rWzfǙwv߽r|Vd,Ry J% ͨ9C:&͢:uK)-)D"3H çX޽}׮^[5=^߹\)R[  Õ,@3DKc֭Xj՟zz 5 !BAE`O__ M"þ}ĭ޺SW8xBhd2SKl.\؊TiĒoaeҲ̈VW \&mJҊ,qu}`E{p%5f ƹzX鳫h4iuUU0X;qna3DQ|>_yDF8?u* uJȕZBI/)2ZB,Tf$ɢro@G" J-iWu >oΥjűQI, è* сi8Ps.KiϵD"D" GhB1 T*U,5u*B,#H$f%1 #>3g[oᅯe8g@Z&!Nj=X,F&!HP*5!  y,t:ѣGuצ, PSԂ{[˲0MT*E*Z.vg! B4t]'piΞ=[Ϧ :LT*Zceq2LMFᅙatM+B`Y\.G.sJRݤ {KDi:ur+4Mt]W4 c#he:Dj˲dddBP&_;57 [E1HuZZZf 4M4M,*Eﺮ8'O$Lk{_1=J0$͒祔n`, ]IXiZ$ҼʥKx+̶>n&Ovh `\.Q@MEiLQb]wmذ~zǎ_5]X|>bdwĩ7zM8(Ï?^X^ ܉{Of*eQ(r.R͍=wbrrR~zbXJ{dd2yĽϲ,!n ˕_XW4M[Ua)'x|.bB0B{q^.BL4E-|=,F{@?55ťKfYb^ظq#===a尔rO>9P)10MMM}׭ӫJګxB癞UeFww7q3r:Z˲bO* \- ^4Ms]RT\B0뼥X,dj.MZ_0dڵtttefoO?]011qrqj޻ʫ23qd՜|>dYf7ƪVf戢(۟}T D{{ƽ6r& ishBguPZZZf`ahB l庺/^x/k/{S UU"K<+XUUZ[[kn3`s=wEwwøetFQ֬YC4Eo uտ;(===nJ)90yi`l]UV{VI/͸(N[)2,/.pUrǁm###wҤ݌]׹i9t]P(xYrRJ[رcߊp5 ufWנj֫?I8RʁW^yeYUJ L={v?n|ܺ}BqBZ;3 ?~|%k=l;s-D,{SlEQ !xs3f͚#ӧO?[KD]C78qi7Wl' -Y,X"B mI2'N?ׯ_SWWBSkllJꘚJ9A}DWWN[nzZt]8N^/X,ib>zzzF9<ftt4TUUܜ)Sb r|2)%S۷|gffhhh`0*z$kbbN:Żw2VM0.rhhEKK KKK m?~e43lNFQLgΜԱVVVֵbx^޽KWW(477355?_x7oHaaaWi&u].^hw~~^DD~qXY{ѣG\r4M[wN޽}%I] BlmۆvSRRB{{;%a9tiښ#SWKKKI+A)%J)9w&FviJ?N8͛xZs~1ݎ9“'O顰0]|<{'O7z6 .+n7e\0ܹC0”>ٳgU0-9v x!ŋtttgՖ2<<̽{#PJQ__O$ݞV|Z[[y%gϞ]WKD"nܸS c2FFFA***`0Heev _JͲe.]d1v;POG5OKxۆY/{$NbY+_VY1 u[r2fcbbؿ."pbl6J)P fyL":R5MIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/modemS.svg000066400000000000000000000070441506574241500235140ustar00rootroot00000000000000 filius-2.9.4+ds/src/main/resources/gfx/hardware/rj45.png000066400000000000000000000013121506574241500230310ustar00rootroot00000000000000PNG  IHDR ֛^?sBIT|d pHYs  ~tEXtCreation Time06/05/07ԷtEXtSoftwareMacromedia Fireworks 8hxIDATHŗAByAdAQCJ,vXH5by).&9.TD"N0a7o o뺡)eܢs1͗|7~00 )%k=.]̳+<xp_B*l.ɋ5]oq,;"w!B`Yzaf pJd|ylnApi}7gBv\l,~ǡ}O 3>qᄤT nh654G MUJVP"[GZqΓ}3HOedaұ/mL `ܤȃ#P9KdYj\[^ H;K>-t)I ZRyܹ JD0W!$g1_>J?Dy!xAP -()533viiig^{ق(MMMqX lͭ*D;Bknn^)[ٯlX,5wGo̙3?A&U999f#t;@;uTxOAlZp>wZ]]]% qڅ럑Aaa!**˲,XPrH}fc#q:XDSTdgg,B?!z&`pо8AB.}hllDQ1?xLDNj%##cqv~k}gvO7n̍7ƭt6whjb4GZh='|rp<,ߺ]n<lfڴia-BwJJJ콰m۶Jkk""#_ u$TㄺxT*rm0G}Tj '--SS h4"I޽{Ϲ\SQQQ\iMcP2}X,#.+8$Ŵ4Q$u9..pcf3d8GՒdv_\\N h#'''c0 7 HQTL2 dY4d2I[03paNgț%&&vC wBlF&I;99Y4$I\x6͛V%??ۍVC$<O@qJ`%\HYKII VJy%>>lmݻvZ uU t:>u:[n1002F 'چFb`XPaj &&IE7MRR(RSSCEE(Ṳ,iZHOOKՉHFF$&&!TRRRHKKCeikkG᫯`tttk׮,xDQ ;"--"t:݄x'%%QRRBFF@Hnb0Ȑ1wh>cJJJ8t˗/? رFìYڵkuYIJJB$zzzp\cv^j4zQq8lٲ3gʥKhkk#//|n݊f#==/r ͛;,^3gPQQA~~~[)..d2!Z(dggSXXHTTTP"IRRRFQiiinj1c`ɒ%9rׯc4?>w`Ŋttt444l26mڄh^S<08 ())h4?&&D2ʕ+|w̞=:jl6o9~8.\@׳fܠ +_|K,̚5 [v:AHIITT*m[`T/8Z- Zo6Ŏ;(//ns9Z->Ϣl6ׯ_NNj/HRRG5`"& Jww7/2,sE P[RRR((( 33Jcq]NG^^ŗ͇.YA5,5 8jkkXp!;wGtR~LN;جV*4`0pA^ʏ?(̝;@OOAjFF#* Ӊ O$LBff0KAuTTͷFۙ9s&\r˗/je۶m̙3DYY(EQUҺuN%%%rDQtRWWѣG$//^O?#dTEz= ǣ|3d"++GCjhJBE#7-jnnjbXسg$$$`X0 jDQEqaii鿃-~:nfٸy&ì̿xcO?DOOSN%11VKﻲH Ijƒ@JJ )))O BY+:5C+d~l6wKBBj(U^^d29(bS={yfzzzBzVXKMnݺn{X x $uhJ$I"˲o)[Ξ=˯3<.IRܹsZmmr(Rr8}4:W\ <Ġ( .z{{@[Dk4a M$jkk1cZ$I6+@ѠEQj\.W墫M6q:fcΜ9UQz{{OeAjv'dY|ƭ9rĠ(J(yӉm6z{{C/qqq,\KIE͘\:ESVC=8%IZhQD48p`(+y˗ٺu+N$gu^\KVeYS#5FhՒ$U.^8bW4iEQ6\8۷o޻_尮8h'$I\l٘]q.;A(իWOijj]j/%cQeYZ|]1]{,ZmsݛK8֮]O<XE읂 Ȳ̍78t.\K5`h`0O>$^fΝ466XսSH{ߘFF!&&^MK;!IRs=7!LF4yꩧvKNVtR|Ibcc\Ue?W#;::n@A@5k&ʕ+}CjYjC/ӅJb``ΰT"jv w`"$<i]vn3goFkk+,SjY<>x㡧`dG 'Oe $w5(R(J7=βMMMj}1m;XNDD1.IRիzG,kL7|r*y'.EV, Z$Iom%#rW^ZMT\8o"C__'`$Uwɤ({ .v3TAooO@KTqƻ䎝\zJN 000aЪ%I|+;~xY [Ɉ" I*9|`kJE כ_IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/routerE.png000066400000000000000000000051471506574241500237040ustar00rootroot00000000000000PNG  IHDRP0 pHYsodtEXtSoftwarewww.inkscape.org< IDAThMl[U/ϱ8N$SR*Ah-e!4A&U5a1C두la*QՑXT* 3),jSpP4Ub&uIowga&nNSJ~+}s#slpvΆȆȆ !Y#_8_ʺ" !Y?r=Bu||\_%$n髯z4?Ԙ$ 0033Q !w:IdBuo/"?J9o?,?}~'o+`a )J۵k,kؿ??s vv:B{{{SNu'[ZZ_|A=`vvFry'x=9::UUG}d !]b~׫|嗴 . ܇֞\.H$;v@4 ir=u,755Dk+?sHR8{,4Mö00??@ Þ={D"4醆 [*ՠSD>!h'NfyQxpydYɓ' Ox}ggig o>!VNMM}lǏ!>88UU(뾢( \.Ν;`Νxw . gaaA۷o%"B###'c&/BQ8b٠( fzٽ{79Bc/]tڵk*!DJv/\W_. :t:9G0l/2|M0~?44^BIooD"޽{q1[^n_X֕t: ߏVkg3<@lZ"@OO%Z'oBӃ>eYs4dY\.' ?3MsssJkGExe{ H(J,ˤH&j1MرceREQe4 ,.."H!B|>DqUeg||i͛u!"Lrs/;sNcd333XXX(J9G,i.7( TU l6!H$"͢(Auu5***4|5mM,$AipݷG) `V$Ir%UR7n`qql]7o\9(p0 ###ze_6mTm+UUo+H(Tc9c양LAkk+TUjZYG@׻&7.k{8x w@MqٸxZxMo]} ٳ:$a^a)YH(JiS]C(N͜H+MU9#q Bv|/ Ϗ8ٔ8AK:iӦURJ+pzzĕ+HP[['͆3a?4Mlݺfz))&|_y@>?"?#G b/^*8(2 QQ/7%uP@>|L1"3L"N/o9^. dׯ_tMD,)4NeWJDzuf-n.]͛OIP]] `yԄ)ߏ&y Du$ ("N[)k[\0 axx@^@ χQOՎv իZܹieJ&$IVJOZ: IҚ<() ciA]-{EEed2n4MX,>9hkkó>[ݟt:anȲt:x<߿&#NO>$0ꬎra۶mbزe55Mc Vx<سg|a9GKK TUie 90 8NOv) =l z;APYU9Mt{\ߌVD"Kfr8m%}JXQ^H!R o_RM.nɍM[G.˾͵ uܟnyGsr`0 =644  ٪FxC*E-*`KK :~p ;Y&ѣGRc..) _KK8\Rt: s^9B6snRJS듄 Ji@Rjr 97cYA%Is 0((r"s1@)\(qXV+ggguJPSSC tR).SU`9tx" Bnf[[?tعIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/routerE.svg000066400000000000000000000412501506574241500237120ustar00rootroot00000000000000 3image/svg+xml filius-2.9.4+ds/src/main/resources/gfx/hardware/routerS.png000066400000000000000000000062131506574241500237150ustar00rootroot00000000000000PNG  IHDRDD8 pHYs B(xtEXtSoftwarewww.inkscape.org< IDATx\P];~GТ`4Nc4DĤNh&Uh6$0q fL$f8Mf8Q LDĉ5JQl&`2Eqr|DZ去]̛}{[FD?& ko?! DScl6%dH``op@'W/l6r a O<`FQ;D ^@DA/V8 \l AƘ`<|/e˖)@ I\5D/ K/$@1F^ {!Oʟ0> W%d$\EjTUU(<)(GFeeGj۷oK8pt]zYVʢKҭ[*;iBB=K]֣hҥߋH5['_"I!w}'Sr%2L2RE8:~8?bFj0ƨ٫{E 3z|#gL|Afz)!!cvZj@NԮ,&;{{{tAI7\.jjjR2L#Q6vV\)#O? M 7o YAܠ:SS4 Rj RRR&ǣ<N:ܐa28`""F5!5f3B+&&FvL$DI t!wڑ} &:ߏ۷ctt4亦fcȠTkXBj͛7zjo#G! >m``?#vuù7n //&MN{~z=z4{5:(C/^t7n#lN^nIB>v$|A@Jjjjn1Qkk*&""Aϧ^^^IO~,]0oNG Ptt4ؘ*BMNNgb5tRJJ o__BI9+]s)XVt2"##1F111RCC"GIIɤYRz-&{c|***|*D_|N>MIjHiLnRf/^Μ9FhllE)8`F >t!7) 6g=FM &hÆ J"lE䛖ha|qZ*:U/0A[[Mzpt.'Eq1Φ{N+#IB__SVV|Mן`;/VCff&rssb ,\9sWYv9sW^[Dt3(~@f(KW|"66Fѳ p8`ە{~"3}'C0kpb4EC@!2<ː)b,%p;$}b t C:@35U IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/routerS.svg000066400000000000000000000123121506574241500237250ustar00rootroot00000000000000 filius-2.9.4+ds/src/main/resources/gfx/hardware/router_hover.png000066400000000000000000000102661506574241500250000ustar00rootroot00000000000000PNG  IHDRP=:{)sBIT|d pHYs  ~tEXtCreation Time06/14/06&lɈtEXtSoftwareMacromedia Fireworks 8hx IDATx͜klYUk}~̝4&vlG?&0"QC̀2 ( AIGBDDNJ>@(Q15}owUw:gsNuU}WU]NT*w:.9 3[:"On+Qۓ_C= /4cঢ়؏D2t5%t)>P_3*pluxHW`P)˨~B}1=q[Nur J99>tS΅_`Ӭ;3j'FLUE  U<9J(rMBW}B}_`lo=9j7j'bAO>Jp7CfWrwxNluxJTbW5̤f@UY/NluOGfҝj^=9=XD%C@_ڹuNs^i3bmq<;ng0hYy +`}vC/.9DTQkw-v;5e{Cl'&+l"b2 q7н z=L5TB}q$yK=S5S4~p60=bP @%߽ ^9Ÿ^+VPP_ID:͇?$z=S 31UyXľ22T w-H7P *ۑX]|s6j00iYAPbpj2dq) 85Ԁ!hʰ1#)b*F+"ݐֺz7B_5E/Jm諭Z4 4@UACEUnPUmBbDJvɜD\}$ Xtgï @=eN_ّ9D˚@[!Jہ'smVyWn+H8VlM&9jeA0J7 n/9AMO JT*.9;稂#Q2Wi" [lNhMWhx(p,+ODL@eV%jEÎ̑X7%J+V=V.lw|ÎiNՂz'nq!dv$mu6L{fIt$ Z1ӢsY^~#Zvl >-sU#Q y27*SnfjjܽNKW C!n%2'q6bFAViDzKS6rv56rJz~ YcP7 @4 svC,l՟h$;mqdo)1 ^]ڱKWpŒ;5qzFMm%}f._=W~!>uѮn _n{bS)b(IdjAElWSҰjDЖRJL,щԢN% s2 c4_<'Ε uʺ_%X4D-VĎ4PU3Y{NSW$ѶDQ̟%e )dۯy@J(B|M߰`6J$1TSP]> \2P!P_|i^q>K#kR\SNfw}rԌONEίRqdj'uEk,%yT|b.asƜ u-+ًAڃR-M*'ݐ˞ɹd:}r Njݯ\ϯ ݱ6\5nwAss= .|&f8aOvh Avf`~&g5tGFV̓ %U_KC\/z૭NGeNX]˗=NpZF׃YGKO jDfILŭ g<ڱ +`t瞾?~渝96^V#b$^؝'I+S?zAü 0 Ff$,/%Zcuw[_iu8cT'^$=ClFk&=|q. N㮭Ns6~UomQz0ik'lpOV=;7 _[~#og&yIn݌_Iѹd;.o n،7&j쪝r阝x./ğ9HNڙ}kG sbNU?c]jA3I^%s'={c yE$#~h^bMIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/server.png000066400000000000000000000074461506574241500235710ustar00rootroot00000000000000PNG  IHDR@^5sBIT|d pHYs  ~tEXtCreation Time06/14/06&lɈtEXtSoftwareMacromedia Fireworks 8hx{IDATx\mleُٙV`!!* /%/xD0{$U1iUO\ r*Т"!&h#ʶeiǶvw{Ώv'K~tfWݙ~Yi6b1?!d/!dfE6bس# !BUBnn߾3?P< |)]vZ1N唞{ȈI?!d/:}Fy\]B@4}!į R; !{nw]na) !xT)`I"tpwV,!`||~^N5PPiiZAȘY5f`n A$ 8ɌC[ aLE p`p4v˄PU5/"dYvX,;!>>Tk4mz6s-N@.7?8Lk,C~t8PB\. 8[rTEn/]7o^+K0CD n*TUJEQ,ppF"۷Ë^IJ,8C 0eCӴ,Py"#Gfejd2.4ua͚5Xz5VXa|C' x0n޼ a@4Ν;bfAw Xf ^?)׋;b@kk+9w}%SE< dYr9Ya,N'|>/1/\.8{,MS  addHT ,ad2y$C' > 浽,sN<#8vZUq~' 닝E[_$IMϨPFQ If1>>~ׯǖ-[~H*{_/wA$=◾,#>~ݍl6 At: ݩv Xb/_E۶m-/]3g C$ÊE) 8~8(9+Wor8xꩧo>lݺbp}}}1==mtJMfꫯu^v-yw}6699A!LBU%~l0 ?%ӻvŽ;%a!C@4|B`: ^Ο?wyDz.˔%I*Fp8|H$ 裏eY)?}ɤiY(Spoo/~Z ?0 8SgRpA0ҥKr 8ʕ+AQ6mڄ% u"ZB=\ݍQC% xF-`֭ ѣuZ1w-[СCp\ 0ppز} zYnx <PX,s!2؊(Kscc#^y啺z099?D’WXھ};q*D.-WEOhhh={/" .@:122b@UvZ߿O>$֬YcHdgΜK\z||Hj6l؀ 6 N#cppDQDgg')| vQSqFJ bUmq>}T ˽Sf2?o߮F\❜$IB4]WE0 > . XOO^z%ĴrN8eQ^QB:;;=Py ƹs.@%<̙3FP Ϲ\otIE "cݺuشiӢO<.^{lsdYƩS044d,nEy+w(ʔN|MMeLMM!ܱP|Z ׯG[[6ykE 5e5fMOO B䤁^l.k^ÈD"PMMMhkkCkk+ZZZt:%ﯩְi:CQGPhဦiHR8N_rh41:|>~?64M(H&_FZ*vdٺˡPh540Z.WU3$8n*X5@R 4MMөL&T(aeBv^fuob( (,~|ĉz!CۍB#n7r첦0EzJ6 `PE`\ӖbPv=Ob"|5SB0C6E&M^`!$If!EY̌:EQM$CPtmӠA&q,fAQ3eY0 I ( EEQ=}6m?NQ DC!2166t:*4n7G>G:M`YE(, _$hkPUn[v8g''' &l| @Oj4XͿP(jVm !0R=Nѣ0Mpر|<wGSE=:NB*dY.> YQt*h7{Tjad3/իW?clz`YL\Nߴ0 \.8n׃`he`0xV ۶mLQk׮uX4\.t: ͦ?2o(=G[![o}`nU~t>'s$IߔX: ν.MNN B֨>+\pŸ/9ˑiDQԧRŋ.R-E!LxrI,_b'O ; K@іeBӴ B,ʳXY?\j]ax<l+;X,ȦT*EKq=_Twz'Ok"xvlll}2!vk ?nDn$NS.](޾}!zlp\|CC(q(Mb|E"{{{駟È,B)IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/serverE.png000066400000000000000000000075141506574241500236720ustar00rootroot00000000000000PNG  IHDRPK pHYsodtEXtSoftwarewww.inkscape.org<IDATxŕ?U{gfk/66`+mD:qE#Q""$@c#t Gb$=#",lwm]Nϯggf^wO0T~Uy'R?Ǯg?f.'p=\>`t"}'8 Gu >9 X@i7f!.t-K۶BQJl&j&j`YtM/jK-[LARuF)m;iۍe2i|- !RR@hX&OB@W}uΝbѢE SضTҦ\,^zGye``sńIO*zx /^m[ieX]u\o|ܣ;6CCGٴi~GXG^[n8ŲJq 3MRT.E 0 ￟˗Bnz P?~F!cWׯ_ ˲ؿ7ȮN!9pYYY O @_fϞEiy)|>H$l mB- @g֑*آZoP!DMRl^S@A O?gZlӏvg_"@;o}T*)۶U3P*T*R>w@+N(){^O!"}߿7Xgk( cBi3gbe PJCh+8&ߛ+ؽ' 188;{,yQ׳B4coǹkݻwRJ-ÙxzA}Q!o2_k("R,hٳZM78ZhzIy9u Xܵ=l$oEt>LB!̙R`Yr K>&#Ax 7 S)Ak摨cݺu/纗QX !WZա@4)WtRZ|lݺb8r\:~n5j_^HDM5uyZ9!O F#p5oR[@U ΆaiA?~|P+[ qGoO|NWR{(b^¸r==䮹V. 7?LX1 --v-d2 W F ^ _ t5P1/_*r\ 9 8)ӯWkW^I(}&сR&ٰNj`wwZ{k &~^z%_Ys}L&@f!>k(4J)VS}<1dY$R@T:n֭͗if~(]ekJǒ Sr\(8S{Ʌ? K!ѤD٪Ҙ\.D8^E09B5o qqQ%`UNŊ/z{i}eN>pcзLpkؑ**\9RNP~ADI/ϓf=y~vjW;Οּ`n<~#@!dyR/Xp!O:;;&M0aA<guk~"k`)DQZMC,ˢX,"$T5zz*VN'|uݸeY?~L&C  DH$QA @ץmÇWV۷Vjt]G4,qwyɽꪫX/FH_(RJó>J%8 R),bΝ F%0 4MkKI Fl޼C{ظq#< J)Kyg~v: J$H 8]{/_AY D"r9 ̟%.w}h4eYߜ b?Z B0-([*ץܙq"7>Ï'N˖-#dΩSOV2r]ȩ"ojRiF"֭[ǦMoSO=댏W =-,ZMwoh^ ЏeB V(5t*ɖBV(ʆ#F(*D@AB:1x=>C@b6H|o;`hh|>8#޽{ݘx|0'O+XbŴ R,gs/.}[#'Ҳ،Oc3fFgD["04E^Jxem[RR0bB._,sE*{J"O)q#Gp "efmލU?Ww^֭#ӊSšKp `HE\8.DJ #! ,ٺn'B%BE)+jVhi,9iS(Ȅ*͔ }P Iu L,Z5.LB Bi`KhR!oIٙtQc؈51l񣙖O &7poOI6ehgwgppt_k n(yq3n(lmmk3x"d%vuKkHO'NefOٱ\2s*̎X>͚I T’` C[8m*!zٱc/_K.~mlfʕ޽AnV9‡~B_p֤ܽ{S{%DP1eS6k'>ͥE>Q+k3S\:3wpՐYb=躋="dK 8uaeŊm6Ldʕ{l߾5kְ~z{{Yp!ߠVe %Cu><+\=t&vտx?]9rJH{K YR:{NV ˚ۻ#SMOC"5\wʹj\ 7"~w:ny"\s 4Mpu9oNs|<r}ٌȧ xE;/Ϻ~ix>TU%3|옓D6?2u 8#NJ-0p]fH$L&*s1TN<xC*r<'ѫ-!tv<_}۹i?~PUK{L]vq3g+۶kٷo!V\0,Y)eU iRaO9Û'J:b !į/?p#L2::BP(;MMr$;wu]RU368gW9ct Rͳ?:I` ˲ԫ-8 \ t$η˼1r%uRʍmWn1o<4M?*oWɳ)28ߒm@3 qHl׿/Rjy 9;(`/8f;.pYƪછ.Z 3I_aTg'j9y!PC,C{%KbY*YzLK{Y8DY}[ B6S[IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/serverE.svg000066400000000000000000000504211506574241500237000ustar00rootroot00000000000000 filius-2.9.4+ds/src/main/resources/gfx/hardware/serverS.png000066400000000000000000000015171506574241500237050ustar00rootroot00000000000000PNG  IHDR<DݍV pHYsodtEXtSoftwarewww.inkscape.org<IDATx1HQGADFBBKI&K"Kւsi;:9t)YZ;etrP;$W:yy]}p;{\D_t'u<ӆJGsp;D7/FDD^ =:"IvJ+5==RINpzzZ H@|ߗ0 ۄa(#I7ɢu`aa3^b3*A}(J ŗukX1+++eڢZZvY'w֋V\f{{;,meAҺ4 Ȓ GGGA`[-*333V5r7pɝ599FYkq׭t 7u$,LLL000`UZ^3;;k[-܃N8Nzcmm-,meAE:N88ㄳ:Rw47oBCi@KX)w:/ui$TYDxJ{hrQZukj<&/ VG5&;W@ED/9RwDNܭN88ㄳ:N87e?mk"\#"vDQn|]δy#@2<<ۯlmm_=D;Z7wo4jI?3d|֞;MvjcIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/serverS.svg000066400000000000000000000047161506574241500237240ustar00rootroot00000000000000 filius-2.9.4+ds/src/main/resources/gfx/hardware/server_hover.png000066400000000000000000000070241506574241500247640ustar00rootroot00000000000000PNG  IHDRB_\sBIT|d pHYs  ~tEXtCreation Time06/14/06&lɈtEXtSoftwareMacromedia Fireworks 8hx iIDATx[l\y9gw\$%QE)rL;MAӇ}hЗu -ޢ68H(h_z ZEZ  cːئ$j%mswWߴK{̜7y85iV!rA6~ pŵd3"d_ f6#*(ےS}5!rAYy."<z ki)0JT*2E*f%aYk٭pG, PNK-Q&$SyN.>E<6iW]UڽM*>?8W\<SGURD<.աlX̢-~G_>ee )%N&ߓ/،ڨQEFZN3hJj#U?ksXumsn'qZ BE%8x&-0:q:؀ |ʸ8q/$%Ԯb)ɤUa C>ƌB 0" !I'&~[eI0i(Ȅ䘫y;GyL;Dv}2iƘfVFJ2T3t*TLb-%oyqpkY'B*,F7|8aL34$%M %Ʊc FG4G7c+ՄXR(~;f9[شeQ G\iN̼wc0k;`&`jU/ dڎl31|Ą$cdV3f*ދ$fz;CsL=Bڱ4l_h/*C]i(wvT{CХ{l-6qɞ mIZ2 %[$'"ķK1(5!"a:HZ {N$ KS/YXKۍ.HMa4y;ېU(vf]6uB 6GgԄHJVi}1g ˔XHRnGZ2/#1iS~QQ0 f`8tZzJ`MaNuI3aNKAQb]MͶs êVژ4c;˶DthҥqHJB(?aVʘ$s$ugVi'%, ᨱSG(d$DPb|{"MG4J,~j:O6\ivjB6`|uVY,#qy;{P909sdl8|F6b7j>[v s m@؀;fys;,Kȵ*&P [.sQ$iC$% ~wjy=>~-o,bm/)i#+kpxy.Y8)(W?w1I _gڎD n9FIq%zv>۴<ŦUy4>‹)־0Puzp\:Gۛh!)12ҹ-3-G/%^[gE;OތЧN>ct;țލrI]}n9m qIpT2k0 kz8s) %[|:Qτ3]CӮ7$ţ]Zj䭘$ÀXꤤyy7y;1'Y{>OI3ƌN8Z1%=Տnơ]ZwC6p>usw7g钞uxU_YB"%o -ofo*82{[K~uRoF3r9x$?omWV:{rI)R'x'1 M2,Т_2gǿr۽<Ǹ^s'#x7ѣӥШvBuOy`0Gw[\JJGڰvO2A㌛"|y@^_(!w1Z.U_&H 3RͺtS0k&l?_6߮r7: )zqFQQ9 2w- =xQu:*di>*!y3´`2M #{1 8szG z#Llmi;NޥlKntsF?N2E ـ|O O&+%[+PmWv&De炃wY~xu@-hߊ>c&؞̈́'%:-7Ѓ;Sl{V].Ȧ k{n7UUŤ'9dLJl~Bzi_،BN?%[. Cԃ]^ ](ґ-+[rBM^gsS't2_1}`>|LGDHK&$y#MOb7֫'N=@w? ݨ[E Zq"IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/switch.png000066400000000000000000000101051506574241500235460ustar00rootroot00000000000000PNG  IHDRF47NNsBIT|d pHYs  ~tEXtCreation Time06/14/06&lɈtEXtSoftwareMacromedia Fireworks 8hxIDATh[klWvΐ3Ň(>$_lJ7Yil' R,Pc $BSΦŢ ;Afīm:wqY7Z %)Q$3wC)_փ~$p;{.QEVczP3E`A80Z(JD>:|1 ^YD6Ts9^M8 /+ 033l6VE&`׮]To|pLQǡZ$I,ŢZ0d&o^0=0GTikkCww78y$I P:q` Õc755a۶ma]}~L%T*5y3z1`~a[njq7TL$z#<#E90 SV_zq<Z[[KEiBgl-0O=0# , Ud`d@5  e7G LT`0m۶VBO?p2 Oi]Ph lFggg]&*20 8NzzzH3 38g*7zu+FFxd5Pz=6X,k׮UhCCn7xܲ*& ###hkk 0Ru:<Fc]JqD"-Z˲p\7eHD3Q9t:p88X5z=GcbB!ppԥd:bT^777tޔf3gXYؽ>|m[o@ e8Ah%"H$UzF:nc&* l6sf+<` xʕo`X SvL=G)EWW (k$2bb(7"YN]& M?/BrNd2B6ǫ#$l6b@k;u:z=DQ0rCP(Tf6Giqp:uy:F(*{ Ày3 ˲##{C1î]y33lڋp)q0 `(Yj!(bH$hllԎL&%mk X*_&*bXyeyZ'ĉϠ( f[t?N+8Έ_#~e,0[o(j9 aA)JRU YQ**444r#Rb12Ub޽{R4&+,®ck`)_8L&BIeǎ;vISA9s v܉x^,..9U^ P~-I&XZZS$5Btxd} E$Dw&9~p{Z@'E0L&),,,`YvwY)ab1 NdeJpl66*lXDE ^ZȽދ_+?FoO~WVtu Ӹ|pk0 "DuVF, L,D1߆0=JD";wD!dСCb*0*<@$I6FY^^F:l^!R)>dYN%|>ߨtUinnk3~v bl:I;wsss`Y6q}饗jMM`EI(-+cL&˗/pnk{JQϓ$R   F%l<gDQDc܉8^|eR)\_Ya˦קqE5ÇoQǃp8~- ߘFH&hiiPIe T"N UL&chF)]رc.+>| : N&\Ntuv KBNyi,pO?ύC)=(Mǵ5IXv%Q7L&VSBRPJďN!*rBPK6mU|Fuc9El6 ; pBƧ^UEQ裏%I|WeIMreaXA)fNo9\.'IP=s8= o}yؚh0KwSSpIBؙ3g#k@)d2x@" ^!Ԭ<sEeEE-h0{n݅Vw3󯘙Y3NL~ϞNӁ_ԩ3H;|fSK(Ê[lL74jS0}Η(eYU\RT1:: FO h1IOwk?}d2, }UQ?.RJ)BӃ֪UꌗJE=WEk+bw  ߏyf q u5ҥK>L)el*Zyz<|>C)=$I#hǃQͬf#t₈~R9ɲ*0044R:I)ex\[7qxHRkR[ OƁd{*!c~]Q뭐n:us󊢌f`@2D.+F=2PSSSfj\4%f_|ढ(c ÌL&`@6 uEQ\{Zol[~ tRzPeJ h4b~~~]R`ƪ.^4 \XXwRG7o\gLeZR!"KBJAJ:L˲`>bYvLolj-,t>v=-I )s2fU%{%q\J)KͲے$bvwCP ZIENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/switchE.png000066400000000000000000000045071506574241500236640ustar00rootroot00000000000000PNG  IHDRP# pHYsodtEXtSoftwarewww.inkscape.org<IDATh[le;8חuID "H}TU(CRE!"!ڔ('.%Kp(q۱7ڻޝNqiDCH;ߜ~{|.kZxZxZxZxZxZxZxWkݭ?~iL23gpݯt]eY~|/Iu]_ U[\.m[qWܭzyWeX,WN|- !D<Oqg}T*mі-[xxei?f"@!4Bp8 {1bHMqr۶m<Ӯ`'Bo+A  EQ۷ogpp]O<ɓO>ET*nze4`0TX|CӴ믿~O$X,ƩS"+a) >#XV~!ꯋѣGEWW=oI$F LrQH$DX(Vl뺴&&&Z+"~= z]_Φi~w``@ȲOyH@WRw楗^²,N8A$g-qCA?G!طo/ž}BǎOr9Lsu {E@Md2$I4M;gϞhTeٿdh&Bqd:YaBe R)sqrq.z#.Z\Y'D2hs$v +$%;fιL6)1qVlr\QJ·7rRUe/d?aSl63ЪRcHsW\ǐ=:Ť=K C=8,/k(,,, I7;'%3+&eNO9{g!Bx: <P(Doo/mPQQCua*++ٹsW555BeE`ƍih+ Gee%Qu>߶mϿ$IB!zzzذaX+$A]-})4ۮx6!w럕Ε7 >a  7uI+?Z2رc`X̤G}>36mڄٳl6XMMM|>`֭XxG|21dH$|@Tࡇ²,?@4T*Too/ J%FFFhooGU66eJMM iR]]$I}ΪQ<#2Hx<p8,4662??O$.ڒ-@ee%\Y a$I" R]]M\F4Ek׮ PU!YXX@QeKgϢ( /^]I9ddí67B_9Ly~iILu(^Q hh4p{͖-6JVT}Kņ'Q ZDu4Gbi4v?,~U"(SإbGL+Itu^RcM z,vwZb>fL4tŤ$aAy_!otuu0 ["x˘ kl60155AKUI&TTTwާe Bw*ƻB |rdYֿڶ=)IRNGu7q(bNͶ@8_=o I ˲iS+ݫF"EU{o2M筷~1H=IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/switchE.svg000066400000000000000000001276041506574241500237030ustar00rootroot00000000000000 2image/svg+xml filius-2.9.4+ds/src/main/resources/gfx/hardware/switchS.png000066400000000000000000000030111506574241500236670ustar00rootroot00000000000000PNG  IHDR7:E pHYsodtEXtSoftwarewww.inkscape.org<IDAThKTi?qF'0WU@ь . !,^t!ESR]$ބ,4 K0F& I$::3w/uΙsly9BJ[;`%[(!D3P}3~RF(R"!u֙q%WIRE#Gd,k)ywCzrssWD^^ sOֲ{֪y Zqøn8`Xd֊顦ƐȬ*jkkٷo_"dƽ{ ջwggg룶ܹÞ={a>|h=Mdoo/nG Y~Yp8!8y$)*bffF r:l6y%<`KmyXvv(p8H&x^|>;vHφYWiiiI[FF"PTTpi\[g.p24Jq}@睋D"ܼy@ n'Lx5HKoߦ|,+nTwݭ{ݻwzu!^RO>F(ɱ]5}7q(RPPSػw/9k%x>Nkk+۶mx<$nb֭\t?5yq^מΟ?Oss36lX΢ L^%ƍ2_d;BTUe˖-rR<,}>gϞ=n``y(Bqq1׮]ĉ˾°\\EER(FCCԓ3H$FXJرcs!qdϮ]x+vy!Q2L z*cccs6V@=%NUU=zą %Hh_Ad5ˊD]xQ"Q_=RUU{^U(DB~YTT$sssyk"S2,].MMM;w@ ˗`fffQf*++uO\ZZ{V,eggܴit:+^F1T8;N7p8ئ+;j[ؼyU~ԯlL#rRIXaŖ,JvJKKYJw՚/|/\<:_,pʌ7@jk!~YpH)N 8g IENDB`filius-2.9.4+ds/src/main/resources/gfx/hardware/switchS.svg000066400000000000000000000067111506574241500237140ustar00rootroot00000000000000 filius-2.9.4+ds/src/main/resources/hilfe/000077500000000000000000000000001506574241500202505ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/hilfe/design_mode_de_DE.html000066400000000000000000000101641506574241500244350ustar00rootroot00000000000000

    Entwurfsmodus

    Rechnernetz aufbauen

    Komponenten können hinzugefügt werden, indem sie mit gedrückter Maustaste auf die Entwurfsfläche gezogen werden

    Gelöscht werden die Komponenten über das Kontextmenü (unter Linux und Windows: rechte Maustaste über der Komponente betätigen).

    Kabelgebundene oder drahtlose Verbindungen werden erstellt, indem die Komponente Verbindung ausgewählt wird (per Mausklick oder Tastenkombination ALT+1) und anschließend die zu verbindenen Komponenten nacheinander mit dem Cursor ausgewählt werden.

    Verbindungen werden gelöscht, indem im Kontextmenü einer angeschlossenen Komponente der Eintrag zum Löschen aller Verbindungen ausgewählt wird.

    Komponenten konfigurieren

    Zur Konfiguration der Komponenten wird durch einen Doppelklick auf die jeweilige Komponente ein Eingabebereich am unteren Bildschirmrand geöffnet. Der Name kann beliebig gewählt werden. Diese Einstellung hat keinen Einfluss auf die Funktion des Programms. Alle anderen Einstellungen beeinflussen den Datenaustausch und damit die korrekte Funktionsweise des Rechnernetzes.

    Rechner/Notebook: Alternativ zur manuellen IP-Konfiguration kann ausgewählt werden, dass die Einstellungen durch einen DHCP-Server bezogen werden sollen. Dazu muss auf einem Rechner im lokalen Rechnernetz der DHCP-Server aktiviert und konfiguriert werden.

    Vermittlungsrechner: Ein Vermittlungsrechner verfügt über mehrere Netzwerkkarten, die manuell konfiguriert werden müssen. Außerdem wird hier die Weiterleitung von IP-Paketen zwischen verschiedenen Rechnernetzen mit dem Gateway und der Weiterleitungstabelle eingestellt. Eine Firewall kann über die entsprechende Schaltfläche aktiviert und konfiguriert werden. Das ist aber auch noch im Simulationsmodus über eine Web-Schnittstelle möglich.

    Switch: Der Switch verbindet Komponenten in einem lokalen Rechnernetz und erfordert für die Funktion keine weiteren Einstellungen.

    Modem: Mit dem Modem können die in Filius erstellten virtuellen Rechnernetze auf verschiedenen realen Rechnern miteinander verbunden werden. Dazu muss auf einem Rechner ein Filius-Modem auf eingehende Verbindungen warten und von einem anderen Rechner eine Verbindung mit Hilfe der IP-Adresse des realen Zielrechners aufgebaut werden.

    Eigene Anwendungen erstellen

    Mit dem Assistenten zur Software-Erstellung ist es möglich, eigene Programme mit Java zu erstellen und zu verändern. Der Assistent wird über die entsprechende Schaltfläche am oberen Fensterrand gestartet.

    Wechsel des Anwendungsmodus

    Um ein erstelltes virtuelles Netzwerk zu nutzen, muss vom Entwurfsmodus in den Simulationsmodus gewechselt werden. Das erfolgt über die entsprechende Schaltfläche in der Zeile am oberen Fensterrand.

    Das Netzwerk kann im Dokumentationsmodus u.a. mit Hilfe von Textelementen dokumentiert werden. Außerdem ist es möglich, eine einfache Dokumentation zu erzeugen:

    filius-2.9.4+ds/src/main/resources/hilfe/design_mode_en_GB.html000066400000000000000000000067451506574241500244610ustar00rootroot00000000000000

    Design Mode

    Construct a Computer Network

    Use the mouse to drag and drop the components from the left panel to the whiteboard.

    Use the context menu of the component to delete the icon (Linux and Windows: press right mouse button over the component).

    Connect stations by selecting the cable icon (using the mouse or shortcut ALT+1) and then press the mouse button on two components.

    Use the context menu of the component to delete all connections to this station.

    Configure Components

    Select the component with a double-click to configure its properties on the lower panel of the window which will be opened. Select any name for the component. This property does not influence the functionality of the network simulation.

    Host/Notebook: Besides the manual configuration of IP properties it is possible that the IP configuration is provided by a DHCP server. In order to use this functionality one of the hosts within the local computer network must provide the DHCP server.

    Router: The Router provides several Network Interface Controller (NIC). This NICs must be manually configured. Furthermore IP forwarding is configured by the default gateway and the forwarding table. And it is possible to activate and configure a firewall. The configuration of the firewall can be processed additionally when the simulation mode is selected using a web-interface.

    Switch: The switch is used to connect components within a local computer network. This component does not require any configuration.

    Modem: The modem is used to connect virtual networks built within Filius which are located at two different real machines. For this purpose it is necessary to configure the modem at one machine to wait for incoming connetion requests. The modem located at the second machine must initialize the connection and therefore the real IP address of the first machine must be configured.

    Programming Individual Software Components

    The software assistant provides a simple interface to programme and modify individual software components which can be used in the simulation mode. The assistant is started using the icon at the upper panel of the window.

    Switch Application Mode

    In order to use the previously built computer network it is necessary to switch to the simulation mode. The button is located at the upper panel of the window.

    To add explanations and to generate a simple documentation switch to the Documentation Mode:

    filius-2.9.4+ds/src/main/resources/hilfe/design_mode_fr_FR.html000066400000000000000000000071121506574241500244720ustar00rootroot00000000000000

    Mode conception

    Ce mode permet d’assembler les différents constituants d’un réseau informatique.

    Construction du réseau

    À l’aide d’un glisser-lâcher à la souris, sélectionner des composants dans la marge de gauche et les disposer sur l’espace de travail.

    Utiliser le menu contextuel d’un composant (clic droit) pour le supprimer.

    Raccorder les composants à l’aide de cables. Pour cela, sélectionner l’outil cable en cliquant sur l’icône cable dans la marge de gauche (ou à l’aide du raccourci clavier ALT+1), puis cliquer successivement sur deux composants à raccorder.

    Utiliser le menu contextuel d’un composant pour supprimer toutes ses connexions.

    Utiliser le menu contextuel d’un cable pour le supprimer.

    Paramétrer les composants du réseau

    Double-cliquer sur le composant à paramétrer. Un ensemble de paramètres apparait dans la partie inférieure de la fenêtre qui s’ouvre pour cela. Pour paramétrer un autre composant, il suffit de cliquer dessus (clic simple). Les noms des composants peuvent être choisis arbitrairement : ils n’ont aucune répercussion sur le fonctionnement de la simulation.

    Ordinateur/portable: L’adressage IP peut être spécifié manuellement ou de façon automatique à l’aide d’un serveur DHCP. Pour utiliser cette seconde option, l’un des postes (ordinateur ou portable) devra avoir été configuré au préalable comme serveur DHCP (en activant son service DHCP).

    Switch: Un switch, ou commutateur, permet d’interconnecter plusieurs composants entre eux au sein d’un réseau local. Ce composant ne nécessite pas de configuration.

    Routeur: Un routeur dispose d’au moins deux interfaces réseau. Chaque interface doit être configurée manuellement. Il faut également paramétrer le routage, soit en activant le mode automatique, soit en définissant des règles manuellement. Enfin, un parefeu peut être optionnellement paramétré et activé. En mode simulation, le parefeu peut également être configuré via une interface web.

    Modem: Le modem permet d’interconnecter des réseaux virtuels créés dans FILIUS sur des ordinateurs réels distincts. Pour cela, il faut configurer l’un des modems en mode d’attente des connexions entrantes, et sur les autres, il faut spécifier l’adresse IP (réelle) du poste du modem en attente.

    Changer de mode

    Pour passer au mode simulation, cliquer sur le bouton de la barre supérieure :

    Change au mode documentation pour ajouter des éléments visuels qui facilitent la lecture du réseau virtuel :

    filius-2.9.4+ds/src/main/resources/hilfe/documentation_mode_de_DE.html000066400000000000000000000041041506574241500260320ustar00rootroot00000000000000

    Dokumentationsmodus

    Gliederungsfläche

    Gliederungsflächen sind Rechtecke, die mehrere Rechner visuell gruppieren können.

    Neue Elemente werden dadurch erstellt, dass sie von der Seitenleiste in den Arbeitsbereich gezogen werden.

    Mit Hilfe des Kontextmenüs lässt sich die Darstellung anpassen.

    Textelemente

    Textelemente erlauben es, erläuternde Texte oder Beschriftungen zur Darstellung des Rechnernetzes hinzuzufügen.

    Neue Elemente werden dadurch erstellt, dass sie von der Seitenleiste in den Arbeitsbereich gezogen werden.

    Mit Hilfe des Kontextmenüs lässt sich die Schrift formatieren.

    Rechnernetz exportieren

    Mit der Exportfunktion kann die Darstellung des Rechnernetzes als Bilddatei exportiert werden.

    Report

    Hierüber lässt sich ein Report als PDF-Datei erzeugen, in dem neben einer grafischen Darstellung des Rechnernetzes unter anderem die wichtigsten Konfigurationsparameter aufgeführt sind.

    Wechsel des Anwendungsmodus

    Der Aufbau des virtuellen Rechnernetzes und die IP-Konfiguration der Komponenten erfolgt im Entwurfsmodus, der über die Schaltfläche am oberen Fensterrand erreicht wird.

    Um ein erstelltes virtuelles Netzwerk zu nutzen, muss in den Simulationsmodus gewechselt werden. Das erfolgt über die entsprechende Schaltfläche in der Zeile am oberen Fensterrand.

    filius-2.9.4+ds/src/main/resources/hilfe/documentation_mode_en_GB.html000066400000000000000000000033201506574241500260430ustar00rootroot00000000000000

    Documentation Mode

    Structure Field

    These boxes are used, e.g., to group some elements of the network.

    New boxes are created by dragging the element onto the working space.

    The format can be edited via context menu.

    Text Elements

    These text elements support, e.g., documentation of the network elements.

    New elements are created by dragging the element onto the working space.

    The text format can be edited via context menu.

    Export

    The export function allows to generate and save the network as image.

    Report

    The report is a PDF file with the network diagram, important configuration parameter etc.

    Switch Application Mode

    In order to use the computer network it is necessary to switch to the simulation mode. The button is located at the upper panel of the window.

    The infrastructure of the virtual computer network and the IP configuration of the components can be modified in the design mode. Switch to this mode using the icon at the upper panel.

    filius-2.9.4+ds/src/main/resources/hilfe/documentation_mode_fr_FR.html000066400000000000000000000052211506574241500260710ustar00rootroot00000000000000

    Mode documentation

    Ce mode permet d’ajouter des éléments visuels qui facilitent la lecture du réseau virtuel.

    Blocs structurels

    À l’aide d’un glisser-lâcher à la souris, sélectionner le bloc structurel dans la marge de gauche et le déposer sur l’espace de travail.

    Déplacer ou redimensionner un bloc à l'aide de la souris.

    Utiliser le menu contextuel d’un bloc (clic droit) pour modifier sa couleur.

    Zones de texte

    À l’aide d’un glisser-lâcher à la souris, sélectionner la zone de texte dans la marge de gauche et la déposer sur l’espace de travail.

    Lorsqu'une zone de texte est sélectionnée, elle est entourée par un cadre bleu. Dans le cas contraire, un simple contour gris délimite la zone.

    Pour modifier la zone de texte, cliquer dessus. Le texte peut alors être modifié directement.

    Utiliser le cadre bleu pour déplacer ou redimensionner la zone de texte.

    Utiliser le menu contextuel pour changer la couleur, la taille et le style de la police de caractères.

    Exporter

    Le bouton Exporter permet de sauvegarder une image de l'intégralité de l'espace de travail au format PNG ou SVG. Dans les deux cas, l'arrière plan est transparent.

    Rapport

    Le bouton Rapport permet de générer un rapport sur l'état de la simulation. Ce rapport comprend le schéma du réseau ainsi que, pour chaque adresse MAC, une table des paquets reçus et émis.

    Changer de mode

    Pour changer de mode, cliquer sur le bouton correspondant de la barre supérieure.

    Si l’infrastructure du réseau a besoin d’être modifiée, il suffit de rebasculer vers le mode conception à l’aide du bouton de la barre supérieure :

    Pour passer au mode simulation, cliquer sur le bouton de la barre supérieure :

    filius-2.9.4+ds/src/main/resources/hilfe/gfx/000077500000000000000000000000001506574241500210345ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/hilfe/gfx/aktionsmodus.png000066400000000000000000000021531506574241500242630ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#%tEXtSoftwareMacromedia Fireworks MX 2004vtEXtCreation Time04/23/06kIDATx]UofN~wbmuuقJbQ)ԢJ/;AQ.UA7Puӭl7̌1amwM&sҤ $s}|=gΑ !";P7Hs "!h~*6G6)Tt*P=gUS;'tEs6^ᔷ[?"gr*]bS1ʴ3Oxȁ U&K1 L59;ϡ,D׷"jUlCtI~oJ"s.VU/z[Θ(^قm̓؊TGyz/g(%fD g!"7C6br%M/8۬QެPVUx\j|kjg-BJ4Lq06GYjɋgx=l4NHGR3g؅-βEU̞mu˱`b|/}*zh|ȇgae%kE=Z纜0Ϯ>a*cPSDE9Ǥs?f bsGzg7^qOf=^=| Ù=g=s7h'MxҼw ԭgBt Bɑѽl6ҁ㵄Ʋ;C|Mxz}'ٯj΄lfx;5&=:fOS X5`QvV;EGH]v><,#Gk]9vsXM I:Nm|1^N=$Z(7_Nj3CYEFvUqy":v)G2۷o7? K855y Fbۊ~J>/IGɁ!.9QRX8o`iFXv=icoɲq ߈ݻf{>. !r p68|rL^@;f}o8JWȑi}NgU9k V? ŃsI#vhK*+׻罝spSƘyf=S,J8 :!fj-w=iGMˮd6>JXq(m9`rrhm֮?SXaHt88n/nØfZ{o'F 8U.<焄^)停k#O;?:ŊKǾ64FlM退61<$Ꮶ72NG i,_9Op1Te`[ \}w} 6ԙcl| ;ߍ: 4tVŞ?eh͵HR#6H4C7 N8ʕ);0 5JȺ5#\58Foaɝ@8w1h0 _ d?㡇-UT Ω05^ʗ$p'FnF%pz1ڠ@"VҸd+r{ C_F ^O/ ~}'-k%q2~Vgp55.L2X^RghDd2 JCS-sLzkVl⎛>9n"ǏᮻJ?ڵs6 C1 ѺX%.%5SZNr/~ݭX,ːRʫ;=$I̖ā0uG "d"] Jg'|ì:<1FE Y090;;Ŗ{'_,@ !1-0ruwrw3]kP(?Yh5Hӌ(JfכwGNrIs/#_˽"RѦá$|J"==%V, "ItR*8|xÇ1tv_bYے(K␀}8\c8v z1l9R uCJH__j@㺊V^M*@(X,0/lmr(%PR %q6H^c JؖC ZkzΛ&q b@Zժm&$1Y븮KRaϞ45;k-w >?BY8UoEr;!I4ijHS@byL ǵд) ۶i4uH)yWضIJ-%-Trj-[͹I\fD1ۆvjeD݆-hl\,y墄"mX֚84 2?8qrCbeM~[ސ@dDm#Me[fBKӎ Q$IR4"uC|f\JGRqlylE."ZNHt:s,&i.DDQ>ؐY(]_T*Q*Hqͷ`Yv & C}'(%,憯T~qwQ"FOO˲v+&KAy^XKo_޾"bH8v@NJ!<%RgϞ}ypUͲ/~/3cb[6sSfOa#r>WEST*!B ,PZ7FZ-: 8ÓO~$86di>ɽŀ\HjP,W%cy۲`9ϕah6Ab v:b8Vx5LiKf1n;]I4s|5 &Ga=C:8s]q' A! K^s===] | ^,$"XRhf~N{{ϗz=>aX$CzZ΢ov!kmß[b=}Z!ڜM =ojh5W_MOwvz8(hɲ}#/G2??,f&MSn8LOO1I000@j5(bjjBK/Ľ}UثQ\\.wϳ,#c癞frr4Mj$S=!}c =[n] K"J( ˜kk{۶u?lQ*F9j4q(KT$6R.MbFaIENDB`filius-2.9.4+ds/src/main/resources/hilfe/gfx/dokumodus.png000066400000000000000000000046751506574241500235700ustar00rootroot00000000000000PNG  IHDR22?sRGBbKGD·( pHYs筯tIME d0D =IDAThݚ]]{s{grg&DnjԢN1h1U|0Ul V+X*՗>EDi -@HmF1qFc|܏3s{݇sgLbcf|lgZ{?8"6lZA+PJWn|o%x45\x hݵM7aΞ# (էӱ1s$0 .]HnEi}Fu΀v/2:2x&w.7܀j JQF^ٙtj3 nKG9 ٦4ezwFn6q!Dp-h48yIҗ_7r!m=lLw5uQrؾ}$"AkBh >q0$ߎZ`'̵:og2)tԃ9DZ
    KIQaK^] 2(P:!߁ mlktp1">͏YmEiF)T63Hi **$Dx7ۙ,f13,(ie:m>?J-*K:Jk1̲Pkn^uFeZcR &̈́A~Df%nqKdk eShRP!A6W>݋Dt -Fςx1QRaz* D0m:g *^*ìcɑ1ɯyW9fU_2ǟ#VY_0 JBd8ȳǕ7gD?BiCHBnJQ:\TZf(t}CI>$BnMƙNA\m.XG>31ĝ֚=ϽB\ޏ)!dvvYsF9NlXqG11vlɃ.\luJuuY%QD}(atNkeHr%7ZM_^njx bAQ~1քh ReżOJ$ !l1GA A/:R4z%c8"C3&q)_IU#4p }ZחTܔ"0REe1\s 48" {q)8Pa *(̙L1 HZ$_yibڦM4j+BgKk ֚ⴳV^M2RL1l;'Y6,v:D)xk;;nѝB˩X5Bpk8rP˾;Дn{l9ai5DpB,گDz0¥8Բ6WC')7<./; >Ҏ(4=:rtKP7*5.ܮB2[*C vkW۲gɇ+Y̯B@l|5ٵ/bq1/} .\qR"{pIS4k._>žQ|QI U*ACV\jF | O (p{'(z׾ҷU`JFüM)`jC^mwBa6fUJ@ DAy<' ]p{*bhXK[dBB@~:m'%G8Et g\N^m:`DŽXD"8}07с)_ #@Xć@7 5 <H\b2c$0A }82BY@D#Ȅ\ICYLR sFJ(Zv9#X &FS85,F';≝\ržcɋ8 BV^BD(< 2Q8de 42]#'%l)n) *T`;~ rARJ.ʕ[TS7U*aŞ# hQ+֌4͠NC9'կKTHGD*ZN?|;@EM`S;*Zv׶ 4J }!0^KM 'E4E _S䬀M{rxAt͠7kP3Er+cS,^"il  $Hue3ਘ5$BA&&08Ǔ1ܨ ˆD仐+! L5S.TQN~ *t(΄T)S<)u@ժWZ$(c#^9|Г8ˢ}@TUC^rgTS;(GEնUPU tUy @ov80/ջbh1Ĭ9e})R[ZwF$K\uks)9r (] 9&/C })%R'LmVp={Z鮉+ЮǗGkrF|4_g9$S /ԂUexu@M1 R X I)x҇U8 /a 4xmBRL~v-!CeB!B!۷`1>>Q!2~?>裢fvjve'8<I 2TUN"UU( TUr=,hnn '2DQE]4Sҕ$ ˗/G(Jymɒ%D")K:n:x<Ȳ q<%IBKK DQdʋn.9ag<σ y.g d2QirIL\LEQ C4$I0 YMhVHX &IWv] TULyhBi,sD"XfbX^+֞O^UUE8( ϐ Z{`0L㏪By%dʛpN{&%F$QR1OQ$;, UU+"LSQDQpGq$X p8 i#"~Qt FcQ9eYpats8M1*x4h4h4 YucL׋vU*Դ0L qJ!IRV|^bziYF0F(]=AB$i3ㄝ #i( bXAB/2Eb-0pڭPU?}UUM~F ynb[ qWyI<Z78dkLlX.,S \e4<f#u$IB(Jx^芢HL鐼XRa/J$*$EK@=jM!NZ\bP오{.!ٯ 1\($L ޵tV:e«&G!t5Rhtɢ']Ndb Kh16Z z%%e23e:k$\,K5u=E}O1E-],tK |72ظzjBG<ìHj'Ptp]^ L( v_x^&Z; h4J3(3K7 &-Rϕxy:hȏo {)-e\ t=z7ueH v@ߥ@B:q>h!Jn 9 \d1R1%y.݅neZ.bXc#P^^^,E;ւfBuf =ZsF]N6 =F#5 *ڭb92$QLO=Ţ;]"3X,(h[V]@2<?憧Dȵ+D!a&[ĜI=yGEETI 5i]hqli'i1t2NlIrR-H3QYp1U[Z;^$]zF Y,M2,tv;l68UM@d=o t>3#A6 F.+oVTښӑ7y[ h,*\MX,r`0`wa2V[:HtFL(XطnݚrbAyyyANJxIhI|N+T-yɗ /0M c VkAļ!>[׋ݻweK:6FFFsatt4#]b6edޠO@FHC8=BLRDc {WtSAf8l6jRM/ToP,e!"ш7$i0⌟aYheلt $|8 @$ ye߄TDUU޽{0y5557a6`1``aa80v8>>&Ǐhܹs&S InБt%?z㸔6yq,u=>OGhL(3]vuu?ݻwOm*f J1 _i&YF`ll @p8Ѝb01k,]FQ9r]]] 0o<]a|8r(6\q.] Rm&d x#úb1jpmmm8pu 裏ꫯ;ex^ܹO?4N8[{Rm&<^|E<ŋvcΜ9i[, I0<< a`Za6a2,***t:aۋ1Bhmm;rUS2(>8Q__ZET|;(Xkjj066gA([lI0 C/ ! 0J|Œx<4"D"2MMM +N@ /C^ts իWrx'yӧiHlܸUUU=۹͘￿$/n$y]tnZ|_,I27Mf閊o vۍǺ/k& ׽pK/ս䭪 7|n3A}]=olX*dcJPVV 6ɓOXSqXp!v;f& dZ 'AHӉn !  3ҟ&˲(//[$=l6ntň QOC]]&6 WFwO&&& &*5kmxyׇ;weYQZ?k,Ç'DH9sDxwz à& ⭷l6cٖEF7`E XE],L&f*g ]@fa͚5XCC K>'eYnX,ͷb̘všC000@\/j~ZCatt]t`\veKػ Iuz\.͕f¡C jqؿ?DQrP__X,`0HER'9>>@h4BUUر@`@mm-2,"F|0 X&I5&&&tٲ؈ /G$M*rVͷBo כ%y®0 (=Ӄ\rZy,]WlE4*CCCTǴtReap8ۋ}Ańe/zq*++Q[[ EQH4f,CUU4a߷o0669sL orٳgvCUUa2c,M7x…- e2,xb,Q:i®7 ǏcΝB s.9(zK;88cǎ)!ed2vcݺuhii&GL%z b IeYbPرtx...+!>N<ٌ yaYH. 555xw\ ;˲@__jjjR7 v;(.Z,XOve1:6'ObΜ:NBQ⩾~܌FX,3  @)ߪV8q)%due---8xp7oLr*attt &4̮O>{l|'PUuZgð/{ASSS\nQaX(p:MI}_'|nll /"V^ /p]Qp:(+kos&Uv/pՕWa'~'΀[n ׈&N-Xވ9i| xp8L}@]oߎG}}dx`x|vT.:aXlB{{{|be(`f)78t,Jс;v`fNMЧ~3lGsǏÓO> 1-GWg/ODp&3`p8H4p8 I`6Q指#V8Ͳ l2ڍq^yèp9`3J@ GaQ~tChƬ 1Hm)*,hu;Eͷ܌glvZCG>šO+,yuVo,xu.wt50wķBVj,W^y%O<|ׇFv;Υ(ÁGf*.ɴľM( ^0  A22 8M֮Y  a:9:o7 ^ !ȴtFGG"m7G?9. n;!g4@(.OӧsZ +++n*̮EΨv)ivEX4ofc?}&oWI:ThQ7J ]Uee%フ@466&8BVK>y;Kٌ /AZT;{:v}vױtz)1ǔ oaϴp x衇`AD*d*̟?)ޓOÍ:eU]_5DFq= }kW(sX)Ngʕ[(T|2 '.Z(=7l؀hZE+a6P[k/Oj̇o^XFom:" bgEЁ~466^QQ|;8y$?em"Jͻ N@Q6_0L{岅Sixh_l6| _v܉X,Y{Wp:Β/t_*N!r͗11S>D|tB<;Ků³5#Grz_SS~vmpSۛZҎ̩~d${4+^ #0-<,l6ڵkZZC{yE7p5נ>gJŷ\xm66}Pfy .B\xzhooɓ'?}H,˦`IoR3nnQSnxތLltUUQ]]jDQ7&DH*}UU111AΝ;s΅(8iB w%GGח^U5d0/ Ap7F *++a0QQ]]0 |>vEKH^I5m!כ&a )<@X`8Ckk+^8ms8z3 aOvEw+w8;`96l,UQa$ J2!}ْM$'w{9 NWWz4ŭQIDAT뮻hL$LtD]]]BlThw$I|Gҗ\;,ɓӎmI'P6 NX LKTUM+Ӡ vq,Żヒqm""!˹Eʦ)R-b^|E 5( F#,YJ6˕W_W^y?lBZ9 AȲ7xTɣ8}4jrT{<eA $Ȳe&!0{6hjObe]?%qPjqq5-lIvCC, A+e555hiiAccTLqaptvvl[MZlh H 8`jbddNDWQ9:P%X¬YPUUhT0%L 6p=y |Cx?qMݍ={>Dee%Q]]Yf!>D󎮇"dr͆n $>ׯ/LM@rj b?t:QVVZZ,MRߏ ɶ5uүQk4UFD"xCtb4@4T8૪b1D"QqZg㸄6u{ :ʝv\eSU@ A$DQ8 QmC$A?vEl44H$|e'NHH*fW+VM7ݔs( a'iٌH$ d45&E*i_'%(x,tFq\ ;}䤐QiMmY90$tQL6PPFh`ph)Cp%ᛢ(0 =J%@;pL '=HD@ ڭhhE#ܵth4 E|@蒉d6iT AQXL -bb$ahJp!(:*dYq+pD a'A,dfC8F 0yNiTZ#67LHvT0=$) 00X,qsn2fFz@3oi vq]wլ`q1>>N2B!ZP!B*a'Z^ʕ)ġ;w.N'5/H.Q8Ux38QNz-[wd2 ~?nҘLxv T>@U5yK7!2p 7+(4]Q N~>&6pHX,esҋZZ{f:Ga8NY%NS$j k`XJb6˲d *@UلB]vfdAqDXdt}7"Zx1Δ=D AGJzdRY he2>98z00KZ; `vMMMp\ނ @8a(~Fr\"J6I%W;b%.(YjE0=9 IKBЄd| ?+_ VXjb*r-hÇEQ&J@ ! ΫF N'.~"=d4,b`ܹ O,%Dv.Cb @UGD"*,RL "MW ՂQ(JalG*ʈ$86:1vss3t &Qk֬-[tZkkkihz0LD A "ٕZ%rB30A$K/E]]fBH$BMl;ny6/a$Z0D&082'&# NӬ#`4@^LpBø<=*7Ңdف0</8M"#m<܏ܓDL&m]}o0ߛLH$BcU( c!XLa36'O!dqv}^ʐUUs̡5zPN6j|_.L@A6ʕ+k׮p8 h:zjNb͠٣jUb3ϑPYee%.8mE$%AS%wѮ0V5aht:!"P!FN>ђhRpUb6@UFԊYm">U?EƗL@S֒h|detݸ;s*Wivax[oMU#9(d7Q5m'؋ZlhPɄK.ٴ+qkz-Wβ,V+UL8¦Mo# י 8@]b{gB(|hm=y Φi8C}}=̈#GlltE :Halh$g=Y^o*`WT0P*~000Jb1] D9\|׿sV(vlڴ)20 ݡ#vd T qTUUUؼy3*dkmm;˲l >A1-ڄ0$)mMٲ`0拓Pjd2ZFSv%@P#X@UUUl)p("I1mttN;'.o>,Y$Kq`3&'OiO"mItyaҥ4UkZ3cp###Iܥ&8tF:S&F(,0L4@K#Ԍac\CVxLˎBA86LCR,ıL6cW˹sbhhHwe]͛74_vuSSֆ{f}XPp`Z,477eY %sgAxbX,tttPHK rƽA׹E&5<ׇٳg.vͅ63gڜ &@K.EKK >S;vJv;Z^j@ `0XT:󨪪uuuHQFϧmiiAGGsj& Q-ǡuuuXN} *A… pBv@& ճиp%_!F#F mmmhooϹxb _}BٌM6aӦMhoo#G`ll,)v,Y_JZ,ts]]]Bp Xpaδc&2DQ( 󨩩IF;"~?f7XpaBL6?6.HCcMxlvф%+סjbw؎ [+Em4 ؃x)ڷ ߡ{2@Q` b˂.zWu3|< ff{{n x~}ڇ:^<ۇ۶oEl| yPuβ@T\˕]>܀Neǃ7t'~v}gY슲?xyl۶ ?\Nzۙ=c[,ۅ{ nFϏCz {zԡe\=Kx_5'Ȼoy䇳nƤcen{72b7½~ЏmӍ۟x l+znx͛+`>^js&\+=g@-݂F<[0 nV ~-=܂,Jzh~|b sRkL RO/q3Zw63O<˯kf>.ӖGf`7Q/gUIw'IENDB`filius-2.9.4+ds/src/main/resources/hilfe/gfx/entwurfsmodus.png000066400000000000000000000027731506574241500245000ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs&&:#%tEXtSoftwareMacromedia Fireworks MX 2004vtEXtCreation Time04/23/06kJIDATxݙOOW͘18DHI,eaȢYxee|vE]d}VJ(Mp" %TMQP 4DQT$`\tAl<&ϘGzh,{9FDQT!G]bDK]B $`Z| ###,..9}4\.㨖X[nF0==͝;w$,eGVWWk׮ֆemmׯ377ɓ' K3Xnܸ,AGG===:SSS߂ Ν@bb||".]BURϻߩ½^/pP(T3Y# ( RJ(Ry&5S^糜ș3g󬬬*(J8p&aiiyN'@`Xx111ax%###,//j? MOOϟ)ӧܾ}T*U"S.`hh-J# t?1Dm$RwoHg/Bi: rq7⻹yi 4z7I{{ _aɗ1. .T[P 8Ozy?| aHϡJ 8X)La^6XB &}IENDB`filius-2.9.4+ds/src/main/resources/hilfe/gfx/icon_softwareinstallation.png000066400000000000000000000106411506574241500270300ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs.#.#x?vtEXtSoftwareMacromedia Fireworks 8hxtEXtCreation Time04/08/07B~?IDATx՛ilu.ERh"Ev ۬lɎ[Sq&iРAЦ*]m4@K_ EE@R7NfuXHʴQI[p{{&sϹg c WĮRe%(~ᖇǯg@xn||WZhP1*:Sצ3ƌ+>rg&jH_*\RNm=Ѧ¥7ZK dI;H;uWo} xu_9"&df SqL B7ي\w,i3s5:iߊ\W}k]Iuj˪k"׍)mvt݊d ny4xB~XZArCzw4HОڊkeN,b1P1FQK(Sj¶Й 2>%*Q_۾}tO٭ino mθ\k011202h!+%]\;Cی˔*+K(Ӗ3ӷmn~|ew34j^u,¦ i0s@kRXxNzctLsFozòJ5_ЪRfxdm'Yo3TYws'>aGA@u'$&2?6عsGi`[^_>}xŵ3(?сJ\Ϟ)>q~6Ġ^p4/XN8'4W!Ah+RD:Ok'H <49Y~~h(]7k@Җ17 )A AbP'eb-\ >5&ہ%m~tu&'β{-}}U|DR fO!W0<@ 6HjG%mӀ Qv;8wfWFgzǂ2h+?_Uu=PKRzP>D~ Po3&V|c҃#ۖq%_XSo_ M!H4IH!B<19Yؔm[p,_e]K!kZq4B^;y:o[W϶- -K5ʘ X:V|xt.XFK;Sl'J7_MD)2 L`.X*hp4m.X7^> te[yʥ 0s!Z,P ̰֐/P."sgf:ho ҖsL!$ 1k"uڨa󦮖RyσJ%X*rrN~bDam;xhvZ*'1jASv5x)km}2l-3t3e8}5VyJ2RB*ؽZښID@dB6Zr7A#s$[7sR.py2Sϕq# )׎MЊ±KaaKUkS'Y[U@ *#ҽt&M!_ #b#ilh  ߯G@$SSiѐ`C5AY-9L6"bx /&FMlrE~<6ɋHjѯJy1T*.ca}zM n[,W ǔ  %[Μ;M_P05zxn/S.iHA;JqO080 t8YK&"Ic4Ҭ?ƠH|}=\A|ՉTm\BT<A`^:e&peq $Zzu%X66F%!g 't1y |&!qQJ#IJ,|_P,j Øm :btAj/.@{uer 6'"w ;HhVW}X#.!B":dyh5hN/ۖ=K<{2ޥicG;aBı%0:=TԬ*|VWUvY e60|Zκ4X|R)JImd4M>L`k_GvR.r|Hd2,q23 !ҎCpF@ѷ:w޽ r/}b|$qx\GZ1ćPMN X8Ãgf羘ﭗُ70zzr^_k@?"#k3z|/pEAmf'iUk$<7XǓ2<2=Og`к |04ݺm@>ϧ ?dJV+E#x;!9~1=}X?Ԯ W:;̜6C۷{|Ucmc^>:DCN F1f]Rv 9^z_ax^r_NޠkD&IH-^Z";:ΡF$~^b9_?rb˗Jsf~㣿wmw 7Gy~E"<8؏ t;t%B-(UT<Ƭ1fcf ~Bw7GvH?rc | 4Gbnť;ɜixSDrͯ~rs.OIa)XAsQfBn`(]qXlvveysgمGb'PF0zҤ?M* LEjIENDB`filius-2.9.4+ds/src/main/resources/hilfe/gfx/netzwek_aus.png000066400000000000000000000026121506574241500241020ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYstEXtCreation Time07/09/078tEXtSoftwareAdobe FireworksONtEXtXML:com.adobe.xmp Adobe Fireworks CS3 2007-07-09T08:24:09Z 2007-07-09T08:26:11Z image/png GIDATHA 0 E@փ'lqfjO_C/$4 E q]  H)vӶm1:;,0 |/`{ LtdwtkH bf<*69Y!r=xVP5r>͍s{VPկgT`fyL)_qiD<+8Y/wreIENDB`filius-2.9.4+ds/src/main/resources/hilfe/gfx/verkabeln.png000066400000000000000000000221351506574241500235160ustar00rootroot00000000000000PNG  IHDRHG,sBIT|d pHYs  ~tEXtCreation Time08/13/07tEXtSoftwareAdobe FireworksON IDATxwx\ՙ?ӋŒ,[&7ml,Ò`3$X_6[([h aƄ1Ƌ\( `c"w[,Y4#Mo9I. }=ҙ{}:H4f3eD_x_q_< Oah\*HRtuuiZ8@("q8& ( ("_34x,# q$!.xRUa]]]RALBN2pR)v;&=b2ZX xH$EAul62W4mi0H$7B!'|>ĠaTUbk&ǙL&ik4Md2JD"BfٌbtbƂ',Y_<$i u'Ph4*=złd?5Ñ\?.p MB|>q & hJ}]RyKqZK LFzl 8.iqF(JhT`h@ p8oh\d~?drPpl6 `-oh&ljD"$ ^z *xݎL{NlvSxh\$x0Ldggg q>ȪEQ}dcXZ}ϙ~KJ0hiHӴ4?ELG RN(7h '(B 'Ȍq3TF>8dTUMP@ @,|ӚٌR)'P%d`qp?+.x2>h\`}"2BA= |!PD"1MVVV&ki(-WUNHЁ"c*Fe2O֭_:7o+ Lę3g4mPtljbR&ɴ:dG<T\(Ad2;4.x2p!nv/^~3ϟϲeزeK}>2dQ2LSIla,7?A34Mj Ad`d2MPD:8lD(Ge֬Y 8N&Nܹsٸq#-bɒ%lܸ1msuh4+f D"9O*iNF3'xWyسg >G`[[[%/|;L4钯&hT^D!ԾH/ Ac}Q~PQQA]]yyy2"h˅dbǎ9rɲe˸뮻:uE_S A<zww7D"-T!&>'YYY2m,R)l6h,\UU4MKkdXYqAgϞ婧G?paÆ1rH0$I]]]?~\bxõ^=ӧ0v3 @v^mDžȸpdR>O`x2R(z L&y駙3g<fɓ'3}tQUU>N,|GtttHmk٨dر޽믿n7|3mm3(5A2ɓx^rssFIRR З4M5Goc=ƦMp:RYYIVVT*̈́ZVE8VMPUl(**< q!b .䮻bʔ)HK\>'|.K"#z;~Be:7555裏rJb555zljMss3@@fEnSZZJyy9NS r͛q8,X'x#FH~ Svv6ׯrqwS]]M$Au$J.~VyУ 7O~ă C yH[[eeeTWWSPP +jb6 XBQVVFNN4bl6TJjKNNMMM/|@ss3vtʄ!Bg  j aMD"i7qq7Ug1nZbY'x;wǴi(,,j^hQan8mmmzϧ\@kVLqq6_j^ I44Q"??ۍb1Vkقp8,`{^⋸8˱cǨdĈvizZV8|>R\#]x<zeK޴s8} x[nüeTTWt9 äRVoI$~Ƣ;ĉ3f457aYhj_o}3fZ\Hxz-3g3f̠B#%I<&MСCv7_ ]hG餾znؿ?|/2X2jONMM%]?Rjm曘l`C/.&khL H rJKy024M8N"f3~ߏl9sfO~,m-'صk/u]庨9J'IEtvD"lذ;vPWWGUUyyy%fqq1.тDeE?77Yf1k,n<{ڵky饗bڴi̝;Ç蚆fCUGZćE2XKJPrr;j cXDvG8r3ybd>9HV.S]=ӧ1ed8?~$9ټC46ri˿iBX|d hoolv;ۍdq Ξ=˕W^f#LY<Ν;eWu1LR^^θqҚJTU% xxaP\\ԩSYp!f" ;j*vA"Θq@0pM#6]GSIKi(9w^DBEa$hژ KWWWMr0H܀+Vx2dL\.l6^nfիٸq#v].B!&LwMMq8uصk5\gc׬ao0JO#n6RUN'cd+EQ9H$"(c***Xx1uuu}~N:A,H/i9OڰaX<ki@ [Q` ֯_OSSr NS6G S`ZFDQ|>r8a˖-+27ЗOgϞe̙{cnJ4UqmR#GF;z$`4(@WJ/I}y̛?z_իW}vZ[[Jmep7 ~(@(--%??_իW3vX*+[j*L&| k.N>6mrˠMZtuugnNa7 176jk/TTÁ |'OCVU6ΝK}}=ňFΒ%Kx^"}ɄM$&L`ĉ(R[laTVVb2j*RT>C$-PVVƒ%K5k֠LHF" /L,/*-^) t\|7C%ě=`_|uQZZx7nƍ靈?:tرcYxN`D$aȐ!R]]MYYLl۶^xh4鼨Ј c޼y|_@'cXd親̬P,ȼpFpPl3fPt EE7^Rrnb8{pkqJѨl[Q@EM[u]' v?+Ev]f\.P[errrNR)v%;Z.V($P__ҥKihhyu!li.L?4]<}( FIolnV:jjOʕÆrLp:\uUر˗˖|;)sww&.$X0;sO $"-v(wlVq0.Equ٘1i>={aN:… ٽ{7Ob)<03W^|+s=2K4M R]mOYm6 k.V8qDnƴIw3go>ZZZصktp8<@n|rf̘AzlMA+41q$|P(eɒ%,ZB8j+qHP&l~ww7C d\o' [J3"bK،t>9x8|O'0iBPPP@vv6x\V!b#0.DZp!/Bbe 2<^ n6ɡJj@c8op.7vKVv{#|;cf6248I4lGGIhgůbX0?{)?܎ml^1\W?FEqq1'N@~DQtd$xD^~R0AW+Զf4wz(9z<$SX>*!I|z-9],Yt[{값Σ4d4vF6=3~`EѴ@'M_8>8}=FV5|3i9DyL&@ WkΚWѱ3]1 .Ayy94N#U9O&lSG߸iL~3 8hot-j=^Zh<P0Jomb$捣L˜l{s=qPqcgNA?կ~lFUh<cqTՓyClWpp_3)fFZ0u1R>ܺrG1&ɢd iJ2COװ1/ ̈/gT0x,K3m@'c __CI%1󈙾l3ڦr}i |<G<IENDB`filius-2.9.4+ds/src/main/resources/hilfe/simulation_mode_de_DE.html000066400000000000000000000067341506574241500253600ustar00rootroot00000000000000

    Simulationsmodus

    Anwendungen installieren/verwenden

    Durch einen Klick auf einen Rechner/Notebook wird die zugehörige Arbeitsfläche (Desktop-Oberfläche) in einem kleinen Fenster dargestellt. Über das Symbol in der linken oberen Ecke wird ein Fenster zur Installation und Deinstallation von Programmen geöffnet.

    Die graphischen Benutzungsschnittstellen der Programme werden über die jeweiligen Symbole auf der Arbeitsfläche geöffnet. Ob diese Fenster geöffnet oder geschlossen sind, beeinflusst die Funktionsweise des Programms nicht (insbesondere für Server-Programme wichtig).

    IP-Konfiguration ansehen

    Die IP-Konfiguration eines Rechners/Notebooks kann über ein Symbol in der rechten unteren Ecke des Fensters mit der Arbeitsfläche angezeigt werden.

    Datenaustausch beobachten

    Die Geschwindigkeit des Datenaustauschs kann über den Schieberegler in der Leiste am oberen Rand des Fensters variiert werden.

    Alle Dateneinheiten, die von einem Rechner/Notebook versendet und empfangen werden, können eingesehen werden. Dazu muss das Anzeigen des Datenaustauschs über das Kontextmenü eines Rechners (unter Linux und Windows: rechte Maustaste über der Komponente betätigen) ausgewählt werden.

    In der Tabelle wird jede Dateneinheit durch eine oder mehrere gruppierte Zeilen dargestellt. In den verschiedenen Zeilen werden die Daten angezeigt, die den vier Schichten des Internetschichtenmodells zugeordnet werden.

    Firewall Vermittlungsrechner einrichten

    Für die Konfiguration der Firewall eines Vermittlungsrechners im Simulationsmodus steht eine Webschnittstelle zur Verfügung. Dazu muss auf einem angeschlossenen Rechner ein Webbrowser installiert sein, in dessen Adresszeile eine IP-Adresse des Vermittlungsrechners eingegeben wird.

    Die Konfiguration und Aktivierung bzw. Deaktivierung der Firewall erfolgt über ein Formular. Daneben ist es möglich die Log-Daten der Firewall einzusehen.

    Switch-Zustand einsehen

    Der Switch speichert die MAC-Adressen eingehender Dateneinheiten und den Anschluss, über den die Daten empfangen wurden. Dazu legt er die Source Address Table (SAT) an. Diese Tabelle kann durch einen Klick auf den Switch eingesehen werden.

    Wechsel des Anwendungsmodus

    Der Aufbau des virtuellen Rechnernetzes und die IP-Konfiguration der Komponenten erfolgt im Entwurfsmodus, der über die entsprechende Schaltfläche am oberen Fensterrand erreicht wird.

    Das Netzwerk kann im Dokumentationsmodus u.a. mit Hilfe von Textelementen dokumentiert werden. Außerdem ist es möglich, eine einfache Dokumentation zu erzeugen:

    filius-2.9.4+ds/src/main/resources/hilfe/simulation_mode_en_GB.html000066400000000000000000000061061506574241500253630ustar00rootroot00000000000000

    Simulation Mode

    Using and Installing Software

    Performing a click on a computer/notebook the virtual desktop of the component is opened as a new window. Using the icon at the upper left corner of this window opens another internal window to select the installed software at the computer, i.e. to install and remove programmes.

    The graphical user interfaces of these programmes are opened by selecting the icons at the virtual desktop. The behaviour of these programmes does not depend on whether the graphical user interface, i.e. the internal window, is opened or not.

    Looking at the IP Configuration

    It is possible to have a look at the IP configuration by selecting the icon in the lower right corner of the desktop window.

    Observing Data Exchange

    The time delay during the simulation of the data exchange can be changed by the slider at the upper panel of the main window.

    Every data unit which is sent or received by a computer is logged and can be displayed. Use the context menu to open the window which displays the window with the data units.

    In the table with the data units every protocol data unit is represented by one or more rows. The protocol data units of the four layers of the Internet layer model are shown.

    Configuring the Firewall of the Router

    To configure the firewall every router provides a web interface. Therefore, to configure the firewall in the simulation mode it is necessary to start the webbrowser at a connected computer and to start with an IP address of the router to address its web-server.

    The configuration and activation or deactivation of the firewall is done by a web formular. Besides there is the possibility to look at the logging data of the firewall.

    State of the Switch

    The switch stores the MAC addresses of incoming protocol data units in connection with the port of the physical connection. This data is stored in the Source Address Table (SAT). It is possible to show this table by a click on the switch.

    Switch Application Mode

    The infrastructure of the virtual computer network and the IP configuration of the components can be modified in the design mode. Switch to this mode using the icon at the upper panel.

    To add explanations and to generate a simple documentation switch to the Documentation Mode:

    filius-2.9.4+ds/src/main/resources/hilfe/simulation_mode_fr_FR.html000066400000000000000000000077031506574241500254130ustar00rootroot00000000000000

    Mode simulation

    Ce mode permet de simuler le fonctionnement du réseau virtuel de FILIUS.

    Installer et utiliser des logiciels

    Chaque ordinateur et portable possède un bureau à partir duquel peuvent être installées puis lancées des applications. Un clic sur l’icône d’un ordinateur ou d’un portable fait apparaitre son bureau dans une fenêtre indépendante. Pour installer des logiciels, cliquer sur l’icône suivante :

    Les icônes des applications installées apparaissent alors sur le bureau. Un simple clic sur une icône permet de lancer l’application correspondante. Chaque application s’ouvre dans une fenêtre du bureau. Fermer la fenêtre d’une application, ou fermer la fenêtre du bureau ne ferme pas les applications. Pour fermer une application, il faut la désinstaller.

    Afficher l’état du réseau

    Lorsque le bureau d’un ordinateur ou d’un portable est affiché, en plaçant la souris au dessus de l’icône réseau située en bas à droite, l’adresse IP du poste apparait dans une infobulle.

    Pour obtenir le paramétrage IP détaillé du poste, cliquer sur l’icône.

    Observer les échanges de données

    Un journal conserve une trace de tous les paquets de données envoyés ou reçus par un poste. Pour visualiser ce journal, cliquer droit sur l’icône du poste et choisir Afficher les échanges de données.

    Le journal est présenté sous la forme d’un tableau dont chaque ligne correspond à un échange de données. Les échanges sont basés sur une architecture en quatre couches matérialisées par des couleurs différentes. Un clic sur une ligne permet d’afficher dans la partie inférieure de la fenêtre le détail du paquet de données échangé.

    Il est possible de ralentir la vitesse des échanges de données en déplaçant vers la gauche le curseur situé au centre de la partie supérieure de la fenêtre principale. Les cables deviennent verts lorsqu’ils sont parcourus par des données.

    Configurer le parefeu du routeur

    En mode simulation, le parefeu d’un routeur peut être configuré via son interface web. Il suffit pour cela dans le navigateur web d’un poste de saisir comme url l’adresse IP du routeur à configurer.

    Outre la configuration, le formulaire web permet d’activer et de désactiver le parefeu, ainsi que de consulter son journal d’activité.

    Afficher la table du switch

    Le switch (ou commutateur) enregistre les adresses MAC des paquets entrants avec le port d’arrivée. Ces deux informations sont stockées dans la table des adresses sources (SAT). Pour afficher cette table, cliquer sur le switch.

    Changer de mode

    Si l’infrastructure du réseau a besoin d’être modifiée, il suffit de rebasculer vers le mode conception à l’aide du bouton de la barre supérieure :

    Change au mode documentation pour ajouter des éléments visuels qui facilitent la lecture du réseau virtuel :

    filius-2.9.4+ds/src/main/resources/img/000077500000000000000000000000001506574241500177355ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/img/email_icon.png000066400000000000000000000070731506574241500225510ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time04/12/07㟷 IDATx[[OǶ{Œ @oE>%%ǣ<(Dv%Ql%qqe3C`a{Tg {R jWVZM033*Z;xynY{ב?|裏<*J)( M@)%R*!(C>*eBs~,_!BM xGeN:0.!DH߇iL&:/ud2`㶬ּ>zDy`0 !DD}"FZ;]A)`ϯeE\h9;Ԏ]r2|###4 dY8hgraggSMaLď?wy\R-`!l&Gϕ1,߫껻Y9GP𤬡P\%z,SLT٬_+p8.u]0c !8Bc 8૮UU}]kv攫 H0߇y ſq)TScݓ8q]@a  ~B_fϝu]Xϟcwwpl~b!J!@4SkȵÒN:%Iz H&D">mFֱQ9i7|uR:zzz044QP5J={v& :+rrl Ͼ>'޵va6~w۾ˍUUW!w_XX8kEN144sUQBsT tFrmnn"cttXKT0(_Np8)S(rֿIçN,J[oU5*48\Ú'8ENu\p ߫"[_k^Zunmm! uߩiׇ% ,4$cmm Ƕ8:*w)Z/~, n8%<6===^¼BVVVp%5+|I[Y":B]R]h@2DPӧOە!N]loo_o1.\o_&P(瘞ijÈD"xqGBvVٳgV<t9< ks'19b``yǔR??)Ne$IܸqDPu]rDq}J4|"R^%-OPm2 Ţ#gX^^FPDw% pαB&''9ǏYWWWQ(o5BՉt:|>\|0M~-G#Oq0??uLNNl.ׇ^{ x$ ܻweG]BL?bBZ:n֖TR4T*;wTU066,,,`ii HRH$- l&40::T*lnnbaaJK6L[]BL"4MԔ -,H(0 |GJۦ\.l6 ۶oWlCCC<lfi4MqΏRaii abbdφa`ddðm888@>cp]#G8~M"@2Ǯ\\.Yħ )8XZZchhX]סiΝ;Wu9*8 [9R @7ooYLOO#L ?lc O>뺸q^J6*UF)Buxz \Çe4±UmσsW_}7ol*L+hVՅ!BGw2­[Yt$b}*AhwHK#YׇT* |w-M͜Gs^4 T 4͎iV~V]xXYYi.S,N`@ .D"$ tuuA {QJqUayyX.<>򒪪sݲPPe!cooLX[[C&eY`UB"l3X,۶ BRRQ,q׍OyÌbߚA4x{zX px)XiYr077LOOcxxl6O>d޽{x7۶cQqTEQ*t]yFF5 qP(W ȉ?/.rw=midmHb1b1: AeaaasLNNlll??/1WkRiȲ8Hm4w ÀaD"F i֐}GT*x7Y*sg4͔m۪(~zH,ʳ`0X(݌˲۶166 i8wLm;bY(iAڒ4+Vcakk BsbZo.2/J3.9C06GbBCR˲%vvvG}R4aY֕B.uh]]]bqrl6/M<˨[nuI۶߳,k eiTUE(e?dfb9}$IENDB`filius-2.9.4+ds/src/main/resources/logback.xml000066400000000000000000000014521506574241500213070ustar00rootroot00000000000000 ${FILIUS_LOG_LEVEL:-INFO} %-5level %msg%n ${user.home}/.filius/filius.log ${FILIUS_LOG_LEVEL:-OFF} true %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n filius-2.9.4+ds/src/main/resources/tmpl/000077500000000000000000000000001506574241500201355ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/tmpl/firewall_konfig_webseite_de_DE.txt000066400000000000000000000121301506574241500267440ustar00rootroot00000000000000 Konfiguration der Firewall auf dem Vermittlungsrechner :errorMsg:

    Firewall-Konfiguration

    :firewallActivate: Firewall aktivieren
    Erst durch Aktivieren der Firewall werden die Firewall-Regeln angewendet. Bei deaktivierter Firewall findet keine Filterung statt.
    :dropICMP: ICMP-Pakete filtern
    Alle ICMP Pakete, z.B. Ping-Anfragen, werden von der Firewall verworfen.
    :onlySYN: nur SYN-Pakete verwerfen
    Neue Verbindungsanfragen zu gesperrten Ports werden verworfen. Der Rückkanal für erlaubte Verbindungsanfragen wird akzeptiert.

    Firewall-Regeln:


    Die Regeln werden von oben nach unten geprüft.
    Sobald eine Regel zutrifft, wird die angegebene Aktion ausgeführt.
    Falls keine Regel zutrifft, wird folgende Standard-Aktion ausgeführt:
    :ruleset:
    lfd. Nr. Quell-IP Quell-Subnetzmaske Ziel-IP Ziel-Subnetzmaske Protokoll Port Aktion
    Aktion für ausgewählte Regel:
    :errorMsg:

    Neue Regel erstellen


    Alle Felder sind optional und werden
    -- falls leer gelassen --
    für die Filterung nicht verwendet.
    Quell-IP:
    Quell-Subnetzmaske:
    Ziel-IP:
    Ziel-Subnetzmaske:
    Protokoll:
    Port:
    Aktion:
    filius-2.9.4+ds/src/main/resources/tmpl/firewall_konfig_webseite_en_GB.txt000066400000000000000000000122641506574241500267660ustar00rootroot00000000000000 Router Firewall Configuration :errorMsg:

    Router Firewall Configuration

    :firewallActivate: Activate firewall
    Firewall rules will only be applied, if the firewall is activated. In the case of a not activated firewall no packet filtering will take place.
    :dropICMP: Filter ICMP packets
    All ICMP packets, e.g., ping requests, will be dropped.
    :onlySYN: only drop SYN packets
    New connection requests to blocked ports will be dropped. Back channels for allowed connection requests will be accepted.

    Firewall rules:


    The rules are evaluated from top to bottom.
    If a rule matches, the given action will be followed.
    If no rule applies, the following default policy is used:
    :ruleset:
    no. source IP source subnet mask dest. IP dest. subnet mask protocol port action
    Action for selected rule:
    :errorMsg:

    Create new rule


    All fields are optional and will be
    -- if empty --
    ignored for filtering.
    source IP:
    source subnet mask:
    dest. IP:
    dest. subnet mask:
    protocol:
    port:
    action:
    filius-2.9.4+ds/src/main/resources/tmpl/firewall_konfig_webseite_fr_FR.txt000066400000000000000000000133361506574241500270130ustar00rootroot00000000000000 Configuration du parefeu du routeur :errorMsg:

    Configuration du parefeu du routeur

    :firewallActivate: Activer le parefeu
    Les règles ne sont appliquées que lorsque le parefeu est actif.
    :dropICMP: Filtrer les paquets ICMP
    Tous les paquets ICMP seront rejetés (ceux d’un ping, par exemple).
    :onlySYN: Ne filtrer que les paquets SYN
    Les requêtes vers les ports bloqués seront rejetées. Le canal de retour pour les requêtes de connexion autorisées sera ouvert.

    Règles du parefeu


    Les règles sont parcourues du haut de la liste vers le bas.
    Seule la première règle satisfaite rencontrée est appliquée. La règle par défaut s’applique lorsqu’aucune règle de la liste n’est satisfaite.
    Règle par défaut :
    :ruleset:
    ID IP source Masque IP dest. Masque Protocole Port Action
    Actions applicables à la règle sélectionnée :
    :errorMsg:

    Nouvelle règle

    Tous les champs sont optionnels, sauf protocole et Action.
    IP source :
    Masque :
    IP destination :
    Masque :
    Protocole :
    Port :
    Action :

    Retour au menu principal

    filius-2.9.4+ds/src/main/resources/tmpl/gateway_index_de_DE.txt000066400000000000000000000003731506574241500245510ustar00rootroot00000000000000 Administration des Gateways

    Firewall-Administration

    Konfiguration

    Log-Einträge anzeigen

    filius-2.9.4+ds/src/main/resources/tmpl/gateway_index_en_GB.txt000066400000000000000000000003371506574241500245630ustar00rootroot00000000000000 Gateway Administration

    Firewall Administration

    Configuration

    Show Log

    filius-2.9.4+ds/src/main/resources/tmpl/gateway_index_fr_FR.txt000066400000000000000000000006671506574241500246150ustar00rootroot00000000000000 Administration du gateway

    Parefeu

    Configurer le parefeu

    Afficher le journal

    filius-2.9.4+ds/src/main/resources/tmpl/http_fehler_de_DE.txt000066400000000000000000000003011506574241500242140ustar00rootroot00000000000000 Fehler :meldung: (:code:)

    :meldung:

    Es ist ein Fehler aufgetreten (HTTP-Status: :code:)

    filius-2.9.4+ds/src/main/resources/tmpl/http_fehler_en_GB.txt000066400000000000000000000002641506574241500242360ustar00rootroot00000000000000 Error :meldung: (:code:)

    :meldung:

    An error occurred (HTTP-Status: :code:)

    filius-2.9.4+ds/src/main/resources/tmpl/http_fehler_fr_FR.txt000066400000000000000000000003331506574241500242570ustar00rootroot00000000000000 Erreur :meldung: (:code:)

    :meldung:

    Une erreur s’est produite (HTTP-Status: :code:)

    filius-2.9.4+ds/src/main/resources/tmpl/quelltext_vorlagen/000077500000000000000000000000001506574241500240615ustar00rootroot00000000000000filius-2.9.4+ds/src/main/resources/tmpl/quelltext_vorlagen/client.txt000066400000000000000000000213401506574241500261000ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.rahmenprogramm.I18n; import filius.software.transportschicht.TCPSocket; /** *

    * In dieser Klasse wird das Client-Programm einer einfachen Client-Server-Anwendung implementiert. Nachrichten an die * graphische Benutzungsoberflaeche werden durch den Aufruf banachrichtigeBeobachter(Object) versendet. *

    *

    * Aufrufe folgender Methoden des Sockets blockieren: *

    *
      *
    • verbinden()
    • *
    • senden()
    • *
    • empfangen()
    • *
    • schliessen()
    • *
    *

    * Deshalb muessen Methoden dieser Klasse, die Aufrufe dieser Methoden enthalten, ueber die Methode * ausfuehren(String, Object[]) aufgerufen werden. Das bewirkt, dass diese Methoden in einem eigenen Thread * ausgefuehrt werden und damit der aufrufende Thread nicht blockiert. Das ist wichtig, wenn die Aufrufe von der * graphischen Benutzungsoberflaeche ausgeloest werden. *

    *

    * Ein Beispiel fuer die Verwendung von ausfuehren() ist folgendes:
    * public void verbinden(String zielAdresse, Integer port) {
    *    Object[] args;

    *    args = new Object[2];
    *    args[0] = zielAdresse;
    *    args[1] = port;

    *    ausfuehren("initialisiereSocket", args);
    * } *

    *

    * Dabei wird als erstes Argument der auszufuehrenden blockierenden Methode * uebergeben (hier: initialisiereSocket) und dann in einem Array die zu uebergebenden Parameter (hier: * zielAdresse und port). Der Aufruf der Methode verbinden(zielAdresse, port) * bewirkt also das Ausfuehren der Methode initialisiereSocket(zielAdresse, port) in einem anderen * Thread. Damit blockiert die Methode verbinden nicht. *

    *

    * Achtung: Die indirekt aufgerufene Methode (d. h. ueber * ausfuehren(String, Object[])) muss als public deklariert sein! *

    */ public class ClientBaustein extends ClientAnwendung implements I18n { private static Logger LOG = LoggerFactory.getLogger(ClientBaustein.class); /** Port-Nummer des Servers, an dem Verbindungsanfragen angenommen werden */ private int zielPort = 55555; /** * Adresse des Rechners, auf dem der Server laeuft als Domainname oder IP-Adresse. */ private String zielIPAdresse; /** * Methode zum Verbindungsaufbau zu einem Server. Hier wird der Client-Socket als TCP/IP-Socket erzeugt. Wenn UDP * verwendet werden soll, muss diese Methode ueberschrieben werden.
    * Diese Methode ist nicht blockierend. Diese Methode veranlasst den Aufruf von * initialisiereSocket in einem anderen Thread. */ public void verbinden() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), verbinden()"); Object[] args; args = new Object[2]; args[0] = zielIPAdresse; args[1] = Integer.valueOf(zielPort); ausfuehren("initialisiereSocket", args); ausfuehren("empfangeNachricht", null); } /** * Methode zum Aufbau einer Verbindung mit einem TCP-Socket. Diese Methode ist blockierend. */ public synchronized void initialisiereSocket(String zielAdresse, Integer port) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), initialisiereSocket(" + zielAdresse + "," + port + ")"); if (!istVerbunden()) { try { socket = new TCPSocket(getSystemSoftware(), zielAdresse, port); socket.verbinden(); benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg2")); } catch (Exception e) { LOG.debug("", e); socket = null; benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg1") + e.getMessage()); } } } /** * Methode zum trennen einer Verbindung. Der Socket wird durch den Aufruf der Methode schliessen() geschlossen und * und der Socket fuer diese Anwendung auf null gesetzt.
    * Diese Methode ist blockierend. */ public void trennen() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), trennen()"); if (socket != null) { socket.schliessen(); socket = null; benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg3")); } } /** * Diese Methode blockiert bis die Nachricht versand wurde. Der Empfang der Antwort erfolgt asynchron. */ public void senden(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), versendeNachricht(" + nachricht + ")"); if (socket != null && socket.istVerbunden()) { try { socket.senden(nachricht); benachrichtigeBeobachter("<<" + nachricht); } catch (Exception e) { benachrichtigeBeobachter(e.getMessage()); LOG.debug("", e); } } else { benachrichtigeBeobachter(messages.getString("sw_clientbaustein_msg4")); } } /** * Methode zum Empfang einer Nachricht vom Socket. Die empfangene Nachricht wird mit * benachrichtigeBeobachter an die GUI weiter gegeben. Diese Methode ist blockierend und sollte nicht * direkt von der GUI aufgerufen werden. */ public void empfangeNachricht() { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ClientBaustein), empfangeNachricht()"); String nachricht; while (socket != null && socket.istVerbunden()) { try { nachricht = socket.empfangen(Long.MAX_VALUE); if (nachricht != null) { benachrichtigeBeobachter(">>" + nachricht); } else { socket.schliessen(); benachrichtigeBeobachter( messages.getString("sw_clientbaustein_msg5") + " " + socket.holeZielIPAdresse() + ":" + socket.holeZielPort() + " " + messages.getString("sw_clientbaustein_msg6")); } } catch (Exception e) { benachrichtigeBeobachter(e.getMessage()); LOG.debug("", e); } } } /** Methode fuer den Zugriff auf die Server-Adresse */ public String getZielIPAdresse() { return zielIPAdresse; } /** Methode fuer den Zugriff auf die Server-Adresse */ public void setZielIPAdresse(String zielIPAdresse) { this.zielIPAdresse = zielIPAdresse; } /** * Methode fuer den Zugriff auf die Port-Nummer, an dem der Server zu erreichen ist. */ public int getZielPort() { return zielPort; } /** * Methode fuer den Zugriff auf die Port-Nummer, an dem der Server zu erreichen ist. */ public void setZielPort(int zielPort) { this.zielPort = zielPort; } } filius-2.9.4+ds/src/main/resources/tmpl/quelltext_vorlagen/client_gui.txt000066400000000000000000000173671506574241500267620ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import filius.software.clientserver.ClientBaustein; /** *

    * Diese Klasse stellt die Benutzungsoberflaeche fuer das Client-Programm einer einfachen Client-Server-Anwendung zur * Verfuegung. *

    *

    * Nachrichten von der Anwendung werden nach dem Beobachtermuster durch die Benachrichtigung der Beobachter angenommen * und verarbeitet. *

    */ public class GUIApplicationClientBausteinWindow extends GUIApplicationWindow { private static final long serialVersionUID = 1L; /** * Textfeld fuer die Ausgabe gesendeter und empfangener Nachrichten sowie fuer Fehlermeldungen */ private JTextArea taAusgabe; /** * Textfeld fuer die Adresse des Servers, zu dem die Verbindung hergestellt werden soll */ private JTextField tfServerAdresse; /** * Textfeld zur Angabe des TCP-Ports, auf dem der Server auf eingehende Nachrichten wartet */ private JTextField tfServerPort; /** Textbereich zur Eingabe der Nachrichten */ private JTextArea taSenden; /** Schaltflaeche zum initiieren des Verbindungsaufbaus */ private JButton btVerbinden; /** * Schaltflaeche zum Senden einer zuvor eingegebenen Nachricht */ private JButton btSenden; /** * Standard-Konstruktor, der automatisch zur Erzeugung der graphischen Benutzungsoberflaeche fuer diese Anwendung * aufgerufen wird. */ public GUIApplicationClientBausteinWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); initialisiereKomponenten(); } /** Methode zur Initialisierung der graphischen Komponenten */ private void initialisiereKomponenten() { JScrollPane scrollPane; Box hauptBox; Box hBox; JLabel label; hauptBox = Box.createVerticalBox(); hauptBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); hauptBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); label = new JLabel(messages.getString("clientbaustein_msg1")); label.setPreferredSize(new Dimension(140, label.getHeight())); hBox.add(label); tfServerAdresse = new JTextField(); tfServerAdresse.setPreferredSize(new Dimension(100, 20)); hBox.add(tfServerAdresse); hauptBox.add(hBox); hauptBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); label = new JLabel(messages.getString("clientbaustein_msg2")); label.setPreferredSize(new Dimension(140, label.getHeight())); hBox.add(label); tfServerPort = new JTextField(); tfServerPort.setPreferredSize(new Dimension(100, 20)); hBox.add(tfServerPort); hauptBox.add(hBox); hauptBox.add(Box.createVerticalStrut(5)); btVerbinden = new JButton(messages.getString("clientbaustein_msg3")); btVerbinden.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("verbinden")) { ((ClientBaustein) holeAnwendung()).setZielIPAdresse(tfServerAdresse.getText()); ((ClientBaustein) holeAnwendung()).setZielPort(Integer.parseInt(tfServerPort.getText())); ((ClientBaustein) holeAnwendung()).verbinden(); } else { ((ClientBaustein) holeAnwendung()).trennen(); } aktualisieren(); } }); hauptBox.add(btVerbinden); hauptBox.add(Box.createVerticalStrut(5)); label = new JLabel(messages.getString("clientbaustein_msg4")); label.setPreferredSize(new Dimension(100, 20)); hauptBox.add(label); taSenden = new JTextArea(); scrollPane = new JScrollPane(taSenden); scrollPane.setPreferredSize(new Dimension(400, 50)); hauptBox.add(scrollPane); btSenden = new JButton(messages.getString("clientbaustein_msg5")); btSenden.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((ClientBaustein) holeAnwendung()).senden(taSenden.getText()); taSenden.setText(""); } }); hauptBox.add(btSenden); hauptBox.add(Box.createVerticalStrut(5)); taAusgabe = new JTextArea(); taAusgabe.setEditable(false); scrollPane = new JScrollPane(taAusgabe); scrollPane.setPreferredSize(new Dimension(400, 200)); hauptBox.add(scrollPane); hauptBox.add(Box.createVerticalStrut(5)); add(hauptBox, BorderLayout.CENTER); aktualisieren(); } /** * Methode zum aktualisieren der Komponenten der graphischen Benutzungsoberflaeche in Abhaengigkeit vom Zustand der * Anwendung */ private void aktualisieren() { ClientBaustein client; client = (ClientBaustein) holeAnwendung(); tfServerAdresse.setText(client.getZielIPAdresse()); tfServerPort.setText("" + client.getZielPort()); if (client.istVerbunden()) { btVerbinden.setText(messages.getString("clientbaustein_msg6")); btVerbinden.setActionCommand("trennen"); btSenden.setEnabled(true); tfServerAdresse.setEditable(false); tfServerPort.setEditable(false); } else { btVerbinden.setText(messages.getString("clientbaustein_msg3")); btVerbinden.setActionCommand("verbinden"); btSenden.setEnabled(false); tfServerAdresse.setEditable(true); tfServerPort.setEditable(true); } } /** * Diese Methode wird automatisch ausgefuehrt, wenn eine Nachricht an den Beobachter der Anwendung gesendet wird. * Der Parameter arg enthaelt die Nachricht, die von der Anwendung verschickt wurde. */ public void update(Observable o, Object arg) { if (arg != null) { this.taAusgabe.append(arg.toString() + "\n"); } aktualisieren(); } }filius-2.9.4+ds/src/main/resources/tmpl/quelltext_vorlagen/server.txt000066400000000000000000000071271506574241500261370ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.transportschicht.Socket; import filius.software.transportschicht.TCPSocket; /** *

    * In dieser Klasse wird das Server-Programm einer einfachen Client-Server-Anwendung implementiert. Nachrichten an die * graphische Benutzungsoberflaeche werden durch den Aufruf banachrichtigeBeobachter(Object) versendet. *

    *

    * In dieser Klasse wird mit einem eigenen Thread der Server-Socket auf eingehende Verbindungen gewartet. Die * Verarbeitung der eingehenden Nachrichten erfolgt durch einen Mitarbeiter, der in der Methode * neuerMitarbeiter(Socket) erzeugt wird. *

    *

    * Die Verarbeitung eingehender Nachrichten wird von einem Mitarbeiter uebernommen, der erzeugt wird, sobald eine neue * Verbindungsanfrage vom Server-Socket empfangen wurde. *

    *

    * Einstellungen, die in einer Projektdatei gespeichert werden sollen, muessen in dieser Klasse als Attribute verwaltet * werden und mit Getter- und Setter-Methoden zugaenglich sein (z. B. fuer das Attribut int bspAttr mit * * public int getBspAttr() und public void setBspAttr(int) * ). Attribute, die in der Mitarbeiterklasse vorhanden sind, werden nicht gespeichert! *

    *

    * Das Server-Programm wird durch Aufruf der Methode der Oberklasse setAktiv(boolean) aktiviert bzw. * deaktiviert (d. h. die Verbindungsannahme wird aktiviert bzw. deaktiviert). *

    */ public class ServerBaustein extends TCPServerAnwendung { private static Logger LOG = LoggerFactory.getLogger(ServerBaustein.class); /** * In dieser Methode wird ein neuer Mitarbeiter zur Verarbeitung von eingehenden Nachrichten erzeugt und der Liste * der zu verwaltenden Mitarbeiter hinzugefuegt. */ protected void neuerMitarbeiter(Socket socket) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerBaustein), neuerMitarbeiter(" + socket + ")"); ServerMitarbeiter mitarbeiter; if (socket instanceof TCPSocket) { mitarbeiter = new ServerBausteinMitarbeiter(this, (TCPSocket) socket); this.mitarbeiter.add(mitarbeiter); mitarbeiter.starten(); } } } filius-2.9.4+ds/src/main/resources/tmpl/quelltext_vorlagen/server_gui.txt000066400000000000000000000140621506574241500267770ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.gui.anwendungssicht; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import filius.software.clientserver.ServerAnwendung; import filius.software.clientserver.ServerBaustein; /** *

    * Diese Klasse stellt die Benutzungsoberflaeche fuer das Server-Programm einer einfachen Client-Server-Anwendung zur * Verfuegung. *

    *

    * Nachrichten von der Anwendung werden nach dem Beobachtermuster durch die Benachrichtigung der Beobachter angenommen * und verarbeitet. *

    */ public class GUIApplicationServerBausteinWindow extends GUIApplicationWindow { private static final long serialVersionUID = 1L; /** * Textbereich zur Ausgabe von Log-Daten, die vom Server-Programm erzeugt und durch Benachrichtigung der Beobachter * an die graphische Benutzungsoberflaeche weiter gegeben werden */ private JTextArea taLog; /** * Schaltflaeche zum Starten bzw. Stoppen des Server-Programms, d. h. zum Starten bzw. Beenden der * Verbindungsannahme durch den Server */ private JButton bStartStop; /** * Textfeld zur Eingabe des Ports, an dem der Server eingehende Verbindungsanfragen entgegen nimmt. */ private JTextField tfPort; /** * Standard-Konstruktor, der automatisch zur Erzeugung der graphischen Benutzungsoberflaeche fuer diese Anwendung * aufgerufen wird. */ public GUIApplicationServerBausteinWindow(GUIDesktopPanel desktop, String appName) { super(desktop, appName); initialisiereKomponenten(); } /** Methode zur Initialisierung der graphischen Komponenten */ private void initialisiereKomponenten() { JPanel hauptPanel; JScrollPane spLogScroller; JLabel lbPort; Box hBox, vBox; hauptPanel = new JPanel(new BorderLayout()); vBox = Box.createVerticalBox(); vBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); vBox.add(Box.createVerticalStrut(5)); hBox = Box.createHorizontalBox(); bStartStop = new JButton(); bStartStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("start")) { ((ServerAnwendung) holeAnwendung()).setPort(Integer.parseInt(tfPort.getText())); ((ServerAnwendung) holeAnwendung()).setAktiv(true); ((ServerAnwendung) holeAnwendung()).setPort(Integer.parseInt(tfPort.getText())); } else { ((ServerAnwendung) holeAnwendung()).setAktiv(false); } aktualisieren(); } }); hBox.add(bStartStop); hBox.add(Box.createHorizontalStrut(5)); lbPort = new JLabel(messages.getString("serverbaustein_msg1")); hBox.add(lbPort); tfPort = new JTextField("" + ((ServerAnwendung) holeAnwendung()).getPort()); hBox.add(tfPort); vBox.add(hBox); vBox.add(Box.createVerticalStrut(5)); taLog = new JTextArea(); taLog.setEditable(false); spLogScroller = new JScrollPane(taLog); spLogScroller.setPreferredSize(new Dimension(400, 400)); vBox.add(spLogScroller); vBox.add(Box.createVerticalStrut(5)); hauptPanel.add(vBox, BorderLayout.CENTER); add(hauptPanel, BorderLayout.CENTER); aktualisieren(); } /** * Methode zum aktualisieren der Komponenten der graphischen Benutzungsoberflaeche in Abhaengigkeit vom Zustand der * Anwendung */ public void aktualisieren() { ServerBaustein server; server = (ServerBaustein) holeAnwendung(); if (server.isAktiv()) { tfPort.setEditable(false); bStartStop.setText(messages.getString("serverbaustein_msg2")); bStartStop.setActionCommand("stop"); } else { bStartStop.setText(messages.getString("serverbaustein_msg3")); bStartStop.setActionCommand("start"); tfPort.setEditable(true); } } /** * Diese Methode wird automatisch ausgefuehrt, wenn der eine Nachricht an den Beobachter der Anwendung gesendet * wird. Der Parameter arg enthaelt die Nachricht, die von der Anwendung verschickt wurde. */ public void update(Observable o, Object arg) { taLog.append(arg.toString() + "\n"); aktualisieren(); } } filius-2.9.4+ds/src/main/resources/tmpl/quelltext_vorlagen/server_mitarbeiter.txt000066400000000000000000000063361506574241500305270ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.clientserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import filius.software.transportschicht.Socket; /** *

    * In dieser Klasse erfolgt die Verarbeitung von eingehenden Nachrichten an einen Server. *

    *

    * Die Oberklasse ServerMitarbeiter erbt von der Klasse Thread. In der run()-Methode der * Oberklasse wird der Socket auf eingehende Nachrichten ueberwacht. Sobald eine Nachricht eintrifft, wird diese an die * Methode verarbeiteNachricht(String) zur weiteren Verarbeitung weiter gegeben. Ausserdem wird dort der * Socket automatisch geschlossen, wenn das Client-Programm den Verbindungsabbau initiiert. *

    *

    * In dieser Klasse sollte nur die Methode senden(String) des Sockets verwendet werden! *

    */ public class ServerBausteinMitarbeiter extends ServerMitarbeiter { private static Logger LOG = LoggerFactory.getLogger(ServerBausteinMitarbeiter.class); /** * Standard-Konstruktor. Wenn der Server auf einem bestimmten Port auf eingehende Verbindungen warten soll, muss die * Port-Nummer hier mit setPort(int) initialisiert werden! */ public ServerBausteinMitarbeiter(ServerAnwendung server, Socket socket) { super(server, socket); } /** * Methode, die automatisch aufgerufen wird, wenn eine neue Nachricht eintrifft. Hier erfolgt die Verarbeitung der * eingehenden Nachricht. */ protected void verarbeiteNachricht(String nachricht) { LOG.trace("INVOKED (" + this.hashCode() + ", T" + this.getId() + ") " + getClass() + " (ServerBausteinMitarbeiter), verarbeiteNachricht(" + nachricht + ")"); try { socket.senden(nachricht); server.benachrichtigeBeobachter("<<" + nachricht); } catch (Exception e) { LOG.debug("", e); server.benachrichtigeBeobachter(e.getMessage()); } } } filius-2.9.4+ds/src/main/resources/tmpl/routing_de_DE.html000066400000000000000000000010251506574241500235300ustar00rootroot00000000000000 :title:

    :title:

    :routing_entries:
    Rechnernetz Hops Gültigkeit Nächster Hop
    Adresse Netzmaske (sec) Ziel Öffentliche Adresse

    :hint:

    filius-2.9.4+ds/src/main/resources/tmpl/routing_en_GB.html000066400000000000000000000007671506574241500235560ustar00rootroot00000000000000 :title:

    :title:

    :routing_entries:
    Network Hops Validity Next Hop
    Address Netmask (sec) Target Public Address

    :hint:

    filius-2.9.4+ds/src/main/resources/tmpl/routing_fr_FR.html000066400000000000000000000011221506574241500235640ustar00rootroot00000000000000 :title:

    :title:

    :routing_entries:
    Réseau Sauts Validité Saut suivant
    Adresse Masque (sec) Destination Adresse publique

    :hint:

    Retour au menu principal

    filius-2.9.4+ds/src/main/resources/tmpl/vermittlung_index_de_DE.txt000066400000000000000000000006261506574241500254710ustar00rootroot00000000000000 Administration des Vermittlungsrechners

    Firewall-Administration

    Konfiguration

    Log-Einträge anzeigen

    Weiterleitung/Routing

    Anzeige des aktuellen Status der Weiterleitungs-/Routing-Tabelle

    filius-2.9.4+ds/src/main/resources/tmpl/vermittlung_index_en_GB.txt000066400000000000000000000005141506574241500254770ustar00rootroot00000000000000 Router Administration

    Firewall Administration

    Configuration

    Show Log

    Routing/Forwarding

    Show current routing/forwarding table

    filius-2.9.4+ds/src/main/resources/tmpl/vermittlung_index_fr_FR.txt000066400000000000000000000010211506574241500255150ustar00rootroot00000000000000 Administration du routeur

    Parefeu

    Configurer le parefeu

    Afficher le journal

    Routage

    Afficher la table de routage

    filius-2.9.4+ds/src/main/resources/tmpl/webserver_index_de_DE.txt000066400000000000000000000010521506574241500251070ustar00rootroot00000000000000 Standardseite

    FILIUS - Webserver

    Herzlich Willkommen auf dem Webserver der Anwendung FILIUS!

    Diese Seite wurde automatisch mit der Installation des Webservers eingerichtet, es lassen sich jedoch auch eigene Seiten hier unterbringen.

    https://www.lernsoftware-filius.de

    filius-2.9.4+ds/src/main/resources/tmpl/webserver_index_en_GB.txt000066400000000000000000000006741506574241500251320ustar00rootroot00000000000000 Standardseite

    FILIUS - Webserver

    Welcome at FILIUS!

    This page has been delivered as default page. Feel free to add your own contents!

    https://www.lernsoftware-filius.de

    filius-2.9.4+ds/src/main/resources/tmpl/webserver_index_fr_FR.txt000066400000000000000000000011321506574241500251440ustar00rootroot00000000000000 Page d’accueil

    FILIUS - Serveur web

    Bienvenue sur le serveur web de FILIUS

    Cette page a été créée automatiquement à l’installation du serveur web. Vous pouvez modifier cette page et en ajouter d’autres dans le dossier /webserver du serveur.

    https://www.lernsoftware-filius.de

    filius-2.9.4+ds/src/scripts/000077500000000000000000000000001506574241500157125ustar00rootroot00000000000000filius-2.9.4+ds/src/scripts/Filius.command000066400000000000000000000007051506574241500205070ustar00rootroot00000000000000#!/bin/bash # # This script is meant as supportive start script for # UNIX-like systems, e.g., Linux or Mac OS X # echo "Possible parameters:" echo -e "\t-l\t\twrite log file 'filius.log'" echo -e "\t-wd \tchange base directory for working directory '.filius'" echo # change to directory where filius is installed relpath=$0 cd ${relpath%`basename $0`} # start filius java -jar filius.jar $@ # change back to former directory cd - > /dev/null filius-2.9.4+ds/src/scripts/filius.sh000066400000000000000000000005011506574241500175350ustar00rootroot00000000000000#!/bin/bash # # This script is meant as supportive start script for # UNIX-like systems, e.g., Linux or Mac OS X # # change to directory where filius is installed script_path=`readlink -f $0` installation_path="${script_path%`basename "${script_path}"`}" # start filius java -jar "${installation_path}filius.jar" "$@" filius-2.9.4+ds/src/test/000077500000000000000000000000001506574241500152025ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/000077500000000000000000000000001506574241500161235ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/000077500000000000000000000000001506574241500174165ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/gui/000077500000000000000000000000001506574241500202025ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/gui/documentation/000077500000000000000000000000001506574241500230535ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/gui/documentation/ReportGenerator2Test.java000066400000000000000000000030251506574241500277620ustar00rootroot00000000000000package filius.gui.documentation; import static org.mockito.Mockito.when; import java.awt.image.BufferedImage; import java.net.URL; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import com.itextpdf.text.Document; import filius.Main; import filius.gui.GUIContainer; @RunWith(JUnit4.class) public class ReportGenerator2Test { private static final String PDF_OUTPUT = "test2.pdf"; public static final String[] COLUMNS = { "No.", "Time", "Source", "Destination", "Protocol", "Layer", "Comment" }; @InjectMocks private ReportGenerator generator; @Mock private GUIContainer guiContainer; @Before public void initMocks() throws Exception { MockitoAnnotations.openMocks(this).close(); when(guiContainer.createNetworkImage()).thenReturn(new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB)); } private void initScenario(String filiusProject) { URL resource = ReportGenerator2Test.class.getResource(filiusProject); String file = resource.getPath(); Main.starten(file); } @Test public void testGenerateReport_EmptyDocument() throws Exception { initScenario("/empty.fls"); Document document = generator.initDocument(PDF_OUTPUT); generator.addOverviewSection(document); generator.addComponentConfigSection(document); generator.closeDocument(document); } } filius-2.9.4+ds/src/test/java/filius/gui/documentation/ReportGeneratorTest.java000066400000000000000000000055201506574241500277020ustar00rootroot00000000000000package filius.gui.documentation; import static org.mockito.Mockito.when; import java.awt.image.BufferedImage; import java.net.URL; import java.util.Arrays; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import com.itextpdf.text.Document; import filius.Main; import filius.gui.GUIContainer; import filius.rahmenprogramm.nachrichten.Lauscher; @RunWith(JUnit4.class) public class ReportGeneratorTest { private static final String PDF_OUTPUT = "test.pdf"; private static final Object[][] DATA = { { "5", "16:49:09.224", "141.99.5.11", "141.99.5.10", "ARP", "Vermittlung", "Suche nach MAC für 141.99.5.10, 141.99.5.11: 73:E5:39:B7:DC:79" }, { "6", "16:49:09.231", "141.99.5.10", "141.99.5.11", "ARP", "Vermittlung", "141.99.5.10: 0E:FA:11:C2:E0:67" }, { "7", "16:49:09.447", "141.99.5.11:10959", "141.99.5.10:55555", "TCP", "Transport", "SYN, SEQ: 3440300023" } }; public static final String[] COLUMNS = { "No.", "Time", "Source", "Destination", "Protocol", "Layer", "Comment" }; @InjectMocks private ReportGenerator generator; @Mock private Lauscher lauscherMock; @Mock private GUIContainer guiContainer; @Before public void initMocks() throws Exception { MockitoAnnotations.openMocks(this).close(); when(guiContainer.createNetworkImage()).thenReturn(new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB)); } @BeforeClass public static void initScenario() { URL resource = ReportGeneratorTest.class.getResource("/einfaches_rechnernetz_komplett.fls"); String file = resource.getPath(); Main.starten(file); } @Test public void testAddOverviewSection() throws Exception { Document document = generator.initDocument(PDF_OUTPUT); generator.addOverviewSection(document); generator.closeDocument(document); } @Test public void testAddComponentSection() throws Exception { Document document = generator.initDocument(PDF_OUTPUT); generator.addComponentConfigSection(document); generator.closeDocument(document); } @Test public void testAddNetworkTrafficSection() throws Exception { Document document = generator.initDocument(PDF_OUTPUT); String interfaceId = "My Interface"; when(lauscherMock.getHeader()).thenReturn(COLUMNS); when(lauscherMock.getInterfaceIDs()).thenReturn(Arrays.asList(interfaceId)); when(lauscherMock.getDaten(interfaceId, true, 0)).thenReturn(DATA); generator.addNetworkTrafficSection(document); generator.closeDocument(document); } } filius-2.9.4+ds/src/test/java/filius/gui/nachrichtensicht/000077500000000000000000000000001506574241500235235ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/gui/nachrichtensicht/AggregatedMessageTableTest.java000066400000000000000000000103401506574241500315330ustar00rootroot00000000000000package filius.gui.nachrichtensicht; import static org.fest.assertions.Assertions.assertThat; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.Arrays; import java.util.List; import javax.swing.JFrame; import org.junit.Test; public class AggregatedMessageTableTest { private static final String EMPTY_TABLE_EXPORT = "+============+======================+======================+======================+======================+======================+==========================================+\r\n" + "| Nr. | Zeit | Quelle | Ziel | Protokoll | Schicht | Bemerkungen / Details | \r\n" + "+============+======================+======================+======================+======================+======================+==========================================+\r\n"; @Test public void testWriteToStream() throws Exception { PipedOutputStream outputStream = new PipedOutputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(new PipedInputStream(outputStream), "UTF8")); AggregatedExchangePanel exchangePanel = new AggregatedExchangePanel(); new JFrame().getContentPane().add(exchangePanel, BorderLayout.CENTER); AggregatedMessageTable messageTable = new AggregatedMessageTable(exchangePanel, null, null); messageTable.writeToStream(outputStream); outputStream.close(); String output = readString(reader); reader.close(); System.out.println(output); assertThat(output, is(EMPTY_TABLE_EXPORT)); } @Test public void testPrepareDataArrays_TooMuchRows() throws Exception { String[] values = new String[10]; Arrays.fill(values, "anything"); values[AggregatedMessageTable.BEMERKUNG_SPALTE] = "das ist eine sehr lange bemerkung, die umgebrochen werden muss!"; List result = AggregatedMessageTable.prepareDataArrays(values, 12); assertThat(result.get(0)[AggregatedMessageTable.BEMERKUNG_SPALTE]).isEqualTo("das ist eine"); assertThat(result.get(1)[AggregatedMessageTable.BEMERKUNG_SPALTE]).isEqualTo("sehr lange"); assertThat(result.get(2)[AggregatedMessageTable.BEMERKUNG_SPALTE]).isEqualTo("bemerkung,"); assertThat(result.get(3)[AggregatedMessageTable.BEMERKUNG_SPALTE]).isEqualTo("die"); assertThat(result.get(4)[AggregatedMessageTable.BEMERKUNG_SPALTE]).isEqualTo("umgebrochen"); assertThat(result.get(5)[AggregatedMessageTable.BEMERKUNG_SPALTE]).isEqualTo("..."); } private String readString(BufferedReader reader) throws IOException { int nextChar; StringBuilder buffer = new StringBuilder(); while ((nextChar = reader.read()) != -1) { buffer.append((char) nextChar); } String output = buffer.toString(); return output; } @Test public void testSplitString() throws Exception { List lines = AggregatedMessageTable.splitString("das ist mein text", 10); assertThat(lines).containsExactly("das ist", "mein text"); } @Test public void testSplitString_VeryLongText() throws Exception { List lines = AggregatedMessageTable.splitString("dasistmeinseeeeeeehrlangertext", 10); assertThat(lines).containsExactly("dasistmein", "seeeeeeehr", "langertext"); } @Test public void testSplitString_VeryLongText_MidNotFull() throws Exception { List lines = AggregatedMessageTable.splitString("0123456789012345 0123456789012345", 10); assertThat(lines).containsExactly("0123456789", "012345", "0123456789", "012345"); } @Test public void testSplitString_NormalizeWhitespace() throws Exception { List lines = AggregatedMessageTable.splitString(" hallo \t \n welt", 10); assertThat(lines).containsExactly("hallo welt"); } } filius-2.9.4+ds/src/test/java/filius/rahmenprogramm/000077500000000000000000000000001506574241500224355ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/rahmenprogramm/EingabenUeberpruefungTest.java000066400000000000000000000105031506574241500304060ustar00rootroot00000000000000package filius.rahmenprogramm; import static filius.rahmenprogramm.EingabenUeberpruefung.musterDomain; import static filius.rahmenprogramm.EingabenUeberpruefung.musterEmailAdresse; import static filius.rahmenprogramm.EingabenUeberpruefung.musterIpAdresse; import static filius.rahmenprogramm.EingabenUeberpruefung.musterIpAdresseAuchLeer; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; public class EingabenUeberpruefungTest { @Test public void testEmailAdresse_einfach() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("thomas@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_mitPunktUndBindestrich() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("thomas.peter-mueller@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_mitPunktAmAnfang_Ungueltig() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig(".peter-mueller@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_mitPunktVorAt_Ungueltig() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("thomas.@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_mitBindestrichAmAnfang_Ungueltig() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("-mueller@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_mitBindestrichVorAt_Ungueltig() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("thomas-@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_mitEinemZeichen() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("a@mustermann.de", musterEmailAdresse)); } @Test public void testEmailAdresse_Erweitert() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("Thomas ", musterEmailAdresse)); } @Test public void testIpAdresse_AktuellesNetzwerk() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("0.0.0.0", musterIpAdresse)); } @Test public void testIpAdresseAuchLeer_AktuellesNetzwerk() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("0.0.0.0", musterIpAdresseAuchLeer)); } @Test public void testDomain_StartsWithLetter() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("0a2b3c.a4b5c6", musterDomain)); assertFalse(EingabenUeberpruefung.isGueltig("a2b3c.0a4b5c6", musterDomain)); assertFalse(EingabenUeberpruefung.isGueltig("-a2b3c.a4b5c6", musterDomain)); assertFalse(EingabenUeberpruefung.isGueltig("a2b3c.-a4b5c6", musterDomain)); } @Test public void testDomain_WithDigitsAtArbitraryPos() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("a2b3c.a4b5c6", musterDomain)); } @Test public void testDomain_NoHyphenAtEnd() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("abc-.def", musterDomain)); assertFalse(EingabenUeberpruefung.isGueltig("abc.def-", musterDomain)); } @Test public void testDomain_WithHypenAtArbitraryPos() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("a-b-c.d-e-f", musterDomain)); } @Test public void testDomain_DoNotAllowUnderscore() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("a_b.a_b", musterDomain)); } @Test public void testDomain_AllowEmptyLabelAkaRootDomain() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("", musterDomain)); assertTrue(EingabenUeberpruefung.isGueltig(".", musterDomain)); } @Test public void testDomain_AllowUpTo63Characters() throws Exception { assertTrue(EingabenUeberpruefung.isGueltig("a12345678901234567890123456789012345678901234567890123456789012." + "a12345678901234567890123456789012345678901234567890123456789012.", musterDomain)); } @Test public void testDomain_DoNOTAllow64Characters() throws Exception { assertFalse(EingabenUeberpruefung.isGueltig("a123456789012345678901234567890123456789012345678901234567890123." + "a123456789012345678901234567890123456789012345678901234567890123.", musterDomain)); } } filius-2.9.4+ds/src/test/java/filius/rahmenprogramm/FiliusArgsTest.java000066400000000000000000000075341506574241500262210ustar00rootroot00000000000000package filius.rahmenprogramm; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.matchers.JUnitMatchers.containsString; import java.io.File; import java.io.PrintStream; import org.apache.commons.io.output.ByteArrayOutputStream; import org.junit.Test; public class FiliusArgsTest { FiliusArgs filiusArgs = new FiliusArgs(); @Test public void testParseCommandLine_showHelp() throws Exception { filiusArgs.parseCommandLine(new String[] { "-h" }); assertTrue(filiusArgs.help); } @Test public void testParseCommandLine_Default() throws Exception { filiusArgs.parseCommandLine(new String[] {}); assertFalse(filiusArgs.help); assertFalse(filiusArgs.verbose); assertFalse(filiusArgs.log); assertFalse(filiusArgs.nativeLookAndFeel); assertFalse(filiusArgs.lowResolution); } @Test public void testParseCommandLine_verbose() throws Exception { filiusArgs.parseCommandLine(new String[] { "-v" }); assertTrue(filiusArgs.verbose); } @Test public void testParseCommandLine_WorkingDir() throws Exception { filiusArgs.parseCommandLine(new String[] { "-wd", "/any/path" }); assertThat(filiusArgs.currWD, is("/any/path")); } @Test public void testParseCommandLine_WorkingDirWithSpace() throws Exception { filiusArgs.parseCommandLine(new String[] { "-wd", "/any/path with space" }); assertThat(filiusArgs.currWD, is("/any/path with space")); } @Test public void testParseCommandLine_InvalidRTT() throws Exception { filiusArgs.parseCommandLine(new String[] { "-r", "not a number" }); assertThat(filiusArgs.rtt, is(1)); } @Test public void testParseCommandLine_ValidRTT() throws Exception { filiusArgs.parseCommandLine(new String[] { "-r", "2" }); assertThat(filiusArgs.rtt, is(2)); } @Test public void testParseCommandLine_Log() throws Exception { filiusArgs.parseCommandLine(new String[] { "-l" }); assertTrue(filiusArgs.log); } @Test public void testParseCommandLine_NativeLookAndFeel() throws Exception { filiusArgs.parseCommandLine(new String[] { "-n" }); assertTrue(filiusArgs.nativeLookAndFeel); } @Test public void testParseCommandLine_LowRes() throws Exception { filiusArgs.parseCommandLine(new String[] { "-s" }); assertTrue(filiusArgs.lowResolution); } @Test public void testParseCommandLine_ProjectFile() throws Exception { File testFile = new File("test.fls"); testFile.createNewFile(); filiusArgs.parseCommandLine(new String[] { testFile.getPath() }); assertThat(filiusArgs.projectFile, is(testFile.getAbsolutePath())); } @Test public void testParseCommandLine_NotExistentProjectFile() throws Exception { File testFile = new File("this file does not exist.fls"); filiusArgs.parseCommandLine(new String[] { testFile.getPath() }); assertThat(filiusArgs.projectFile, nullValue()); } @Test public void testShowUsage() throws Exception { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); System.setOut(new PrintStream(buffer)); filiusArgs.showUsageInformation(); String output = new String(buffer.toByteArray()); assertThat(output, containsString("-h")); assertThat(output, containsString("-n")); assertThat(output, containsString("-s")); assertThat(output, containsString("-wd")); assertThat(output, containsString("-l")); assertThat(output, containsString("-v")); assertThat(output, containsString("-r")); } } filius-2.9.4+ds/src/test/java/filius/software/000077500000000000000000000000001506574241500212505ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/dhcp/000077500000000000000000000000001506574241500221665ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/dhcp/DHCPClientTest.java000066400000000000000000000276511506574241500255610ustar00rootroot00000000000000package filius.software.dhcp; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import filius.exception.NoValidDhcpResponseException; import filius.exception.TimeOutException; import filius.software.transportschicht.UDPSocket; import filius.software.vermittlungsschicht.ARP; public class DHCPClientTest { @InjectMocks private DHCPClient dhcpClient = new DHCPClient(); @Mock private UDPSocket socketMock; @Mock private ARP arpMock; private long defaultSocketTimeoutMillis = 100; @Before public void initMocks() { MockitoAnnotations.openMocks(this); } @Test public void testDiscover_SendAndReceive_Successfully() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; String offeredIpAddress = "1.2.3.4"; String subnetMask = "255.255.0.0"; String router = "5.6.7.8"; String dnsServer = "9.10.11.12"; String serverIdentifier = "13.14.15.16"; when(socketMock.empfangen(anyLong())).thenReturn(DHCPMessage .createOfferMessage(clientMacAddress, offeredIpAddress, subnetMask, router, dnsServer, serverIdentifier) .toString()); dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); verify(socketMock, times(1)).sendeBroadcast("0.0.0.0", DHCPMessage.createDiscoverMessage(clientMacAddress).toString()); } @Test public void testDiscover_GetOffer() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; String offeredIpAddress = "1.2.3.4"; String subnetMask = "255.255.0.0"; String router = "5.6.7.8"; String dnsServer = "9.10.11.12"; String serverIdentifier = "13.14.15.16"; when(socketMock.empfangen(anyLong())).thenReturn(DHCPMessage .createOfferMessage(clientMacAddress, offeredIpAddress, subnetMask, router, dnsServer, serverIdentifier) .toString()); IpConfig config = dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); verify(socketMock, times(1)).sendeBroadcast("0.0.0.0", DHCPMessage.createDiscoverMessage(clientMacAddress).toString()); assertThat(config.getIpAddress(), is(offeredIpAddress)); assertThat(config.getDnsServer(), is(dnsServer)); assertThat(config.getSubnetMask(), is(subnetMask)); assertThat(config.getRouter(), is(router)); assertThat(config.getDhcpServer(), is(serverIdentifier)); } @Test(expected = TimeOutException.class) public void testDiscover_NoResponse() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; when(socketMock.empfangen(anyLong())).thenReturn(null); dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); } @Test(expected = NoValidDhcpResponseException.class) public void testDiscover_ResponseAfterTimeout() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; when(socketMock.empfangen(anyLong())).thenAnswer(invocation -> { Thread.sleep(defaultSocketTimeoutMillis + 100); return DHCPMessage.createOfferMessage("andere_adresse", "1.2.3.4", "255.255.0.0", "5.6.7.8", "9.10.11.12", "13.14.15.16").toString(); }).thenAnswer(invocation -> DHCPMessage .createOfferMessage(clientMacAddress, "1.2.3.4", "255.255.0.0", "5.6.7.8", "9.10.11.12", "13.14.15.16") .toString()); dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); } @Test(expected = NoValidDhcpResponseException.class) public void testDiscover_NoResponseToRequest() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; String[] messages = new String[100]; for (int i = 0; i < 100; i++) { messages[i] = DHCPMessage.createDiscoverMessage(String.valueOf(i)).toString(); } when(socketMock.empfangen(anyLong())).thenReturn(messages[0], Arrays.copyOfRange(messages, 1, messages.length)); dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); } @Test public void testDiscover_GetOffer_AfterIgnoredMessages() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; String offeredIpAddress = "1.2.3.4"; String subnetMask = "255.255.0.0"; String router = "5.6.7.8"; String dnsServer = "9.10.11.12"; String serverIdentifier = "13.14.15.16"; String[] messages = new String[11]; for (int i = 0; i < 10; i++) { messages[i] = DHCPMessage.createDiscoverMessage(String.valueOf(i)).toString(); } messages[10] = DHCPMessage .createOfferMessage(clientMacAddress, offeredIpAddress, subnetMask, router, dnsServer, serverIdentifier) .toString(); when(socketMock.empfangen(anyLong())).thenReturn(messages[0], Arrays.copyOfRange(messages, 1, messages.length)); IpConfig config = dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); verify(socketMock, times(1)).sendeBroadcast("0.0.0.0", DHCPMessage.createDiscoverMessage(clientMacAddress).toString()); assertThat(config.getIpAddress(), is(offeredIpAddress)); assertThat(config.getDnsServer(), is(dnsServer)); assertThat(config.getSubnetMask(), is(subnetMask)); assertThat(config.getRouter(), is(router)); assertThat(config.getDhcpServer(), is(serverIdentifier)); } @Test(expected = TimeOutException.class) public void testDiscover_IgnoreOffersForOtherClients() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; String offeredIpAddress = "1.2.3.4"; String subnetMask = "255.255.0.0"; String router = "5.6.7.8"; String dnsServer = "9.10.11.12"; String serverIdentifier = "13.14.15.16"; String[] messages = new String[11]; for (int i = 0; i < 10; i++) { messages[i] = DHCPMessage.createOfferMessage("aa:aa:aa:aa:aa:aa", offeredIpAddress, subnetMask, router, dnsServer, serverIdentifier).toString(); } when(socketMock.empfangen(anyLong())).thenReturn(messages[0], Arrays.copyOfRange(messages, 1, messages.length)); dhcpClient.discover(socketMock, clientMacAddress, defaultSocketTimeoutMillis); } @Test public void testValidateOfferedAddress_valid() throws Exception { String offeredAddress = "192.168.100.100"; when(arpMock.holeARPTabellenEintrag(offeredAddress, 1, false)).thenReturn(null); boolean valid = dhcpClient.validateOfferedAddress(arpMock, offeredAddress); verify(arpMock, times(1)).holeARPTabellenEintrag(offeredAddress, 1, false); assertTrue(valid); } @Test public void testValidateOfferedAddress_NotValid() throws Exception { String offeredAddress = "192.168.100.100"; when(arpMock.holeARPTabellenEintrag(offeredAddress, 1, false)).thenReturn("aa:bb:cc:dd:ee:ff"); boolean valid = dhcpClient.validateOfferedAddress(arpMock, offeredAddress); verify(arpMock, times(1)).holeARPTabellenEintrag(offeredAddress, 1, false); assertFalse(valid); } @Test public void testRequest_Acknowledged() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; IpConfig config = new IpConfig("1.2.3.4", "5.6.7.8", "255.255.255.128", "9.10.11.12", "13.14.15.16"); when(socketMock.empfangen(anyLong())).thenReturn(DHCPMessage .createAckMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); boolean acknowledged = dhcpClient.request(socketMock, clientMacAddress, config, defaultSocketTimeoutMillis); verify(socketMock, times(1)).sendeBroadcast("0.0.0.0", DHCPMessage .createRequestMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); verify(socketMock, times(1)).empfangen(anyLong()); assertTrue(acknowledged); } @Test public void testRequest_NOTAcknowledged() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; IpConfig config = new IpConfig("1.2.3.4", "5.6.7.8", "255.255.255.128", "9.10.11.12", "13.14.15.16"); when(socketMock.empfangen(anyLong())).thenReturn(DHCPMessage .createNackMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); boolean acknowledged = dhcpClient.request(socketMock, clientMacAddress, config, defaultSocketTimeoutMillis); verify(socketMock, times(1)).sendeBroadcast("0.0.0.0", DHCPMessage .createRequestMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); verify(socketMock, times(1)).empfangen(anyLong()); assertFalse(acknowledged); } @Test(expected = TimeOutException.class) public void testRequest_IgnoreAcksForOtherClients() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; IpConfig config = new IpConfig("1.2.3.4", "5.6.7.8", "255.255.255.128", "9.10.11.12", "13.14.15.16"); String[] messages = new String[11]; for (int i = 0; i < 10; i++) { messages[i] = DHCPMessage .createAckMessage("aa:aa:aa:aa:aa:aa", config.getIpAddress(), config.getDhcpServer()).toString(); } when(socketMock.empfangen(anyLong())).thenReturn(messages[0], Arrays.copyOfRange(messages, 1, messages.length)); dhcpClient.request(socketMock, clientMacAddress, config, defaultSocketTimeoutMillis); } @Test(expected = TimeOutException.class) public void testRequest_Timeout() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; IpConfig config = new IpConfig("1.2.3.4", "5.6.7.8", "255.255.255.128", "9.10.11.12", "13.14.15.16"); when(socketMock.empfangen(anyLong())).thenReturn(null); dhcpClient.request(socketMock, clientMacAddress, config, defaultSocketTimeoutMillis); } @Test(expected = NoValidDhcpResponseException.class) public void testRequest_ResponseAfterTimeout() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; IpConfig config = new IpConfig("1.2.3.4", "5.6.7.8", "255.255.255.128", "9.10.11.12", "13.14.15.16"); when(socketMock.empfangen(anyLong())).thenAnswer(invocation -> { Thread.sleep(defaultSocketTimeoutMillis + 100); return DHCPMessage.createAckMessage("andere_adresse", config.getIpAddress(), config.getDhcpServer()) .toString(); }).thenAnswer(invocation -> DHCPMessage .createAckMessage(clientMacAddress, config.getIpAddress(), config.getDhcpServer()).toString()); dhcpClient.request(socketMock, clientMacAddress, config, defaultSocketTimeoutMillis); } @Test(expected = NoValidDhcpResponseException.class) public void testRequest_ResponseFromWrongServer() throws Exception { String clientMacAddress = "01:23:45:67:89:AB"; IpConfig config = new IpConfig("1.2.3.4", "5.6.7.8", "255.255.255.128", "9.10.11.12", "13.14.15.16"); when(socketMock.empfangen(anyLong())).thenAnswer(invocation -> { Thread.sleep(defaultSocketTimeoutMillis + 100); return DHCPMessage.createAckMessage(clientMacAddress, config.getIpAddress(), "7.7.7.7").toString(); }).thenAnswer(invocation -> null); dhcpClient.request(socketMock, clientMacAddress, config, defaultSocketTimeoutMillis); } } filius-2.9.4+ds/src/test/java/filius/software/dhcp/DHCPMessageTest.java000066400000000000000000000164631506574241500257260ustar00rootroot00000000000000package filius.software.dhcp; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class DHCPMessageTest { @Test public void testTypeEnum_valueOf() throws Exception { assertThat(DHCPMessageType.fromString("DHCPACK"), is(DHCPMessageType.ACK)); assertThat(DHCPMessageType.fromString("DHCPDECLINE"), is(DHCPMessageType.DECLINE)); assertThat(DHCPMessageType.fromString("DHCPDISCOVER"), is(DHCPMessageType.DISCOVER)); assertThat(DHCPMessageType.fromString("DHCPNAK"), is(DHCPMessageType.NACK)); assertThat(DHCPMessageType.fromString("DHCPOFFER"), is(DHCPMessageType.OFFER)); assertThat(DHCPMessageType.fromString("DHCPREQUEST"), is(DHCPMessageType.REQUEST)); } @Test public void testToString_WithoutInitialization() throws Exception { assertThat(new DHCPMessage().toString(), is("DHCPDISCOVER\nyiaddr=0.0.0.0")); } @Test public void testToString_WithInitialization() throws Exception { DHCPMessage dhcpMessage = prepareTestMessage(DHCPMessageType.OFFER, "192.168.1.111", "abcdefgh", "192.168.1.5", "192.168.1.1", "255.255.255.0", "1.2.3.4", "5.6.7.8"); assertThat(dhcpMessage.toString(), is("DHCPOFFER" + "\nyiaddr=192.168.1.111\nchaddr=abcdefgh\nrouter=192.168.1.1" + "\nsubnetmask=255.255.255.0\ndnsserver=192.168.1.5" + "\nrequested=1.2.3.4\nserverident=5.6.7.8")); } private DHCPMessage prepareTestMessage(DHCPMessageType type, String yiaddr, String chaddr, String dnsServer, String router, String subnetMask, String requestedAddress, String serverIdent) { DHCPMessage dhcpMessage = new DHCPMessage(); dhcpMessage.type = type; dhcpMessage.yiaddr = yiaddr; dhcpMessage.chaddr = chaddr; dhcpMessage.optionDnsServer = dnsServer; dhcpMessage.optionRouter = router; dhcpMessage.optionSubnetMask = subnetMask; dhcpMessage.optionRequestedAddress = requestedAddress; dhcpMessage.optionServerIdentifier = serverIdent; return dhcpMessage; } @Test public void testFromString() throws Exception { String dhcpMessageString = "DHCPOFFER" + "\nyiaddr=192.168.1.111\nchaddr=abcdefgh\nrouter=192.168.1.1\n" + "subnetmask=255.255.255.0\ndnsserver=192.168.1.5" + "\nrequested=1.2.3.4\nserverident=5.6.7.8"; DHCPMessage dhcpMessage = DHCPMessage.fromString(dhcpMessageString); assertThat(dhcpMessage.type, is(DHCPMessageType.OFFER)); assertThat(dhcpMessage.yiaddr, is("192.168.1.111")); assertThat(dhcpMessage.chaddr, is("abcdefgh")); assertThat(dhcpMessage.optionDnsServer, is("192.168.1.5")); assertThat(dhcpMessage.optionRouter, is("192.168.1.1")); assertThat(dhcpMessage.optionSubnetMask, is("255.255.255.0")); assertThat(dhcpMessage.optionRequestedAddress, is("1.2.3.4")); assertThat(dhcpMessage.optionServerIdentifier, is("5.6.7.8")); } @Test public void testToStringFromString() throws Exception { DHCPMessage dhcpMessage = prepareTestMessage(DHCPMessageType.OFFER, "192.168.1.111", "abcdefgh", "192.168.1.5", "192.168.1.1", "255.255.255.0", "1.2.3.4", "5.6.7.8"); DHCPMessage result = DHCPMessage.fromString(dhcpMessage.toString()); assertThat(result, is(dhcpMessage)); } @Test public void testCreateDiscoverMessage() throws Exception { String clientMacAddress = "01:23:45:AB:CD:EF"; DHCPMessage message = DHCPMessage.createDiscoverMessage(clientMacAddress); assertThat(message.getType(), is(DHCPMessageType.DISCOVER)); assertThat(message.getChaddr(), is(clientMacAddress)); assertThat(message.getYiaddr(), is("0.0.0.0")); } @Test public void testCreateOfferMessage() throws Exception { String clientMacAddress = "01:23:45:AB:CD:EF"; String offeredIpAddress = "1.2.3.4"; String subnetMask = "255.255.0.0"; String router = "5.6.7.8"; String dnsServer = "9.10.11.12"; String serverIdentifier = "14.15.16.17"; DHCPMessage message = DHCPMessage.createOfferMessage(clientMacAddress, offeredIpAddress, subnetMask, router, dnsServer, serverIdentifier); assertThat(message.getType(), is(DHCPMessageType.OFFER)); assertThat(message.getChaddr(), is(clientMacAddress)); assertThat(message.getYiaddr(), is(offeredIpAddress)); assertThat(message.getSubnetMask(), is(subnetMask)); assertThat(message.getRouter(), is(router)); assertThat(message.getDnsServer(), is(dnsServer)); assertThat(message.getServerIdentifier(), is(serverIdentifier)); } @Test public void testCreateRequestMessage() throws Exception { String clientMacAddress = "01:23:45:AB:CD:EF"; String requestedIpAddress = "1.2.3.4"; String serverIdentifier = "14.15.16.17"; DHCPMessage message = DHCPMessage.createRequestMessage(clientMacAddress, requestedIpAddress, serverIdentifier); assertThat(message.getType(), is(DHCPMessageType.REQUEST)); assertThat(message.getChaddr(), is(clientMacAddress)); assertThat(message.getYiaddr(), is("0.0.0.0")); assertThat(message.getServerIdentifier(), is(serverIdentifier)); assertThat(message.getRequestedAddress(), is(requestedIpAddress)); } @Test public void testCreateDeclineMessage() throws Exception { String clientMacAddress = "01:23:45:AB:CD:EF"; String requestedIpAddress = "1.2.3.4"; String serverIdentifier = "14.15.16.17"; DHCPMessage message = DHCPMessage.createDeclineMessage(clientMacAddress, requestedIpAddress, serverIdentifier); assertThat(message.getType(), is(DHCPMessageType.DECLINE)); assertThat(message.getChaddr(), is(clientMacAddress)); assertThat(message.getYiaddr(), is("0.0.0.0")); assertThat(message.getServerIdentifier(), is(serverIdentifier)); assertThat(message.getRequestedAddress(), is(requestedIpAddress)); } @Test public void testCreateAckMessage() throws Exception { String clientMacAddress = "01:23:45:AB:CD:EF"; String acknowledgedIpAddress = "1.2.3.4"; String serverIdentifier = "14.15.16.17"; DHCPMessage message = DHCPMessage.createAckMessage(clientMacAddress, acknowledgedIpAddress, serverIdentifier); assertThat(message.getType(), is(DHCPMessageType.ACK)); assertThat(message.getChaddr(), is(clientMacAddress)); assertThat(message.getYiaddr(), is(acknowledgedIpAddress)); assertThat(message.getServerIdentifier(), is(serverIdentifier)); } @Test public void testCreateNackMessage() throws Exception { String clientMacAddress = "01:23:45:AB:CD:EF"; String notAcknowledgedIpAddress = "1.2.3.4"; String serverIdentifier = "14.15.16.17"; DHCPMessage message = DHCPMessage.createNackMessage(clientMacAddress, notAcknowledgedIpAddress, serverIdentifier); assertThat(message.getType(), is(DHCPMessageType.NACK)); assertThat(message.getChaddr(), is(clientMacAddress)); assertThat(message.getYiaddr(), is(notAcknowledgedIpAddress)); assertThat(message.getServerIdentifier(), is(serverIdentifier)); } } filius-2.9.4+ds/src/test/java/filius/software/dhcp/DHCPServerMitarbeiterTest.java000066400000000000000000000132461506574241500277740ustar00rootroot00000000000000package filius.software.dhcp; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import filius.exception.AddressRequestNotAcceptedException; import filius.exception.NoAvailableAddressException; import filius.software.transportschicht.UDPSocket; public class DHCPServerMitarbeiterTest { @Mock private DHCPServer serverMock; @Mock private UDPSocket socketMock; @Before public void init() { MockitoAnnotations.openMocks(this); } @Test public void testProcessDiscover() throws Exception { DHCPServerMitarbeiter mitarbeiter = new DHCPServerMitarbeiter(serverMock, socketMock); String mac = "01:02:03:04:05:06"; String ip = "10.0.0.101"; String serverIdentifier = "10.0.0.1"; String dnsServer = "10.0.0.22"; String router = "10.0.0.33"; String subnetMask = "255.255.0.0"; when(serverMock.offerAddress(mac)).thenReturn(ip); when(serverMock.holeServerIpAddress()).thenReturn(serverIdentifier); when(serverMock.determineDnsserverip()).thenReturn(dnsServer); when(serverMock.determineGatewayip()).thenReturn(router); when(serverMock.getSubnetzmaske()).thenReturn(subnetMask); mitarbeiter.processDiscover(mac); DHCPMessage expectedResponse = DHCPMessage.createOfferMessage(mac, ip, subnetMask, router, dnsServer, serverIdentifier); verify(socketMock, times(1)).sendeBroadcast(serverIdentifier, expectedResponse.toString()); } @Test public void testProcessDiscover_NoOfferPossible() throws Exception { DHCPServerMitarbeiter mitarbeiter = new DHCPServerMitarbeiter(serverMock, socketMock); String mac = "01:02:03:04:05:06"; String serverIdentifier = "10.0.0.1"; String dnsServer = "10.0.0.22"; String router = "10.0.0.33"; String subnetMask = "255.255.0.0"; when(serverMock.offerAddress(mac)).thenThrow(new NoAvailableAddressException()); when(serverMock.holeServerIpAddress()).thenReturn(serverIdentifier); when(serverMock.determineDnsserverip()).thenReturn(dnsServer); when(serverMock.determineGatewayip()).thenReturn(router); when(serverMock.getSubnetzmaske()).thenReturn(subnetMask); mitarbeiter.processDiscover(mac); verify(socketMock, never()).sendeBroadcast(anyString(), anyString()); } @Test public void testProcessRequest_Ack() throws Exception { DHCPServerMitarbeiter mitarbeiter = new DHCPServerMitarbeiter(serverMock, socketMock); String mac = "01:02:03:04:05:06"; String ip = "10.0.0.101"; String serverIdentifier = "10.0.0.1"; String dnsServer = "10.0.0.22"; String router = "10.0.0.33"; String subnetMask = "255.255.0.0"; when(serverMock.holeServerIpAddress()).thenReturn(serverIdentifier); when(serverMock.determineDnsserverip()).thenReturn(dnsServer); when(serverMock.determineGatewayip()).thenReturn(router); when(serverMock.getSubnetzmaske()).thenReturn(subnetMask); when(serverMock.requestAddress(mac, ip)) .thenReturn(new DHCPAddressAssignment(mac, ip, System.currentTimeMillis() + 1000)); mitarbeiter.processRequest(mac, ip, serverIdentifier); DHCPMessage expectedResponse = DHCPMessage.createAckMessage(mac, ip, serverIdentifier); verify(socketMock, times(1)).sendeBroadcast(serverIdentifier, expectedResponse.toString()); } @Test public void testProcessRequest_Nack() throws Exception { DHCPServerMitarbeiter mitarbeiter = new DHCPServerMitarbeiter(serverMock, socketMock); String mac = "01:02:03:04:05:06"; String ip = "10.0.0.101"; String serverIdentifier = "10.0.0.1"; String dnsServer = "10.0.0.22"; String router = "10.0.0.33"; String subnetMask = "255.255.0.0"; when(serverMock.holeServerIpAddress()).thenReturn(serverIdentifier); when(serverMock.determineDnsserverip()).thenReturn(dnsServer); when(serverMock.determineGatewayip()).thenReturn(router); when(serverMock.getSubnetzmaske()).thenReturn(subnetMask); when(serverMock.requestAddress(mac, ip)).thenThrow(new AddressRequestNotAcceptedException()); mitarbeiter.processRequest(mac, ip, serverIdentifier); DHCPMessage expectedResponse = DHCPMessage.createNackMessage(mac, ip, serverIdentifier); verify(socketMock, times(1)).sendeBroadcast(serverIdentifier, expectedResponse.toString()); } @Test public void testProcessRequest_WrongServerIdentifier_Ignore() throws Exception { DHCPServerMitarbeiter mitarbeiter = new DHCPServerMitarbeiter(serverMock, socketMock); String mac = "01:02:03:04:05:06"; String ip = "10.0.0.101"; String serverIdentifier = "10.0.0.1"; String dnsServer = "10.0.0.22"; String router = "10.0.0.33"; String subnetMask = "255.255.0.0"; when(serverMock.holeServerIpAddress()).thenReturn(serverIdentifier); when(serverMock.determineDnsserverip()).thenReturn(dnsServer); when(serverMock.determineGatewayip()).thenReturn(router); when(serverMock.getSubnetzmaske()).thenReturn(subnetMask); when(serverMock.requestAddress(mac, ip)).thenThrow(new AddressRequestNotAcceptedException()); mitarbeiter.processRequest(mac, ip, "10.0.0.42"); verify(socketMock, never()).sendeBroadcast(anyString(), anyString()); } } filius-2.9.4+ds/src/test/java/filius/software/dhcp/DHCPServerTest.java000066400000000000000000000260631506574241500256050ustar00rootroot00000000000000package filius.software.dhcp; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import org.junit.Test; import filius.exception.AddressRequestNotAcceptedException; import filius.exception.NoAvailableAddressException; public class DHCPServerTest { @Test public void testInRange() throws Exception { DHCPServer server = new DHCPServer(); server.setUntergrenze("1.1.1.1"); server.setObergrenze("1.1.1.1"); assertTrue(server.inRange("1.1.1.1")); } @Test public void testInRange_ComplexRange() throws Exception { DHCPServer server = new DHCPServer(); server.setUntergrenze("1.10.100.200"); server.setObergrenze("200.100.10.1"); assertTrue(server.inRange("50.5.20.7")); } @Test public void testInRange_OverUpperLimit() throws Exception { DHCPServer server = new DHCPServer(); server.setUntergrenze("1.1.1.1"); server.setObergrenze("1.1.1.2"); assertFalse(server.inRange("1.1.1.3")); } @Test public void testInRange_BelowLowerLimit() throws Exception { DHCPServer server = new DHCPServer(); server.setUntergrenze("1.1.1.10"); server.setObergrenze("1.1.1.12"); assertFalse(server.inRange("1.1.1.9")); } @Test public void testInRange_BelowLowerLimitInFirstPart() throws Exception { DHCPServer server = new DHCPServer(); server.setUntergrenze("2.1.1.10"); server.setObergrenze("3.1.1.12"); assertFalse(server.inRange("1.1.1.11")); } @Test public void testIpToIntArray() throws Exception { int[] ipAsIntArray = DHCPServer.ipToIntArray("1.2.3.4"); assertThat(ipAsIntArray.length, is(4)); assertThat(ipAsIntArray[0], is(1)); assertThat(ipAsIntArray[1], is(2)); assertThat(ipAsIntArray[2], is(3)); assertThat(ipAsIntArray[3], is(4)); } @Test(expected = NumberFormatException.class) public void testIpToIntArray_InvalidNumberInAddress() throws Exception { DHCPServer.ipToIntArray("1.2.3.x"); } @Test(expected = NumberFormatException.class) public void testIpToIntArray_NotAnIpAddress() throws Exception { DHCPServer.ipToIntArray("hallo.welt"); } @Test(expected = NumberFormatException.class) public void testIpToIntArray_TooManyParts() throws Exception { DHCPServer.ipToIntArray("1.2.3.4.5"); } @Test public void testIpToLong() throws Exception { assertThat(DHCPServer.ipToLong("1.2.3.4"), is(0x1020304l)); } @Test public void testLongToIp() throws Exception { assertThat(DHCPServer.longToIp(0x01_02_03_04l), is("1.2.3.4")); } @Test public void testCleanUpAssignments_doNotRemoveNeverExpiringEntries() throws Exception { DHCPServer server = new DHCPServer(); server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("dyn-mac", "dyn-ip", 0)); server.staticAssignedAddresses.add(new DHCPAddressAssignment("stat-mac", "stat-ip", 0)); server.offeredAddresses.add(new DHCPAddressAssignment("offered-mac", "offered-ip", 0)); server.cleanUpAssignments(); assertThat(server.dynamicAssignedAddresses.size(), is(1)); assertThat(server.staticAssignedAddresses.size(), is(1)); assertThat(server.offeredAddresses.size(), is(1)); } @Test public void testCleanUpAssignments_RemoveExpiredEntries() throws Exception { DHCPServer server = new DHCPServer(); long timestamp = System.currentTimeMillis() - 100; server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("dyn-mac", "dyn-ip", timestamp)); server.staticAssignedAddresses.add(new DHCPAddressAssignment("stat-mac", "stat-ip", timestamp)); server.offeredAddresses.add(new DHCPAddressAssignment("offered-mac", "offered-ip", timestamp)); server.cleanUpAssignments(); assertThat(server.dynamicAssignedAddresses.size(), is(0)); assertThat(server.staticAssignedAddresses.size(), is(0)); assertThat(server.offeredAddresses.size(), is(0)); } @Test public void testCleanUpAssignments_DoNotRemoveStillValidEntries() throws Exception { DHCPServer server = new DHCPServer(); long timestamp = System.currentTimeMillis() + 1000; server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("dyn-mac", "dyn-ip", timestamp)); server.staticAssignedAddresses.add(new DHCPAddressAssignment("stat-mac", "stat-ip", timestamp)); server.offeredAddresses.add(new DHCPAddressAssignment("offered-mac", "offered-ip", timestamp)); server.cleanUpAssignments(); assertThat(server.dynamicAssignedAddresses.size(), is(1)); assertThat(server.staticAssignedAddresses.size(), is(1)); assertThat(server.offeredAddresses.size(), is(1)); } @Test public void testNextAddress_Sequence() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.101"); String nextAddress1 = server.nextAddress(); String nextAddress2 = server.nextAddress(); assertThat(nextAddress1, is("10.0.0.100")); assertThat(nextAddress2, is("10.0.0.101")); } @Test public void testNextAddress_RepeatInterval() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.101"); server.nextAddress(); server.nextAddress(); String nextAddress = server.nextAddress(); assertThat(nextAddress, is("10.0.0.100")); } @Test public void testAddressAvailable_NoAssignedAddresses() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.101"); boolean available = server.checkAddressAvailable("10.0.0.100"); assertTrue(available); } @Test public void testAddressAvailable_AlreadyDynamicallyAssigned() throws Exception { String ipAddress = "10.0.0.100"; DHCPServer server = prepareDhcpServer(ipAddress, "10.0.0.101"); server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("mac", ipAddress, 0)); boolean available = server.checkAddressAvailable(ipAddress); assertFalse(available); } @Test public void testAddressAvailable_AlreadyStaticallyAssigned() throws Exception { String ipAddress = "10.0.0.100"; DHCPServer server = prepareDhcpServer(ipAddress, "10.0.0.101"); server.staticAssignedAddresses.add(new DHCPAddressAssignment("mac", ipAddress, 0)); boolean available = server.checkAddressAvailable(ipAddress); assertFalse(available); } @Test public void testAddressAvailable_AlreadyOffered() throws Exception { String ipAddress = "10.0.0.100"; DHCPServer server = prepareDhcpServer(ipAddress, "10.0.0.101"); server.offeredAddresses.add(new DHCPAddressAssignment("mac", ipAddress, 0)); boolean available = server.checkAddressAvailable(ipAddress); assertFalse(available); } @Test public void testAddressAvailable_NoApplicableAssignment() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.104"); server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.100", 0)); server.staticAssignedAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.101", 0)); server.offeredAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.102", 0)); boolean available = server.checkAddressAvailable("10.0.0.103"); assertTrue(available); } @Test public void testOfferAddress_SkipAssigned() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.104"); server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.100", 0)); server.staticAssignedAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.101", 0)); server.offeredAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.102", 0)); String offeredAddress = server.offerAddress("01:02:03:04:05:06"); assertThat(offeredAddress, is("10.0.0.103")); } @Test(expected = NoAvailableAddressException.class) public void testOfferAddress_NoAddressAvailable() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.102"); server.dynamicAssignedAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.100", 0)); server.staticAssignedAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.101", 0)); server.offeredAddresses.add(new DHCPAddressAssignment("mac", "10.0.0.102", 0)); server.offerAddress("01:02:03:04:05:06"); } @Test public void testOfferAddress_AddressMayBeOfferedJustOnce() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.104"); String offeredAddress = server.offerAddress("01:02:03:04:05:06"); assertThat(offeredAddress, is("10.0.0.100")); assertFalse(server.checkAddressAvailable("10.0.0.100")); } @Test public void testRequestAddress_AddressOfferedToClient() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.104"); String mac = "01:02:03:04:05:06"; String offeredAddress = server.offerAddress(mac); DHCPAddressAssignment assignment = server.requestAddress(mac, offeredAddress); verifyAssignment(server, mac, offeredAddress, assignment); } private void verifyAssignment(DHCPServer server, String mac, String ip, DHCPAddressAssignment result) { assertThat(result.getIp(), is(ip)); assertThat(result.getMAC(), is(mac)); for (DHCPAddressAssignment entry : server.offeredAddresses) { assertFalse(entry.getMAC().equalsIgnoreCase(mac)); } boolean assigned = false; for (DHCPAddressAssignment entry : server.dynamicAssignedAddresses) { if (entry.getMAC().equalsIgnoreCase(mac) && entry.getIp().equalsIgnoreCase(ip)) { assigned = true; break; } } assertTrue(assigned); } @Test(expected = AddressRequestNotAcceptedException.class) public void testRequestAddress_AddressOfferedToOTHERClient() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.104"); String offeredAddress = server.offerAddress("aa:bb:cc:dd:ee:ff"); server.requestAddress("01:02:03:04:05:06", offeredAddress); } @Test public void testRequestAddress_AddressOfferedToNoOneButStillAvailable() throws Exception { DHCPServer server = prepareDhcpServer("10.0.0.100", "10.0.0.104"); String ip = "10.0.0.101"; String mac = "01:02:03:04:05:06"; DHCPAddressAssignment assignment = server.requestAddress(mac, ip); verifyAssignment(server, mac, ip, assignment); } private DHCPServer prepareDhcpServer(String lowerLimit, String upperLimit) { DHCPServer server = new DHCPServer(); server.setUntergrenze(lowerLimit); server.setObergrenze(upperLimit); return server; } } filius-2.9.4+ds/src/test/java/filius/software/dns/000077500000000000000000000000001506574241500220345ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/dns/ResourceRecordTest.java000066400000000000000000000022321506574241500264640ustar00rootroot00000000000000package filius.software.dns; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class ResourceRecordTest { @Test public void testSetDomainname_validName_appendDotForRoot() throws Exception { ResourceRecord rr = new ResourceRecord(""); rr.setDomainname("valid-domain"); assertThat(rr.getDomainname(), is("valid-domain.")); } @Test public void testSetDomainname_INVALIDName_IgnoreSetter() throws Exception { ResourceRecord rr = new ResourceRecord(""); rr.setDomainname("@xyz"); assertThat(rr.getDomainname(), is("")); } @Test public void testSetDomainname_StartWithDigit_IgnoreSetter() throws Exception { ResourceRecord rr = new ResourceRecord(""); rr.setDomainname("123domain"); assertThat(rr.getDomainname(), is("")); } @Test public void testSetDomainname_StartSubdomainWithDigit_IgnoreSetter() throws Exception { ResourceRecord rr = new ResourceRecord(""); rr.setDomainname("valid-domain.123domain.example"); assertThat(rr.getDomainname(), is("")); } } filius-2.9.4+ds/src/test/java/filius/software/email/000077500000000000000000000000001506574241500223375ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/email/AddressEntryTest.java000066400000000000000000000044341506574241500264560ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import static org.fest.assertions.Assertions.assertThat; import org.junit.Test; /** * @author stefan * */ public class AddressEntryTest { @Test public void onlyName() throws Exception { AddressEntry entry = new AddressEntry(" name "); assertThat(entry.getName()).isEqualTo("name"); assertThat(entry.getMailAddress()).isNull(); assertThat(entry.toString()).isEqualTo("name"); } @Test public void onlyAddress() throws Exception { AddressEntry entry = new AddressEntry("
    "); assertThat(entry.getMailAddress()).isEqualTo("address"); assertThat(entry.getName()).isNull(); assertThat(entry.toString()).isEqualTo("
    "); } @Test public void nameAndAddress() throws Exception { AddressEntry entry = new AddressEntry(" name < address > "); assertThat(entry.getMailAddress()).isEqualTo("address"); assertThat(entry.getName()).isEqualTo("name"); assertThat(entry.toString()).isEqualTo("name
    "); } } filius-2.9.4+ds/src/test/java/filius/software/email/EmailTest.java000066400000000000000000000070031506574241500250710ustar00rootroot00000000000000/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl ** and Stefan Freischlad ** Filius 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 of the License, or ** (at your option) version 3. ** ** Filius 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 Filius. If not, see . */ package filius.software.email; import static org.fest.assertions.Assertions.assertThat; import org.junit.Test; /** * @author stefan * */ public class EmailTest { @Test public void subject_trim() throws Exception { String subject = "test subject"; Email mail = new Email("subject: " + subject); assertThat(mail.getBetreff()).isEqualTo(subject); mail = new Email("subject: " + subject + " "); assertThat(mail.getBetreff()).isEqualTo(subject); } @Test public void subject_caseOfHeaderSpecifier() throws Exception { String subject = "test subject"; Email mail = new Email("subject:" + subject); assertThat(mail.getBetreff()).isEqualTo(subject); mail = new Email("SuBjEct:" + subject); assertThat(mail.getBetreff()).isEqualTo(subject); } @Test public void sender_trim() throws Exception { String sender = "sender"; Email mail = new Email("from: " + sender); assertThat(mail.getAbsender().toString()).isEqualTo(sender); mail = new Email("from: " + sender + " "); assertThat(mail.getAbsender().toString()).isEqualTo(sender); } @Test public void sender_nameAndAddress() throws Exception { String sender = "sender"; String senderAddress = "< senderAddress >"; Email mail = new Email("from: " + sender + " " + senderAddress); assertThat(mail.getAbsender().toString()).isEqualTo("sender "); mail = new Email("from: " + sender); assertThat(mail.getAbsender().toString()).isEqualTo("sender"); mail = new Email("from: " + senderAddress); assertThat(mail.getAbsender().toString()).isEqualTo(""); } @Test public void recipient_one_nameAndAddress() throws Exception { String rcpt = "rcpt"; String rcptAddress = "< rcptAddress >"; Email mail = new Email("to: " + rcpt + " " + rcptAddress); assertThat(mail.getEmpfaenger().get(0).toString()).isEqualTo("rcpt "); mail = new Email("to: " + rcpt); assertThat(mail.getEmpfaenger().get(0).toString()).isEqualTo("rcpt"); mail = new Email("to: " + rcptAddress); assertThat(mail.getEmpfaenger().get(0).toString()).isEqualTo(""); } } filius-2.9.4+ds/src/test/java/filius/software/firewall/000077500000000000000000000000001506574241500230555ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/firewall/FirewallRuleTest.java000066400000000000000000000065771506574241500271740ustar00rootroot00000000000000package filius.software.firewall; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; public class FirewallRuleTest { @Test public void testValidateExistingAttributes_EmptyRule() throws Exception { FirewallRule rule = new FirewallRule(); assertTrue(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_ValidSource() throws Exception { FirewallRule rule = new FirewallRule(); rule.srcIP = "192.168.2.2"; rule.srcMask = "255.255.0.0"; assertTrue(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDSourceAddress() throws Exception { FirewallRule rule = new FirewallRule(); rule.srcIP = "192.168.2.256"; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_SourceSameNetwork() throws Exception { FirewallRule rule = new FirewallRule(); rule.srcIP = FirewallRule.SAME_NETWORK; assertTrue(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDSourceMask() throws Exception { FirewallRule rule = new FirewallRule(); rule.srcMask = "192.168.2.256"; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_ValidDest() throws Exception { FirewallRule rule = new FirewallRule(); rule.destIP = "192.168.2.2"; rule.destMask = "255.255.0.0"; assertTrue(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDDestAddress() throws Exception { FirewallRule rule = new FirewallRule(); rule.destIP = "192.168.2.256"; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_DestSameNetwork_Reject() throws Exception { FirewallRule rule = new FirewallRule(); rule.destIP = FirewallRule.SAME_NETWORK; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDDestMask() throws Exception { FirewallRule rule = new FirewallRule(); rule.destMask = "192.168.2.256"; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_ValidPortProtocolAction() throws Exception { FirewallRule rule = new FirewallRule(); rule.port = 20; rule.protocol = FirewallRule.TCP; rule.action = FirewallRule.DROP; assertTrue(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDPort() throws Exception { FirewallRule rule = new FirewallRule(); rule.port = 999999; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDProtocol() throws Exception { FirewallRule rule = new FirewallRule(); rule.protocol = 999; assertFalse(rule.validateExistingAttributes()); } @Test public void testValidateExistingAttributes_INVALIDAction() throws Exception { FirewallRule rule = new FirewallRule(); rule.action = 999; assertFalse(rule.validateExistingAttributes()); } } filius-2.9.4+ds/src/test/java/filius/software/firewall/FirewallTest.java000066400000000000000000000242241506574241500263310ustar00rootroot00000000000000package filius.software.firewall; import static filius.software.firewall.FirewallRule.ACCEPT; import static filius.software.firewall.FirewallRule.DROP; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; import filius.hardware.knoten.Rechner; import filius.software.system.Betriebssystem; import filius.software.transportschicht.TcpSegment; import filius.software.transportschicht.UdpSegment; import filius.software.vermittlungsschicht.IcmpPaket; import filius.software.vermittlungsschicht.IpPaket; public class FirewallTest { private static final String DEST_IP_ADDRESS = "192.168.1.2"; private static final String SENDER_IP_ADDRESS = "10.10.10.1"; @Test public void testAcceptIPPacket_Syn_Accept() throws Exception { TcpSegment segment = new TcpSegment(); segment.setSyn(true); IpPaket ipPacket = new IpPaket(IpPaket.TCP, -1); ipPacket.setSegment(segment); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterSYNSegmentsOnly(false); assertTrue(firewall.acceptIPPacket(ipPacket)); } private Firewall createActiveFirewall(short defaultPolicy) { Firewall firewall = new Firewall(); Betriebssystem os = new Betriebssystem(); os.setKnoten(new Rechner()); firewall.setSystemSoftware(os); firewall.setDefaultPolicy(defaultPolicy); firewall.setActivated(true); return firewall; } @Test public void testAcceptIPPacket_ICMP_Drop() throws Exception { IpPaket ipPacket = new IcmpPaket(-1); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setDropICMP(true); assertFalse(firewall.acceptIPPacket(ipPacket)); } @Test public void testCheckAcceptIcmp_IsIcmpAndDropIcmp_DoNOTAccept() throws Exception { IcmpPaket icmp = new IcmpPaket(-1); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setDropICMP(true); assertFalse(firewall.checkAcceptIcmp(icmp)); } @Test public void testCheckAcceptIcmp_IsIcmpAndNOTDropIcmp_DoAccept() throws Exception { IcmpPaket icmp = new IcmpPaket(-1); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setDropICMP(false); assertTrue(firewall.checkAcceptIcmp(icmp)); } @Test public void testCheckAcceptIcmp_IsNOTIcmpAndDropIcmp_DoAccept() throws Exception { IpPaket paket = new IpPaket(IpPaket.TCP, -1); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setDropICMP(true); assertTrue(firewall.checkAcceptIcmp(paket)); } @Test public void testCheckAcceptTCP_IsIcmp_Accept() throws Exception { IpPaket paket = new IcmpPaket(-1); Firewall firewall = createActiveFirewall(DROP); assertTrue(firewall.checkAcceptTCP(paket)); } @Test public void testCheckAcceptTCP_IsTCPRuleApplies_Drop() throws Exception { IpPaket paket = createIPPacketTcp(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterSYNSegmentsOnly(false); FirewallRule rule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.TCP, DROP); firewall.addRule(rule); assertFalse(firewall.checkAcceptTCP(paket)); } @Test public void testCheckAcceptTCP_FirstRuleApplies_DROP() throws Exception { IpPaket paket = createIPPacketTcp(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterSYNSegmentsOnly(false); FirewallRule ruleDrop = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.TCP, DROP); FirewallRule ruleAccept = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.TCP, ACCEPT); firewall.addRule(ruleDrop); firewall.addRule(ruleAccept); assertFalse(firewall.checkAcceptTCP(paket)); } @Test public void testCheckAcceptTCP_FirstRuleApplies_ACCEPT() throws Exception { IpPaket paket = createIPPacketTcp(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterSYNSegmentsOnly(false); FirewallRule ruleDrop = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.TCP, DROP); FirewallRule ruleAccept = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.TCP, ACCEPT); firewall.addRule(ruleAccept); firewall.addRule(ruleDrop); assertTrue(firewall.checkAcceptTCP(paket)); } @Test public void testCheckAcceptTCP_RuleDoesNOTApply_DROP() throws Exception { IpPaket paket = createIPPacketTcp(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(DROP); firewall.setFilterSYNSegmentsOnly(false); FirewallRule ruleAccept = createRule(DEST_IP_ADDRESS, SENDER_IP_ADDRESS, FirewallRule.ALL_PORTS, FirewallRule.TCP, ACCEPT); firewall.addRule(ruleAccept); assertFalse(firewall.checkAcceptTCP(paket)); } @Test public void testCheckAcceptTCP_IsTCPRuleAppliesNOSync_Accept() throws Exception { IpPaket paket = createIPPacketTcp(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterSYNSegmentsOnly(true); FirewallRule rule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.TCP, DROP); firewall.addRule(rule); assertTrue(firewall.checkAcceptTCP(paket)); } @Test public void testCheckAcceptTCP_IsTCPAndRuleNOTApplies_NOTDrop() throws Exception { IpPaket paket = createIPPacketTcp(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); FirewallRule rule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 99, FirewallRule.TCP, DROP); firewall.addRule(rule); assertTrue(firewall.checkAcceptTCP(paket)); } private FirewallRule createRule(String sender, String dest, int port, short protocol, short defaultAction) { FirewallRule rule = new FirewallRule(sender, "255.255.255.0", dest, "255.255.255.0", port, protocol, defaultAction); return rule; } private IpPaket createIPPacketTcp(String sender, String dest, int port) { IpPaket paket = new IpPaket(IpPaket.TCP, -1); paket.setSender(sender); paket.setEmpfaenger(dest); TcpSegment segment = new TcpSegment(); segment.setZielPort(port); paket.setSegment(segment); return paket; } @Test public void testCheckAcceptUDP_IsUDPAndRuleApplies_Drop() throws Exception { IpPaket paket = createIPPacketUDP(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterUdp(true); FirewallRule rule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.UDP, DROP); firewall.addRule(rule); assertFalse(firewall.checkAcceptUDP(paket)); } @Test public void testCheckAcceptUDP_IsUDPAndRuleAppliesForDestination_Accept() throws Exception { IpPaket paket = createIPPacketUDP(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 53); Firewall firewall = createActiveFirewall(DROP); firewall.setFilterUdp(true); FirewallRule rule = createRule("", paket.getEmpfaenger(), ((UdpSegment) paket.getSegment()).getZielPort(), FirewallRule.UDP, ACCEPT); firewall.addRule(rule); assertTrue(firewall.checkAcceptUDP(paket)); } @Test public void testCheckAcceptUDP_IsUDPAndRuleAppliesForSender_Accept() throws Exception { IpPaket paket = createIPPacketUDP(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 53); Firewall firewall = createActiveFirewall(DROP); firewall.setFilterUdp(true); FirewallRule rule = createRule("", paket.getSender(), ((UdpSegment) paket.getSegment()).getQuellPort(), FirewallRule.UDP, ACCEPT); firewall.addRule(rule); assertTrue(firewall.checkAcceptUDP(paket)); } @Test public void testCheckAcceptUDP_IsUDPAndRuleApplies_UDPFilterInactive_Accept() throws Exception { IpPaket paket = createIPPacketUDP(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterUdp(false); FirewallRule rule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.UDP, DROP); firewall.addRule(rule); assertTrue(firewall.checkAcceptUDP(paket)); } @Test public void testCheckAcceptUDP_FirstRuleApplies_Drop() throws Exception { IpPaket paket = createIPPacketUDP(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterUdp(true); FirewallRule dropRule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.UDP, DROP); FirewallRule acceptRule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.UDP, ACCEPT); firewall.addRule(dropRule); firewall.addRule(acceptRule); assertFalse(firewall.checkAcceptUDP(paket)); } @Test public void testCheckAcceptUDP_FirstRuleApplies_Accept() throws Exception { IpPaket paket = createIPPacketUDP(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80); Firewall firewall = createActiveFirewall(ACCEPT); firewall.setFilterUdp(true); FirewallRule dropRule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.UDP, DROP); FirewallRule acceptRule = createRule(SENDER_IP_ADDRESS, DEST_IP_ADDRESS, 80, FirewallRule.UDP, ACCEPT); firewall.addRule(acceptRule); firewall.addRule(dropRule); assertTrue(firewall.checkAcceptUDP(paket)); } private IpPaket createIPPacketUDP(String sender, String dest, int port) { IpPaket paket = new IpPaket(IpPaket.UDP, -1); paket.setSender(sender); paket.setEmpfaenger(dest); UdpSegment segment = new UdpSegment(); segment.setQuellPort(5555); segment.setZielPort(port); paket.setSegment(segment); return paket; } } filius-2.9.4+ds/src/test/java/filius/software/nat/000077500000000000000000000000001506574241500220325ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/nat/InetAddressTest.java000066400000000000000000000034001506574241500257370ustar00rootroot00000000000000package filius.software.nat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; import filius.software.vermittlungsschicht.IpPaket; public class InetAddressTest { @Test public void testEquals_True() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 5555, IpPaket.TCP); assertTrue(address.equals(new InetAddress(address.getIpAddress(), address.getPort(), address.getProtocol()))); } @Test public void testEquals_Null_False() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 5555, IpPaket.TCP); assertFalse(address.equals(null)); } @Test public void testEquals_NOInetAddress_False() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 5555, IpPaket.TCP); assertFalse(address.equals("hallo")); } @Test public void testEquals_DifferentIPAddress_False() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 5555, IpPaket.TCP); assertFalse(address.equals(new InetAddress("2.2.2.2", address.getPort(), address.getProtocol()))); } @Test public void testEquals_DifferentPort_False() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 5555, IpPaket.TCP); assertFalse(address .equals(new InetAddress(address.getIpAddress(), address.getPort() + 100, address.getProtocol()))); } @Test public void testEquals_DifferentProtocol_False() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 5555, IpPaket.TCP); assertFalse(address .equals(new InetAddress(address.getIpAddress(), address.getPort(), address.getProtocol() + 100))); } } filius-2.9.4+ds/src/test/java/filius/software/nat/NetworkAddressTranslationTableTest.java000066400000000000000000000014171506574241500316660ustar00rootroot00000000000000package filius.software.nat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Test; import filius.software.vermittlungsschicht.IpPaket; public class NetworkAddressTranslationTableTest { private NetworkAddressTranslationTable table = new NetworkAddressTranslationTable(); @Test public void testFind_EmptyTable_Null() throws Exception { assertNull(table.find(1, "", IpPaket.TCP)); } @Test public void testFind_EntryExists() throws Exception { InetAddress address = new InetAddress("1.1.1.1", 2002, IpPaket.TCP); table.addDynamic(1, "192.168.0.1", IpPaket.TCP, address, NatType.DynamicEntry); assertEquals(address, table.find(1, "192.168.0.1", IpPaket.TCP)); } } filius-2.9.4+ds/src/test/java/filius/software/nat/PortProtocolPairTest.java000066400000000000000000000027371506574241500270300ustar00rootroot00000000000000package filius.software.nat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; import filius.software.vermittlungsschicht.IpPaket; public class PortProtocolPairTest { @Test public void testEquals_True() throws Exception { PortProtocolPair pair = new PortProtocolPair("192.168.0.1", 5555, IpPaket.TCP); assertTrue(pair.equals(new PortProtocolPair("192.168.0.1", pair.getPort(), pair.getProtocol()))); } @Test public void testEquals_Null_False() throws Exception { PortProtocolPair pair = new PortProtocolPair("192.168.0.1", 5555, IpPaket.TCP); assertFalse(pair.equals(null)); } @Test public void testEquals_NOPortProtocolPair_False() throws Exception { PortProtocolPair pair = new PortProtocolPair("192.168.0.1", 5555, IpPaket.TCP); assertFalse(pair.equals("hallo")); } @Test public void testEquals_DifferentPort_False() throws Exception { PortProtocolPair pair = new PortProtocolPair("192.168.0.1", 5555, IpPaket.TCP); assertFalse(pair.equals(new PortProtocolPair("192.168.0.1", pair.getPort() + 100, pair.getProtocol()))); } @Test public void testEquals_DifferentProtocol_False() throws Exception { PortProtocolPair pair = new PortProtocolPair("192.168.0.1", 5555, IpPaket.TCP); assertFalse(pair.equals(new PortProtocolPair("192.168.0.1", pair.getPort(), pair.getProtocol() + 100))); } } filius-2.9.4+ds/src/test/java/filius/software/system/000077500000000000000000000000001506574241500225745ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/system/DateisystemTest.java000066400000000000000000000033551506574241500266000ustar00rootroot00000000000000package filius.software.system; import static filius.software.system.Dateisystem.FILE_SEPARATOR; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import javax.swing.tree.DefaultMutableTreeNode; import org.junit.Test; public class DateisystemTest { private static final String TEST_DIR = "test"; private Dateisystem filesystem = new Dateisystem(); @Test public void testChangeDirectory_AbsolutePath() throws Exception { filesystem.erstelleVerzeichnis(FILE_SEPARATOR, TEST_DIR); DefaultMutableTreeNode newDir = filesystem.changeDirectory(FILE_SEPARATOR + TEST_DIR); assertThat(newDir.getUserObject(), is(TEST_DIR)); } @Test public void testChangeDirectory_RelativePath() throws Exception { filesystem.erstelleVerzeichnis(filesystem.getRoot(), TEST_DIR); DefaultMutableTreeNode newDir = filesystem.changeDirectory(TEST_DIR); assertThat(newDir.getUserObject(), is(TEST_DIR)); } @Test public void testToAbsolutePath_RelPath() throws Exception { filesystem.erstelleVerzeichnis(filesystem.getRoot(), TEST_DIR); DefaultMutableTreeNode testDir = filesystem.changeDirectory(TEST_DIR); String relPath = "a" + FILE_SEPARATOR + "directory"; String path = filesystem.toAbsolutePath(testDir, relPath); assertThat(path, is(FILE_SEPARATOR + TEST_DIR + FILE_SEPARATOR + relPath)); } @Test public void testToAbsolutePath_AbsPath() throws Exception { String absPath = FILE_SEPARATOR + "this" + FILE_SEPARATOR + "is" + FILE_SEPARATOR + "a" + FILE_SEPARATOR + "directory"; String path = filesystem.toAbsolutePath(absPath); assertThat(path, is(absPath)); } } filius-2.9.4+ds/src/test/java/filius/software/transportschicht/000077500000000000000000000000001506574241500246525ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/transportschicht/TCPSocketTest.java000066400000000000000000000040251506574241500301550ustar00rootroot00000000000000package filius.software.transportschicht; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class TCPSocketTest { @Test public void testNextSequenceNumberTcpSegment_SYN() throws Exception { TcpSegment segment = new TcpSegment(); segment.setSyn(true); segment.setSeqNummer(42); long newSequenceNumber = TCPSocket.nextSequenceNumber(segment); assertThat(newSequenceNumber, is(segment.getSeqNummer() + 1)); } @Test public void testNextSequenceNumberTcpSegment_FIN() throws Exception { TcpSegment segment = new TcpSegment(); segment.setFin(true); segment.setSeqNummer(42); long newSequenceNumber = TCPSocket.nextSequenceNumber(segment); assertThat(newSequenceNumber, is(segment.getSeqNummer() + 1)); } @Test public void testNextSequenceNumberTcpSegment_NoSYNAndNoData() throws Exception { TcpSegment segment = new TcpSegment(); segment.setSyn(false); segment.setSeqNummer(42); long newSequenceNumber = TCPSocket.nextSequenceNumber(segment); assertThat(newSequenceNumber, is(segment.getSeqNummer())); } @Test public void testNextSequenceNumberTcpSegment_WithData() throws Exception { TcpSegment segment = new TcpSegment(); segment.setSyn(false); segment.setSeqNummer(42); String data = "11"; segment.setDaten(data); long newSequenceNumber = TCPSocket.nextSequenceNumber(segment); assertThat(newSequenceNumber, is(segment.getSeqNummer() + 2)); } @Test public void testNextSequenceNumberTcpSegment_NumberOverflow() throws Exception { TcpSegment segment = new TcpSegment(); segment.setSyn(false); segment.setSeqNummer((long) (Math.pow(2, 32)) - 1); String data = "1"; segment.setDaten(data); long newSequenceNumber = TCPSocket.nextSequenceNumber(segment); assertThat(newSequenceNumber, is(0l)); } } filius-2.9.4+ds/src/test/java/filius/software/vermittlungsschicht/000077500000000000000000000000001506574241500253615ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/vermittlungsschicht/VermittlungsProtokollTest.java000066400000000000000000000147441506574241500335070ustar00rootroot00000000000000package filius.software.vermittlungsschicht; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Arrays; import org.junit.Test; import filius.hardware.NetzwerkInterface; import filius.hardware.knoten.InternetKnoten; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.system.SystemSoftware; public class VermittlungsProtokollTest { @Test public void testGleichesRechnernetz_Success() throws Exception { boolean isBroadcast = VermittlungsProtokoll.gleichesRechnernetz("10.0.0.1", "10.0.0.2", "255.255.255.0"); assertThat(isBroadcast, is(true)); } @Test public void testGleichesRechnernetz_Failure() throws Exception { boolean isBroadcast = VermittlungsProtokoll.gleichesRechnernetz("10.0.0.1", "10.0.1.2", "255.255.255.0"); assertThat(isBroadcast, is(false)); } @Test public void testIstBroadcast_No_InCaseOfLocalhost() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.0.1", "10.0.0.1", "255.255.255.0"); assertThat(isBroadcast, is(false)); } @Test public void testIstBroadcast_Yes_InCaseOf255() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.0.255", "10.0.0.1", "255.255.255.0"); assertThat(isBroadcast, is(true)); } @Test public void testIstBroadcast_Yes_DifficultNetmask() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.63.255", "10.0.63.1", "255.255.192.0"); assertThat(isBroadcast, is(true)); } @Test public void testIstBroadcast_No_DifficultNetmask() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.31.255", "10.0.31.1", "255.255.192.0"); assertThat(isBroadcast, is(false)); } @Test public void testIstBroadcast_Yes_GleichesRechnernetz() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.0.255", "10.0.0.11", "255.255.255.0"); assertThat(isBroadcast, is(true)); } @Test public void testIstBroadcast_No_AnderesRechnernetz() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.0.255", "10.0.1.11", "255.255.255.0"); assertThat(isBroadcast, is(false)); } @Test public void testIstBroadcast_No_ToNetworkAddress() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("10.0.0.0", "10.0.0.1", "255.255.192.0"); assertThat(isBroadcast, is(false)); } @Test public void testIstBroadcast_Yes_GenericBroadcastAddress() throws Exception { boolean isBroadcast = VermittlungsProtokoll.isBroadcast("255.255.255.255", "10.0.0.1", "255.255.192.0"); assertThat(isBroadcast, is(true)); } @Test public void testIpAddressToInt_FourthPartSet() throws Exception { int addressAsInt = VermittlungsProtokoll.ipAddressToInt("0.0.0.12"); assertThat(addressAsInt, is(12)); } @Test public void testIpAddressToInt_ThirdPartSet() throws Exception { int addressAsInt = VermittlungsProtokoll.ipAddressToInt("0.0.1.0"); assertThat(addressAsInt, is(0x100)); } @Test public void testIpAddressToInt_SecondPartSet() throws Exception { int addressAsInt = VermittlungsProtokoll.ipAddressToInt("0.1.0.0"); assertThat(addressAsInt, is(0x10000)); } @Test public void testIpAddressToInt_AllPartsSet() throws Exception { int addressAsInt = VermittlungsProtokoll.ipAddressToInt("1.1.1.1"); assertThat(addressAsInt, is(0x1010101)); } @Test public void testIpAddressToInt_Complex() throws Exception { int addressAsInt = VermittlungsProtokoll.ipAddressToInt("11.12.13.14"); assertThat(addressAsInt, is(0xb0c0d0e)); } private class VermittlungsProtokollTestable extends VermittlungsProtokoll { public VermittlungsProtokollTestable(SystemSoftware systemSoftware) { super(systemSoftware); } @Override public void starten() { throw new RuntimeException(); } @Override public void beenden() { throw new RuntimeException(); } } @Test public void testIsApplicableBroadcast_Success() throws Exception { String localIpAddress = "10.10.10.5"; String subnetzMaske = "255.255.255.0"; InternetKnotenBetriebssystem betriebssystem = initSystemSoftwareAndNodeMock(localIpAddress, subnetzMaske); VermittlungsProtokoll protokoll = new VermittlungsProtokollTestable(betriebssystem); boolean isApplicableBroadcast = protokoll.isBroadcast("10.10.10.255"); assertThat(isApplicableBroadcast, is(true)); } private InternetKnotenBetriebssystem initSystemSoftwareAndNodeMock(String localIpAddress, String subnetzMaske) { InternetKnoten internetKnoten = mock(InternetKnoten.class); NetzwerkInterface nic = new NetzwerkInterface(); nic.setIp(localIpAddress); nic.setSubnetzMaske(subnetzMaske); when(internetKnoten.getNetzwerkInterfaces()).thenReturn(Arrays.asList(nic)); InternetKnotenBetriebssystem betriebssystem = mock(InternetKnotenBetriebssystem.class); when(betriebssystem.getKnoten()).thenReturn(internetKnoten); return betriebssystem; } @Test public void testIsApplicableBroadcast_NotBroadcast_Failure() throws Exception { String localIpAddress = "10.10.10.5"; String subnetzMaske = "255.255.255.0"; InternetKnotenBetriebssystem betriebssystem = initSystemSoftwareAndNodeMock(localIpAddress, subnetzMaske); VermittlungsProtokoll protokoll = new VermittlungsProtokollTestable(betriebssystem); boolean isApplicableBroadcast = protokoll.isBroadcast("10.10.10.254"); assertThat(isApplicableBroadcast, is(false)); } @Test public void testIsApplicableBroadcast_WrongNetwork_Failure() throws Exception { String localIpAddress = "10.10.10.5"; String subnetzMaske = "255.255.255.0"; InternetKnotenBetriebssystem betriebssystem = initSystemSoftwareAndNodeMock(localIpAddress, subnetzMaske); VermittlungsProtokoll protokoll = new VermittlungsProtokollTestable(betriebssystem); boolean isApplicableBroadcast = protokoll.isBroadcast("10.11.10.255"); assertThat(isApplicableBroadcast, is(false)); } } filius-2.9.4+ds/src/test/java/filius/software/www/000077500000000000000000000000001506574241500220745ustar00rootroot00000000000000filius-2.9.4+ds/src/test/java/filius/software/www/HTTPNachrichtTest.java000066400000000000000000000026061506574241500262060ustar00rootroot00000000000000package filius.software.www; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class HTTPNachrichtTest { @Test public void testHttpEncodePath_MitLeerzeichen() throws Exception { String result = HTTPNachricht.encodePath("ein test"); assertThat(result, is("ein%20test")); } @Test public void testHttpEncodePath_WithUmlaut() throws Exception { String result = HTTPNachricht.encodePath("späßchen"); assertThat(result, is("sp%C3%A4%C3%9Fchen")); } @Test public void testHttpEncodePath_PathSeparator() throws Exception { String result = HTTPNachricht.encodePath("/"); assertThat(result, is("/")); } @Test public void testHttpEncodePath_ComplexPath() throws Exception { String result = HTTPNachricht.encodePath("/hallo welt/eine datei.txt"); assertThat(result, is("/hallo%20welt/eine%20datei.txt")); } @Test public void testHttpDecodePath_ComplexPath() throws Exception { String result = HTTPNachricht.decodePath("/hallo%20welt/eine%20datei.txt"); assertThat(result, is("/hallo welt/eine datei.txt")); } @Test public void testHttpDecodePath_WithUmlaut() throws Exception { String result = HTTPNachricht.decodePath("sp%C3%A4%C3%9Fchen"); assertThat(result, is("späßchen")); } } filius-2.9.4+ds/src/test/resources/000077500000000000000000000000001506574241500172145ustar00rootroot00000000000000filius-2.9.4+ds/src/test/resources/config/000077500000000000000000000000001506574241500204615ustar00rootroot00000000000000filius-2.9.4+ds/src/test/resources/config/filius.ini000066400000000000000000000000151506574241500224510ustar00rootroot00000000000000locale=de_DE filius-2.9.4+ds/src/test/resources/einfaches_rechnernetz_komplett.fls000066400000000000000000003253221506574241500262040ustar00rootroot00000000000000PK fA)projekt/anwendungen/EigeneAnwendungen.txtU1!Sxlee,4H X=q{r#2h5&b.u$U|՚ᝡs2_S`}X|뚸T%PKqPK fAUprojekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow$1.classT[OAux b VQhbPugJ+>೉VٿdbƁrތZdvJa* pa7\p vhc z5:Z15 ?%S<\G ই)L{a㔫x4R" B3CJ á"[zp]`賵"y(ʕH 6mVr$&a2*BؓC71e+SX4SC ']5lf*f5 V:RkdA 4u _S΂xadWhe樭c-mɓ-̽(&tBFܺ_7RI%Klpع!Qrѵlx8l9L0"`O/q+wwpi S./PKLPK fASprojekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow.classV[U]H0ZEM+VRCԥN 3qںպkZ >>y$`iZs9_8b؈u]Z ca"# $7C s3ұ{ɨ1 pXtV x:kPr:7aeϒܨc)ߤcUl5p9c@w@2%6ȜTH/awsyJ3<^0"^22^1*^3:X\: \3T0GzgLoxG W0ST drV~[ʜRϭe%;&MOӶEe8E3aG6b|Z0L{msjRxAz6Y3yLQ\3cZ])_sBn+|-vFLoO<ւӀ7N瑆9Z?gY mg,N)mOXcK/؎'А&֕Q';-o6.xGl1z^6S )V 1Y'SgP}DE|EX@9cp+fT\ݮ<9$=OfYn4{2FUR@T(>s訌 %rK),]Dw)k"i}\> '$&~.gFzp2.A7=U^s2MtQ&֨vT?`A[{GUmT7Q0Z\ ~_UUX4Q 4BPSB3 o ypd 0vO36>E4i<GC,`hLhM'м-Z2_JEɍqj[-`Y<<p4Tg`UdN'ӫ8cky\@c _yo;Dki Hk#EfD壃uΒVZZl-;C]9xnc>tR'&FV(SfϥC.+5.)eLVKI 7d%J,|et<Ul(ɜ>QYdFBrH U*ȵAmp$鈥G-I{ષD kigb% ]eg.$yh>&VoiX̡8hڈpL?;:}Pg@9PtdՈKras4h˼p_jSQw2I#<._U[]J]5 vR>q8Gx,8441^|66$+h) =G@JEG1]ɬ&%5(iGbe2j v15ER.1/.- Qd>}v:I p.Hjע / &C[Aӯ7ںM&K[\G,}^f.GȽ58Ћ闉r(?*fGku=jjW)2$gS?̟~9.0iHO ))yWI٭ a*ǘ$1iuS:D_|zyL~.LJ[ mS,L<8YWp5\a?SZ ^[rHPiK")y,)+X*+ BoRUa]adhvfloxÆQO8d$(+L,_*g4GRHi7ŎcO7 KTIm5Epf[ 0;ws5UX^]B3޺9詭Sj]?ª4Nd2T0=I0;LK^_7eLث g(Ē +5 Ss%!H`{tM!Y/=TqͣЈ,7T>*Q[;|q8m-<&'TSL2OPK8;PK fA@projekt/anwendungen/filius/software/clientserver/MeinServer.javaUn0}ц|@J^)Q ]mvaכ&U(k\9saVJg˺WCC{jYy+Ww|`-|W9\>k |s{e%H!b&+߈Cr[ X=teA|@!N<8' Og*M/.`Vl6Sc*[4𶸫~bW@xA_W?9̴EM; ÙbDDA-v~T O~-R<*\ [򬌌nAT"oϜoGt32wS mb]DpM8Qpf,=g(\;gfB6?MJ,z\'9S3G:ADJ;}ʰܸ?qBnDbC3}NÄ2y{#^T%Ĥc}BOZ\szi8L@Pm /LޒTZ=Tn䋥(^8UخEMcg$~%)ј|WQ`*OTRXۨN#r(cR߼w/,ĩO[>%l1= }/-/#O/ YBW2As֌Ztü^J*ŭU:2E#&oNwgմp KxS#󁥙 Q lIFe *p:Lv{);2tJS"hK+5che2++l!ezx8۔e"z5߯&K-e'R<}ceEct7lR&J!e6NnSD͐]XL_/ro9z'RNRd~t L`3`M Q:!IF:?kGE# cD biUP[Lb]2/ L AT@=)w# - hli 4H\0 o1C7n38p;EPK^PK fAKprojekt/anwendungen/filius/software/clientserver/MeinServerMitarbeiter.javaUn0 }&ޛ.݀7٦m!lHtmȿ\Ž(l* ]%-h,&YѐCF{EZK'*Jg%6[!*+Xtwj V!-ں$9hMQSoJXP+49xRYe%4+#K,9!ɐ]L.\2]9I'+*OFlAloqT7h@%U#{n0s_$>L2F@]xqßS#J,IlPh8jE/J䏺eh0BWmK4&NAMZs=Qk rʁy]vNB#wiQק l]prQYV {o[x1%)+Z+jm?=mB,h EGiD6߹фQZ{Zp#=W:{,*&B܈:Uk'&OgɽE[aLG8d&w0^,7d&O}J}!%H12̒Ho."EH)޶aCZx}\߲&Gws=91I ;Ҳ];y+sxvYXSk! %*cb~vpps︫07o\yfb!юw !llUe¤DzD`|9FPK\jPK fAprojekt/konfiguration.xmliȑE6WeF|;&{ 8nr̪"ٳ,6C**z Tt~~~)m.)sl_ fg}pǟ4lOUa@:/~?i߹.G |F? !o?4=;uQO/sQVJ۟~vEIK0I-6-3ri+iOi'0L?v}:?h,U(hG"Ipy{5@-m27#y3l:?^?ҙ+:wobs~jS:.ɘ~w_8__/݊dο?_/"_WNE;;}qok~o߻'ߍy>}7|}[V3 C?7?>y^11HyHSX];5YYۤa\~ZM.vYHOZqe8뙹z{]6 gJ)}?ݲ~qEnS:sf_>tUH_>(wEϗƟV6_n휎0N,ߵ-$kݟ$jg>u&<`͏d?_-7 4 {߲?_?ca}fEi X`~G˾J]?.ә\JvKdijn?֩~5}޶Fm},B"_XVi߬ba_/*8ّs6~].nK *%ߌiRy߆_8_.%.R}-WZj?ߕjIK)-lճkˆad~}3V s~Ͽp.ߑ>2@ƿkd=BƯ-"#d *2_ 2?凿Udœ2~_//GF 2GƯ>2]!Wu7+d0#2¿EdBF䷊W7Oo07_ ˑ#F.}p i+ VsdQi<.o?^KL71?mDݯ -oK7}pN૝&}K=FujiuIw1JyrHOmo#O9X=;3_`< L;ܜ8,~2b7I%z|n;}wy.1s~ܱNRK0~<}o_t)?t_z}qrS?4ӏv8Qӿ|~Ƅ3G;ǥpm}'kN.OI|?9&wFo%pn5iѦr|n{7M|'Hz|ږ1IOw b:gwΊ߿針㔦OY:~t>M>TIw?t=>S^|9 7}[ѤhOdO7)>OI/M^-'?Mo6emz$~!Fo6G~nngy~iNv&~\RMOM^C>5Drn|0[)Ko8w3-lfO#uôޟTJo>L7.D4q㐶ǕqĆgESx퇑4r<8&NrdQZ<7qeȽ<\鬐er7Hd'kP-Fea&GyjKڻ,w"x\ X>@=|+GR&4zG|^=*O/"ٕ# TP!0ՉeC\gJ#~DA6E!c@ xEyE&l{c=Jn*%K mkl3[B C2ϱc&3$|nc:S$ ]vEt4s4ITo hg0U솆և, .oօ : z2ssut SZ 7Fs/ظ 3 v/ Q^\Ey4y}hTRVp#CxE AS2LP7~,{@O}^o7 ؤN>>a.ml(<"MvSؑBF H.,+?=8B8S6ܤMh6 TӺyI(WBfU6_Lsm] x \s^tn>Bna>ԟ0@dLawL 5\|}]RȔ)" iHKŘx\ ݳ|^+־1g{즙=BfZO{Rf2G9le\ !;gH Veiݳf8dKݒϳIf+RqA*>GFoՍ@Q 6zDψ[h=Uz"醯ࢱdR&_aMYN~LU:IQ‰iX2j^xiF$s"4=$aixm2ꂝ<I6|֕˙Iû]:#Ǯ  <.!)s܇U,3g[Zd< ITB]eՙʹ^ ځ*tpx= !9%JXo"}òLx%HS^ԋud"fj+.*` t4|{P>y4UH"Jiw;>õljjkl#tlF5>G)Ԩpb_DÃl}l\9 i&o#-E YgO!;ĐE#sԡ}hb.հRܢeВŤ}w_9PϞSSs pxY+,ۀ:ނ٭xt4b'~Ìe۲&nju9%e6/p_ ҫMf0'x9:^@i_΍>'R!c\ TZniVz*q+Ax<_dl >쁃"썵$*,îę-I,qo"x$:=FSPru$1nʩSz=oi 8A)l19cB%ƒs=FKR!MWR2#IL6$`so鬛QW6dYVFbj/m~HKs06Xp^L)N][Z4\^}s|T˺ӜayA$ n]wIV=;i)1Ze'{ori']P\aR>uyDJϽxc:7Py)XuG`~9 u7=IBx"k׼E>EkΣt 'a6SQRyAjLhru-?UzAPNN Ej.-!dPd]$/^c $qG5ܘOxee21?D1HQ5w֢S>ʿk] T!oGT۵w'aj,#Jޘ6;\S|&f"F..#i SJi[l wh `Pq7t ~\sˏ{ϢbvNwz7vVsjH.UޛWPoya$<ֺT [$'r#xɄ@xwt.[%¯۫SCzJ9ژAu<?bE_wTJtN5B-vBQ`3^ʭ$XGhhFrK*1Iu/wC\;׍dY'xAZk@.gDxW7xEظ4 ]ADi.ݫf*:kR?Cwr|BѠ#X#ɞVKڳ=Gd/C /Gp|ҍ 8lAbEJIÑxw{+$Եz W(Қ ܡy((D\͈LJvֳ(AC=KI]bzv'vy.O'jH&XQZ1I~B6r5_vW[P4?. RrzZ5%.Ls:> LOJqfHJ{ U@qodmw!0?nNc2Eqbg,5.Qs F e0Ge'g,4I9-0\ mo@\AKu!S2VU2q/G qEUvpJ&R#0/@;]w^ uBV=7"wR N3E}l'TiD x]M23 2^ci×QMg{樂˜`lG3(}ħi$ $8Q)<'wY 4l|Sml\7qΌ^v }j _QtIm3S:U@ƽZxdy!,&iFHc)!)F4;A:@)qK"'s 2P_ǡ@+1/dp?m/gX73pWsnNwzdzJnŘ. qӪi$Elm$xzxnLq*+,nxx)8xq䪥8;=.x#ƆQ) 9=v~t@~_Yjm z sH$ȿM)6u?@b ⌌ MG4jR5?X&|1«DR5zh$DiĄkwp6R\ Z㾽 B9x]uQ)+ܲFfٵ$]LG"ItWᜦQ'*xMW䋕QG~[[19|q*)ܛ%8ŋXG3_){"eoeyUTN q#> vAU>;Z>/?.oI4)'0%2g)rgn|$3cE:4YG;!7e815# i&e9{pUH&sxHt9 )&8kUn͐4N>7ӳ//eO߷fz%,6m<%By)tƠ4dp噩]WHH):1ZmW, _Q`F =mﰨaP #C;P8'{B 2kζHo1Kaua2e2wJpಁLZ(tn&Ixf&hykRGlݾb 4 wC5D LW7R'S#1ii7'~"WYLqxI\JVn^ɣ)o%Srbnj˶aH X,tXKJwjsCb j,LktEBH"7(1gm5r@*ww6EãJsOx'f $)i~"g mn- ͨjlQXmH~גՒ8^@Jt;>oi>?&ΔWnBQ*/.R;w/O(goOxu0Xr2sj7͗f-PQy˩)P]:']ttAe:v6ɡZGV]F]jL2\;ڂ>Q9F`D+>`b9z БQAO2+ ˉqfV.p:?8wp6l9i;6R~O[-qGɅo Z^P'tl[a\L/|Ilч*Q}Be[0u{G0qAUվb׶ 颮 ؈.ʩ,q1ɫ&D[ߗ|ڰeTrcf>ȘNKR` -;_W1>)@x쏿EƮl.ED<|aN%: *_4:ԜMeT :t0IiY/7z)._v]MtŹ0r $]}Ք 03GA'#ˋ|Wq1uԷ i~#z͏V{xTu kNA%{n8?٫ʄaAKfĀ(^R%t97ㅆ= Vm%+A^NPfZ]fN]#*f30'///ݽjS_MǣOmܺ TliR(d<ȬziN@Z_x%w݌U(`|hz[%iYAKV޵@A{~օ'(n-ryž4E4<|1ŷX$VWEB`>53VL/.D]OoBb?VC+qwQDkҐ,=>i%16-H=Ȉ?N{r_Z̹>PNh s 4WjY Kbs7f]#Vhc!Jhz]cdQ+EK.|y<#q u;aR賀QLS0d,E:Ca Vf&O0t`2EhmqX"g't2uRjMG c]޶8@*oolnn!x V-W/S|&\3=1VKDm_pO#:y)qZq=bDN{V@2nN/9y/2)#{2ɀЎTZDDQ~rx ܡ{y([TRH^$E+Da!<q[{a| 6Na̋2jiZZAx_vFvH74R׽!^`. ->MjmWK[ۛ*[]]ZG Y3jv'yh1@F*:Ī!Ee:do!b}P/b6)몉rQȠQsJ߼ + 1G|\W[AM~f.~+< x$H<e)e҉WKЦU' drՍF!zy)t pQ{.X1y$MK<-ҌšEr}(`f%95g'.t.͵hi.)MM?Jq̑!Pc5ņ`VwHeaGm)Nf"Ȗ p a ]3e3k/cHL=(ɪ x~>&, 5926v *fodڞǹh?Ga|Qk6TfмζinRgEG3>m$==3G7[|;gό&v{+܍sڌT=˴No8v2|JƬDaڪ ^S)2{\&uJ"?+45@EoӰ,ҋˢWS#+ۙ͑K4# Afm߿2٭ %3sst3R)ƼU}Dt0bЈxC{SX er,.&lbRtFfPrZAv2-gWώj$ k/wV$PLoѽMYv@Fuk)~'7eN—>$+~XoV\8Z^\mZ'ȥwiap$UuO:=T)ևeBBоz#`O_T¥ Jm>?$)N1xX2wuE"Oc}nFwˋ/sfni]}f5&:&T>{K#X8)ny*p80Qsˣĉ9eyjz||7`9ହ3MW_Gtᗈqk wYoZP=m9e! \vmjr-adkgb}xDrXC`^W]'0ac.vQ;q6+Cg>#jJU,ᰠBEx yޫK7"."U\IDn F092 [.iyAʴgŕEkUN&p.E4׵}7ԴM p$ $q rΰPI≡BJ2kr,Y ʋ`1+ 2Nqs:%#ꕄrTi5]OxA 5?,Y{u\5DO8;*Nv}P>6݆:okƦ'ܙ/Pa`7 +Z.W_I"?U୯[%6/?BPR,AmI9rؖ݉ˤ;ec5d8m}aM;:m>{erb/jKfBcP~CJ>s+tB 1wڙsSav%(Nla=Vܓn<_ÖBVn7q틴#̈qSiJ洛K|1h6x >EpB%b2At![I:[A6HvQHvEբF֑a+rB s )M:6bt;B¸s7"aL".T̷PuIp Lŝ㦙/%OTn+!i0mT,p^OMm@ǼLHF6cuŷzx9J,]`DĬ#5+eדaE&kk94 9Gq< ^ByԣB=$a%얳w*?>]Ї4$и`#]j C{vUy;JbL|U3*Wi|=F{Bl {P [j9wK sJMAxG&/Sl,"fOb!@dg攏K2 ѾxNIy(.20=\$swc'+;^`OMɗa!Ʋʃqtj+. ,7aG;_^vyy1(3s98hQ{|8Fgv:0"{w 5l$G +ܵ6Y: b}Zj +VD[|+Y6g%M0H& InIIꗈ 1f[tAd&Rn@{PԋaW[jF4d>9Sr1Z6- .}'tHkNVAiti4l3pݶoZR8Du2S!a2"ņz(wbৼ鿫cvUK7?+1VcC]5NxmQlmLympÃ)_W4\{z%^69tn3:,2!Eh33puS?76i1@l-Ob.]J~Qf.UMG\^v>U`}\ʊE %5nz,kȬCWSxX0ȵZ`.db/fRIӌqGw {RspűT'_ V݄x٬&޲ͽ~Z/f =,yϴvY{!! cʎ |Oi3/V`AHa^3pCx!t N ő8;|v"?d^zaϸ)$M4͠m@%ʭX,6MUo5<])2Xc(/)H|hJԥ̪%Ϛϙ9BOq@d/> ^#Vp?IBjZG:yE_@UѧdYo!e5Ucp\EȦĺTi78pH8x@}l[̈Hߑ;GߠJdm|;-Ʌ|hUv;yVol-V][;Bxņ/fۮ]VDIHQƧmm#<^eJY2r+|Y6eżFs[Hje6nʓmf;W ccd&v~,XYW/~g uN=ۼbi ѵwN~Noi瑬Qh tJ'@HxIۏR` { >3'CBk@ %'}u.Ѷ{;oYaKM1Ẍ́1 Dž3c,R4?/gy`q 'ԠZJbW o.B]q ZTb uY㱷_h~ƝP7^a&˝sƆ1$~QER_- '?20 ZQw.D1\=M¸"Fxx.'RaSF^&u H1Z3e:3jңOSݺiF!Dp>JNjx~'ۘ<˸b0Cyߋ>6^=rHuu)&3ԫdJs4˲uצq} i.*RV kz$xU>AmFD]wi6PnM)3,Ĕ>'=?zݕܗ¥y:ZQ\[i rEO\>_*y g4/f5w2_<{%`bgj;hw)P,lv;wmQr+T= FL]_5,A!{!#GaDه1g`f mgkv*1#34 '.qc=܈9avMb$(=bOteWk="#_6&6E/Ҷk84h>eWrdۈ XMM|>c~myAaT[X!j0J!g0z0+ɀYs黫kW<,#%)02vvjĻL^kݕ%[oVx*HK>{ج) ]}2EYK-K{ ( (jm p˝@t_Ϧ5 . @bgI$:MgLSa:ÑÈFzj\d[<N7y]*U B̭Ye=i殝^X訵xξ^s˙ɆYGcW1o_՟pKXsp].MZ`XGಎ?yEAx6@mbW#P"[&~ڟO,aҫcwBcLU ?\myz014&jYHb 3w1PW\ʩԚ44!]%6Lׁc#2\\dʔn3@7`+j<]ƭk:_ d& F7fv Mڼ[Bɕ@kn,X6A Hd֜lz%ט&#jDJW3籥Q''@bW`؝br6 sG{? =o&-?DXs/Bx~g9Kei'X>[i 类RȵqyLOZÜx]L˞U} 6D6z;Xvxn*X-K'S-M&1X_z+ Ϟ;^|SX@p-]X[e8ŰGXMioΗGIO8; {~fv5Y1U.Ȱml'i,>įt p^exos}tœ[)QQ/ X IAD[$^BJDW3FPi U1Qr&@{mJ?.WK)U(8if{z#mXXf#bO}y{ O[]d71 0 9 ȠGM&%YPҢ#TҋgT`݈߱k` ;w[ ?MxQG!N3  r$.4D7 o=KgMgy k@YAXvy,`-Z2t +MƹYN՚҃vt[? SC-FGGL"/ ZU39~!4=tbLۢSaX) ]︋d0(ASBqNf1䀍phX_d(*4쏜i@g7^ gԲ-)`{4GAc|3UC~`j|=V- 퐙}Y6-=HDŽ_G*uJ@7Ӫ.9x:qC5C,!O#DxK9/Ef~IQ ѝ44R֋f8(-'eZDٖT^R{=yY^6hV_-3n=L#F}4cFq+Rwڎ }6up(J$#z$(S!+ ];q2rW+M9"-}%pFPF]i^=[τ^ג;ܙ ͲP ԣx_#OtNI@Ahi.7[V]) G%S:-0q郈CL`H6W`9Pr|g~B 1v?^sW?95c Ȇ}OTld˅gs2`~k0ص9~IO`+ɒʡu'.xs)Y/ G`84%'?l{ћ!W-Yb#| ElS"N+^7$לq{M@H-8.B|L|VuqWSvG.!r֒[!ឞbO۽},ĉޮt\-g9n4qx-rzPsx.WPV -DD S&{k۪27&(Yd*?@0pS֮S#ָBo=AKlZ"MĮEY%~ ާ :͸ F 9jk[XCzpĪ=&eQ;vqC9+vJuӚW,u@8IĮfBuoc3NTc`V_7xf(ƝqTsO[\#HrSAN6}Lbdjjg.]G!jCGX;XGObzINyrL]ÜvS[=U0$Y`]Fi@69 \+/^@ Iwvn)'ܐ Ϧ}pp? 8x^OB!y)֤soj?s1g};Yy cʱ$7/649n=#.^ SJ{B2@~vP4>@Du0 fUIJ[nv6C#ܠ1ñ _2@qV}BU rQ˵3V:4xMª۪ YsK(:vsL2> mG_rlֲI A[t6HwO^/%;e97ms 81ޮ_Pe'_Q(SrIxA[DM/DJCͪK"p8o4IFUN w%x'/=CKpz<8KP@ψ>R~E{hDֳ!۷F3rkC鴗g{nRe6|#v+f(T)žbq &D΀ ^މ غ~NiBĞؕD^i_Zx,fZ<ʰ44dEJƦd$k% U{:択fȹiX6W۫v<~HNvPʤgjcVs(Wԕr}0 ٿ,qT)fԄv ZiуQv^_g1:`0 _vq77r4]>l lyV9_˭i/aӁ^#?x*OBdbe _o(kTeoiĀ}a.:?VG$ʰS""92e) M0ѹP.~&33H몸GP~Sn~xPSou,IG ^PVRK<RoA|֕' ܉|i$T6ż/k3LnS: lgC#gtA['<'P{q) Nbֶ*WVpmFUk&g [}})_h'Flϫiz_hXA:Ec5.C+Y{ŖetsZU9 ~i)j]Qm;`s[[1MI>XJȤwYM$o]Ƅ/?Ag,)WGň]ka72b޺nD3*΀tI>HGp)@|I#.`*4OGv[<nR׾OiR?ܨ컚JN3XG9p4yP(/4}#ƒ-)Zj9(ѝ>\v2d]Qn 7@5KG*(‰0WfAwgE:W2ȭPQu0hş?%&#N<2G3u&gYgF9_Tۙ [*R$lF©7/Jr׹?+V0BcQoy!| $9ԲXWu~չ;ܚ`j^ԟ Si>>p,Q2T'Վjd:Z}li?i=ՄbM\aOz vQ[+Pt%q,} *{8SC;w:x~K֯7Xq?j\(߯ĕ.s_,</%?%Ђ'%xܮܶ2ݓM2r@-XP}=uwǢ҉oG;O'cɈzaA걽,>ʺE޷xUv"hao]_SJ78&@ZjSYy̪fz>cz#>?ؤZ B%+$PIufJBTv׭j8Lp >(F=Ц=3=^{^&/O虫c芚M/5k'_ֆxU k엀BmqNYlscѯ*kt0^Mpm׳5oϊA8%%d}q_caW~f?Ÿx! xb3>]-Y`^WVIˏ:sDrSD/ˉٞP)I) E9?MWlfp&GKЬ%Ǘsm{]ϔmV_ӕ`k'tYd8 k(176ы/ o`QJAl>:;ׁsއvOsfթֲYv~N4:7I"[M,ƀr.@Fkt Iy121} |D=I{C /k_w3=Maotbg16HLh],s' o~!enCXbx뫝|1$HNfaزkyL߷> (}&Gt-.Ǖ`XZ@GUuG{5t7?ԮxTلǾ^yn{%Dlh/0<>m6NR:|S}WaU8xmMpMB5a }i<{Kr`-לKگn,_w `W~PlTeg[6t9+e(n0Q2oErP32v\(' L᮱\/VkU ecTM@,|fx}w\Hy:W5]`v1/!oiV%va)> קy^/;WԶ{^em1 tⴇH6oV)╿]}a[7UH7F"g _/v?YdM<-W-P=)ʸ9MWt.HDUR/ #'u=̝8GIW\qD(@?3WA|0]cCly>e<c5/BUIh@6I[~`:yEm¦e5(Id!404̓%3?i"OFS?ޛ ?Z[4p ?. b#1^R[u)i〝Q-‡E_p˔>g z-em[ D$_E6{P hPZ)~l%(8'H cV5MͶӆa*K|Bwi?~:$vO{HdlU˭ҽ!`pjFE+?#JQoz3,?݆nf>-Yzc gȮݬlK K n.`PY4N#ޒHl ܟZǢ<0L y`$L4GG Q_[0d`ۖ6)SU`[)P Zr8wE$/fZg?d/xlSJ([u_ ȇɻS-a=tJm5"^xLUx)]yiN-ZK! 8K_^:,3E8>i "ld]7F/}<])]؛ ]LsmؽxufJs|\dYńE4+&JF 5$66W{e@z L{t0Et7c7&@78'Ι>Kc#}I@Bgk qbַ?7+Յ Oܐ4χ&;)`Iըk6Oi!B"yPry*gɟiߥu`GEm˗,s_3ꪏ.Ӟ@MnRV[2 ]>axj~!>9c7 1}^SsH^|䇮7E ?HkYжm$4!+f(P_I$BgS8 $#ҥ( viZQM_nFrFj`9fccGP*HrulV̝t\UYVd݁(>5I`cvqr @, 4h"VZhi6wh\"4 j:N4l3; Uz=8~(h˖`1)<9@=7@EޕNe'ju89in>G?k%CcpoW 8{J8P3@m0LZI6ucڐ+ o"TtIE*#gB&a+R'=m1:h=̬?ohnxZ zXAnpY +TOwgmF!hI4x+؀EjIV0) rP@{~EȡsFБDΙ_fg<Ƃw9Iw"EBVx,*K,kvmj+ȚЙ^P.M[Z:B PDigNhxJ*cDzݧhc tp6#]kTwgm6 W|>3{ 9 ER+DyRrc_.@7]н*-f} 1Z쩵aS|ZCTON Z");KRX?$(}#6 r8!SD~_~Jx&nn.U 5yS7d^xRgF07xWɳ^I l[XT h~SObp  d1;ӷ(PƆ>;39 H RQ;Е\Q+h$dբ{|pNPJS;r{,WqVRV< q_F0k5HbJ.D`|4ĺ@a1zxL7As5AVh9E߃M"IM%JnD*V& o{[M 0 &8&=ɛNf"Ű疶yDNs[so 7~K=: \AWec% PL#cK>4+W_J}X U~VnLwսylcVz cNh[EYvVd FQ:lH+ >3kIh:"mn'"M`Et{?7[)^_⥻Y50v4f? <ͻ~KWOI;F?(4NkP51 # Lq;Y|Es$J kQ,Vލάs #$COŦ1]jh fG B톫=.mN}vNAVqE$ wͥ*UVoB^Dbs6Qm5JC71-13s, -*=˚ֺyvJ!ӧ6MEY%VZ (S$0bei7bf Ly!C}aßUAx,]E?|}Fa>"F%y\yCq;40b|Jf+fRwb1qe/ bvUفVM#"g-8h:-cu[j,CY꺯[kc,I۔';~fE 81ekC~XF!" ۹8Ա韸7ez>T~ Drc,o£I[!toCdVzvdBqOC~\4S!;T ]޶%=Es2&f7~R>5^EClԀ× qw>ˑީ4B r#y?n(3qKD1/jꋕaíެǂ_oN2苧<ȍI߶p,G nCcyAXlEO7cUe;?sU# 6l.QTNBA'&{kFʾb%:>=AhKOH'1m>?!}^2>LԱ5vx NP#52F1Y5չy6(3cL87M|JEYJuwJ[ ֕l$j$Rsു}Y4h*?{<@tiq?tccNLPW8hc #lCe5#&7d?c*BY'Jb7w7gm}{ 6ooϜv'[-f}~Kb8gJ8" ȗPӦ]T6N.~Ӧr}yUJ٠8r<62b0{ w2Q& bb =&u)>@QP/xH# p">=blД_`Uߛ겁0+e81<0PÜQaOASZ;PpA&`< _gGIT,oZp3 2uou T0sW%jWQuHKߖ,7.sci03grm$a*}~43k +: T1rr}W!L ̈ឦULzPki&!$70[}j]3Qq8 -O c ǴTTcQv5^R$h"?O nᝬ4p?duy fj\}/1W p9W _USDl[ Cy7,lWZDdi*uAl]k2;ܒ0T0b^bwy\8a3+;-\o1u~)76Uw5՘-,\3+CRx9/6~v ^:ǛOq4|v!@ol04Λt{7Na 5kv[Ѐ~Ce1[RK)dv෋ar݊X 5>-*j<-\y:Օ:<2w?x=pco`e"U aYف|mj&䱔涅5#9GFpG?E'El@|#Aƒ\}Lt3tg^=i}oƦK=ї(}F>bM 󙘢t$liBeI.H08!X`})2a ^-?j¼[ڿ-:m>T дq+r*{#$A^LW"@θJ :瑐ȿR T F Cc~{r~JƏ\ϧ +Nx+s yg-pC[5 OIaY~ö7k}4_Jc{QotB:N&,ܿVp@N{vg hfvc@P?JҺ~;Vժ|{sszI@W=`_YE~]ퟂ9ҷZH ,ex%2JEifr\g eatgfkCㅑ]V-o:pGC GwE֤P%ͲڬB6$,?\Y2e^)jud<ƔZջ$Ngr&lu$ Kiyv-m Mj &RB FnZ Zl PSu78Q~05a<5 #Q ~dbmC^ƺ煮qLCzdaPW ': |GhzD^f6;ɳ޳ȏa cLK.*++QN ~?$Y o?c9tˢÍwԠG1(߄3 ֳ JyGbxBg#X7zfƙI1vmXQ7 z0M:^\D[Ggo]=5R5KI=r^1s#=Og-MMP:}Z(miμ&Օگ<SP:novv.U맶tآiעN#s2[d畈WJX_T{Mb%=sKJ5dJT3*Uok *$ @S.f%5 fOͣen%۔ Sntkv+x /SfP p#$So:+zJ cIccISv+p~]uiw¶o]ɂENn_<12agͲ@0aoCc .d87D6;Y_174kҤu'z],VMk %Lah]aHA't檭I8B-lwY>#hPߌBmh>We2OЯb6#f7l En@κZ[S 4fAvPPHyw:M'ncT_#l :s3;y;/&~%,Vq žkBSOhgREXe#X)P<]gEU1sSGzG))$X?|=Yy}gQiCL|4{R+ed:-U^rؿ(DAѩ^ep(!}nA>v,|M0iy0EC ̸ؕ\dnos; bctc|oKK'0zszK8oG7~fe!`W:&IzfUOqۑCxN$pr4ev·_ȫa;IaT:T9.G_Jv7V(Qp8? )g+0g+ ?JF4^{/lRvZ%"iZ1:)JD\|еݍ<75r+%׃&IeL쩘БNͺ> b>.PQ`T]P20O`a~=Wk`BwlO/a: *G[mZFGe8w m 5,jD$h MbT"~ĈbB87hhb^0x4L]66/ē߃6>4 j = 9NWjD̕bK٬5ڠak4z Ҥ˲ #Njջ^Z`^) MRniЀ뢲5ccGzwcc(2&vñca˷2D'_3&6B|Oy_7D["}* *ttФxU̔?rG@Ъ;[@o.eWHU,̜A-"*Op>`OR)+EˑAL!o"&2wh j4:,-5;B7;=T5P9|L;j$6΃_R3Q1~{&[H'kg0v|IF90`|HȰfIz{~89UzQCp()?h`0k~쐩$5WiZw9aXeFcJL:Ɣv9?Q8omX"GVv|A1/kڰ .>;hGU~r$)3^ ix~sņ*ARU>KՌu])%t7ߋߣ0Ӳs&{O}@ ~Y'*rRMAq;ُ.KH>UOll$(\xzraOn[}-}ǀjv3 $<(];MUvכ7VpН}P 2! LҘ؜iJo!gqveX_}"c].8N>@@~YS&K@#;!s@I=QU)j|c2:W!jI*w-92S}787or_Vtg M\ݯKP0+\k i~'Q˶b0Hڬ7NKݬhK~;xwzxUСF#jJ$f g2x9uf̈́CN" +w]B"/Q2b|ۯY X1ϯ& uCLy쥏a<6G8WZ* OUHS|<.em7%.R0/E6oMj*FȾyMmqZT%NCP^MJwoӏiMunh}`E/ǞhR|21teb~7w8uv"%Tp)kF )"_6)i2/oB3$Rwsg;\o;.0m1`Lt2,)0ʥ{λZW  U].#Yp|WI4{EwA RnSKv 5PƝϡ?E&%4z?m <4'o|_hWs~NgX@C ;ޯ0oo?;,|w6Mo€J(.F/SH'0-TO:{ 'uYH2qsNu4~⡜~çCؚ<eSC{j۷]~pC?hn7 aGBmnt;ðwNm2dSʤ(3HOH~(9kO-2KӼÉ(0>>ET{CGXсb)Ǵav5vHAf7щZIlqPq}Zb֙F)tWco骿xNa~)JýON?=|ف.P-cّb_W/&P5 7(uFpI#fq:vb- a@= Ȍ(|bdB3amiE:O1Pav)׽5r`s4LJ[F}}n@ VaΉw2ZY7HOXϜoL7B2GȴHjpS19)" :,0s|A .hYb͕Y )x,e30FlU^Ogѱ@*=&BqPϐ!|ՒKo_.Yh I/'C< u6?Xۧ BqXec_ mINl{p_qOpEl~*pQ)ccĵWr e_^R5Ehxouk4]&០EnUϙ`/%91U1@Ps/-aMH'.4mx,;C^1wpS]#SIڻZ6L2.;Fwd A7N>3!XAL=93k7V[ #䀲1 ΀-z^6=I1-`^f8~ԉlxQ. $_s8KǬ44ţ5㺟OU7-18Xs}Kc0l=N:^>mc!~k#ĺIңR2'16hESyp8$$">8,Ѱj IE_J}Q*c&8Hf'Ӭ>'Q^&@drenm`b]ѠK[3/҆@ӵĚ#!)8m"VMdb`zb)2"V1ئpore^Tk>nvTGI]Y5DÈ9}Fg;]Z^ l.b2ݴ=2Q6`_nhz3yc{np"pJi/+6=%ub>kC"MQwo 4㓻$ N>F#bX~-&Q7fU^bF}˦Iwus/?H0UČf w*4u p,N1BZ6$> -dh5m6sc ٻ "=р}A (ɣawUPJc$"H{i%z&v d@N)ݽHM k?U1vN|Ah3{p F`y"hM&aؾ^]"1RUIs}'Ϗɛh^r V!F! W])PDgϸ(T9"/0\*`ginjw!J pG1J[vr-[ 1I: 1y&_v_hØOP2mHNG? 0YKOhbqLeZLSpQ]a+V+O%xȚiz0}ԅ “y9/Nv'~H;FR{dIwrr‹ 7MNIi]p#a y;!%ZD.9WC9I0).9q*4K5v;1x 3x>n7Q/:mИnO+{OX%Ƒx;7H~H)/|S8%w{+46{(Yp#&$&$,Ku={wBf$H^nOzKe1, z_ʵ|>& Σt[l#tL&Wa`QڠBGA]llyv{:jG1ydtƒq_1h#d*}GVz$s@J`kakbW⫅UP80PhSa.SdF{Wq d߾ p_ĥyb 'vi3/JzH&C|S͝ f-dx?}^Q(jG\&?O .y.r'޴VQ|]{UlU;1>gծkZjДQЃ7:oxp4ݰ M |?5@rBS>A*ɤ"m]3Ao*gRVi4c0;:M)K H'!0 o5F{N`:a P'uj/qF Fq=AXW sWpr9.RFtKd"eN4| g@G7zvZ>HDFKˠ!LrYQ *{}:lu'ՔŏWmR}m1_ 8ѝQn$?`@9+uaý t B*{*Fz*8g͚a!@ՌUn\ +f]43G1Zf`gi@ˠɽ9۠u Qz $2JrfhzUL~w^[寲 kߢI%(k_E?vեŒĈL Lím7<" Sif3+,ўI{O7OKLofԄ]e"8ΰ̸4FCȇ;D_@:@ӘDlx ]G*~k"mM|}FcwY?\Ԩi\dm;]"X<_QޡKC5hɱHu)ST;qRœ&2D72㖜9,u#wYVOX#p 0!UЇ%N\(ݦvr?iL' Q 76`޺e`LW1W#б_ BNiBd3ݰ~:w)ߦ6@vɨ*9*)Ss0ļ9!Zp%H_p^O:tP[ ("2f%FJ?A`g] i" ?kG,Kb/>2}p1F/sd'Vk~TVIB}y03!1bp"H}Ƅ>jN҇,o3{91ท;ŗF?xMyޕ*6?kÁpL_,p+efg$rpYo#3"Hxv]p L0MQ Tr̽e|ݤ0[Usr-SVH)\Ņ$|LJtRexh}.#:^Mtcd.Ύ!ץ3&9871ٵ['H(kS)NJ%I㊼&&mKRS;ceDy{ƪPn>WٯDɯ~170ۥ~ ,ɸj,4:wotQ4 X0V|,8`ڜoa @sJTo*U2'6֡1"mբK@3M3paB\az`;xcnu_)C6WR!! nVue #R7 4p^H7s*irٔ +m㤡WG`0n6/jT@PS"lPDS+6OW8ӟ̚ !Gc[_Sa}Mn 1z7o=`xKe}}REnd>3U*ѻtׄ_߮;:YxRqtjPy&cJ09ԑLΙmw[t?IZ?'H8}tMT!L\ 3 ,bKwêJ*vmselz`o 6jU%?w!*֟©)oBE g7s)Ds_U^O@m,{\kwP$Je0Xd2BS26%.1~t>Y= $8/څ9wm0MWbw,FA~ޡWf9-S-Q>?}6?cn )vGß*gHAVQTJoq[W"^0)4 >U"OK$Y{LW:g,EP kiJ^m2چŘ?xhfEG6}ٽ u3loο2:U X\ V^si-XJ7Q _" j=_kL*|6%wdo#ί4_ `}(/9%{+ â]wsX7uy[]+nh{KfќE\[u0aX'*FMَ1p끵m̆S-Qw(7P6MG|`]\[$*5.#З'6qƤE,xڕıdElzKKO~ ]tpc]31t9"xeӉ|186^"'S95 wFR%l=Lj𵾆s̩ +Qd4 +U' Hq3ڲ>!R),djLɡ^k-"%zxoQ -ToW]f[K{<5<E,Dۺ=6?G1KY/}Q$s'JO>p`bV8ZD@]٨]W 9gþ!=WR('i+ّTg3}ߔ|M+&7wi./IKrWnEpxToe2eP\kW#Ø#GCO*mFqKq WmYR4j"R{ BTȃ&:3mս!OƹLDbouvǢGx6NS;@pCi 0=0Q Mh Ct%B1kC U vsCr>A(.7m+25Q[rlH޺ͧA<^_2"Yx.X.y{~/7[Gx;=@mίUU1Edu+0;rA%dԊ oEr)[(J_˙jK~s֛{ե=_0G#s}L 9~S°&y9%+FECofb89s,-DMgvy0IDNSjmj&} zn |/9Bcyfټ_Z1DZ ]r+v"Ik_g):Ji7`Ȗf߉YD ne|]ڻ]ms";~*8@yOB_"8CGmQe "^dVE?K^C~CƒDxܣ|G$0oKtr xmhMˤ K5LAV8M`'< }cSF1Ef9bot5#Y2 >'qob#G$ oM!ٍ!B ϲ~]DuC|RЙ1ZH:;5Eqx L:{"1d7`sH,taB~RAi8Fݱ%&9PNJ]­Lgdnk]TDv R #|N#_Ԗt59ηu@7ƢSo_f;7ՁV^6̕DE>,Lcҟ~Mޏ|d6?Lw}.hB<%4nc&7ODHW򫃗vx%1dJ)pzg:$ Wƌ68o+0 発 {~^N&(\|3"do>񰳜TP:U;̖'bj|pݾL77gxx=UL&'•dӔ5g5i}OcߥarAyʗJ:x@؈16kZOESq18u @rΓeEvwUyLZ'&okۆUXL*Ú U !B?q5R}-ZSV2KNɶ_[Ts#'~VCn3\/+PG(:f&Nvv}P`r"snjxhW\O;'gw=8nL?M}wK;y{* R A~Wcv~Pn%&C=R{8<2T)u'҅]KNU w]*g9S>#jv P4=O֓ 2&:~XI~ʴW*}!`򵉚z?Ҝـ\SNT wR;s.ᶜKLTUہ3 #N 44vHT')3~Oecv ޤh-t:gXRgo^RZPDxZ\ YsN^c|&,(!A'V#ǀ 6!`+kO¡A4ɒfS@Tw8t hy.aD%-?2iHqoW$rꞀ>#Ę'i|Qykr^6#w~~7ɸX-)oEBҹ_j3i4Y8]N;iSih.z}1XɉV5ƒj rNKV(R3nfm e, #H:qݺO"%h Mok/œq'؂'j/NU̽@W?t^[gȹՖS8t݊􈈫\A+&̂'n"[ySo<?7 #ee3eyo1_V&O}N'1<_3քСCbOG;ga{ҹο` oO U[![ 8Oxq*;]O9پ)O/8 !U]yk м Y]ATφMPE#2ESjr.s=>$Vyc TXT όWcQ``bdp296v^,'"xQjA>A8#Do {hn^cKg<mDæ{{2j7O&􆱂O!'lޅD":;/zl:K%-Dj:Lnp0Yyގ!)f6 gR0h& OhaD*Sm[SCBΜkV37nrג #i'Pz뼴V!FkL*˕(X(F.D( !S\Q,}e'MFcdhݼ회)z3fE4=4G6dbF,d}h^}W%CpBED,&k2y^GaP8l""\p9\ma]&/6ē] SHH\ /RJ%LSM* |ؿj,AM9")iXuuP N̩"t~=QEʿ)SKÎ_x\NP?#Ųf!j2v4".s lMC 7pVgR#Jq|uLVa˼ I<~8[,\NdZ|o?*6 ZЬ{N)7bFl@.}*$`DGy sɚ!QD< >Q3?O(:D$qU yyII0 8;PuT'LoZSM9F^kC?4ÿa゚%7}m%X>Vs޵pq&"[NI^@Q>ہ#aUec8{('3 h_N|o4MVsbwA R,'002c?l8 e6}+{N QYUJZiRRFR.U76Gw:` -;|jJlPv=%Oʑ 2ZpCF_y f~h4q8#o{Q Dn*1+F~~kr߀:6[F6[ϝLеbCnIUF46ɽ}jm0%Z$Drt:5$N +pK81v#8Sh+ƝJMĤ?~R0 'i {!ZD[g<WZNN^gvم+D8w^ekrOrm 9m 1!? $%3Ou;i'?^c8,L}|Ƶx0@j:;lYd19\ †޻#ථs_;$Ф%XDBPöOEt\7ߡs xQXCNѫ%EDۙRXo=ǿ~)"&pvw aOͻv50@6!7tڔ3^SR),Po:8!@0K`ִ^ ZkӦ`ultžf&%ir؁gQLFWa!#5r\/h7lMv"dꇇbP XK!\ o` .}%]/N5^2y>ZG84J?k_I/$<|ja4J 2nV-k.Jҭg|&?0C_,G--+)Qd tn\"Q3d-]KKUKl؀jM1"LkG"GM-늆9@L2.Ԧ![̇/OR}\esFE]铠[Ӓ P‚5RrIA:B, 'm?}F J(#p)TI:Fj[KZC^6RXqĊ_0/u࿦b㊇{Εmgp~jH0]\gW2wªw@!¥2)wɦǗ`p~$RȑZST(RfnZ̺{ugZ[b{N2q=Rgr! ့E4=*Su?x"o6ivVU֯lo_ xڐ64z?↘ο9/ӷ+0ZeTSSEnU5мBT|f^2*׬| Azҁ]Wl7JlIe]횷>c?-}1zJTpzIb;oyIqEʴ _2KAa9&$"Cؗԫ ԭA0nQKAKYp%k1~ŸqTRwVIM)UX' #t=?9.0hzᭉ"[ںWFJ~0J{%m8 2X6He߀=ۦ>*}Wxy ~బjORK_9M[vއ^ 0{; :Q^n՘(N"2Ĺ,iEFԑbcH pT>dO.24jQFqdN3\zZB-Ͽ C_+.:/ڪH|q]YȂY#VK[s:Gj~a'gretd+pӳO~AtUll|~ BaebbG4.[EeF(D+D.=p$-m9~g~ݝZA;Oowˆqq>km]X{z]̹/[I\~huHPztLbd+uM^枂 |\Fq b!E:uCa4֚4"|I@'a6<Չ(\_w/AOZy:Z?KF 듉\3 OW[N VMYt'ND5xٟcQ}Ж;z_fĨp_RpkʪH}&Y}y FՅH OHmgfҤR8ɋq΍ pMpϬ.Vo1 ćjvՠf,V}4)j7;R _FKWpTTnT}ɬȋ4Fd0o6!i9ʠ( _)Gv^b/#wY|vLrA2aEĽ.?ͪNneb-(I#4aJDD:4&䨾(֕M>[άXz:.7C GlT*,rzJ:9"4CdT 1BfqxsiTA޺?fGC|jb}Y͔WNF@Q:iWwfFXJ9b)sUxԆݣ?v0cxMq?#!/ilDq4]wFqO}9=ᛜToN>Kj4B4R1V( nHR2"]4?k() b`'7:ʢװ7쾈\᏿Z<ԥ6P=01Ncn (%ŵ[f2Pr>0MjI:~Bm0Ya[^&ָIվJ޺2a A qFfM|9isc6br$ĵ>6AȇL"Ln^ ~cH;@b(feW_;"%2R5D!7o)4oN5tJg,/V ;Y0 J|ɔ u*M"UeW%d>'i,res.I/4,9r8zX. ,5mKZb t'f8h0R!0Awt=ЧW!oK;Ry"IYXdoi RgVɷU~g:|A*Ao8ŹkC>(}D9rua@IMY$=!F5HL{ i[,{ǝb+g$ Pgm@Oǰvٳvsu#\]i;R$Y0ؑlR;;hGTgp%4.p*FEP}Toԛ;GNU+lDtlB4tj2%Q\[[WQֲze'ʬڃ/\Tkiʪkb.a'/N]m9/W߁=fc_ָ$z&EO-_eÆUn[Ԟζn1EYP*8۟Z"{Jï;]\'13@lopƬd>eJjR$',u7g~q%"Exru_tNyUAmaxqWWk*|:##^=:ΕKC|`2ʆA& n$$ƋBB/HVUXi(t{e7F֘)%ťs=ڪ~u]-u6ʬ'REV|oM݄g\-i(e@.u Uwnhsa";as.JFԈZy o @?p9_){`l8k29 HByd7UK9FIZ|{W($^A/D畇&܀𸲚Lm؏iyS4 L5|Mb-aMݸ{,9M8>Bă#GnQJg'?@WlAwY(n ;( 4 0 N ֆy.P?k*ˑẟw-qF'ݱ(NSiK".o'@e0mFoT#zoJ5|f)bgxG\)=K}6J~UƐɻy5d˜d97,g :/-ۺ(B8O1^C1i. wlC($5l l@b0mN 5P÷nRo\)=HQ|<o 6NƖm_is¬Wz2V*l ߠ o.~X4{-q&kҖeG'@a=^Ӱju.`u(wVGl'$e#C%o8 A  E'j´M$)0r@}Hf,h&Z}ӨŬ9yy8l${1jOYBLt!C(Zд`s3^FnUBnuk@g{EґzO=@E1ېwDsTW,X_ )_KV;^_V-ytӪ7}ֆ"<*V㑻Lu~օԏ_ѮQ7&^+H7YdP͏}X.clRk߇Ν'"I+l˹dnǓuUw%S0|^cla-`]|rGYj=-Ew_mOe"TUp>!h=wǯ[qRi ϓkK}L<}q>Jʆ;ɇ_6f 1Z~ڎK&ǭĶ.ϵ ]nӻt${0E]:ɽm# p*$Ӂ؍E+a8FPPKuNw_V-n9ߎnEY@N"Q.b>ɻ~X+9Ji7f0by_a ۟!_6 VW2!i#Q"y(Wo[>i@z-Z,\# $\6S)m*|>rmqI\b9:M%[qOoɺ}ڸk+GY+ =OTJΆtGhkŦv%dS_QD=K-*(Qӱui0A+"rM`:&n:W)-A:LڦCj.2Hu/%Ϯ1V$Sܻcn{QKn<.鳚eƳdH-T}?x mX'F7^]f|g&Eծh qLT }0W4~FϾ؆im`~\`YO+U'Qww[G/SuసBH +wl~j9$SMEC58csMGM1$!~ڙ,Dۣ/r:DæRˮ^$$8\C"sCNw iq)Z/⠱9\5>nILg<(״rGӇ*Ylwact*B SyH
      yO6;Oj ,.{CӘL›"w4j/SK)V ee13S}\ ^=z[]7xzmx4_κ)B%@&K‚շԭdt`;Ko!xbrHUN$TD4+ct|l{Ru^*)~қm_ՀjݨDx|1[o z0(Wumҫo Mɷ5 l$JS'7D0J@}Ol؝]U/ooZk=í Bbi,+`)EݥuOF !1=캰t>A:%(h6t@7KI_HFJ&&V4 m9"]~kΘIZƙ:U~n[4*O+dEס)f9X -E}uTF-jv}0W 479 #YNS_A%ѪT7W3{8IyD;)f (96XhѯǽЗnAxϨJKܵQor\o-ԻtokDGCa*xYHAªs QaP!]\yKkme5u$Kͱ'Tr Av=Džq<=+jpm쓱wՒ,"h+90g+;ͬdq ^/1YA{̒ w;<5Z>x#z,ݯV 1Ld3T>b!]7jHa?BG CX+1: +/m6F-S&OӢ?F,E;o"Kw@ gwz=~jSM5EE莧;}}̤`KU{b4^yO\[ &̮֘LmsR`b"jX37L~u\O؊Vc] .dXۓh^'M5Є7WE#MJn_mޒAk] &U700tf7mT1P)gU;^ Xٮ sq|[ȥҹA3R<⟐Zd)J~S Mmʜ~]U+gW=@{7{dӭn^w}w`! %"mH|JoqހQ-PU .4 ^>zA⋁x#;(z.Pp-38[fw5+uEa3Nej-6dO}Cj b9z =@iVkkma;T$ d-~=k7R86XY/?M}k T$Jc.x"*/~o5>Aݺo%1\L+&^YS-U_ . k;[ FiÁp*s>~R"*GUUG*{ 3ڨ\H_7w4rqQ+!Mkoբ`^`M5OJtr߿à˽R8'{ϵ=H[ k.[f;"*_rδj/)Z82/Z7OI:%UU"HyO|nxٜ>5(ިct 9OM|BEi1sHް&1CX)([E {da?4iZ珞2JGCϿ 1qyw>˟eu~# 8rKͨhket&Zb GЎ_CbBg=ӣp)=M[16؟y:m P;Xʆ~ZhET>"eH7gUx*Imiʽ,D1IawMC~ًw/$4W(F,oG::l#3vߗCLgaevXa&A K#=|{('ݛz^z{T0aHVi_2pBV=ob@o8~]eAaL"O6[FK/MxwY:^? ?|5MDKqPEQ U#m4:5$`km6Q=^ۦ]|*gw%%`Zmynspe䒅.-KD^<~6pdG A8P-8䟆gOЦ)5pzod^%oF9fWo>)LNB^V)X Fm49*#/LmC)P&S(Rz@(HڋBWz]j-lMO)ߵ:0EJ>3*W2w45u7h^ 6ZZOubD)qnT, (EnxǸfN b ;U~]#adx>;#H4)aLI6<~0^>"=t;=aꍅ/HoN; FPBL 9<Ц;c۸( gd/bX"׾8tfRlvl{ %[ @|W_"]O{pSE_EDw޵vj@ה4mgGixo+%0ӕ"4 h54Q̝;"\d8 ke9da(YΡf1#h-L:T#M3[ J6v"cYNA^^Zjۥs޵.O뙗^÷"&^^ۤ_LìD|nk&\ߠnUvy}ӰGտÿ Á@.7tȓY+xߋg ?BX&+RyԻp`B}4YOly^{[KjlY6؅Rװ0\<츶Qw5#Jng5%YGr9Ť虴 _U<įNsA|9﹈D n<&+م' ʷP%c"{(~rk LuLř1 o\jDIUIצ;Z/59k <,thFoBF"Fazm%"/8E{⊩LDtxM213PRKa%l߃|ԶXYBQT%j8n=Ӝ-S$/zY$5 ;&&m %'^&C} Җw[>ᘒ-mD*~8iWJ,;KAe\뻰c#Z* t[o_s'j>½RQgWfU=G̉1G[JPLG7 KĄmݔ +ٷ8ċP׵ L]+>c$AסNKQ ë"Wϓ[ եX,ygtlF;ĪQQe ٣,u6R>a$Q C}o 8<.p 55CiD^Z- G"G婫fO#b!]ɘ.Dcwk٦-`)'@0,b>Ԥ p6̬+ᮒAX8]"Wk^IJv@fRyɶ߂qq.hG3$NU#o83\%LAefqXGLm:j~;\7*1: TF&N`^$xB^* ^}g<"OZwE#kRBem3w?Y5vԳ^:~%(맷L3X8R9OeENAFfeiZR9bibO_ʨ%̑d`2'[u+n.y~^Ǎio[?!qМewL#*qC;2ɰ~8SdDŽOJc#qF]y}|,Dh^ IUY|P3QC)mWS`ߖ;"Vz=W6G]a;{e͚\oD^]wKE~ܧv t/ >t,t"gd/N[hg3ݝ=ŊL=Ԉ1EnU>}>8΋Ot g'ofBbX^nPRzɿ~wۯK BV^F"f4&[鰑BW)C'ϛFGt~p9aci͑ Tm~REgʾ&EP5:B؟ ٧M&\$f:|0 V\Yxd$Aq~ˆŐ20`_KyE#xYЄ[ހ:9t)uft̄Huk*TAHഡb #G^ uq鐼8h9,ȭ%!'0o8Oc0a={C`•4Nu߃& !,؇M᮰WI2;yJԞVGEqi s=LjS*X ,K(}u1P9˽0ץ;%H~{j4mܾErc#x;cMa dH'0mQ\V4(g81Zsж}ee)!Hwv+\zJ|Zx9=d} ?=%Ӑm"y& U8^B'zFroQ=,b Xz"dw'F"~نO1nr6jtU @5v'Z`KG"<4v(_bmVo(!ΗdV˷ iCQoE9PCwHnB% j"] ǏJ9Љ{sOri/ "Zmh^k Z0"=pۗ,Uj4Wb՛<8GbDyׯ!S҆k=J.D?K_n0~=bj#/i_*bl%h6~H:U?/(._wg`K_ E6Ehr=`b8S5H9rm mN(+w|Gt LF/bk|]ғ@'Tk^>ѫ $07wNgi O蚠>@T;P_B!o18N0A̶@!t'#+?Ͽ\֜ȶg1c >{o㗻|VLSx{ʛZ.YxJҟPk_N] ~$iDAl6Y+hnV/N T # HV Kl {ZCAXJEd&A+HՎ2.Ib9hvoV懡P{Tw`'9W BwP>h#i] ?0z8jkT@znPºx6Aշ{ÉE".0GVFJ N47031~CQ~PLh1oAFRIŐE'J15Гd§Jun"0!=su0=:U`FBC5 {G1!2t[X,`f֤/&f*Iۣ967E&.}Pb$ EBOtL{a݅:I'dY/D U&}WmFw;G=wƌ&Wŕ}Aa-)(}ڙV㗙$MDKR eCfF| W@YVKNɎuOIbQ>C݀Io /3JU?>3TXl}~ D%\`cmZ[?/wc2 ,z:F4/H?s cqзYǠ5'[P?@G*)_/Q6=ې&W92 }1du.Go {$BHNHާSA$c_tH7hq"/$"`yHYΖM97,;ڹ5P<f?N/ X߫UV[i{;6lA҃}a޻{#0jOʟ[MhDNǂr"Ia 8=zo7!+-R?KIx̮4ty=t܈"!&\ 8;FV9F(IZ8wuW#"eAN;T~3Jf_+;^7ykןx$FX4vb(|.ރɛ5Hs9(bu#+{'P:N\fJd2RMP~WnǛ\tN)9ͦ| ~PUY#0oLV\#jZﺴıڔ)u4 G"[UE'v3¼~ ߇qPUאg!>|KCU=c\dߵCj;J|%P Gfvb-âѶ>^Dh?x} *4T0ˊ抶?ʺxmN&u;Y 7eIgSBUszԻTzR; VEֈ2fSI$H܉brS }5E\vNhKt,C4T1\4·B3ղՇK%Ց5i9T YfVRv=}s ~O"* xEN\ 172iN0Isg|XB;U Q)뺥q,_F޳DƱ*@ܾEQh;Y_?|E*ܱoI^Ȇe킚&k^}o NMc$IN'1 eߩڂg\\8071Pq; m,3e\2BMcK֗}0M2a0WFuf2QQE+v) - |IKVD7s2ʌdžX*ght%fQyO+T'(`-s}~ =F=F BiJ}Êݼ*Eg"0ė|n"r`$ɪ¡ ]S,Ǹ㪯6ڋC4*yWVjYKGb#'G֥$0xP;uhALb؍ݕTxUy'_!uM]_g+<ߑuY}=|qUXⳕY?xB u]KZ ciASs#0o.^e)cAbѹ9nj;'C醂ƴ7e{ҟ$ HœeѴoaЭmZf{I%r}&R׋@ NE:zz{:n#FI Y7 =|"!w@?.-Po7R/Jt`xP]yb|kϧwo3iG:Au$(c~ LH(f6Njw "퉖OWUkF_Ү"؛!y]W+GdZpT QbY;h 갾_KQ|;۲Ƿ9Oe w\iػ܏h}u̳6n =^n O+- j:XhWKh;fU1#D$2+z+6 MMTק'~TA@դ5.:Ae7 %^,׽vGɿCW.Җ/ bfƽi zWf} BRϫ@Du=hPs=VSs(|uؠC!7邪*1]bGi97J ȹTK.tpcwUCM7N|qIFQOjOM8 iyb y"A !~h1𫞆OLDAcOX[ b5RC:+y&mf vݣ6~>66!:q0}]^#vJy!T,>Cr>Zcخ n2SH8^zPBN:]A68xhMÚ`I-qTv,!yCT?>-ZG`CYtߋoWgsQ&d\m'q+tEY'?Y0~66}FѪ #wZsԧ&g "ƿy(i6oHe钅bX45w\`."Hm`0dvl`et;t:Vdiwz1[ZJ rQq6<۠brA6 8IZ$EA)cz`<}+< zW8(HAd9~ۖWaZ/mȾ&LeGUqg5%'QgkU0+\,֕@0W7duz >[ѧCke쪀YeF;)$MbQ}!.hgCQ.X˭J12Իm̮SB9ac/H"{A=R{r9 <Oo$ &J[īwCu-+\} 9IUi7yZ Z9KCf./, l6\[ߚ~"II$\…IA[ms[8P\]ĺ !iT:;MM'SК_Wu4%dS*' S&IDWqK̏67?t@;?LnĎjMk}RH,.U:\eM^& #ٕ: POᑭhN m`*t# 4kv5ݸU:.4,cS: +6ytY Vj??#*@ݖWUaBgBܝ`yѱLQ-LX DMbׯQ[_B W *;uZ-~g'{6vUG}C yt3VKgMoSThC5!$}-sJ54u8޲1 5&}[,B}]MMA.p^5P0Y֣3,CCvX7RE :k#Jx{`5@tc:*~Ӿ(ew>MAѤ .f&Dlmȟg9XӼ~Fk<5jY֣B$ZAzZ uņһq(:t=%䰩1`m>Ο1%'bzJvXB12|Y$*5Fk#eud4\hq gNkN)ᬸqOo"Oo-)Y urjXP1$1d,.O&Ou/t|`Z|Lv}Ѣ Au>\H1`C-[ T7͞ `M˺}ݨ> m:8W7g( !61$u/vw`^=fd) U_"V~b^{oFTl^k< FݯAtK#aʘzgʧ1v6IΈb^٦0}추O Mzw8|gv1GS^\ͩOepSp]FX~LM A,t_%n]88mMNB+ 54[S9y]ddHQۡ>Y)ger 2݀#_CM\UQh0ቛcࠣFd-D&]UH$ƪ&ʂn&53;}hK=$JXy#>ヵ;J lRH"np[Ae2F fy?}l 0 &xǃI@4"sѧWZ0[|aeeؘ^$n.Իv[&d?aeD_Ȑz}אӗ&Nt SEp_yfIoVA6#0tEz.Iۥhp$h>_M+YmkSB5@@C,D<T@n2RWaVa͆,TS8==,7P$o;Fj1QyTkبmdYh JK1d­~5`7kR8{5ؔVos KqL$L':oL0ߚƤXR37)6+d5e>d*Wmq3M% -e a25d!S1+G*v{ex1N+ f47"_wckioޑBEBzl9RΜ  ԯމu$9ljV8'vP(՛okƩn_07~?vh,.4l]݂C=f3TFVOprǪ.a16_{ SLraXxs!&߿y~|DDdNYn޻Yew %6jP ͇r#i }_Kvun5qdlT K5t5 )D>W)mtccx0A;m~6M#ZM<a_`PG܎m#*Օ`#?s}k rT˦g,b))aw9>LTT->M+< ZLO^JxD7*Mq,64r}o %kPqŽע4#BM:G42LPLj&$"D㘩Y94I Vs{0̀CΞIo-57j?g<@TMϱ.-if 5{$K͛"\g Zc`Xl CE"w^jw7WD LYTt AGC!§tME{MSDwn^<fæxg >Ɔo$~4Z$.-y,>lRwp텎ctvW\0 1xoeowmFQՆ+aW,k}+F-lc~$Y7lK Dѡ?)w7eG֭Hސ>S-3~^(sUXwGŬsҸM A?Šx\hzXԳ.bJly/_Cv>f۰7̧V?,M] ;Gn?#ݒN: Q8G`0Pcp@xbKH#] |gn+LKdX6cwcdxi6u\&>C~",ΕdMxpJ,>|`&pFX '@Sk ~򄱸(<6]ܞՓ;s݆?Mď ʮ=+Y:-SrAټS}aupflX ]Gg2}0 "ny,w*A2Qg} $#0U7Β 1k8 Lmϧ:v$92ͽv#+vRmduÍnq:^IvjnǩuhQ!#uA}@И@#<^"BEIV.zJ'MWڍ?aq[,Ǭ!odf[gK~M_"bB9(1ްS&scy=-Eo6oM׆NBa͞{~!z9[š!Md|jt8"@@(5tc3,;\?.֫j O:\4o@l>kq3gVsj;6㞵?HkC=)h^,w1/<>eF}Q@JE`7:0/7L1%K9cDC֨A&Iubp?YI1F7??W3A_iT僶gOlķm ~24}R joX9ЇG^Tk<+خ>&kc.=|5+v\<Ť= V DUm[ﻼsWm!:kvצXH4a^̉2cnaVNu{gYJH|mܭ6փel|OԪc$=qDi%z,C*dK`Y3?1wqxQJ!disN8B#ߌ z@ќ2$|RODԃۧ\Z/N..+ɞThV eX,O0{:r񎹫'Em֊ڄt'i]*A𴘈wFֳ]T('yfG2Ko _Ef":tx4x`ٖ"m&71R^TU] sDMW|ʑ9lTl4ZxTYSNv%Jh`P8DnRIOkV0ȑ\Z͢v EkJ.EGt~Soak˽T6m䉴Nn+pLCU~ڙNrY BHdIIٴp3xtI'?Q-|^ǿ>rhyTtP8 s˅_h&fYa2!b}F^n~Z7?z|AuVbz)禍u`i F=CZ/5*QI W wBR J(Ӧ&~ kiF4p$݊o ]A@}&@3ws'lRrx`UB+kG|[dOk7hǙ~BFgftʳl_ׅ,D{B!@W*Py=gq`ȩ=J{Ĉ""f|gVn\ MdXΛh;87+bݦ|b^k Fe̘MtoؙWk{PiB!! zgX'i<DELz~ Ϊ4>=w?@<^| x0 Y5N]'Oh=;zmԻ*r0 d#ie5~#FݣBͿ +;l|RpB=; :{/ZZuvf,QwU^c}}9I9eSSd "ւpwWP\ d D">o ڝmzSuPn V ).[gK#H{k.?Әv)03/ l#0B85ʗO'_{n.Mlp8eFzuޛ(1wح@v L+a2 Y%vbY0\9ӶZ$[ހԦpPs.&kɟWY cS&&}15E~\L5^pK td,U bپuB%O2Adܮ\`^GQDYG6ٲܑSG0/NT:ݳ/j@8N|.ZfaNvS H8MCYHoʬYu?Qni0]?f'!K> R̀׺mr1$k:v9 5l`+\Y "7^+>Ku ;Won^ =}>srU ?\7bʳwfq yŀ[5Uͻ`*ǒVYB)BV/@bnX5^^Ұ$У܉`"ϧh|lσny31i۫'Z6'= ~@2Go^qg(-]◅iSD_wY%xhֲAܫr=\toBJOT F^ޫqITBMi<T~޴TxX;(dL^l%=M>φ00RI=\z qdim\RREq0#F=8飣8c)g-6PUmOȏ$=S^:c_!=7 ax}V]QΊ`Zho=X%-"^*- 䗻2<#V.#)a{ig5}!,'#ד`DS*Vdˡ$ʗK/qDq >aXrH@23vcmxb]P yl"_ Ȋu*) ]d;>>e@4Kwj=t 30jϚzP"|cgzIy)E\o3Pj ͩQw.'lq>K]P"ul|-0QnBL꽍Ɩ"})Y[n 6$bt~<`` Dk4 .li }5yn[лK$dWՑqe N`߀!ʾ 1mVogpS-zY|vӠM }~=!Q\,7Mu9=MI%褍y䫥z>5*lɏ\ydInN@o"-Χhv CꦃFƷQlЗ] /nPk"Y?1)R XT긋 WGz9zzfcR4hnY:g0C#`JS2GCL~!@:"Carv֙^Hn$s 襁_!K4۬9l`22*H~-[Yq!EYj1@+"M0pJ?r*W3Rz{v6Tbhpb++JM6Edp-]*JG"Œ$18u:*J*Q)ܨX8슂HCBz`bzicUT(gSat>TskeDv\{&Z’+ DoSq!"Wb+]!5&[ʧL];옄r+xzE #Sr}PG턢40|㪷`'{}S+i^qBK>l$J *s )eaLެ/|' &vda:{|6?-Se`Qho !Ubxt|;3'SM+enO<,~~;g)\I6Qzf 欯B ,3V>,8Q@9w@HQ˹lZ$zJ#h8w #c0}T`QS>ƦI;#h|wGQvPʇ[N 󟴼X,iyT8EO#Ci⨟3C x-|e `1 5)+m~X_z%sd+&㫢O*[BZO~8>fx; a^lc^i:4O&O Ą#|k~lc@nkN S!M3*Cҕ`"'1)9 V (m & =Bi|%:fvH)75Z9 pbY{8Wb3jEt21lб"}-C%ؔwx޽"D C7:,۵tTu]YmOq MOTIՋ_9j%\P5̢L(#fm!TV؛92]xC ^&pH6$Oߩ>F|nH0o3Wz5uM7uh3xj֏9zkcz!ҩ<s&\qeݹ'd\Z#GAJY H0>@Sv6LJg{szXRn`w Ej__tX4Eķ&OA(L@ GnK ~}~wsYkY8"cJ3>K@*`e$zӆI:3xpŞCy&;22kZ vjۃPͧ85`(1k*0d%K7V166Wh)Fl}9N0$HڢB;{2xm|Up87B(_Ɔ^vc%t8h[*%͍%t20<UoLִXmkȻ$u߽ : .#"j L $6˲frx6.RY4H?X{ߝx_Qt ]• G*WQ!s#Exoϖm3o7~W\{fUb]ӲcP>%_ _yA8⛀p [sY.ƽ=dY#GTAu*N{$,G>!Rw$)Fr}*1 b+N 2|c}Phi4:9&G0uŔRx&l8i=9SfHB%GPxJcKKirY,!?M 7 R"IqYwg8皟;e؜28D3oOfsWYUJ1@i[Ir 7myƦOHYĞ~6qz~sk{/dla~mu-Q[^7ߌ^d&P_OhTq[Ш`/bSNPwvl*khzOϟ]rf7D{+]x@=Ӓ!k|nOj޽ X`V/T꾃 s0/ IAiRJoz:/e E\q֘уu@PLEPSXdbuPK|b,}p…NjGy?v: ᚎ)lVof9lد&!P*hlx4oWƁ. 3G l!&_| \ME_Z\ 3K] a!ijwݒ#!'CAF;[ז@艈_򡅮LpBE! -')K]#n yзA ogT4$SAzbIT=sFs:}_+I _| ";}+޶WMҦ؀cwX+N/1Ⓚ43#o ZqТ7F/ۗT9#5 U:Reuz,ұ[Kuu:ܗAgJU(f ->ӞRF|Fe)*Nz1Z 9=-A &i<`__>}q*7O6Gm/S6"\WoyZטmq( lɇ|h(,,&n <.]v^$;oz+BQ4l{|8y 4}m&HVUUͲs"@6+p"`F;󀧲N+X>jˤ[EW>,ՇV1do&vFO Lڄ̀B'{DT/Exb{lQp4ʉ(QBn5 Rm(鬧5֩()/>i, pﬕ$r0m%DN]Rcm2}]:n A"=N Id 3AW5;Z$Q8 PrY XH= gnE)@𘟏'2U?ٺ~O$ٙӉWPyeol0R7'GTj2:gqzz-c9V0䟄%y_4k褹4b} c\ `bpJk4M4XC=ͺW! ( yTb1μV9U0Io|"8vX͡s뺚_c,@u'5q# >\l8:-࿽)VW%ˆ}i xg{E$Jq@KiR#ˌDGW1z[4<"ͪ{~5~Nz+ajH^f*kA}?L<-{7౺llxYg ǹz c4No*QqtI_Ѹ@ńW.ht  kܞ2wO}H0idKOY*c-P]|v^ccv 0 qk\ݤ珁n-c/L)MVO}trO2AgR}JýGLidtATkQB_ r`Ž/ @/oD*_)mlqnR]ߝn^{]3d2PAlo{,bs!CU6бT;E̚ ҉07LwKM? tv40X[mI<{)H _g|)QRqcT …H Iq|-A4(æA(߼ a{+7ȹ>hߴT,zc1z$> /)rV>/1 `AA 5#}Vj*p*{# BSӖ:{]z񺘈ם٧W y@ɦL 3zv@ lv虌[D@%yt`FS(Fjo4ifqNVhIQ##C\xvݙB'FjTie^߱c=5:1B1@yQT/׍-! BS2LLzON )t2/yv-,dp0#LOD">Fm'qS$@S7Ͳb pwL֩3uC1ye'l[obU/ UK@B眡~D{% ("G!lQR:h))y;/e`CW*^ՇGŬ۸OÔ/,O|Q${?mX,+꧓"Ny,$;r^Jai1UNB1tS|J/R; P=)`9+EID=h!b(U[MA$+69dL~NIXG:Va<$o?Jx{rX])!:Z(SE  Di풗B c;^7DEN颹&jY'x,oMiqsg(EtC,Y#<,?iiGtQlL[)+;"-Cm5*"% QLڬ5u._w؜QQ[4'bſVz% AfLJ~|p#19vҲs01=1.^CHGV.]QM}gB9IF-_5< 1;foXV=m ]}X*}lcOҺ `G_]r#Q."p%h"/%GgM'v-J^[$dbْbFnJ%qMaߺs̏(" ٕ"5sK{Dž+l>`(:kt>nUmKn0xMtHn{_-⭯,1VʶѨچRo4F>iUô9OB zq_#)ET_!knl=JlX,Oo \yl&wz7 롪$)+HI n&h'RAqVnc&v$ @F4$5e!$u؟50[)PAkmkkB@i;Z!u (ߔ?b'Չ P8`t_- *>d#9MS|gmy'wHӇ^W?W_5-PWJpm~vY=T?9p'ٷ`"F*qp]5qK_;O )"l9J0 ?[>^w- J75u3,QY-~Yħŏ9踦k߃&3nbqw`W7z+ ѓ5"`q5 ߳UIL{M>EEGXE1vs7 sj! 0>߫%ۅ{8'RZLy (>^y_C3#[= ?JXpp,L RL %n㳓#,@i/q`t Z3̗& I}'?t7A bi2wZnt>f*@"*"E1p1/˨t͍zWEh6z.Iq;FpD6 KmLPٹGׅ!sƒhC%^co=NK\Q#PVU^%㰞d.+8А (Rs0d>Szֳ͞g(hRҌԭD}tvfI/kGi"Hؤw!ft, r~2δS*L%bM>rt5ݻG.$ywx8}M^C@*%ri)3.--ǚ2W2R/M!L!~ E {6s$u"'3v9bNސn;0(e)|E$JUpIKliI=x"1e:ZMgX 25pwayL ?jI+efmyX;}d!0^C ^!u}ޜM ,Uba;I s-p祉Ẇu6P6q _hBfY>7`2-^Kݿe'}0YUҌl^ FA =Tkc+ <3;6"t.9dz`.6g~}QŸw#1ܦ;+Re{Ζ>ucmaGhXB:Q+*&V1^slsa;\JWB}_=4{jEI/|n+ ኞ߷^ 5 qA4bV\rmȃ?yK욍ₑ.$}DG0ԞxQ=N<Am3Bf+*ٙAm{PE pY=&uSP"1@lxżi-ӱC &b$ӷ6eQW /G/U@"0Sb |3X랤z,Ѹ7U}_8YC4}2;;V> MKyi"~[Uf锅LPL=kw A=Ǎ v NUxғe$ 7,eiQRj ^w{vtܩIteCgW}mx%n[/g>ɝF0'9•r(lT`}d*,([S>_єS{>ӿ 0?ødP^B1 D&\7?a4"nLMa6;lw}Gׇ uRnTvղPNrT]x"9`o=|yVIڲI *UfeGhݯI8ɺ'vRc-Ep W)`h0v(%K-Lmm'(ϐh}u b>l\ ~{=O\>}B7!-s|^9vW#֚<6DX01x@ _ 쮴zǺ4DlO[A/] HWS„.᧳K2P,?AVi"97w%r&sٯ_>"\a[ dĢp$|AI Bӽ^ YO}dZ݈}#{Gx%Eezjzrn`s:x'L=B P/#udB(*(rqWvxn:2,6>į]rqSԁlݪQR0lvѩZx!^8-F 7o;V(:& qeB%| &.ȭoxS "U`ېD|#*y%Ofr ]=G’B=KЬbﳍu9bL$xUץ.kӈ&4~zA.06{kĄ*%p+T/1y@Pt n!,ڤëq2&=B;>y*!yKuLp+D$I 2PЗ-?t~~I6ᑑY^9a(w4 Ve/ t8WY|֋._d2X6i Z6bJ\AE<a[s>p OmBn;5I{S jŐO:Xc8RP֊6lv:b¯t u=_Omy5‗Ea%+2ir<9^Ty6+$ z~r̥M B&n#BbQ-n3-{ ԓK&}u`,x{J/@elJYn!@$Ⱦ5`} ;nf"; "k MTP a 8b;Aa'r?QELv=-&,(wס SevB٣|}r*E]tڵ&+|h`L` MA v4;~֋0"A5*bDtjT]ܠeJ ऄ>o٪Lԅx-*.^N0mp6 0N&dMo/<KY2abd㽛tjho19fܱN{\b.[/NHy3[qzX!uj6.),=لΗo =IM8,%^!̢$@Af9lvs~ x oԧ9m* *Pn޻fh`Z&qw&KyC6Z 5u=>B1:BE6`?^z KDwFxYA}6Ѳ ÎrjO#[UٓӇWBr͔O($PՁ1B+ T֒~) @.^h\vA2SxZX.j(A~kWqi0t[/}~!="HY9)ױpC$ܢ-Yqޣ&Aiq].YqU~4E _\j~@2PnSFS#8Sw^miCt{K9W Up\cudȌ |䕉ǎ(0c5t  k*. XY6*`-âw6Ր0N>MY\s=' Y)֘P/<v_.mSDW:a lKjh>xT OsF|җeئSr)y#zg4GK+ ڦ[) ? mps?J?9.~Ui͒"ЯBHTCK|(භ:S#dNtVφYj 3լ=Pq7bOfWPRox&)7&Rꯪʩxcj32Ylm">|tn%8"}b`e::n98&w6dB%؏7Ŏ:VnPj kFTgϙ1` a[P] 2_KG.cV2gJrj--FӇMܱL E.Űzə? i~vMor?`86h('^AW:#b u{kҟL!otܪ -B UAٚ^bh&&ZO,iz0/ЮSs1XsKDU Ţ lo+a~ڀ`IbM h<נvC[ E0ZkE)%;ϊ$mz듹2V)6QyoVuFzh#d3r"c9V{'az ot͟#SsM\ڍ{|'.8ETǑ>N?ޏ.\qݙ"C_p7877m~ݛ(7>4Qݔb&YW ܶ(+_' ޼N6/Sw'&G_kFͥ*ycñIt%}Tڷ(dm>q9qdd'Zĥ :c ѷP(" Aً1Eٖ[>aK'ʠ!J cK%ΪJYS̍C? F />AxX8dikͶ)RFaJޏ!4x9q MOZf ]zP8kGd6c dh\ߞ;S6p$ N\lA K5Dcy$682cH%'l0te-5t aKcxX&bOkg.O L%; &ދPXL찪uZV5vz?t< {P!,.iQǢ}YJr uuQm#YK54]6.~ ڊv알k e2$AäF-JKnfUIL1dntdzF#9^ \oZGEi7C,2Wz i6K  *ݵz$sn ^@֪acɶuLeZ 3` nKJkH8]K#pSJ.2D:en#=`6cv?|Z96zS̛D~sXj1ֱSsWh3gv{eG0Rg3z?5D*L8[Չ8~*wN0`$ Uˆp[ L@R"dĩEm WT9 \srIsFA"͛(rLmK#^\g4nwdlM|mN_R0se.O 20zްUDBfh|q3%XnN(v(8Q@Yʣ1j݄?:J.M!J@@,`G)LB}m2rn17 #d(z276i͈U:S|i;ao\O=eE!*H̜C|TS'?%UiL3l.ŁTʷaDK{tϧ/d XӱRLM !✘ԫ0V`wp J2Ols<ՠBz#8t 9L)9X J*#6QiMb'FM+_.2f:ChuHzOmi+)ڥtY6i 0ׂ{{`P 9 0^@kpA,wc4/atZH("N-^D"Ɯ/@UcLl_F-g[Z6i)тp)T#Qb[d;@dCe:N\-rl L*p[v/[7 HC]yU]ZRoojb -RF :XLtĕMvC|,"r s, RլC5h(Tlӥq#<ᙱpO$Jz2Vf^2;/."`e3&yg/Lt y[v,+Zs!j&N.SX7MkH|ѴbrVMCӄR{I漙M@9nעo]dJYgv*'&m1 80OrAt̖ ͬ=?J̫FsrKv6Z$YlZ o mrH"˩t2B+" szsL^Xu4+>cݣ`n5z5k8*Mk9Rzu^1 4-sԍkhu$//uxLCSLp3Ȝ9 B 2{qxɪ)w3f}fgZxh a-^')m- Թ)h6^u41Eţj^Ҹ1*u1Hі+{kUxp hl_|G /-LuݣG_9!)x]nH )kjC=R96\ rdHnze[ר u&vTHe0RIix3o'ɔnދtѮՎV#m6 QAgU$1?$p?/j]$;ItuF:^-l<)C850X1AWB2J4h-_aB6ٰGQ L{ꦑ3o:&nGX-N݅[w77+Cx^ltĎCtJk;BWFb$zsSȪ[O7}¢":Zѐ7AqIQbǺo$ޝG?nDWF{qa&EY|Y}3>qcq} ZF$[o[zM{Մ“맗OڋRCGqmV;v}3?K}$J Dg#{Vn4;u; iN=~1$[r+^to~#I$(?s} CG_p_Ǫuw?GO@OvG_; #/ϕ5^ kTvvEnOm-7lݑ士}Lzށʻ!BPx[2 IilZӣ[g$*Gl^ęYqGv5/tKӽMc$4ƅ.Ћ|eyǩΒԽ9aC㓇w2~1}{D>qaaeCwL 4O)66mmڼc^+A/AqNվsq@4$<1ސ tqkXNe^g%BJ n_K(䕺J l\N7™|%Ov\GҧrK E["-Т2E`}/ TxО/8,5%\<{j/1 ~|RVI>W_<[]rvT;qwCuǼR]섞]=U2?Pͩ.dA77=YuETe NUvd!.~d?qwCv罒]]=U2?P.!F?Lv|ϒsZ;o!_uA>$g.qd8qwCv罒]℠]=U2?P.!ɲbbGe&'H}Ede ɂ_u@s]K]OݰGz44}T}3z_Fڿ9$=]໿^lkҋ[~:Jk^^_M_[wix:*|4|4^)92?i79!cw9rgМܕ௡9+h@i΃x@Uz܋ۚ{Ž\/:;^p&@_QWz?݄v˞ҝ3ާ`s(~0u^b/cp2?G#z/轢@1i|<G'˄[:4},i^狨@o^,M| M| ,GqnjY2xz{~x >s`ӭߘx$d9t<;QZטMЫ>H %ˡ$$NQ%Ot4k_@IZEA:{y^w$ɧ @<}Ir I 9| 9^!H|h 9|$Al%@Ϝ3 HB ye _ $J;I @+sg_H>s0ysK?lwOKп%2.WpE~D)JW''U_J;]P򙣵tu/KBo7+ ;Q'P:)BKL)LCo;EI P+@s^J>s*%AK$qY`yH ~`bKRsV kI$~ yr8= $9s!~QD$/J( _3ɗ~'L4y-0IS$&Oջh^}'Lϭ s&2|iawoPr a:9Qo 3}gNt6\/ `+I  x $x8yr '9}3N>s$ԅqrx~=&?`` Z`򝞾];sE~[䋧 = v~N>k;ɿn18:zPK!rgLχ^PK fAq)projekt/anwendungen/EigeneAnwendungen.txtPK fALUprojekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow$1.classPK fA S;projekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow.classPK fAPcoRx projekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow.javaPK fA8;Aprojekt/anwendungen/filius/software/clientserver/MeinServer.classPK fA@U@projekt/anwendungen/filius/software/clientserver/MeinServer.javaPK fA^Lprojekt/anwendungen/filius/software/clientserver/MeinServerMitarbeiter.classPK fA\jK#projekt/anwendungen/filius/software/clientserver/MeinServerMitarbeiter.javaPK fA!rgLχ^projekt/konfiguration.xmlPK ʦfilius-2.9.4+ds/src/test/resources/empty.fls000066400000000000000000000216361506574241500210700ustar00rootroot00000000000000PKP)projekt/anwendungen/EigeneAnwendungen.txtU1!Sxlee,4H X=q{r#2h5&b.u$U|՚ᝡs2_S`}X|뚸T%PKqPKPUprojekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow$1.classT[OAux b VQhbPugJ+>೉VٿdbƁrތZdvJa* pa7\p vhc z5:Z15 ?%S<\G ই)L{a㔫x4R" B3CJ á"[zp]`賵"y(ʕH 6mVr$&a2*BؓC71e+SX4SC ']5lf*f5 V:RkdA 4u _S΂xadWhe樭c-mɓ-̽(&tBFܺ_7RI%Klpع!Qrѵlx8l9L0"`O/q+wwpi S./PKLPKPSprojekt/anwendungen/filius/gui/anwendungssicht/GUIApplicationMeinServerWindow.classV[U]H0ZEM+VRCԥN 3qںպkZ >>y$`iZs9_8b؈u]Z ca"# $7C s3ұ{ɨ1 pXtV x:kPr:7aeϒܨc)ߤcUl5p9c@w@2%6ȜTH/awsyJ3<^0"^22^1*^3:X\: \3T0GzgLoxG W0ST drV~[ʜRϭe%;&MOӶEe8E3aG6b|Z0L{msjRxAz6Y3yLQ\3cZ])_sBn+|-vFLoO<ւӀ7N瑆9Z?gY mg,N)mOXcK/؎'А&֕Q';-o6.xGl1z^6S )V 1Y'SgP}DE|EX@9cp+fT\ݮ<9$=OfYn4{2FUR@T(>s訌 %rK),]Dw)k"i}\> '$&~.gFzp2.A7=U^s2MtQ&֨vT?`A[{GUmT7Q0Z\ ~_UUX4Q 4BPSB3 o ypd 0vO36>E4i<GC,`hLhM'м-Z2_JEɍqj[-`Y<<p4Tg`UdN'ӫ8cky\@c _yo;Dki Hk#EfD壃uΒVZZl-;C]9xnc>tR'&FV(SfϥC.+5.)eLVKI 7d%J,|et<Ul(ɜ>QYdFBrH U*ȵAmp$鈥G-I{ષD kigb% ]eg.$yh>&VoiX̡8hڈpL?;:}Pg@9PtdՈKras4h˼p_jSQw2I#<._U[]J]5 vR>q8Gx,8441^|66$+h) =G@JEG1]ɬ&%5(iGbe2j v15ER.1/.- Qd>}v:I p.Hjע / &C[Aӯ7ںM&K[\G,}^f.GȽ58Ћ闉r(?*fGku=jjW)2$gS?̟~9.0iHO ))yWI٭ a*ǘ$1iuS:D_|zyL~.LJ[ mS,L<8YWp5\a?SZ ^[rHPiK")y,)+X*+ BoRUa]adhvfloxÆQO8d$(+L,_*g4GRHi7ŎcO7 KTIm5Epf[ 0;ws5UX^]B3޺9詭Sj]?ª4Nd2T0=I0;LK^_7eLث g(Ē +5 Ss%!H`{tM!Y/=TqͣЈ,7T>*Q[;|q8m-<&'TSL2OPK8;PKP@projekt/anwendungen/filius/software/clientserver/MeinServer.javaUn0}ц|@J^)Q ]mvaכ&U(k\9saVJg˺WCC{jYy+Ww|`-|W9\>k |s{e%H!b&+߈Cr[ X=teA|@!N<8' Og*M/.`Vl6Sc*[4𶸫~bW@xA_W?9̴EM; ÙbDDA-v~T O~-R<*\ [򬌌nAT"oϜoGt32wS mb]DpM8Qpf,=g(\;gfB6?MJ,z\'9S3G:ADJ;}ʰܸ?qBnDbC3}NÄ2y{#^T%Ĥc}BOZ\szi8L@Pm /LޒTZ=Tn䋥(^8UخEMcg$~%)ј|WQ`*OTRXۨN#r(cR߼w/,ĩO[>%l1= }/-/#O/ YBW2As֌Ztü^J*ŭU:2E#&oNwgմp KxS#󁥙 Q lIFe *p:Lv{);2tJS"hK+5che2++l!ezx8۔e"z5߯&K-e'R<}ceEct7lR&J!e6NnSD͐]XL_/ro9z'RNRd~t L`3`M Q:!IF:?kGE# cD biUP[Lb]2/ L AT@=)w# - hli 4H\0 o1C7n38p;EPK^PKPKprojekt/anwendungen/filius/software/clientserver/MeinServerMitarbeiter.javaUn0 }&ޛ.݀7٦m!lHtmȿ\Ž(l* ]%-h,&YѐCF{EZK'*Jg%6[!*+Xtwj V!-ں$9hMQSoJXP+49xRYe%4+#K,9!ɐ]L.\2]9I'+*OFlAloqT7h@%U#{n0s_$>L2F@]xqßS#J,IlPh8jE/J䏺eh0BWmK4&NAMZs=Qk rʁy]vNB#wiQק l]prQYV {o[x1%)+Z+jm?=mB,h EGiD6߹фQZ{Zp#=W:{,*&B܈:Uk'&OgɽE[aLG8d&w0^,7d&O}J}!%H12̒Ho."EH)޶aCZx}\߲&Gws=91I ;Ҳ];y+sxvYXSk! %*cb~vpps︫07o\yfb!юw !llUe¤DzD`|9FPK\jPKPprojekt/konfiguration.xmlM 0\su+:ulMئԿO!^< ӹ2@Ƹ[sB!ުA}ikȀVQ CHƚ>dCJ! .yQǨZ]ǟyEi]7҄?5k